Blender  V3.3
editcurve_pen.c
Go to the documentation of this file.
1 /* SPDX-License-Identifier: GPL-2.0-or-later
2  * Copyright 2001-2002 NaN Holding BV. All rights reserved. */
3 
8 #include "DNA_curve_types.h"
10 
11 #include "MEM_guardedalloc.h"
12 
13 #include "BLI_listbase.h"
14 #include "BLI_math.h"
15 
16 #include "BKE_context.h"
17 #include "BKE_curve.h"
18 
19 #include "DEG_depsgraph.h"
20 
21 #include "WM_api.h"
22 
23 #include "ED_curve.h"
24 #include "ED_screen.h"
25 #include "ED_select_utils.h"
26 #include "ED_view3d.h"
27 
28 #include "BKE_object.h"
29 
30 #include "curve_intern.h"
31 
32 #include "RNA_access.h"
33 #include "RNA_define.h"
34 
35 #include "float.h"
36 
37 #define FOREACH_SELECTED_BEZT_BEGIN(bezt, nurbs) \
38  LISTBASE_FOREACH (Nurb *, nu, nurbs) { \
39  if (nu->type == CU_BEZIER) { \
40  for (int i = 0; i < nu->pntsu; i++) { \
41  BezTriple *bezt = nu->bezt + i; \
42  if (BEZT_ISSEL_ANY(bezt) && !bezt->hide) {
43 
44 #define FOREACH_SELECTED_BEZT_END \
45  } \
46  } \
47  } \
48  BKE_nurb_handles_calc(nu); \
49  } \
50  ((void)0)
51 
52 /* Used to scale the default select distance. */
53 #define SEL_DIST_FACTOR 0.2f
54 
58 typedef struct CutData {
59  /* Index of the last #BezTriple or BPoint before the cut. */
61  /* Nurb to which the cut belongs to. */
63  /* Minimum distance to curve from mouse location. */
64  float min_dist;
65  /* Fraction of segments after which the new point divides the curve segment. */
66  float parameter;
67  /* Whether the currently identified closest point has any vertices before/after it. */
69  /* Locations of adjacent vertices and cut location. */
70  float prev_loc[3], cut_loc[3], next_loc[3];
71  /* Mouse location in floats. */
72  float mval[2];
74 
78 typedef struct MoveSegmentData {
79  /* Nurb being altered. */
80  Nurb *nu;
81  /* Index of the #BezTriple before the segment. */
83  /* Fraction along the segment at which mouse was pressed. */
84  float t;
86 
87 typedef struct CurvePenData {
89  /* Whether the mouse is clicking and dragging. */
90  bool dragging;
91  /* Whether a new point was added at the beginning of tool execution. */
92  bool new_point;
93  /* Whether a segment is being altered by click and drag. */
95  /* Whether some action was done. Used for select. */
96  bool acted;
97  /* Whether a point was found underneath the mouse. */
99  /* Whether multiple selected points should be moved. */
101  /* Whether a point has already been selected. */
103  /* Whether a shift-click occurred. */
105 
106  /* Whether the current handle type of the moved handle is free. */
108  /* Whether the shortcut for moving the adjacent handle is pressed. */
110  /* Whether the current state of the moved handle is linked. */
112  /* Whether the current state of the handle angle is locked. */
114  /* Whether the shortcut for moving the entire point is pressed. */
116 
117  /* Data about found point. Used for closing splines. */
122 
124  {HD_AUTO, "AUTO", 0, "Auto", ""},
125  {HD_VECT, "VECTOR", 0, "Vector", ""},
126  {0, NULL, 0, NULL, NULL},
127 };
128 
129 typedef enum eClose_opt {
130  OFF = 0,
131  ON_PRESS = 1,
132  ON_CLICK = 2,
134 
136  {OFF, "OFF", 0, "None", ""},
137  {ON_PRESS, "ON_PRESS", 0, "On Press", "Move handles after closing the spline"},
138  {ON_CLICK, "ON_CLICK", 0, "On Click", "Spline closes on release if not dragged"},
139  {0, NULL, 0, NULL, NULL},
140 };
141 
142 static void update_location_for_2d_curve(const ViewContext *vc, float location[3])
143 {
144  Curve *cu = vc->obedit->data;
145  if (CU_IS_2D(cu)) {
146  const float eps = 1e-6f;
147 
148  /* Get the view vector to `location`. */
149  float view_dir[3];
150  ED_view3d_global_to_vector(vc->rv3d, location, view_dir);
151 
152  /* Get the plane. */
153  float plane[4];
154  /* Only normalize to avoid precision errors. */
155  normalize_v3_v3(plane, vc->obedit->obmat[2]);
156  plane[3] = -dot_v3v3(plane, vc->obedit->obmat[3]);
157 
158  if (fabsf(dot_v3v3(view_dir, plane)) < eps) {
159  /* Can't project on an aligned plane. */
160  }
161  else {
162  float lambda;
163  if (isect_ray_plane_v3(location, view_dir, plane, &lambda, false)) {
164  /* Check if we're behind the viewport */
165  float location_test[3];
166  madd_v3_v3v3fl(location_test, location, view_dir, lambda);
167  if ((vc->rv3d->is_persp == false) ||
168  (mul_project_m4_v3_zfac(vc->rv3d->persmat, location_test) > 0.0f)) {
169  copy_v3_v3(location, location_test);
170  }
171  }
172  }
173  }
174 
175  float imat[4][4];
176  invert_m4_m4(imat, vc->obedit->obmat);
177  mul_m4_v3(imat, location);
178 
179  if (CU_IS_2D(cu)) {
180  location[2] = 0.0f;
181  }
182 }
183 
185  const float pos_2d[2],
186  const float depth[3],
187  float r_pos_3d[3])
188 {
189  mul_v3_m4v3(r_pos_3d, vc->obedit->obmat, depth);
190  ED_view3d_win_to_3d(vc->v3d, vc->region, r_pos_3d, pos_2d, r_pos_3d);
191  update_location_for_2d_curve(vc, r_pos_3d);
192 }
193 
195  const int pos_2d[2],
196  const float depth[3],
197  float r_pos_3d[3])
198 {
199  const float pos_2d_fl[2] = {UNPACK2(pos_2d)};
200  screenspace_to_worldspace(vc, pos_2d_fl, depth, r_pos_3d);
201 }
202 
204  const float pos_3d[3],
205  float r_pos_2d[2])
206 {
208  vc->region, pos_3d, r_pos_2d, V3D_PROJ_RET_CLIP_BB | V3D_PROJ_RET_CLIP_WIN) ==
210 }
211 
212 static void move_bezt_by_displacement(BezTriple *bezt, const float disp_3d[3])
213 {
214  add_v3_v3(bezt->vec[0], disp_3d);
215  add_v3_v3(bezt->vec[1], disp_3d);
216  add_v3_v3(bezt->vec[2], disp_3d);
217 }
218 
222 static void move_bezt_to_location(BezTriple *bezt, const float location[3])
223 {
224  float disp_3d[3];
225  sub_v3_v3v3(disp_3d, location, bezt->vec[1]);
226  move_bezt_by_displacement(bezt, disp_3d);
227 }
228 
232 static void remove_handle_movement_constraints(BezTriple *bezt, const bool f1, const bool f3)
233 {
234  if (f1) {
235  if (bezt->h1 == HD_VECT) {
236  bezt->h1 = HD_FREE;
237  }
238  if (bezt->h1 == HD_AUTO) {
239  bezt->h1 = HD_ALIGN;
240  bezt->h2 = HD_ALIGN;
241  }
242  }
243  if (f3) {
244  if (bezt->h2 == HD_VECT) {
245  bezt->h2 = HD_FREE;
246  }
247  if (bezt->h2 == HD_AUTO) {
248  bezt->h1 = HD_ALIGN;
249  bezt->h2 = HD_ALIGN;
250  }
251  }
252 }
253 
255  BezTriple *bezt,
256  const int bezt_idx,
257  const float disp_2d[2],
258  const float distance,
259  const bool link_handles,
260  const bool lock_angle)
261 {
262  if (lock_angle) {
263  float disp_3d[3];
264  sub_v3_v3v3(disp_3d, bezt->vec[bezt_idx], bezt->vec[1]);
265  normalize_v3_length(disp_3d, distance);
266  add_v3_v3v3(bezt->vec[bezt_idx], bezt->vec[1], disp_3d);
267  }
268  else {
269  float pos[2], dst[2];
270  worldspace_to_screenspace(vc, bezt->vec[bezt_idx], pos);
271  add_v2_v2v2(dst, pos, disp_2d);
272 
273  float location[3];
274  screenspace_to_worldspace(vc, dst, bezt->vec[bezt_idx], location);
275  if (bezt_idx == 1) {
276  move_bezt_to_location(bezt, location);
277  }
278  else {
279  copy_v3_v3(bezt->vec[bezt_idx], location);
280  if (bezt->h1 == HD_ALIGN && bezt->h2 == HD_ALIGN) {
281  /* Move the handle on the opposite side. */
282  float handle_vec[3];
283  sub_v3_v3v3(handle_vec, bezt->vec[1], location);
284  const int other_handle = bezt_idx == 2 ? 0 : 2;
285  normalize_v3_length(handle_vec, len_v3v3(bezt->vec[1], bezt->vec[other_handle]));
286  add_v3_v3v3(bezt->vec[other_handle], bezt->vec[1], handle_vec);
287  }
288  }
289 
290  if (link_handles) {
291  float handle[3];
292  sub_v3_v3v3(handle, bezt->vec[1], bezt->vec[bezt_idx]);
293  add_v3_v3v3(bezt->vec[(bezt_idx + 2) % 4], bezt->vec[1], handle);
294  }
295  }
296 }
297 
298 static void move_bp_to_location(const ViewContext *vc, BPoint *bp, const float mval[2])
299 {
300  float location[3];
301  screenspace_to_worldspace(vc, mval, bp->vec, location);
302 
303  copy_v3_v3(bp->vec, location);
304 }
305 
311 static bool get_selected_center(const ListBase *nurbs,
312  const bool mid_only,
313  const bool bezt_only,
314  float r_center[3])
315 {
316  int end_count = 0;
317  zero_v3(r_center);
318  LISTBASE_FOREACH (Nurb *, nu, nurbs) {
319  if (nu->type == CU_BEZIER) {
320  for (int i = 0; i < nu->pntsu; i++) {
321  BezTriple *bezt = nu->bezt + i;
322  if (bezt->hide) {
323  continue;
324  }
325  if (mid_only) {
326  if (BEZT_ISSEL_ANY(bezt)) {
327  add_v3_v3(r_center, bezt->vec[1]);
328  end_count++;
329  }
330  }
331  else {
332  if (BEZT_ISSEL_IDX(bezt, 1)) {
333  add_v3_v3(r_center, bezt->vec[1]);
334  end_count++;
335  }
336  else if (BEZT_ISSEL_IDX(bezt, 0)) {
337  add_v3_v3(r_center, bezt->vec[0]);
338  end_count++;
339  }
340  else if (BEZT_ISSEL_IDX(bezt, 2)) {
341  add_v3_v3(r_center, bezt->vec[2]);
342  end_count++;
343  }
344  }
345  }
346  }
347  else if (!bezt_only) {
348  for (int i = 0; i < nu->pntsu; i++) {
349  if (!nu->bp->hide && (nu->bp + i)->f1 & SELECT) {
350  add_v3_v3(r_center, (nu->bp + i)->vec);
351  end_count++;
352  }
353  }
354  }
355  }
356  if (end_count) {
357  mul_v3_fl(r_center, 1.0f / end_count);
358  return true;
359  }
360  return false;
361 }
362 
366 static void move_all_selected_points(const ViewContext *vc,
367  const wmEvent *event,
368  CurvePenData *cpd,
369  ListBase *nurbs,
370  const bool bezt_only)
371 {
372  const float mval[2] = {UNPACK2(event->xy)};
373  const float prev_mval[2] = {UNPACK2(event->prev_xy)};
374  float disp_2d[2];
375  sub_v2_v2v2(disp_2d, mval, prev_mval);
376 
377  const bool link_handles = cpd->link_handles && !cpd->free_toggle;
378  const bool lock_angle = cpd->lock_angle;
379  const bool move_entire = cpd->move_entire;
380 
381  float distance = 0.0f;
382  if (lock_angle) {
383  float mval_3d[3], center_mid[3];
384  get_selected_center(nurbs, true, true, center_mid);
385  screenspace_to_worldspace_int(vc, event->mval, center_mid, mval_3d);
386  distance = len_v3v3(center_mid, mval_3d);
387  }
388 
389  LISTBASE_FOREACH (Nurb *, nu, nurbs) {
390  if (nu->type == CU_BEZIER) {
391  for (int i = 0; i < nu->pntsu; i++) {
392  BezTriple *bezt = nu->bezt + i;
393  if (bezt->hide) {
394  continue;
395  }
396  if (BEZT_ISSEL_IDX(bezt, 1) || (move_entire && BEZT_ISSEL_ANY(bezt))) {
397  move_bezt_handle_or_vertex_by_displacement(vc, bezt, 1, disp_2d, 0.0f, false, false);
398  }
399  else {
401  bezt, BEZT_ISSEL_IDX(bezt, 0), BEZT_ISSEL_IDX(bezt, 2));
402  if (BEZT_ISSEL_IDX(bezt, 0)) {
404  vc, bezt, 0, disp_2d, distance, link_handles, lock_angle);
405  }
406  else if (BEZT_ISSEL_IDX(bezt, 2)) {
408  vc, bezt, 2, disp_2d, distance, link_handles, lock_angle);
409  }
410  }
411  }
413  }
414  else if (!bezt_only) {
415  for (int i = 0; i < nu->pntsu; i++) {
416  BPoint *bp = nu->bp + i;
417  if (!bp->hide && (bp->f1 & SELECT)) {
418  float pos[2], dst[2];
420  add_v2_v2v2(dst, pos, disp_2d);
421  move_bp_to_location(vc, bp, dst);
422  }
423  }
424  }
425  }
426 }
427 
428 static int get_nurb_index(const ListBase *nurbs, const Nurb *nurb)
429 {
430  return BLI_findindex(nurbs, nurb);
431 }
432 
433 static void delete_nurb(Curve *cu, Nurb *nu)
434 {
435  EditNurb *editnurb = cu->editnurb;
436  ListBase *nurbs = &editnurb->nurbs;
437  const int nu_index = get_nurb_index(nurbs, nu);
438  if (cu->actnu == nu_index) {
440  }
441 
442  BLI_remlink(nurbs, nu);
443  BKE_nurb_free(nu);
444 }
445 
446 static void delete_bezt_from_nurb(const BezTriple *bezt, Nurb *nu, EditNurb *editnurb)
447 {
448  BLI_assert(nu->type == CU_BEZIER);
449  const int index = BKE_curve_nurb_vert_index_get(nu, bezt);
450  nu->pntsu -= 1;
451  memmove(nu->bezt + index, nu->bezt + index + 1, (nu->pntsu - index) * sizeof(BezTriple));
452  BKE_curve_editNurb_keyIndex_delCV(editnurb->keyindex, nu->bezt + index);
453 }
454 
455 static void delete_bp_from_nurb(const BPoint *bp, Nurb *nu, EditNurb *editnurb)
456 {
457  BLI_assert(nu->type == CU_NURBS || nu->type == CU_POLY);
458  const int index = BKE_curve_nurb_vert_index_get(nu, bp);
459  nu->pntsu -= 1;
460  memmove(nu->bp + index, nu->bp + index + 1, (nu->pntsu - index) * sizeof(BPoint));
461  BKE_curve_editNurb_keyIndex_delCV(editnurb->keyindex, nu->bp + index);
462 }
463 
469  const ListBase *nurbs,
470  const float point[2],
471  Nurb **r_nu,
472  BezTriple **r_bezt,
473  BPoint **r_bp,
474  int *r_bezt_idx)
475 {
476  *r_nu = NULL;
477  *r_bezt = NULL;
478  *r_bp = NULL;
479 
480  float min_dist_bezt = FLT_MAX;
481  int closest_handle = 0;
482  BezTriple *closest_bezt = NULL;
483  Nurb *closest_bezt_nu = NULL;
484 
485  float min_dist_bp = FLT_MAX;
486  BPoint *closest_bp = NULL;
487  Nurb *closest_bp_nu = NULL;
488 
489  LISTBASE_FOREACH (Nurb *, nu, nurbs) {
490  if (nu->type == CU_BEZIER) {
491  for (int i = 0; i < nu->pntsu; i++) {
492  BezTriple *bezt = &nu->bezt[i];
493  float bezt_vec[2];
494  int start = 0, end = 3;
495 
496  /* Consider handles only if visible. Else only consider the middle point of the triple. */
497  int handle_display = vc->v3d->overlay.handle_display;
498  if (handle_display == CURVE_HANDLE_NONE ||
499  (handle_display == CURVE_HANDLE_SELECTED && !BEZT_ISSEL_ANY(bezt))) {
500  start = 1, end = 2;
501  }
502 
503  /* Loop over each of the 3 points of the #BezTriple and update data of closest bezt. */
504  for (int j = start; j < end; j++) {
505  if (worldspace_to_screenspace(vc, bezt->vec[j], bezt_vec)) {
506  const float dist = len_manhattan_v2v2(bezt_vec, point);
507  if (dist < min_dist_bezt) {
508  min_dist_bezt = dist;
509  closest_bezt = bezt;
510  closest_bezt_nu = nu;
511  closest_handle = j;
512  }
513  }
514  }
515  }
516  }
517  else {
518  for (int i = 0; i < nu->pntsu; i++) {
519  BPoint *bp = &nu->bp[i];
520  float bp_vec[2];
521 
522  /* Update data of closest #BPoint. */
523  if (worldspace_to_screenspace(vc, bp->vec, bp_vec)) {
524  const float dist = len_manhattan_v2v2(bp_vec, point);
525  if (dist < min_dist_bp) {
526  min_dist_bp = dist;
527  closest_bp = bp;
528  closest_bp_nu = nu;
529  }
530  }
531  }
532  }
533  }
534 
535  /* Assign closest data to the returned variables. */
536  const float threshold_dist = ED_view3d_select_dist_px() * SEL_DIST_FACTOR;
537  if (min_dist_bezt < threshold_dist || min_dist_bp < threshold_dist) {
538  if (min_dist_bp < min_dist_bezt) {
539  *r_bp = closest_bp;
540  *r_nu = closest_bp_nu;
541  }
542  else {
543  *r_bezt = closest_bezt;
544  *r_bezt_idx = closest_handle;
545  *r_nu = closest_bezt_nu;
546  }
547  return true;
548  }
549  return false;
550 }
551 
555 static void get_bezier_interpolated_point(const BezTriple *bezt1,
556  const BezTriple *bezt2,
557  const float parameter,
558  float r_point[3])
559 {
560  float tmp1[3], tmp2[3], tmp3[3];
561  interp_v3_v3v3(tmp1, bezt1->vec[1], bezt1->vec[2], parameter);
562  interp_v3_v3v3(tmp2, bezt1->vec[2], bezt2->vec[0], parameter);
563  interp_v3_v3v3(tmp3, bezt2->vec[0], bezt2->vec[1], parameter);
564  interp_v3_v3v3(tmp1, tmp1, tmp2, parameter);
565  interp_v3_v3v3(tmp2, tmp2, tmp3, parameter);
566  interp_v3_v3v3(r_point, tmp1, tmp2, parameter);
567 }
568 
572 static void calculate_new_bezier_point(const float point_prev[3],
573  float handle_prev[3],
574  float new_left_handle[3],
575  float new_right_handle[3],
576  float handle_next[3],
577  const float point_next[3],
578  const float parameter)
579 {
580  float center_point[3];
581  interp_v3_v3v3(center_point, handle_prev, handle_next, parameter);
582  interp_v3_v3v3(handle_prev, point_prev, handle_prev, parameter);
583  interp_v3_v3v3(handle_next, handle_next, point_next, parameter);
584  interp_v3_v3v3(new_left_handle, handle_prev, center_point, parameter);
585  interp_v3_v3v3(new_right_handle, center_point, handle_next, parameter);
586 }
587 
588 static bool is_cyclic(const Nurb *nu)
589 {
590  return nu->flagu & CU_NURB_CYCLIC;
591 }
592 
596 static void insert_bezt_to_nurb(Nurb *nu, const CutData *data, Curve *cu)
597 {
598  EditNurb *editnurb = cu->editnurb;
599 
600  BezTriple *new_bezt_array = (BezTriple *)MEM_mallocN((nu->pntsu + 1) * sizeof(BezTriple),
601  __func__);
602  const int index = data->bezt_index + 1;
603  /* Copy all control points before the cut to the new memory. */
604  ED_curve_beztcpy(editnurb, new_bezt_array, nu->bezt, index);
605  BezTriple *new_bezt = new_bezt_array + index;
606 
607  /* Duplicate control point after the cut. */
608  ED_curve_beztcpy(editnurb, new_bezt, new_bezt - 1, 1);
609  copy_v3_v3(new_bezt->vec[1], data->cut_loc);
610 
611  if (index < nu->pntsu) {
612  /* Copy all control points after the cut to the new memory. */
613  ED_curve_beztcpy(editnurb, new_bezt_array + index + 1, nu->bezt + index, nu->pntsu - index);
614  }
615 
616  nu->pntsu += 1;
617  BKE_curve_nurb_vert_active_set(cu, nu, nu->bezt + index);
618 
619  BezTriple *next_bezt;
620  if (is_cyclic(nu) && (index == nu->pntsu - 1)) {
621  next_bezt = new_bezt_array;
622  }
623  else {
624  next_bezt = new_bezt + 1;
625  }
626 
627  /* Interpolate radius, tilt, weight */
628  new_bezt->tilt = interpf(next_bezt->tilt, (new_bezt - 1)->tilt, data->parameter);
629  new_bezt->radius = interpf(next_bezt->radius, (new_bezt - 1)->radius, data->parameter);
630  new_bezt->weight = interpf(next_bezt->weight, (new_bezt - 1)->weight, data->parameter);
631 
632  new_bezt->h1 = new_bezt->h2 = HD_ALIGN;
633 
634  calculate_new_bezier_point((new_bezt - 1)->vec[1],
635  (new_bezt - 1)->vec[2],
636  new_bezt->vec[0],
637  new_bezt->vec[2],
638  next_bezt->vec[0],
639  next_bezt->vec[1],
640  data->parameter);
641 
642  MEM_freeN(nu->bezt);
643  nu->bezt = new_bezt_array;
644  ED_curve_deselect_all(editnurb);
646  BEZT_SEL_ALL(new_bezt);
647 }
648 
652 static void insert_bp_to_nurb(Nurb *nu, const CutData *data, Curve *cu)
653 {
654  EditNurb *editnurb = cu->editnurb;
655 
656  BPoint *new_bp_array = (BPoint *)MEM_mallocN((nu->pntsu + 1) * sizeof(BPoint), __func__);
657  const int index = data->bp_index + 1;
658  /* Copy all control points before the cut to the new memory. */
659  ED_curve_bpcpy(editnurb, new_bp_array, nu->bp, index);
660  BPoint *new_bp = new_bp_array + index;
661 
662  /* Duplicate control point after the cut. */
663  ED_curve_bpcpy(editnurb, new_bp, new_bp - 1, 1);
664  copy_v3_v3(new_bp->vec, data->cut_loc);
665 
666  if (index < nu->pntsu) {
667  /* Copy all control points after the cut to the new memory. */
668  ED_curve_bpcpy(editnurb, new_bp_array + index + 1, nu->bp + index, (nu->pntsu - index));
669  }
670 
671  nu->pntsu += 1;
672  BKE_curve_nurb_vert_active_set(cu, nu, nu->bp + index);
673 
674  BPoint *next_bp;
675  if (is_cyclic(nu) && (index == nu->pntsu - 1)) {
676  next_bp = new_bp_array;
677  }
678  else {
679  next_bp = new_bp + 1;
680  }
681 
682  /* Interpolate radius, tilt, weight */
683  new_bp->tilt = interpf(next_bp->tilt, (new_bp - 1)->tilt, data->parameter);
684  new_bp->radius = interpf(next_bp->radius, (new_bp - 1)->radius, data->parameter);
685  new_bp->weight = interpf(next_bp->weight, (new_bp - 1)->weight, data->parameter);
686 
687  MEM_freeN(nu->bp);
688  nu->bp = new_bp_array;
689  ED_curve_deselect_all(editnurb);
691  new_bp->f1 |= SELECT;
692 }
693 
705 static void get_updated_data_for_edge(const float point[2],
706  const float point1[2],
707  const float point2[2],
708  const int point_idx,
709  const int resolu_idx,
710  float *r_min_dist,
711  int *r_min_i,
712  float *r_param)
713 {
714  float edge[2], vec1[2], vec2[2];
715  sub_v2_v2v2(edge, point1, point2);
716  sub_v2_v2v2(vec1, point1, point);
717  sub_v2_v2v2(vec2, point, point2);
718  const float len_vec1 = len_v2(vec1);
719  const float len_vec2 = len_v2(vec2);
720  const float dot1 = dot_v2v2(edge, vec1);
721  const float dot2 = dot_v2v2(edge, vec2);
722 
723  /* Signs of dot products being equal implies that the angles formed with the external point are
724  * either both acute or both obtuse, meaning the external point is closer to a point on the edge
725  * rather than an endpoint. */
726  if ((dot1 > 0) == (dot2 > 0)) {
727  const float perp_dist = len_vec1 * sinf(angle_v2v2(vec1, edge));
728  if (*r_min_dist > perp_dist) {
729  *r_min_dist = perp_dist;
730  *r_min_i = point_idx;
731  *r_param = resolu_idx + len_vec1 * cos_v2v2v2(point, point1, point2) / len_v2(edge);
732  }
733  }
734  else {
735  if (*r_min_dist > len_vec2) {
736  *r_min_dist = len_vec2;
737  *r_min_i = point_idx;
738  *r_param = resolu_idx;
739  }
740  }
741 }
742 
747  const ViewContext *vc, CutData *cd, Nurb *nu, const int resolu, const float point[2])
748 {
749  float min_dist = cd->min_dist, param = 0.0f;
750  int min_i = 0;
751  const int end = is_cyclic(nu) ? nu->pntsu : nu->pntsu - 1;
752 
753  if (nu->type == CU_BEZIER) {
754  for (int i = 0; i < end; i++) {
755  float *points = MEM_mallocN(sizeof(float[3]) * (resolu + 1), __func__);
756 
757  const BezTriple *bezt1 = nu->bezt + i;
758  const BezTriple *bezt2 = nu->bezt + (i + 1) % nu->pntsu;
759 
760  /* Calculate all points on curve. */
761  for (int j = 0; j < 3; j++) {
762  BKE_curve_forward_diff_bezier(bezt1->vec[1][j],
763  bezt1->vec[2][j],
764  bezt2->vec[0][j],
765  bezt2->vec[1][j],
766  points + j,
767  resolu,
768  sizeof(float[3]));
769  }
770 
771  float point1[2], point2[2];
772  worldspace_to_screenspace(vc, points, point1);
773  const float len_vec1 = len_v2v2(point, point1);
774 
775  if (min_dist > len_vec1) {
776  min_dist = len_vec1;
777  min_i = i;
778  param = 0;
779  }
780 
781  for (int j = 0; j < resolu; j++) {
782  worldspace_to_screenspace(vc, points + 3 * (j + 1), point2);
783  get_updated_data_for_edge(point, point1, point2, i, j, &min_dist, &min_i, &param);
784  copy_v2_v2(point1, point2);
785  }
786 
787  MEM_freeN(points);
788  }
789  if (cd->min_dist > min_dist) {
790  cd->min_dist = min_dist;
791  cd->nurb = nu;
792  cd->bezt_index = min_i;
793  cd->parameter = param / resolu;
794  }
795  }
796  else {
797  float point1[2], point2[2];
798  worldspace_to_screenspace(vc, nu->bp->vec, point1);
799  for (int i = 0; i < end; i++) {
800  worldspace_to_screenspace(vc, (nu->bp + (i + 1) % nu->pntsu)->vec, point2);
801  get_updated_data_for_edge(point, point1, point2, i, 0, &min_dist, &min_i, &param);
802  copy_v2_v2(point1, point2);
803  }
804 
805  if (cd->min_dist > min_dist) {
806  cd->min_dist = min_dist;
807  cd->nurb = nu;
808  cd->bp_index = min_i;
809  cd->parameter = param;
810  }
811  }
812 }
813 
814 /* Update #CutData for all the Nurbs in the curve. */
816  const ListBase *nurbs,
817  const float point[2],
818  const float sel_dist,
819  CutData *cd)
820 {
821  cd->min_dist = FLT_MAX;
822  LISTBASE_FOREACH (Nurb *, nu, nurbs) {
823  update_cut_data_for_nurb(vc, cd, nu, nu->resolu, point);
824  }
825 
826  return cd->min_dist < sel_dist;
827 }
828 
829 static CutData init_cut_data(const wmEvent *event)
830 {
831  CutData cd = {.bezt_index = 0,
832  .bp_index = 0,
833  .min_dist = FLT_MAX,
834  .parameter = 0.5f,
835  .has_prev = false,
836  .has_next = false,
837  .mval[0] = event->mval[0],
838  .mval[1] = event->mval[1]};
839  return cd;
840 }
841 
842 static bool insert_point_to_segment(const ViewContext *vc, const wmEvent *event)
843 {
844  Curve *cu = vc->obedit->data;
845  CutData cd = init_cut_data(event);
846  const float mval[2] = {UNPACK2(event->mval)};
847  const float threshold_dist_px = ED_view3d_select_dist_px() * SEL_DIST_FACTOR;
848  const bool near_spline = update_cut_data_for_all_nurbs(
849  vc, BKE_curve_editNurbs_get(cu), mval, threshold_dist_px, &cd);
850 
851  if (near_spline && !cd.nurb->hide) {
852  Nurb *nu = cd.nurb;
853  if (nu->type == CU_BEZIER) {
854  cd.min_dist = FLT_MAX;
855  /* Update cut data at a higher resolution for better accuracy. */
856  update_cut_data_for_nurb(vc, &cd, cd.nurb, 25, mval);
857 
859  &nu->bezt[(cd.bezt_index + 1) % (nu->pntsu)],
860  cd.parameter,
861  cd.cut_loc);
862 
863  insert_bezt_to_nurb(nu, &cd, cu);
864  }
865  else {
867  (nu->bp + cd.bp_index)->vec,
868  (nu->bp + (cd.bp_index + 1) % nu->pntsu)->vec,
869  cd.parameter);
870  insert_bp_to_nurb(nu, &cd, cu);
871  }
872  return true;
873  }
874 
875  return false;
876 }
877 
883  Curve *cu, View3D *v3d, Nurb **r_nu, BezTriple **r_bezt, BPoint **r_bp)
884 {
885  ListBase *nurbs = &cu->editnurb->nurbs;
886  BezTriple *bezt;
887  BPoint *bp;
888  int a;
889 
890  *r_nu = NULL;
891  *r_bezt = NULL;
892  *r_bp = NULL;
893 
894  LISTBASE_FOREACH (Nurb *, nu, nurbs) {
895  if (nu->type == CU_BEZIER) {
896  bezt = nu->bezt;
897  a = nu->pntsu;
898  while (a--) {
899  if (BEZT_ISSEL_ANY_HIDDENHANDLES(v3d, bezt)) {
900  if (*r_bezt || *r_bp) {
901  *r_bp = NULL;
902  *r_bezt = NULL;
903  return;
904  }
905  *r_bezt = bezt;
906  *r_nu = nu;
907  }
908  bezt++;
909  }
910  }
911  else {
912  bp = nu->bp;
913  a = nu->pntsu * nu->pntsv;
914  while (a--) {
915  if (bp->f1 & SELECT) {
916  if (*r_bezt || *r_bp) {
917  *r_bp = NULL;
918  *r_bezt = NULL;
919  return;
920  }
921  *r_bp = bp;
922  *r_nu = nu;
923  }
924  bp++;
925  }
926  }
927  }
928 }
929 
931  ListBase *nurbs,
932  Curve *cu,
933  const float disp_3d[3])
934 {
935  int nu_index = 0;
936  LISTBASE_FOREACH (Nurb *, nu1, nurbs) {
937  if (nu1->type == CU_BEZIER) {
938  BezTriple *last_bezt = nu1->bezt + nu1->pntsu - 1;
939  const bool first_sel = BEZT_ISSEL_ANY(nu1->bezt);
940  const bool last_sel = BEZT_ISSEL_ANY(last_bezt) && nu1->pntsu > 1;
941  if (first_sel) {
942  if (last_sel) {
943  BezTriple *new_bezt = (BezTriple *)MEM_mallocN((nu1->pntsu + 2) * sizeof(BezTriple),
944  __func__);
945  ED_curve_beztcpy(editnurb, new_bezt, nu1->bezt, 1);
946  ED_curve_beztcpy(editnurb, new_bezt + nu1->pntsu + 1, last_bezt, 1);
947  BEZT_DESEL_ALL(nu1->bezt);
948  BEZT_DESEL_ALL(last_bezt);
949  ED_curve_beztcpy(editnurb, new_bezt + 1, nu1->bezt, nu1->pntsu);
950 
951  move_bezt_by_displacement(new_bezt, disp_3d);
952  move_bezt_by_displacement(new_bezt + nu1->pntsu + 1, disp_3d);
953  MEM_freeN(nu1->bezt);
954  nu1->bezt = new_bezt;
955  nu1->pntsu += 2;
956  }
957  else {
958  BezTriple *new_bezt = (BezTriple *)MEM_mallocN((nu1->pntsu + 1) * sizeof(BezTriple),
959  __func__);
960  ED_curve_beztcpy(editnurb, new_bezt, nu1->bezt, 1);
961  BEZT_DESEL_ALL(nu1->bezt);
962  ED_curve_beztcpy(editnurb, new_bezt + 1, nu1->bezt, nu1->pntsu);
963  move_bezt_by_displacement(new_bezt, disp_3d);
964  MEM_freeN(nu1->bezt);
965  nu1->bezt = new_bezt;
966  nu1->pntsu++;
967  }
968  cu->actnu = nu_index;
969  cu->actvert = 0;
970  }
971  else if (last_sel) {
972  BezTriple *new_bezt = (BezTriple *)MEM_mallocN((nu1->pntsu + 1) * sizeof(BezTriple),
973  __func__);
974  ED_curve_beztcpy(editnurb, new_bezt + nu1->pntsu, last_bezt, 1);
975  BEZT_DESEL_ALL(last_bezt);
976  ED_curve_beztcpy(editnurb, new_bezt, nu1->bezt, nu1->pntsu);
977  move_bezt_by_displacement(new_bezt + nu1->pntsu, disp_3d);
978  MEM_freeN(nu1->bezt);
979  nu1->bezt = new_bezt;
980  nu1->pntsu++;
981  cu->actnu = nu_index;
982  cu->actvert = nu1->pntsu - 1;
983  }
984  }
985  else {
986  BPoint *last_bp = nu1->bp + nu1->pntsu - 1;
987  const bool first_sel = nu1->bp->f1 & SELECT;
988  const bool last_sel = last_bp->f1 & SELECT && nu1->pntsu > 1;
989  if (first_sel) {
990  if (last_sel) {
991  BPoint *new_bp = (BPoint *)MEM_mallocN((nu1->pntsu + 2) * sizeof(BPoint), __func__);
992  ED_curve_bpcpy(editnurb, new_bp, nu1->bp, 1);
993  ED_curve_bpcpy(editnurb, new_bp + nu1->pntsu + 1, last_bp, 1);
994  nu1->bp->f1 &= ~SELECT;
995  last_bp->f1 &= ~SELECT;
996  ED_curve_bpcpy(editnurb, new_bp + 1, nu1->bp, nu1->pntsu);
997  add_v3_v3(new_bp->vec, disp_3d);
998  add_v3_v3((new_bp + nu1->pntsu + 1)->vec, disp_3d);
999  MEM_freeN(nu1->bp);
1000  nu1->bp = new_bp;
1001  nu1->pntsu += 2;
1002  }
1003  else {
1004  BPoint *new_bp = (BPoint *)MEM_mallocN((nu1->pntsu + 1) * sizeof(BPoint), __func__);
1005  ED_curve_bpcpy(editnurb, new_bp, nu1->bp, 1);
1006  nu1->bp->f1 &= ~SELECT;
1007  ED_curve_bpcpy(editnurb, new_bp + 1, nu1->bp, nu1->pntsu);
1008  add_v3_v3(new_bp->vec, disp_3d);
1009  MEM_freeN(nu1->bp);
1010  nu1->bp = new_bp;
1011  nu1->pntsu++;
1012  }
1013  BKE_nurb_knot_calc_u(nu1);
1014  cu->actnu = nu_index;
1015  cu->actvert = 0;
1016  }
1017  else if (last_sel) {
1018  BPoint *new_bp = (BPoint *)MEM_mallocN((nu1->pntsu + 1) * sizeof(BPoint), __func__);
1019  ED_curve_bpcpy(editnurb, new_bp, nu1->bp, nu1->pntsu);
1020  ED_curve_bpcpy(editnurb, new_bp + nu1->pntsu, last_bp, 1);
1021  last_bp->f1 &= ~SELECT;
1022  ED_curve_bpcpy(editnurb, new_bp, nu1->bp, nu1->pntsu);
1023  add_v3_v3((new_bp + nu1->pntsu)->vec, disp_3d);
1024  MEM_freeN(nu1->bp);
1025  nu1->bp = new_bp;
1026  nu1->pntsu++;
1027  BKE_nurb_knot_calc_u(nu1);
1028  cu->actnu = nu_index;
1029  cu->actvert = nu1->pntsu - 1;
1030  }
1032  }
1033  nu_index++;
1034  }
1035 }
1036 
1041 {
1042  LISTBASE_FOREACH (Nurb *, nu1, nurbs) {
1043  if (nu1->pntsu > 1) {
1044  int start, end;
1045  if (is_cyclic(nu1)) {
1046  start = 0;
1047  end = nu1->pntsu;
1048  }
1049  else {
1050  start = 1;
1051  end = nu1->pntsu - 1;
1052  }
1053  for (int i = start; i < end; i++) {
1054  if (nu1->type == CU_BEZIER) {
1055  BEZT_DESEL_ALL(nu1->bezt + i);
1056  }
1057  else {
1058  (nu1->bp + i)->f1 &= ~SELECT;
1059  }
1060  }
1061  }
1062  }
1063 }
1064 
1065 static bool is_last_bezt(const Nurb *nu, const BezTriple *bezt)
1066 {
1067  return nu->pntsu > 1 && nu->bezt + nu->pntsu - 1 == bezt && !is_cyclic(nu);
1068 }
1069 
1074  const wmEvent *event,
1075  const int extrude_handle)
1076 {
1077  Curve *cu = vc->obedit->data;
1078  ListBase *nurbs = BKE_curve_editNurbs_get(cu);
1079  float center[3] = {0.0f, 0.0f, 0.0f};
1081  bool sel_exists = get_selected_center(nurbs, true, false, center);
1082 
1083  float location[3];
1084  if (sel_exists) {
1085  mul_v3_m4v3(location, vc->obedit->obmat, center);
1086  }
1087  else {
1088  copy_v3_v3(location, vc->scene->cursor.location);
1089  }
1090 
1091  ED_view3d_win_to_3d_int(vc->v3d, vc->region, location, event->mval, location);
1092 
1093  update_location_for_2d_curve(vc, location);
1094  EditNurb *editnurb = cu->editnurb;
1095 
1096  if (sel_exists) {
1097  float disp_3d[3];
1098  sub_v3_v3v3(disp_3d, location, center);
1099  /* Reimplemenented due to unexpected behavior for extrusion of 2-point spline. */
1100  extrude_vertices_from_selected_endpoints(editnurb, nurbs, cu, disp_3d);
1101  }
1102  else {
1103  Nurb *old_last_nu = editnurb->nurbs.last;
1104  ed_editcurve_addvert(cu, editnurb, vc->v3d, location);
1105  Nurb *new_last_nu = editnurb->nurbs.last;
1106 
1107  if (old_last_nu != new_last_nu) {
1109  new_last_nu,
1110  new_last_nu->bezt ? (const void *)new_last_nu->bezt :
1111  (const void *)new_last_nu->bp);
1112  new_last_nu->flagu = ~CU_NURB_CYCLIC;
1113  }
1114  }
1115 
1117  if (bezt) {
1118  bezt->h1 = extrude_handle;
1119  bezt->h2 = extrude_handle;
1120  }
1121  }
1123 }
1124 
1129  struct wmOperator *op,
1130  const wmEvent *event,
1131  const float sel_dist)
1132 {
1133  Curve *cu = vc->obedit->data;
1134  ListBase *nurbs = BKE_curve_editNurbs_get(cu);
1135  CutData cd = init_cut_data(event);
1136 
1137  const float mval[2] = {UNPACK2(event->mval)};
1138  const bool nearby = update_cut_data_for_all_nurbs(vc, nurbs, mval, sel_dist, &cd);
1139 
1140  if (nearby) {
1141  if (cd.nurb && (cd.nurb->type == CU_BEZIER) && RNA_boolean_get(op->ptr, "move_segment")) {
1142  MoveSegmentData *seg_data;
1143  CurvePenData *cpd = (CurvePenData *)(op->customdata);
1144  cpd->msd = seg_data = MEM_callocN(sizeof(MoveSegmentData), __func__);
1145  seg_data->bezt_index = cd.bezt_index;
1146  seg_data->nu = cd.nurb;
1147  seg_data->t = cd.parameter;
1148  }
1149  return true;
1150  }
1151  return false;
1152 }
1153 
1154 static void move_segment(ViewContext *vc, MoveSegmentData *seg_data, const wmEvent *event)
1155 {
1156  Nurb *nu = seg_data->nu;
1157  BezTriple *bezt1 = nu->bezt + seg_data->bezt_index;
1158  BezTriple *bezt2 = BKE_nurb_bezt_get_next(nu, bezt1);
1159 
1160  int h1 = 2, h2 = 0;
1161  if (bezt1->hide) {
1162  if (bezt2->hide) {
1163  return;
1164  }
1165  /*
1166  * Swap bezt1 and bezt2 in all calculations if only bezt2 is visible.
1167  * (The first point needs to be visible for the calculations of the second point to be valid)
1168  */
1169  BezTriple *temp_bezt = bezt2;
1170  bezt2 = bezt1;
1171  bezt1 = temp_bezt;
1172  h1 = 0, h2 = 2;
1173  }
1174 
1175  const float t = max_ff(min_ff(seg_data->t, 0.9f), 0.1f);
1176  const float t_sq = t * t;
1177  const float t_cu = t_sq * t;
1178  const float one_minus_t = 1 - t;
1179  const float one_minus_t_sq = one_minus_t * one_minus_t;
1180  const float one_minus_t_cu = one_minus_t_sq * one_minus_t;
1181 
1182  float mouse_3d[3];
1183  float depth[3];
1184  /* Use the center of the spline segment as depth. */
1185  get_bezier_interpolated_point(bezt1, bezt2, t, depth);
1186  screenspace_to_worldspace_int(vc, event->mval, depth, mouse_3d);
1187 
1188  /*
1189  * Equation of Bezier Curve
1190  * => B(t) = (1-t)^3 * P0 + 3(1-t)^2 * t * P1 + 3(1-t) * t^2 * P2 + t^3 * P3
1191  *
1192  * Mouse location (Say Pm) should satisfy this equation.
1193  * Therefore => (1/t - 1) * P1 + P2 = (Pm - (1 - t)^3 * P0 - t^3 * P3) / [3 * (1 - t) * t^2] = k1
1194  * (in code)
1195  *
1196  * Another constraint is required to identify P1 and P2.
1197  * The constraint used is that the vector between P1 and P2 doesn't change.
1198  * Therefore => P1 - P2 = k2
1199  *
1200  * From the two equations => P1 = t(k1 + k2) and P2 = P1 - K2
1201  */
1202 
1203  float k1[3];
1204  const float denom = 3.0f * one_minus_t * t_sq;
1205  k1[0] = (mouse_3d[0] - one_minus_t_cu * bezt1->vec[1][0] - t_cu * bezt2->vec[1][0]) / denom;
1206  k1[1] = (mouse_3d[1] - one_minus_t_cu * bezt1->vec[1][1] - t_cu * bezt2->vec[1][1]) / denom;
1207  k1[2] = (mouse_3d[2] - one_minus_t_cu * bezt1->vec[1][2] - t_cu * bezt2->vec[1][2]) / denom;
1208 
1209  float k2[3];
1210  sub_v3_v3v3(k2, bezt1->vec[h1], bezt2->vec[h2]);
1211 
1212  if (!bezt1->hide) {
1213  /* P1 = t(k1 + k2) */
1214  add_v3_v3v3(bezt1->vec[h1], k1, k2);
1215  mul_v3_fl(bezt1->vec[h1], t);
1216 
1217  remove_handle_movement_constraints(bezt1, true, true);
1218 
1219  /* Move opposite handle as well if type is align. */
1220  if (bezt1->h1 == HD_ALIGN) {
1221  float handle_vec[3];
1222  sub_v3_v3v3(handle_vec, bezt1->vec[1], bezt1->vec[h1]);
1223  normalize_v3_length(handle_vec, len_v3v3(bezt1->vec[1], bezt1->vec[h2]));
1224  add_v3_v3v3(bezt1->vec[h2], bezt1->vec[1], handle_vec);
1225  }
1226  }
1227 
1228  if (!bezt2->hide) {
1229  /* P2 = P1 - K2 */
1230  sub_v3_v3v3(bezt2->vec[h2], bezt1->vec[h1], k2);
1231 
1232  remove_handle_movement_constraints(bezt2, true, true);
1233 
1234  /* Move opposite handle as well if type is align. */
1235  if (bezt2->h2 == HD_ALIGN) {
1236  float handle_vec[3];
1237  sub_v3_v3v3(handle_vec, bezt2->vec[1], bezt2->vec[h2]);
1238  normalize_v3_length(handle_vec, len_v3v3(bezt2->vec[1], bezt2->vec[h1]));
1239  add_v3_v3v3(bezt2->vec[h1], bezt2->vec[1], handle_vec);
1240  }
1241  }
1242 }
1243 
1248 {
1249  if (bezt->h1 != HD_FREE || bezt->h2 != HD_FREE) {
1250  bezt->h1 = bezt->h2 = HD_FREE;
1251  }
1252  else {
1253  bezt->h1 = bezt->h2 = HD_ALIGN;
1254  }
1255 }
1256 
1261 {
1262  FOREACH_SELECTED_BEZT_BEGIN (bezt, nurbs) {
1264  }
1266 }
1267 
1271 static bool delete_point_under_mouse(ViewContext *vc, const wmEvent *event)
1272 {
1273  BezTriple *bezt = NULL;
1274  BPoint *bp = NULL;
1275  Nurb *nu = NULL;
1276  int temp = 0;
1277  Curve *cu = vc->obedit->data;
1278  EditNurb *editnurb = cu->editnurb;
1279  ListBase *nurbs = BKE_curve_editNurbs_get(cu);
1280  const float mouse_point[2] = {UNPACK2(event->mval)};
1281 
1282  get_closest_vertex_to_point_in_nurbs(vc, nurbs, mouse_point, &nu, &bezt, &bp, &temp);
1283  const bool found_point = nu != NULL;
1284 
1285  bool deleted = false;
1286  if (found_point) {
1288  if (nu) {
1289  if (nu->type == CU_BEZIER) {
1290  BezTriple *next_bezt = BKE_nurb_bezt_get_next(nu, bezt);
1291  BezTriple *prev_bezt = BKE_nurb_bezt_get_prev(nu, bezt);
1292  if (next_bezt && prev_bezt) {
1293  const int bez_index = BKE_curve_nurb_vert_index_get(nu, bezt);
1294  const uint span_step[2] = {bez_index, bez_index};
1295  ed_dissolve_bez_segment(prev_bezt, next_bezt, nu, cu, 1, span_step);
1296  }
1297  delete_bezt_from_nurb(bezt, nu, editnurb);
1298  }
1299  else {
1300  delete_bp_from_nurb(bp, nu, editnurb);
1301  }
1302 
1303  if (nu->pntsu == 0) {
1304  delete_nurb(cu, nu);
1305  nu = NULL;
1306  }
1307  deleted = true;
1308  cu->actvert = CU_ACT_NONE;
1309  }
1310  }
1311 
1312  if (nu && nu->type == CU_BEZIER) {
1314  }
1315 
1316  return deleted;
1317 }
1318 
1319 static void move_adjacent_handle(ViewContext *vc, const wmEvent *event, ListBase *nurbs)
1320 {
1321  FOREACH_SELECTED_BEZT_BEGIN (bezt, nurbs) {
1322  BezTriple *adj_bezt;
1323  int bezt_idx;
1324  if (nu->pntsu == 1) {
1325  continue;
1326  }
1327  if (nu->bezt == bezt) {
1328  adj_bezt = BKE_nurb_bezt_get_next(nu, bezt);
1329  bezt_idx = 0;
1330  }
1331  else if (nu->bezt + nu->pntsu - 1 == bezt) {
1332  adj_bezt = BKE_nurb_bezt_get_prev(nu, bezt);
1333  bezt_idx = 2;
1334  }
1335  else {
1336  if (BEZT_ISSEL_IDX(bezt, 0)) {
1337  adj_bezt = BKE_nurb_bezt_get_prev(nu, bezt);
1338  bezt_idx = 2;
1339  }
1340  else if (BEZT_ISSEL_IDX(bezt, 2)) {
1341  adj_bezt = BKE_nurb_bezt_get_next(nu, bezt);
1342  bezt_idx = 0;
1343  }
1344  else {
1345  continue;
1346  }
1347  }
1348  adj_bezt->h1 = adj_bezt->h2 = HD_FREE;
1349 
1350  int displacement[2];
1351  sub_v2_v2v2_int(displacement, event->xy, event->prev_xy);
1352  const float disp_fl[2] = {UNPACK2(displacement)};
1354  vc, adj_bezt, bezt_idx, disp_fl, 0.0f, false, false);
1356  }
1358 }
1359 
1364  Nurb *sel_nu,
1365  BezTriple *sel_bezt,
1366  BPoint *sel_bp)
1367 {
1368  if (sel_bezt || (sel_bp && sel_nu->pntsu > 2)) {
1369  const bool is_bezt_endpoint = (sel_nu->type == CU_BEZIER &&
1370  (sel_bezt == sel_nu->bezt ||
1371  sel_bezt == sel_nu->bezt + sel_nu->pntsu - 1));
1372  const bool is_bp_endpoint = (sel_nu->type != CU_BEZIER &&
1373  (sel_bp == sel_nu->bp ||
1374  sel_bp == sel_nu->bp + sel_nu->pntsu - 1));
1375  if (!(is_bezt_endpoint || is_bp_endpoint)) {
1376  return false;
1377  }
1378 
1379  Nurb *nu = NULL;
1380  BezTriple *bezt = NULL;
1381  BPoint *bp = NULL;
1382  Curve *cu = vc->obedit->data;
1383  int bezt_idx;
1384  const float mval_fl[2] = {UNPACK2(vc->mval)};
1385 
1387  vc, &(cu->editnurb->nurbs), mval_fl, &nu, &bezt, &bp, &bezt_idx);
1388 
1389  if (nu == sel_nu &&
1390  ((nu->type == CU_BEZIER && bezt != sel_bezt &&
1391  (bezt == nu->bezt || bezt == nu->bezt + nu->pntsu - 1) && bezt_idx == 1) ||
1392  (nu->type != CU_BEZIER && bp != sel_bp &&
1393  (bp == nu->bp || bp == nu->bp + nu->pntsu - 1)))) {
1394  View3D *v3d = vc->v3d;
1395  ListBase *nurbs = object_editcurve_get(vc->obedit);
1396  curve_toggle_cyclic(v3d, nurbs, 0);
1397  return true;
1398  }
1399  }
1400  return false;
1401 }
1402 
1404 {
1405  FOREACH_SELECTED_BEZT_BEGIN (bezt, nurbs) {
1406  bezt->h1 = bezt->h2 = HD_ALIGN;
1407  copy_v3_v3(bezt->vec[0], bezt->vec[1]);
1408  copy_v3_v3(bezt->vec[2], bezt->vec[1]);
1409  BEZT_DESEL_ALL(bezt);
1410  BEZT_SEL_IDX(bezt, is_last_bezt(nu, bezt) ? 2 : 0);
1411  }
1413 }
1414 
1415 static void toggle_select_bezt(BezTriple *bezt, const int bezt_idx, Curve *cu, Nurb *nu)
1416 {
1417  if (bezt_idx == 1) {
1418  if (BEZT_ISSEL_IDX(bezt, 1)) {
1419  BEZT_DESEL_ALL(bezt);
1420  }
1421  else {
1422  BEZT_SEL_ALL(bezt);
1423  }
1424  }
1425  else {
1426  if (BEZT_ISSEL_IDX(bezt, bezt_idx)) {
1427  BEZT_DESEL_IDX(bezt, bezt_idx);
1428  }
1429  else {
1430  BEZT_SEL_IDX(bezt, bezt_idx);
1431  }
1432  }
1433 
1434  if (BEZT_ISSEL_ANY(bezt)) {
1435  BKE_curve_nurb_vert_active_set(cu, nu, bezt);
1436  }
1437 }
1438 
1439 static void toggle_select_bp(BPoint *bp, Curve *cu, Nurb *nu)
1440 {
1441  if (bp->f1 & SELECT) {
1442  bp->f1 &= ~SELECT;
1443  }
1444  else {
1445  bp->f1 |= SELECT;
1446  BKE_curve_nurb_vert_active_set(cu, nu, bp);
1447  }
1448 }
1449 
1450 static void toggle_handle_types(BezTriple *bezt, int bezt_idx, CurvePenData *cpd)
1451 {
1452  if (bezt_idx == 0) {
1453  if (bezt->h1 == HD_VECT) {
1454  bezt->h1 = bezt->h2 = HD_AUTO;
1455  }
1456  else {
1457  bezt->h1 = HD_VECT;
1458  if (bezt->h2 != HD_VECT) {
1459  bezt->h2 = HD_FREE;
1460  }
1461  }
1462  cpd->acted = true;
1463  }
1464  else if (bezt_idx == 2) {
1465  if (bezt->h2 == HD_VECT) {
1466  bezt->h1 = bezt->h2 = HD_AUTO;
1467  }
1468  else {
1469  bezt->h2 = HD_VECT;
1470  if (bezt->h1 != HD_VECT) {
1471  bezt->h1 = HD_FREE;
1472  }
1473  }
1474  cpd->acted = true;
1475  }
1476 }
1477 
1478 static void cycle_handles(BezTriple *bezt)
1479 {
1480  if (bezt->h1 == HD_AUTO) {
1481  bezt->h1 = bezt->h2 = HD_VECT;
1482  }
1483  else if (bezt->h1 == HD_VECT) {
1484  bezt->h1 = bezt->h2 = HD_ALIGN;
1485  }
1486  else if (bezt->h1 == HD_ALIGN) {
1487  bezt->h1 = bezt->h2 = HD_FREE;
1488  }
1489  else {
1490  bezt->h1 = bezt->h2 = HD_AUTO;
1491  }
1492 }
1493 
1494 enum {
1500 };
1501 
1503 {
1504  static const EnumPropertyItem modal_items[] = {
1506  "FREE_ALIGN_TOGGLE",
1507  0,
1508  "Free-Align Toggle",
1509  "Move handle of newly added point freely"},
1511  "MOVE_ADJACENT",
1512  0,
1513  "Move Adjacent Handle",
1514  "Move the closer handle of the adjacent vertex"},
1516  "MOVE_ENTIRE",
1517  0,
1518  "Move Entire Point",
1519  "Move the entire point using its handles"},
1521  "LINK_HANDLES",
1522  0,
1523  "Link Handles",
1524  "Mirror the movement of one handle onto the other"},
1526  "LOCK_ANGLE",
1527  0,
1528  "Lock Angle",
1529  "Move the handle along its current angle"},
1530  {0, NULL, 0, NULL, NULL},
1531  };
1532 
1533  wmKeyMap *keymap = WM_modalkeymap_find(keyconf, "Curve Pen Modal Map");
1534 
1535  /* This function is called for each spacetype, only needs to add map once */
1536  if (keymap && keymap->modal_items) {
1537  return NULL;
1538  }
1539 
1540  keymap = WM_modalkeymap_ensure(keyconf, "Curve Pen Modal Map", modal_items);
1541 
1542  WM_modalkeymap_assign(keymap, "CURVE_OT_pen");
1543 
1544  return keymap;
1545 }
1546 
1547 static int curve_pen_modal(bContext *C, wmOperator *op, const wmEvent *event)
1548 {
1550  ViewContext vc;
1551  Object *obedit = CTX_data_edit_object(C);
1552 
1554  Curve *cu = vc.obedit->data;
1555  ListBase *nurbs = &cu->editnurb->nurbs;
1556  const float threshold_dist_px = ED_view3d_select_dist_px() * SEL_DIST_FACTOR;
1557 
1558  BezTriple *bezt = NULL;
1559  BPoint *bp = NULL;
1560  Nurb *nu = NULL;
1561 
1562  const struct SelectPick_Params params = {
1563  .sel_op = SEL_OP_SET,
1564  .deselect_all = false,
1565  };
1566 
1568 
1569  /* Distance threshold for mouse clicks to affect the spline or its points */
1570  const float mval_fl[2] = {UNPACK2(event->mval)};
1571 
1572  const bool extrude_point = RNA_boolean_get(op->ptr, "extrude_point");
1573  const bool delete_point = RNA_boolean_get(op->ptr, "delete_point");
1574  const bool insert_point = RNA_boolean_get(op->ptr, "insert_point");
1575  const bool move_seg = RNA_boolean_get(op->ptr, "move_segment");
1576  const bool select_point = RNA_boolean_get(op->ptr, "select_point");
1577  const bool move_point = RNA_boolean_get(op->ptr, "move_point");
1578  const bool close_spline = RNA_boolean_get(op->ptr, "close_spline");
1579  const bool toggle_vector = RNA_boolean_get(op->ptr, "toggle_vector");
1580  const bool cycle_handle_type = RNA_boolean_get(op->ptr, "cycle_handle_type");
1581  const int close_spline_method = RNA_enum_get(op->ptr, "close_spline_method");
1582  const int extrude_handle = RNA_enum_get(op->ptr, "extrude_handle");
1583 
1584  CurvePenData *cpd;
1585  if (op->customdata == NULL) {
1586  op->customdata = cpd = MEM_callocN(sizeof(CurvePenData), __func__);
1587  }
1588  else {
1589  cpd = (CurvePenData *)(op->customdata);
1590  cpd->select_multi = event->modifier == KM_SHIFT;
1591  }
1592 
1593  if (event->type == EVT_MODAL_MAP) {
1594  if (cpd->msd == NULL) {
1595  if (event->val == PEN_MODAL_FREE_ALIGN_TOGGLE) {
1597  cpd->link_handles = false;
1598  }
1599  else if (event->val == PEN_MODAL_LINK_HANDLES) {
1600  cpd->link_handles = !cpd->link_handles;
1601  if (cpd->link_handles) {
1602  move_all_selected_points(&vc, event, cpd, nurbs, false);
1603  }
1604  }
1605  else if (event->val == PEN_MODAL_MOVE_ENTIRE) {
1606  cpd->move_entire = !cpd->move_entire;
1607  }
1608  else if (event->val == PEN_MODAL_MOVE_ADJACENT) {
1609  cpd->move_adjacent = !cpd->move_adjacent;
1610  }
1611  else if (event->val == PEN_MODAL_LOCK_ANGLE) {
1612  cpd->lock_angle = !cpd->lock_angle;
1613  }
1614  }
1615  else {
1616  if (event->val == PEN_MODAL_FREE_ALIGN_TOGGLE) {
1617  BezTriple *bezt1 = cpd->msd->nu->bezt + cpd->msd->bezt_index;
1618  BezTriple *bezt2 = BKE_nurb_bezt_get_next(cpd->msd->nu, bezt1);
1621  }
1622  }
1623  }
1624 
1625  if (ISMOUSE_MOTION(event->type)) {
1626  /* Check if dragging */
1627  if (!cpd->dragging && WM_event_drag_test(event, event->prev_press_xy)) {
1628  cpd->dragging = true;
1629 
1630  if (cpd->new_point) {
1632  }
1633  }
1634 
1635  if (cpd->dragging) {
1636  if (cpd->spline_nearby && move_seg && cpd->msd != NULL) {
1637  MoveSegmentData *seg_data = cpd->msd;
1638  move_segment(&vc, seg_data, event);
1639  cpd->acted = true;
1640  if (seg_data->nu && seg_data->nu->type == CU_BEZIER) {
1641  BKE_nurb_handles_calc(seg_data->nu);
1642  }
1643  }
1644  else if (cpd->move_adjacent) {
1645  move_adjacent_handle(&vc, event, nurbs);
1646  cpd->acted = true;
1647  }
1648  else if (cpd->new_point || (move_point && !cpd->spline_nearby && cpd->found_point)) {
1649  /* Move only the bezt handles if it's a new point. */
1650  move_all_selected_points(&vc, event, cpd, nurbs, cpd->new_point);
1651  cpd->acted = true;
1652  }
1653  }
1654  }
1655  else if (ELEM(event->type, LEFTMOUSE)) {
1656  if (ELEM(event->val, KM_RELEASE, KM_DBL_CLICK)) {
1657  if (delete_point && !cpd->new_point && !cpd->dragging) {
1658  if (ED_curve_editnurb_select_pick(C, event->mval, threshold_dist_px, false, &params)) {
1659  cpd->acted = delete_point_under_mouse(&vc, event);
1660  }
1661  }
1662 
1663  /* Close spline on Click, if enabled. */
1664  if (!cpd->acted && close_spline && close_spline_method == ON_CLICK && cpd->found_point &&
1665  !cpd->dragging) {
1666  if (cpd->nu && !is_cyclic(cpd->nu)) {
1667  copy_v2_v2_int(vc.mval, event->mval);
1668  cpd->acted = make_cyclic_if_endpoints(&vc, cpd->nu, cpd->bezt, cpd->bp);
1669  }
1670  }
1671 
1672  if (!cpd->acted && (insert_point || extrude_point) && cpd->spline_nearby && !cpd->dragging) {
1673  if (insert_point) {
1674  insert_point_to_segment(&vc, event);
1675  cpd->new_point = true;
1676  cpd->acted = true;
1677  }
1678  else if (extrude_point) {
1679  extrude_points_from_selected_vertices(&vc, event, extrude_handle);
1680  cpd->acted = true;
1681  }
1682  }
1683 
1684  if (!cpd->acted && toggle_vector) {
1685  int bezt_idx;
1686  get_closest_vertex_to_point_in_nurbs(&vc, nurbs, mval_fl, &nu, &bezt, &bp, &bezt_idx);
1687  if (bezt) {
1688  if (bezt_idx == 1 && cycle_handle_type) {
1689  cycle_handles(bezt);
1690  cpd->acted = true;
1691  }
1692  else {
1693  toggle_handle_types(bezt, bezt_idx, cpd);
1694  }
1695 
1696  if (nu && nu->type == CU_BEZIER) {
1698  }
1699  }
1700  }
1701 
1702  if (!cpd->selection_made && !cpd->acted) {
1703  if (cpd->select_multi) {
1704  int bezt_idx;
1705  get_closest_vertex_to_point_in_nurbs(&vc, nurbs, mval_fl, &nu, &bezt, &bp, &bezt_idx);
1706  if (bezt) {
1707  toggle_select_bezt(bezt, bezt_idx, cu, nu);
1708  }
1709  else if (bp) {
1710  toggle_select_bp(bp, cu, nu);
1711  }
1712  else {
1714  }
1715  }
1716  else if (select_point) {
1717  ED_curve_editnurb_select_pick(C, event->mval, threshold_dist_px, false, &params);
1718  }
1719  }
1720 
1721  if (cpd->msd != NULL) {
1722  MEM_freeN(cpd->msd);
1723  }
1724  MEM_freeN(cpd);
1726  }
1727  }
1728 
1731  DEG_id_tag_update(obedit->data, 0);
1732 
1733  return ret;
1734 }
1735 
1736 static int curve_pen_invoke(bContext *C, wmOperator *op, const wmEvent *event)
1737 {
1738  ViewContext vc;
1740  Curve *cu = vc.obedit->data;
1741  ListBase *nurbs = &cu->editnurb->nurbs;
1742 
1743  BezTriple *bezt = NULL;
1744  BPoint *bp = NULL;
1745  Nurb *nu = NULL;
1746 
1747  CurvePenData *cpd;
1748  op->customdata = cpd = MEM_callocN(sizeof(CurvePenData), __func__);
1749 
1750  /* Distance threshold for mouse clicks to affect the spline or its points */
1751  const float mval_fl[2] = {UNPACK2(event->mval)};
1752  const float threshold_dist_px = ED_view3d_select_dist_px() * SEL_DIST_FACTOR;
1753 
1754  const bool extrude_point = RNA_boolean_get(op->ptr, "extrude_point");
1755  const bool insert_point = RNA_boolean_get(op->ptr, "insert_point");
1756  const bool move_seg = RNA_boolean_get(op->ptr, "move_segment");
1757  const bool move_point = RNA_boolean_get(op->ptr, "move_point");
1758  const bool close_spline = RNA_boolean_get(op->ptr, "close_spline");
1759  const int close_spline_method = RNA_enum_get(op->ptr, "close_spline_method");
1760  const int extrude_handle = RNA_enum_get(op->ptr, "extrude_handle");
1761 
1762  if (ELEM(event->type, LEFTMOUSE) && ELEM(event->val, KM_PRESS, KM_DBL_CLICK)) {
1763  /* Get the details of points selected at the start of the operation.
1764  * Used for closing the spline when endpoints are clicked consecutively and for selecting a
1765  * single point. */
1766  get_first_selected_point(cu, vc.v3d, &nu, &bezt, &bp);
1767  cpd->nu = nu;
1768  cpd->bezt = bezt;
1769  cpd->bp = bp;
1770 
1771  /* Get the details of the vertex closest to the mouse at the start of the operation. */
1772  Nurb *nu1;
1773  BezTriple *bezt1;
1774  BPoint *bp1;
1775  int bezt_idx = 0;
1777  &vc, nurbs, mval_fl, &nu1, &bezt1, &bp1, &bezt_idx);
1778 
1779  if (move_point && nu1 && !nu1->hide &&
1780  (bezt || (bezt1 && !BEZT_ISSEL_IDX(bezt1, bezt_idx)) || (bp1 && !(bp1->f1 & SELECT)))) {
1781  /* Select the closest bezt or bp. */
1783  if (bezt1) {
1784  if (bezt_idx == 1) {
1785  BEZT_SEL_ALL(bezt1);
1786  }
1787  else {
1788  BEZT_SEL_IDX(bezt1, bezt_idx);
1789  }
1790  BKE_curve_nurb_vert_active_set(cu, nu1, bezt1);
1791  }
1792  else if (bp1) {
1793  bp1->f1 |= SELECT;
1794  BKE_curve_nurb_vert_active_set(cu, nu1, bp1);
1795  }
1796 
1797  cpd->selection_made = true;
1798  }
1799  if (cpd->found_point) {
1800  /* Close the spline on press. */
1801  if (close_spline && close_spline_method == ON_PRESS && cpd->nu && !is_cyclic(cpd->nu)) {
1802  copy_v2_v2_int(vc.mval, event->mval);
1804  &vc, cpd->nu, cpd->bezt, cpd->bp);
1805  }
1806  }
1807  else if (!cpd->acted) {
1808  if (is_spline_nearby(&vc, op, event, threshold_dist_px)) {
1809  cpd->spline_nearby = true;
1810 
1811  /* If move segment is disabled, then insert point on key press and set
1812  * "new_point" to true so that the new point's handles can be controlled. */
1813  if (insert_point && !move_seg) {
1814  insert_point_to_segment(&vc, event);
1815  cpd->new_point = cpd->acted = cpd->link_handles = true;
1816  }
1817  }
1818  else if (extrude_point) {
1819  extrude_points_from_selected_vertices(&vc, event, extrude_handle);
1820  cpd->new_point = cpd->acted = cpd->link_handles = true;
1821  }
1822  }
1823  }
1825 
1826  return OPERATOR_RUNNING_MODAL;
1827 }
1828 
1830 {
1831  /* identifiers */
1832  ot->name = "Curve Pen";
1833  ot->idname = "CURVE_OT_pen";
1834  ot->description = "Construct and edit splines";
1835 
1836  /* api callbacks */
1840 
1841  /* flags */
1842  ot->flag = OPTYPE_UNDO;
1843 
1844  /* properties */
1846 
1848  "extrude_point",
1849  false,
1850  "Extrude Point",
1851  "Add a point connected to the last selected point");
1852  RNA_def_enum(ot->srna,
1853  "extrude_handle",
1855  HD_VECT,
1856  "Extrude Handle Type",
1857  "Type of the extruded handle");
1858  RNA_def_boolean(ot->srna, "delete_point", false, "Delete Point", "Delete an existing point");
1860  ot->srna, "insert_point", false, "Insert Point", "Insert Point into a curve segment");
1861  RNA_def_boolean(ot->srna, "move_segment", false, "Move Segment", "Delete an existing point");
1863  ot->srna, "select_point", false, "Select Point", "Select a point or its handles");
1864  RNA_def_boolean(ot->srna, "move_point", false, "Move Point", "Move a point or its handles");
1866  "close_spline",
1867  true,
1868  "Close Spline",
1869  "Make a spline cyclic by clicking endpoints");
1870  RNA_def_enum(ot->srna,
1871  "close_spline_method",
1873  OFF,
1874  "Close Spline Method",
1875  "The condition for close spline to activate");
1877  ot->srna, "toggle_vector", false, "Toggle Vector", "Toggle between Vector and Auto handles");
1879  "cycle_handle_type",
1880  false,
1881  "Cycle Handle Type",
1882  "Cycle between all four handle types");
1883 }
struct Object * CTX_data_edit_object(const bContext *C)
Definition: context.c:1370
struct Depsgraph * CTX_data_ensure_evaluated_depsgraph(const bContext *C)
Definition: context.c:1528
void BKE_nurb_handles_calc(struct Nurb *nu)
Definition: curve.cc:3995
struct BezTriple * BKE_nurb_bezt_get_next(struct Nurb *nu, struct BezTriple *bezt)
Definition: curve.cc:973
void BKE_nurb_free(struct Nurb *nu)
Definition: curve.cc:622
struct ListBase * BKE_curve_editNurbs_get(struct Curve *cu)
Definition: curve.cc:426
void BKE_curve_nurb_vert_active_set(struct Curve *cu, const struct Nurb *nu, const void *vert)
#define CU_IS_2D(cu)
Definition: BKE_curve.h:67
int BKE_curve_nurb_vert_index_get(const struct Nurb *nu, const void *vert)
struct BezTriple * BKE_nurb_bezt_get_prev(struct Nurb *nu, struct BezTriple *bezt)
Definition: curve.cc:1015
void BKE_curve_nurb_vert_active_validate(struct Curve *cu)
Definition: curve.cc:5076
void BKE_curve_editNurb_keyIndex_delCV(struct GHash *keyindex, const void *cv)
Definition: curve.cc:363
void BKE_curve_forward_diff_bezier(float q0, float q1, float q2, float q3, float *p, int it, int stride)
Definition: curve.cc:1717
void BKE_nurb_knot_calc_u(struct Nurb *nu)
Definition: curve.cc:1234
General operations, lookup, etc. for blender objects.
#define BLI_assert(a)
Definition: BLI_assert.h:46
#define LISTBASE_FOREACH(type, var, list)
Definition: BLI_listbase.h:336
void BLI_remlink(struct ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition: listbase.c:100
int BLI_findindex(const struct ListBase *listbase, const void *vlink) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
MINLINE float max_ff(float a, float b)
MINLINE float min_ff(float a, float b)
MINLINE float interpf(float a, float b, float t)
bool isect_ray_plane_v3(const float ray_origin[3], const float ray_direction[3], const float plane[4], float *r_lambda, bool clip)
Definition: math_geom.c:1713
bool invert_m4_m4(float R[4][4], const float A[4][4])
Definition: math_matrix.c:1287
void mul_m4_v3(const float M[4][4], float r[3])
Definition: math_matrix.c:729
void mul_v3_m4v3(float r[3], const float M[4][4], const float v[3])
Definition: math_matrix.c:739
MINLINE void sub_v2_v2v2_int(int r[2], const int a[2], const int b[2])
MINLINE float len_v3v3(const float a[3], const float b[3]) ATTR_WARN_UNUSED_RESULT
float angle_v2v2(const float a[2], const float b[2]) ATTR_WARN_UNUSED_RESULT
Definition: math_vector.c:423
MINLINE void sub_v3_v3v3(float r[3], const float a[3], const float b[3])
void interp_v2_v2v2(float r[2], const float a[2], const float b[2], float t)
Definition: math_vector.c:14
MINLINE void copy_v2_v2(float r[2], const float a[2])
MINLINE void mul_v3_fl(float r[3], float f)
MINLINE void copy_v2_v2_int(int r[2], const int a[2])
MINLINE void copy_v3_v3(float r[3], const float a[3])
float cos_v2v2v2(const float p1[2], const float p2[2], const float p3[2]) ATTR_WARN_UNUSED_RESULT
Definition: math_vector.c:411
MINLINE float dot_v3v3(const float a[3], const float b[3]) ATTR_WARN_UNUSED_RESULT
void interp_v3_v3v3(float r[3], const float a[3], const float b[3], float t)
Definition: math_vector.c:29
MINLINE void add_v3_v3v3(float r[3], const float a[3], const float b[3])
MINLINE float normalize_v3_v3(float r[3], const float a[3])
MINLINE float normalize_v3_length(float r[3], float unit_scale)
MINLINE float len_manhattan_v2v2(const float a[2], const float b[2]) ATTR_WARN_UNUSED_RESULT
MINLINE void add_v2_v2v2(float r[2], const float a[2], const float b[2])
MINLINE void sub_v2_v2v2(float r[2], const float a[2], const float b[2])
MINLINE float mul_project_m4_v3_zfac(const float mat[4][4], const float co[3]) ATTR_WARN_UNUSED_RESULT
MINLINE float dot_v2v2(const float a[2], const float b[2]) ATTR_WARN_UNUSED_RESULT
MINLINE void madd_v3_v3v3fl(float r[3], const float a[3], const float b[3], float f)
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
MINLINE void zero_v3(float r[3])
MINLINE void add_v3_v3(float r[3], const float a[3])
unsigned int uint
Definition: BLI_sys_types.h:67
#define UNPACK2(a)
#define ELEM(...)
int min_i(int a, int b)
Definition: Basic.c:6
struct Depsgraph Depsgraph
Definition: DEG_depsgraph.h:35
void DEG_id_tag_update(struct ID *id, int flag)
@ CU_BEZIER
@ CU_POLY
@ CU_NURBS
#define BEZT_DESEL_IDX(bezt, i)
#define BEZT_ISSEL_IDX(bezt, i)
#define BEZT_ISSEL_ANY_HIDDENHANDLES(v3d, bezt)
#define BEZT_SEL_ALL(bezt)
struct BPoint BPoint
#define BEZT_ISSEL_ANY(bezt)
#define CU_ACT_NONE
#define BEZT_DESEL_ALL(bezt)
@ CU_NURB_CYCLIC
@ HD_VECT
@ HD_FREE
@ HD_AUTO
@ HD_ALIGN
struct BezTriple BezTriple
#define BEZT_SEL_IDX(bezt, i)
@ CURVE_HANDLE_NONE
@ CURVE_HANDLE_SELECTED
@ OPERATOR_FINISHED
@ OPERATOR_RUNNING_MODAL
bool ED_operator_view3d_active(struct bContext *C)
Definition: screen_ops.c:225
@ SEL_OP_SET
void ED_view3d_win_to_3d_int(const struct View3D *v3d, const struct ARegion *region, const float depth_pt[3], const int mval[2], float r_out[3])
void ED_view3d_viewcontext_init(struct bContext *C, struct ViewContext *vc, struct Depsgraph *depsgraph)
@ V3D_PROJ_RET_CLIP_WIN
Definition: ED_view3d.h:227
@ V3D_PROJ_RET_CLIP_BB
Definition: ED_view3d.h:225
@ V3D_PROJ_RET_OK
Definition: ED_view3d.h:217
void ED_view3d_global_to_vector(const struct RegionView3D *rv3d, const float coord[3], float vec[3])
eV3DProjStatus ED_view3d_project_float_object(const struct ARegion *region, const float co[3], float r_co[2], eV3DProjTest flag)
void ED_view3d_win_to_3d(const struct View3D *v3d, const struct ARegion *region, const float depth_pt[3], const float mval[2], float r_out[3])
float ED_view3d_select_dist_px(void)
NSNotificationCenter * center
_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 GLsizei GLsizei GLenum type _GL_VOID_RET _GL_VOID GLsizei GLenum GLenum const void *pixels _GL_VOID_RET _GL_VOID const void *pointer _GL_VOID_RET _GL_VOID GLdouble v _GL_VOID_RET _GL_VOID GLfloat v _GL_VOID_RET _GL_VOID GLint GLint i2 _GL_VOID_RET _GL_VOID GLint j _GL_VOID_RET _GL_VOID GLfloat param _GL_VOID_RET _GL_VOID GLint param _GL_VOID_RET _GL_VOID GLdouble GLdouble GLdouble GLdouble GLdouble zFar _GL_VOID_RET _GL_UINT GLdouble *equation _GL_VOID_RET _GL_VOID GLenum GLint *params _GL_VOID_RET _GL_VOID GLenum GLfloat *v _GL_VOID_RET _GL_VOID GLenum GLfloat *params _GL_VOID_RET _GL_VOID GLfloat *values _GL_VOID_RET _GL_VOID GLushort *values _GL_VOID_RET _GL_VOID GLenum GLfloat *params _GL_VOID_RET _GL_VOID GLenum GLdouble *params _GL_VOID_RET _GL_VOID GLenum GLint *params _GL_VOID_RET _GL_VOID GLsizei const void *pointer _GL_VOID_RET _GL_VOID GLsizei const void *pointer _GL_VOID_RET _GL_BOOL GLfloat param _GL_VOID_RET _GL_VOID GLint param _GL_VOID_RET _GL_VOID GLenum GLfloat param _GL_VOID_RET _GL_VOID GLenum GLint param _GL_VOID_RET _GL_VOID GLushort pattern _GL_VOID_RET _GL_VOID GLdouble GLdouble GLint GLint const GLdouble *points _GL_VOID_RET _GL_VOID GLdouble GLdouble GLint GLint GLdouble GLdouble GLint GLint const GLdouble *points _GL_VOID_RET _GL_VOID GLdouble GLdouble u2 _GL_VOID_RET _GL_VOID GLdouble GLdouble GLint GLdouble GLdouble v2 _GL_VOID_RET _GL_VOID GLenum GLfloat param _GL_VOID_RET _GL_VOID GLenum GLint param _GL_VOID_RET _GL_VOID GLenum mode _GL_VOID_RET _GL_VOID GLdouble GLdouble nz _GL_VOID_RET _GL_VOID GLfloat GLfloat nz _GL_VOID_RET _GL_VOID GLint GLint nz _GL_VOID_RET _GL_VOID GLshort GLshort nz _GL_VOID_RET _GL_VOID GLsizei const void *pointer _GL_VOID_RET _GL_VOID GLsizei const GLfloat *values _GL_VOID_RET _GL_VOID GLsizei const GLushort *values _GL_VOID_RET _GL_VOID GLint param _GL_VOID_RET _GL_VOID const GLuint const GLclampf *priorities _GL_VOID_RET _GL_VOID GLdouble y _GL_VOID_RET _GL_VOID GLfloat y _GL_VOID_RET _GL_VOID GLint y _GL_VOID_RET _GL_VOID GLshort y _GL_VOID_RET _GL_VOID GLdouble GLdouble z _GL_VOID_RET _GL_VOID GLfloat GLfloat z _GL_VOID_RET _GL_VOID GLint GLint z _GL_VOID_RET _GL_VOID GLshort GLshort z _GL_VOID_RET _GL_VOID GLdouble GLdouble GLdouble w _GL_VOID_RET _GL_VOID GLfloat GLfloat GLfloat w _GL_VOID_RET _GL_VOID GLint GLint GLint w _GL_VOID_RET _GL_VOID GLshort GLshort GLshort w _GL_VOID_RET _GL_VOID GLdouble GLdouble GLdouble y2 _GL_VOID_RET _GL_VOID GLfloat GLfloat GLfloat y2 _GL_VOID_RET _GL_VOID GLint GLint GLint y2 _GL_VOID_RET _GL_VOID GLshort GLshort GLshort y2 _GL_VOID_RET _GL_VOID GLdouble GLdouble GLdouble z _GL_VOID_RET _GL_VOID GLdouble GLdouble z _GL_VOID_RET _GL_VOID GLuint *buffer _GL_VOID_RET _GL_VOID GLdouble t _GL_VOID_RET _GL_VOID GLfloat t _GL_VOID_RET _GL_VOID GLint t _GL_VOID_RET _GL_VOID GLshort t _GL_VOID_RET _GL_VOID GLdouble t
Read Guarded memory(de)allocation.
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 C
Definition: RandGen.cpp:25
@ KM_PRESS
Definition: WM_types.h:267
@ KM_DBL_CLICK
Definition: WM_types.h:270
@ KM_RELEASE
Definition: WM_types.h:268
@ OPTYPE_UNDO
Definition: WM_types.h:148
#define NC_GEOM
Definition: WM_types.h:343
#define ND_DATA
Definition: WM_types.h:456
#define ND_SELECT
Definition: WM_types.h:455
@ KM_SHIFT
Definition: WM_types.h:238
#define sinf(x)
Definition: cuda/compat.h:102
#define SELECT
void ed_dissolve_bez_segment(BezTriple *bezt_prev, BezTriple *bezt_next, const Nurb *nu, const Curve *cu, const uint span_len, const uint span_step[2])
Definition: editcurve.c:6550
bool curve_toggle_cyclic(View3D *v3d, ListBase *editnurb, int direction)
Definition: editcurve.c:5763
int ed_editcurve_addvert(Curve *cu, EditNurb *editnurb, View3D *v3d, const float location_init[3])
Definition: editcurve.c:5384
const Depsgraph * depsgraph
ListBase * object_editcurve_get(Object *ob)
Definition: editcurve.c:74
void ED_curve_beztcpy(EditNurb *editnurb, BezTriple *dst, BezTriple *src, int count)
Definition: editcurve.c:7049
bool ED_curve_editnurb_select_pick(bContext *C, const int mval[2], const int dist_px, const bool vert_without_handles, const struct SelectPick_Params *params)
Definition: editcurve.c:4729
void ED_curve_bpcpy(EditNurb *editnurb, BPoint *dst, BPoint *src, int count)
Definition: editcurve.c:7055
eClose_opt
@ ON_PRESS
@ ON_CLICK
@ OFF
static void insert_bp_to_nurb(Nurb *nu, const CutData *data, Curve *cu)
static bool update_cut_data_for_all_nurbs(const ViewContext *vc, const ListBase *nurbs, const float point[2], const float sel_dist, CutData *cd)
static void toggle_handle_types(BezTriple *bezt, int bezt_idx, CurvePenData *cpd)
static void move_bezt_by_displacement(BezTriple *bezt, const float disp_3d[3])
static bool insert_point_to_segment(const ViewContext *vc, const wmEvent *event)
static void delete_bp_from_nurb(const BPoint *bp, Nurb *nu, EditNurb *editnurb)
static void extrude_vertices_from_selected_endpoints(EditNurb *editnurb, ListBase *nurbs, Curve *cu, const float disp_3d[3])
static void delete_bezt_from_nurb(const BezTriple *bezt, Nurb *nu, EditNurb *editnurb)
static bool is_cyclic(const Nurb *nu)
static void extrude_points_from_selected_vertices(const ViewContext *vc, const wmEvent *event, const int extrude_handle)
static void move_adjacent_handle(ViewContext *vc, const wmEvent *event, ListBase *nurbs)
static void move_segment(ViewContext *vc, MoveSegmentData *seg_data, const wmEvent *event)
static void delete_nurb(Curve *cu, Nurb *nu)
static void toggle_select_bezt(BezTriple *bezt, const int bezt_idx, Curve *cu, Nurb *nu)
struct MoveSegmentData MoveSegmentData
static void screenspace_to_worldspace(const ViewContext *vc, const float pos_2d[2], const float depth[3], float r_pos_3d[3])
static void move_bezt_to_location(BezTriple *bezt, const float location[3])
static void move_all_selected_points(const ViewContext *vc, const wmEvent *event, CurvePenData *cpd, ListBase *nurbs, const bool bezt_only)
struct CutData CutData
static bool worldspace_to_screenspace(const ViewContext *vc, const float pos_3d[3], float r_pos_2d[2])
static void get_updated_data_for_edge(const float point[2], const float point1[2], const float point2[2], const int point_idx, const int resolu_idx, float *r_min_dist, int *r_min_i, float *r_param)
static CutData init_cut_data(const wmEvent *event)
static void toggle_bezt_free_align_handles(BezTriple *bezt)
static void toggle_sel_bezt_free_align_handles(ListBase *nurbs)
static void get_first_selected_point(Curve *cu, View3D *v3d, Nurb **r_nu, BezTriple **r_bezt, BPoint **r_bp)
static void deselect_all_center_vertices(ListBase *nurbs)
static void remove_handle_movement_constraints(BezTriple *bezt, const bool f1, const bool f3)
static void screenspace_to_worldspace_int(const ViewContext *vc, const int pos_2d[2], const float depth[3], float r_pos_3d[3])
struct CurvePenData CurvePenData
static int curve_pen_invoke(bContext *C, wmOperator *op, const wmEvent *event)
static void init_selected_bezt_handles(ListBase *nurbs)
static void move_bp_to_location(const ViewContext *vc, BPoint *bp, const float mval[2])
static bool is_last_bezt(const Nurb *nu, const BezTriple *bezt)
static int get_nurb_index(const ListBase *nurbs, const Nurb *nurb)
void CURVE_OT_pen(wmOperatorType *ot)
static void get_bezier_interpolated_point(const BezTriple *bezt1, const BezTriple *bezt2, const float parameter, float r_point[3])
#define FOREACH_SELECTED_BEZT_END
Definition: editcurve_pen.c:44
#define SEL_DIST_FACTOR
Definition: editcurve_pen.c:53
static void toggle_select_bp(BPoint *bp, Curve *cu, Nurb *nu)
static const EnumPropertyItem prop_close_spline_method[]
static void move_bezt_handle_or_vertex_by_displacement(const ViewContext *vc, BezTriple *bezt, const int bezt_idx, const float disp_2d[2], const float distance, const bool link_handles, const bool lock_angle)
static const EnumPropertyItem prop_handle_types[]
static int curve_pen_modal(bContext *C, wmOperator *op, const wmEvent *event)
static bool get_closest_vertex_to_point_in_nurbs(const ViewContext *vc, const ListBase *nurbs, const float point[2], Nurb **r_nu, BezTriple **r_bezt, BPoint **r_bp, int *r_bezt_idx)
static bool delete_point_under_mouse(ViewContext *vc, const wmEvent *event)
static bool is_spline_nearby(ViewContext *vc, struct wmOperator *op, const wmEvent *event, const float sel_dist)
static bool get_selected_center(const ListBase *nurbs, const bool mid_only, const bool bezt_only, float r_center[3])
static bool make_cyclic_if_endpoints(ViewContext *vc, Nurb *sel_nu, BezTriple *sel_bezt, BPoint *sel_bp)
#define FOREACH_SELECTED_BEZT_BEGIN(bezt, nurbs)
Definition: editcurve_pen.c:37
static void update_location_for_2d_curve(const ViewContext *vc, float location[3])
@ PEN_MODAL_LOCK_ANGLE
@ PEN_MODAL_LINK_HANDLES
@ PEN_MODAL_MOVE_ADJACENT
@ PEN_MODAL_MOVE_ENTIRE
@ PEN_MODAL_FREE_ALIGN_TOGGLE
static void cycle_handles(BezTriple *bezt)
static void insert_bezt_to_nurb(Nurb *nu, const CutData *data, Curve *cu)
static void update_cut_data_for_nurb(const ViewContext *vc, CutData *cd, Nurb *nu, const int resolu, const float point[2])
wmKeyMap * curve_pen_modal_keymap(wmKeyConfig *keyconf)
static void calculate_new_bezier_point(const float point_prev[3], float handle_prev[3], float new_left_handle[3], float new_right_handle[3], float handle_next[3], const float point_next[3], const float parameter)
bool ED_curve_deselect_all(EditNurb *editnurb)
uint pos
uiWidgetBaseParameters params[MAX_WIDGET_BASE_BATCH]
void(* MEM_freeN)(void *vmemh)
Definition: mallocn.c:27
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
#define fabsf(x)
Definition: metal/compat.h:219
static unsigned a[3]
Definition: RandGen.cpp:78
T distance(const T &a, const T &b)
const btScalar eps
Definition: poly34.cpp:11
return ret
bool RNA_boolean_get(PointerRNA *ptr, const char *name)
Definition: rna_access.c:4863
int RNA_enum_get(PointerRNA *ptr, const char *name)
Definition: rna_access.c:5004
PropertyRNA * RNA_def_boolean(StructOrFunctionRNA *cont_, const char *identifier, bool default_value, const char *ui_name, const char *ui_description)
Definition: rna_define.c:3493
PropertyRNA * RNA_def_enum(StructOrFunctionRNA *cont_, const char *identifier, const EnumPropertyItem *items, int default_value, const char *ui_name, const char *ui_description)
Definition: rna_define.c:3783
short hide
float weight
uint8_t f1
float vec[4]
float radius
float tilt
uint8_t h1
float vec[3][3]
uint8_t h2
MoveSegmentData * msd
Definition: editcurve_pen.c:88
bool spline_nearby
Definition: editcurve_pen.c:94
BezTriple * bezt
EditNurb * editnurb
float next_loc[3]
Definition: editcurve_pen.c:70
float parameter
Definition: editcurve_pen.c:66
float min_dist
Definition: editcurve_pen.c:64
bool has_next
Definition: editcurve_pen.c:68
int bezt_index
Definition: editcurve_pen.c:60
float prev_loc[3]
Definition: editcurve_pen.c:70
float cut_loc[3]
Definition: editcurve_pen.c:70
bool has_prev
Definition: editcurve_pen.c:68
Nurb * nurb
Definition: editcurve_pen.c:62
int bp_index
Definition: editcurve_pen.c:60
float mval[2]
Definition: editcurve_pen.c:72
struct GHash * keyindex
ListBase nurbs
void * last
Definition: DNA_listBase.h:31
short flagu
short type
BezTriple * bezt
BPoint * bp
short hide
float obmat[4][4]
void * data
float persmat[4][4]
View3DCursor cursor
View3DOverlay overlay
int mval[2]
Definition: ED_view3d.h:74
struct Scene * scene
Definition: ED_view3d.h:65
struct ARegion * region
Definition: ED_view3d.h:69
struct Object * obedit
Definition: ED_view3d.h:68
struct View3D * v3d
Definition: ED_view3d.h:70
struct RegionView3D * rv3d
Definition: ED_view3d.h:72
short val
Definition: WM_types.h:680
int xy[2]
Definition: WM_types.h:682
int mval[2]
Definition: WM_types.h:684
int prev_xy[2]
Definition: WM_types.h:728
int prev_press_xy[2]
Definition: WM_types.h:738
short type
Definition: WM_types.h:678
const void * modal_items
int(* invoke)(struct bContext *, struct wmOperator *, const struct wmEvent *) ATTR_WARN_UNUSED_RESULT
Definition: WM_types.h:919
const char * name
Definition: WM_types.h:888
int(* modal)(struct bContext *, struct wmOperator *, const struct wmEvent *) ATTR_WARN_UNUSED_RESULT
Definition: WM_types.h:935
const char * idname
Definition: WM_types.h:890
bool(* poll)(struct bContext *) ATTR_WARN_UNUSED_RESULT
Definition: WM_types.h:943
struct StructRNA * srna
Definition: WM_types.h:969
const char * description
Definition: WM_types.h:893
struct PointerRNA * ptr
bool WM_event_drag_test(const wmEvent *event, const int prev_xy[2])
wmEventHandler_Op * WM_event_add_modal_handler(bContext *C, wmOperator *op)
void WM_event_add_notifier(const bContext *C, uint type, void *reference)
#define ISMOUSE_MOTION(event_type)
@ EVT_MODAL_MAP
@ LEFTMOUSE
wmOperatorType * ot
Definition: wm_files.c:3479
wmKeyMap * WM_modalkeymap_find(wmKeyConfig *keyconf, const char *idname)
Definition: wm_keymap.c:914
void WM_modalkeymap_assign(wmKeyMap *km, const char *opname)
Definition: wm_keymap.c:985
wmKeyMap * WM_modalkeymap_ensure(wmKeyConfig *keyconf, const char *idname, const EnumPropertyItem *items)
Definition: wm_keymap.c:888
void WM_operator_properties_mouse_select(wmOperatorType *ot)