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