Blender V4.3
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
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]);
267 normalize_v3_length(disp_3d, distance);
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 = (BezTriple *)MEM_mallocN((nu->pntsu + 1) * sizeof(BezTriple),
605 __func__);
606 const int index = data->bezt_index + 1;
607 /* Copy all control points before the cut to the new memory. */
608 ED_curve_beztcpy(editnurb, new_bezt_array, nu->bezt, index);
609 BezTriple *new_bezt = new_bezt_array + index;
610
611 /* Duplicate control point after the cut. */
612 ED_curve_beztcpy(editnurb, new_bezt, new_bezt - 1, 1);
613 copy_v3_v3(new_bezt->vec[1], data->cut_loc);
614
615 if (index < nu->pntsu) {
616 /* Copy all control points after the cut to the new memory. */
617 ED_curve_beztcpy(editnurb, new_bezt_array + index + 1, nu->bezt + index, nu->pntsu - index);
618 }
619
620 nu->pntsu += 1;
621 BKE_curve_nurb_vert_active_set(cu, nu, nu->bezt + index);
622
623 BezTriple *next_bezt;
624 if (is_cyclic(nu) && (index == nu->pntsu - 1)) {
625 next_bezt = new_bezt_array;
626 }
627 else {
628 next_bezt = new_bezt + 1;
629 }
630
631 /* Interpolate radius, tilt, weight */
632 new_bezt->tilt = interpf(next_bezt->tilt, (new_bezt - 1)->tilt, data->parameter);
633 new_bezt->radius = interpf(next_bezt->radius, (new_bezt - 1)->radius, data->parameter);
634 new_bezt->weight = interpf(next_bezt->weight, (new_bezt - 1)->weight, data->parameter);
635
636 new_bezt->h1 = new_bezt->h2 = HD_ALIGN;
637
638 calculate_new_bezier_point((new_bezt - 1)->vec[1],
639 (new_bezt - 1)->vec[2],
640 new_bezt->vec[0],
641 new_bezt->vec[2],
642 next_bezt->vec[0],
643 next_bezt->vec[1],
644 data->parameter);
645
646 MEM_freeN(nu->bezt);
647 nu->bezt = new_bezt_array;
648 ED_curve_deselect_all(editnurb);
650 BEZT_SEL_IDX(new_bezt, 1);
651}
652
656static void insert_bp_to_nurb(Nurb *nu, const CutData *data, Curve *cu)
657{
658 EditNurb *editnurb = cu->editnurb;
659
660 BPoint *new_bp_array = (BPoint *)MEM_mallocN((nu->pntsu + 1) * sizeof(BPoint), __func__);
661 const int index = data->bp_index + 1;
662 /* Copy all control points before the cut to the new memory. */
663 ED_curve_bpcpy(editnurb, new_bp_array, nu->bp, index);
664 BPoint *new_bp = new_bp_array + index;
665
666 /* Duplicate control point after the cut. */
667 ED_curve_bpcpy(editnurb, new_bp, new_bp - 1, 1);
668 copy_v3_v3(new_bp->vec, data->cut_loc);
669
670 if (index < nu->pntsu) {
671 /* Copy all control points after the cut to the new memory. */
672 ED_curve_bpcpy(editnurb, new_bp_array + index + 1, nu->bp + index, (nu->pntsu - index));
673 }
674
675 nu->pntsu += 1;
676 BKE_curve_nurb_vert_active_set(cu, nu, nu->bp + index);
677
678 BPoint *next_bp;
679 if (is_cyclic(nu) && (index == nu->pntsu - 1)) {
680 next_bp = new_bp_array;
681 }
682 else {
683 next_bp = new_bp + 1;
684 }
685
686 /* Interpolate radius, tilt, weight */
687 new_bp->tilt = interpf(next_bp->tilt, (new_bp - 1)->tilt, data->parameter);
688 new_bp->radius = interpf(next_bp->radius, (new_bp - 1)->radius, data->parameter);
689 new_bp->weight = interpf(next_bp->weight, (new_bp - 1)->weight, data->parameter);
690
691 MEM_freeN(nu->bp);
692 nu->bp = new_bp_array;
693 ED_curve_deselect_all(editnurb);
695 new_bp->f1 |= SELECT;
696}
697
709static void get_updated_data_for_edge(const float point[2],
710 const float point1[2],
711 const float point2[2],
712 const int point_idx,
713 const int resolu_idx,
714 float *r_min_dist,
715 int *r_min_i,
716 float *r_param)
717{
718 float edge[2], vec1[2], vec2[2];
719 sub_v2_v2v2(edge, point1, point2);
720 sub_v2_v2v2(vec1, point1, point);
721 sub_v2_v2v2(vec2, point, point2);
722 const float len_vec1 = len_v2(vec1);
723 const float len_vec2 = len_v2(vec2);
724 const float dot1 = dot_v2v2(edge, vec1);
725 const float dot2 = dot_v2v2(edge, vec2);
726
727 /* Signs of dot products being equal implies that the angles formed with the external point are
728 * either both acute or both obtuse, meaning the external point is closer to a point on the edge
729 * rather than an endpoint. */
730 if ((dot1 > 0) == (dot2 > 0)) {
731 const float perp_dist = len_vec1 * sinf(angle_v2v2(vec1, edge));
732 if (*r_min_dist > perp_dist) {
733 *r_min_dist = perp_dist;
734 *r_min_i = point_idx;
735 *r_param = resolu_idx + len_vec1 * cos_v2v2v2(point, point1, point2) / len_v2(edge);
736 }
737 }
738 else {
739 if (*r_min_dist > len_vec2) {
740 *r_min_dist = len_vec2;
741 *r_min_i = point_idx;
742 *r_param = resolu_idx;
743 }
744 }
745}
746
751 const ViewContext *vc, CutData *cd, Nurb *nu, const int resolu, const float point[2])
752{
753 float min_dist = cd->min_dist, param = 0.0f;
754 int min_i = 0;
755 const int end = is_cyclic(nu) ? nu->pntsu : nu->pntsu - 1;
756
757 if (nu->type == CU_BEZIER) {
758 for (int i = 0; i < end; i++) {
759 float *points = static_cast<float *>(MEM_mallocN(sizeof(float[3]) * (resolu + 1), __func__));
760
761 const BezTriple *bezt1 = nu->bezt + i;
762 const BezTriple *bezt2 = nu->bezt + (i + 1) % nu->pntsu;
763
764 /* Calculate all points on curve. */
765 for (int j = 0; j < 3; j++) {
767 bezt1->vec[2][j],
768 bezt2->vec[0][j],
769 bezt2->vec[1][j],
770 points + j,
771 resolu,
772 sizeof(float[3]));
773 }
774
775 float point1[2], point2[2];
776 worldspace_to_screenspace(vc, points, point1);
777 const float len_vec1 = len_v2v2(point, point1);
778
779 if (min_dist > len_vec1) {
780 min_dist = len_vec1;
781 min_i = i;
782 param = 0;
783 }
784
785 for (int j = 0; j < resolu; j++) {
786 worldspace_to_screenspace(vc, points + 3 * (j + 1), point2);
787 get_updated_data_for_edge(point, point1, point2, i, j, &min_dist, &min_i, &param);
788 copy_v2_v2(point1, point2);
789 }
790
791 MEM_freeN(points);
792 }
793 if (cd->min_dist > min_dist) {
794 cd->min_dist = min_dist;
795 cd->nurb = nu;
796 cd->bezt_index = min_i;
797 cd->parameter = param / resolu;
798 }
799 }
800 else {
801 float point1[2], point2[2];
802 worldspace_to_screenspace(vc, nu->bp->vec, point1);
803 for (int i = 0; i < end; i++) {
804 worldspace_to_screenspace(vc, (nu->bp + (i + 1) % nu->pntsu)->vec, point2);
805 get_updated_data_for_edge(point, point1, point2, i, 0, &min_dist, &min_i, &param);
806 copy_v2_v2(point1, point2);
807 }
808
809 if (cd->min_dist > min_dist) {
810 cd->min_dist = min_dist;
811 cd->nurb = nu;
812 cd->bp_index = min_i;
813 cd->parameter = param;
814 }
815 }
816}
817
818/* Update #CutData for all the Nurbs in the curve. */
820 const ListBase *nurbs,
821 const float point[2],
822 const float sel_dist,
823 CutData *cd)
824{
825 cd->min_dist = FLT_MAX;
826 LISTBASE_FOREACH (Nurb *, nu, nurbs) {
827 update_cut_data_for_nurb(vc, cd, nu, nu->resolu, point);
828 }
829
830 return cd->min_dist < sel_dist;
831}
832
833static CutData init_cut_data(const wmEvent *event)
834{
835 CutData cd{};
836 cd.bezt_index = 0;
837 cd.bp_index = 0;
838 cd.min_dist = FLT_MAX;
839 cd.parameter = 0.5f;
840 cd.has_prev = false;
841 cd.has_next = false;
842 cd.mval[0] = event->mval[0];
843 cd.mval[1] = event->mval[1];
844 return cd;
845}
846
847static bool insert_point_to_segment(const ViewContext *vc, const wmEvent *event)
848{
849 Curve *cu = static_cast<Curve *>(vc->obedit->data);
850 CutData cd = init_cut_data(event);
851 const float mval[2] = {float(event->mval[0]), float(event->mval[1])};
852 const float threshold_dist_px = ED_view3d_select_dist_px() * SEL_DIST_FACTOR;
853 const bool near_spline = update_cut_data_for_all_nurbs(
854 vc, BKE_curve_editNurbs_get(cu), mval, threshold_dist_px, &cd);
855
856 if (near_spline && !cd.nurb->hide) {
857 Nurb *nu = cd.nurb;
858 if (nu->type == CU_BEZIER) {
859 cd.min_dist = FLT_MAX;
860 /* Update cut data at a higher resolution for better accuracy. */
861 update_cut_data_for_nurb(vc, &cd, cd.nurb, 25, mval);
862
864 &nu->bezt[(cd.bezt_index + 1) % (nu->pntsu)],
865 cd.parameter,
866 cd.cut_loc);
867
868 insert_bezt_to_nurb(nu, &cd, cu);
869 }
870 else {
872 (nu->bp + cd.bp_index)->vec,
873 (nu->bp + (cd.bp_index + 1) % nu->pntsu)->vec,
874 cd.parameter);
875 insert_bp_to_nurb(nu, &cd, cu);
876 }
877 return true;
878 }
879
880 return false;
881}
882
888 Curve *cu, View3D *v3d, Nurb **r_nu, BezTriple **r_bezt, BPoint **r_bp)
889{
890 ListBase *nurbs = &cu->editnurb->nurbs;
891 BezTriple *bezt;
892 BPoint *bp;
893 int a;
894
895 *r_nu = nullptr;
896 *r_bezt = nullptr;
897 *r_bp = nullptr;
898
899 LISTBASE_FOREACH (Nurb *, nu, nurbs) {
900 if (nu->type == CU_BEZIER) {
901 bezt = nu->bezt;
902 a = nu->pntsu;
903 while (a--) {
904 if (BEZT_ISSEL_ANY_HIDDENHANDLES(v3d, bezt)) {
905 if (*r_bezt || *r_bp) {
906 *r_bp = nullptr;
907 *r_bezt = nullptr;
908 return;
909 }
910 *r_bezt = bezt;
911 *r_nu = nu;
912 }
913 bezt++;
914 }
915 }
916 else {
917 bp = nu->bp;
918 a = nu->pntsu * nu->pntsv;
919 while (a--) {
920 if (bp->f1 & SELECT) {
921 if (*r_bezt || *r_bp) {
922 *r_bp = nullptr;
923 *r_bezt = nullptr;
924 return;
925 }
926 *r_bp = bp;
927 *r_nu = nu;
928 }
929 bp++;
930 }
931 }
932 }
933}
934
936 ListBase *nurbs,
937 Curve *cu,
938 const float disp_3d[3])
939{
940 int nu_index = 0;
941 LISTBASE_FOREACH (Nurb *, nu1, nurbs) {
942 if (nu1->type == CU_BEZIER) {
943 BezTriple *last_bezt = nu1->bezt + nu1->pntsu - 1;
944 const bool first_sel = BEZT_ISSEL_ANY(nu1->bezt);
945 const bool last_sel = BEZT_ISSEL_ANY(last_bezt) && nu1->pntsu > 1;
946 if (first_sel) {
947 if (last_sel) {
948 BezTriple *new_bezt = (BezTriple *)MEM_mallocN((nu1->pntsu + 2) * sizeof(BezTriple),
949 __func__);
950 ED_curve_beztcpy(editnurb, new_bezt, nu1->bezt, 1);
951 ED_curve_beztcpy(editnurb, new_bezt + nu1->pntsu + 1, last_bezt, 1);
952 BEZT_DESEL_ALL(nu1->bezt);
953 BEZT_DESEL_ALL(last_bezt);
954 ED_curve_beztcpy(editnurb, new_bezt + 1, nu1->bezt, nu1->pntsu);
955
956 move_bezt_by_displacement(new_bezt, disp_3d);
957 move_bezt_by_displacement(new_bezt + nu1->pntsu + 1, disp_3d);
958 MEM_freeN(nu1->bezt);
959 nu1->bezt = new_bezt;
960 nu1->pntsu += 2;
961
962 /* Set the new points selection. */
963 BEZT_DESEL_ALL(new_bezt);
964 BEZT_SEL_IDX(new_bezt, 0);
965
966 BEZT_DESEL_ALL(new_bezt + (nu1->pntsu - 1));
967 BEZT_SEL_IDX(new_bezt + (nu1->pntsu - 1), 2);
968 }
969 else {
970 BezTriple *new_bezt = (BezTriple *)MEM_mallocN((nu1->pntsu + 1) * sizeof(BezTriple),
971 __func__);
972 ED_curve_beztcpy(editnurb, new_bezt, nu1->bezt, 1);
973 BEZT_DESEL_ALL(nu1->bezt);
974 ED_curve_beztcpy(editnurb, new_bezt + 1, nu1->bezt, nu1->pntsu);
975 move_bezt_by_displacement(new_bezt, disp_3d);
976 MEM_freeN(nu1->bezt);
977 nu1->bezt = new_bezt;
978 nu1->pntsu++;
979
980 /* Set the new points selection. */
981 BEZT_DESEL_ALL(new_bezt);
982 BEZT_SEL_IDX(new_bezt, 0);
983 }
984 cu->actnu = nu_index;
985 cu->actvert = 0;
986 }
987 else if (last_sel) {
988 BezTriple *new_bezt = (BezTriple *)MEM_mallocN((nu1->pntsu + 1) * sizeof(BezTriple),
989 __func__);
990 ED_curve_beztcpy(editnurb, new_bezt + nu1->pntsu, last_bezt, 1);
991 BEZT_DESEL_ALL(last_bezt);
992 ED_curve_beztcpy(editnurb, new_bezt, nu1->bezt, nu1->pntsu);
993 move_bezt_by_displacement(new_bezt + nu1->pntsu, disp_3d);
994 MEM_freeN(nu1->bezt);
995 nu1->bezt = new_bezt;
996 nu1->pntsu++;
997 cu->actnu = nu_index;
998 cu->actvert = nu1->pntsu - 1;
999
1000 /* Set the new points selection. */
1001 BEZT_DESEL_ALL(new_bezt + (nu1->pntsu - 1));
1002 BEZT_SEL_IDX(new_bezt + (nu1->pntsu - 1), 2);
1003 }
1004 }
1005 else {
1006 BPoint *last_bp = nu1->bp + nu1->pntsu - 1;
1007 const bool first_sel = nu1->bp->f1 & SELECT;
1008 const bool last_sel = last_bp->f1 & SELECT && nu1->pntsu > 1;
1009 if (first_sel) {
1010 if (last_sel) {
1011 BPoint *new_bp = (BPoint *)MEM_mallocN((nu1->pntsu + 2) * sizeof(BPoint), __func__);
1012 ED_curve_bpcpy(editnurb, new_bp, nu1->bp, 1);
1013 ED_curve_bpcpy(editnurb, new_bp + nu1->pntsu + 1, last_bp, 1);
1014 nu1->bp->f1 &= ~SELECT;
1015 last_bp->f1 &= ~SELECT;
1016 ED_curve_bpcpy(editnurb, new_bp + 1, nu1->bp, nu1->pntsu);
1017 add_v3_v3(new_bp->vec, disp_3d);
1018 add_v3_v3((new_bp + nu1->pntsu + 1)->vec, disp_3d);
1019 MEM_freeN(nu1->bp);
1020 nu1->bp = new_bp;
1021 nu1->pntsu += 2;
1022 }
1023 else {
1024 BPoint *new_bp = (BPoint *)MEM_mallocN((nu1->pntsu + 1) * sizeof(BPoint), __func__);
1025 ED_curve_bpcpy(editnurb, new_bp, nu1->bp, 1);
1026 nu1->bp->f1 &= ~SELECT;
1027 ED_curve_bpcpy(editnurb, new_bp + 1, nu1->bp, nu1->pntsu);
1028 add_v3_v3(new_bp->vec, disp_3d);
1029 MEM_freeN(nu1->bp);
1030 nu1->bp = new_bp;
1031 nu1->pntsu++;
1032 }
1034 cu->actnu = nu_index;
1035 cu->actvert = 0;
1036 }
1037 else if (last_sel) {
1038 BPoint *new_bp = (BPoint *)MEM_mallocN((nu1->pntsu + 1) * sizeof(BPoint), __func__);
1039 ED_curve_bpcpy(editnurb, new_bp, nu1->bp, nu1->pntsu);
1040 ED_curve_bpcpy(editnurb, new_bp + nu1->pntsu, last_bp, 1);
1041 last_bp->f1 &= ~SELECT;
1042 ED_curve_bpcpy(editnurb, new_bp, nu1->bp, nu1->pntsu);
1043 add_v3_v3((new_bp + nu1->pntsu)->vec, disp_3d);
1044 MEM_freeN(nu1->bp);
1045 nu1->bp = new_bp;
1046 nu1->pntsu++;
1048 cu->actnu = nu_index;
1049 cu->actvert = nu1->pntsu - 1;
1050 }
1052 }
1053 nu_index++;
1054 }
1055}
1056
1061{
1062 LISTBASE_FOREACH (Nurb *, nu1, nurbs) {
1063 if (nu1->pntsu > 1) {
1064 int start, end;
1065 if (is_cyclic(nu1)) {
1066 start = 0;
1067 end = nu1->pntsu;
1068 }
1069 else {
1070 start = 1;
1071 end = nu1->pntsu - 1;
1072 }
1073 for (int i = start; i < end; i++) {
1074 if (nu1->type == CU_BEZIER) {
1075 BEZT_DESEL_ALL(nu1->bezt + i);
1076 }
1077 else {
1078 (nu1->bp + i)->f1 &= ~SELECT;
1079 }
1080 }
1081 }
1082 }
1083}
1084
1085static bool is_last_bezt(const Nurb *nu, const BezTriple *bezt)
1086{
1087 return nu->pntsu > 1 && nu->bezt + nu->pntsu - 1 == bezt && !is_cyclic(nu);
1088}
1089
1094 const wmEvent *event,
1095 const int extrude_handle)
1096{
1097 Curve *cu = static_cast<Curve *>(vc->obedit->data);
1098 ListBase *nurbs = BKE_curve_editNurbs_get(cu);
1099 float center[3] = {0.0f, 0.0f, 0.0f};
1101 bool sel_exists = get_selected_center(nurbs, true, false, center);
1102
1103 float location[3];
1104 if (sel_exists) {
1105 mul_v3_m4v3(location, vc->obedit->object_to_world().ptr(), center);
1106 }
1107 else {
1108 copy_v3_v3(location, vc->scene->cursor.location);
1109 }
1110
1111 ED_view3d_win_to_3d_int(vc->v3d, vc->region, location, event->mval, location);
1112
1113 update_location_for_2d_curve(vc, location);
1114 EditNurb *editnurb = cu->editnurb;
1115
1116 if (sel_exists) {
1117 float disp_3d[3];
1118 sub_v3_v3v3(disp_3d, location, center);
1119 /* Reimplemented due to unexpected behavior for extrusion of 2-point spline. */
1120 extrude_vertices_from_selected_endpoints(editnurb, nurbs, cu, disp_3d);
1121 }
1122 else {
1123 Nurb *old_last_nu = static_cast<Nurb *>(editnurb->nurbs.last);
1124 ed_editcurve_addvert(cu, editnurb, vc->v3d, location);
1125 Nurb *new_last_nu = static_cast<Nurb *>(editnurb->nurbs.last);
1126
1127 if (old_last_nu != new_last_nu) {
1129 new_last_nu,
1130 new_last_nu->bezt ? (const void *)new_last_nu->bezt :
1131 (const void *)new_last_nu->bp);
1132 new_last_nu->flagu = ~CU_NURB_CYCLIC;
1133 }
1134 }
1135
1137 if (bezt) {
1138 bezt->h1 = extrude_handle;
1139 bezt->h2 = extrude_handle;
1140 }
1141 }
1143}
1144
1149 wmOperator *op,
1150 const wmEvent *event,
1151 const float sel_dist)
1152{
1153 Curve *cu = static_cast<Curve *>(vc->obedit->data);
1154 ListBase *nurbs = BKE_curve_editNurbs_get(cu);
1155 CutData cd = init_cut_data(event);
1156
1157 const float mval[2] = {float(event->mval[0]), float(event->mval[1])};
1158 const bool nearby = update_cut_data_for_all_nurbs(vc, nurbs, mval, sel_dist, &cd);
1159
1160 if (nearby) {
1161 if (cd.nurb && (cd.nurb->type == CU_BEZIER) && RNA_boolean_get(op->ptr, "move_segment")) {
1162 MoveSegmentData *seg_data;
1163 CurvePenData *cpd = (CurvePenData *)(op->customdata);
1164 cpd->msd = seg_data = static_cast<MoveSegmentData *>(
1165 MEM_callocN(sizeof(MoveSegmentData), __func__));
1166 seg_data->bezt_index = cd.bezt_index;
1167 seg_data->nu = cd.nurb;
1168 seg_data->t = cd.parameter;
1169 }
1170 return true;
1171 }
1172 return false;
1173}
1174
1175static void move_segment(const ViewContext *vc, MoveSegmentData *seg_data, const wmEvent *event)
1176{
1177 Nurb *nu = seg_data->nu;
1178 BezTriple *bezt1 = nu->bezt + seg_data->bezt_index;
1179 BezTriple *bezt2 = BKE_nurb_bezt_get_next(nu, bezt1);
1180
1181 int h1 = 2, h2 = 0;
1182 if (bezt1->hide) {
1183 if (bezt2->hide) {
1184 return;
1185 }
1186 /*
1187 * Swap bezt1 and bezt2 in all calculations if only bezt2 is visible.
1188 * (The first point needs to be visible for the calculations of the second point to be valid)
1189 */
1190 BezTriple *temp_bezt = bezt2;
1191 bezt2 = bezt1;
1192 bezt1 = temp_bezt;
1193 h1 = 0;
1194 h2 = 2;
1195 }
1196
1197 const float t = max_ff(min_ff(seg_data->t, 0.9f), 0.1f);
1198 const float t_sq = t * t;
1199 const float t_cu = t_sq * t;
1200 const float one_minus_t = 1 - t;
1201 const float one_minus_t_sq = one_minus_t * one_minus_t;
1202 const float one_minus_t_cu = one_minus_t_sq * one_minus_t;
1203
1204 float mouse_3d[3];
1205 float depth[3];
1206 /* Use the center of the spline segment as depth. */
1207 get_bezier_interpolated_point(bezt1, bezt2, t, depth);
1208 screenspace_to_worldspace_int(vc, event->mval, depth, mouse_3d);
1209
1210 /*
1211 * Equation of Bezier Curve
1212 * => B(t) = (1-t)^3 * P0 + 3(1-t)^2 * t * P1 + 3(1-t) * t^2 * P2 + t^3 * P3
1213 *
1214 * Mouse location (Say Pm) should satisfy this equation.
1215 * Therefore => (1/t - 1) * P1 + P2 = (Pm - (1 - t)^3 * P0 - t^3 * P3) / [3 * (1 - t) * t^2] = k1
1216 * (in code)
1217 *
1218 * Another constraint is required to identify P1 and P2.
1219 * The constraint used is that the vector between P1 and P2 doesn't change.
1220 * Therefore => P1 - P2 = k2
1221 *
1222 * From the two equations => P1 = t(k1 + k2) and P2 = P1 - K2
1223 */
1224
1225 float k1[3];
1226 const float denom = 3.0f * one_minus_t * t_sq;
1227 k1[0] = (mouse_3d[0] - one_minus_t_cu * bezt1->vec[1][0] - t_cu * bezt2->vec[1][0]) / denom;
1228 k1[1] = (mouse_3d[1] - one_minus_t_cu * bezt1->vec[1][1] - t_cu * bezt2->vec[1][1]) / denom;
1229 k1[2] = (mouse_3d[2] - one_minus_t_cu * bezt1->vec[1][2] - t_cu * bezt2->vec[1][2]) / denom;
1230
1231 float k2[3];
1232 sub_v3_v3v3(k2, bezt1->vec[h1], bezt2->vec[h2]);
1233
1234 if (!bezt1->hide) {
1235 /* P1 = t(k1 + k2) */
1236 add_v3_v3v3(bezt1->vec[h1], k1, k2);
1237 mul_v3_fl(bezt1->vec[h1], t);
1238
1239 remove_handle_movement_constraints(bezt1, true, true);
1240
1241 /* Move opposite handle as well if type is align. */
1242 if (bezt1->h1 == HD_ALIGN) {
1243 float handle_vec[3];
1244 sub_v3_v3v3(handle_vec, bezt1->vec[1], bezt1->vec[h1]);
1245 normalize_v3_length(handle_vec, len_v3v3(bezt1->vec[1], bezt1->vec[h2]));
1246 add_v3_v3v3(bezt1->vec[h2], bezt1->vec[1], handle_vec);
1247 }
1248 }
1249
1250 if (!bezt2->hide) {
1251 /* P2 = P1 - K2 */
1252 sub_v3_v3v3(bezt2->vec[h2], bezt1->vec[h1], k2);
1253
1254 remove_handle_movement_constraints(bezt2, true, true);
1255
1256 /* Move opposite handle as well if type is align. */
1257 if (bezt2->h2 == HD_ALIGN) {
1258 float handle_vec[3];
1259 sub_v3_v3v3(handle_vec, bezt2->vec[1], bezt2->vec[h2]);
1260 normalize_v3_length(handle_vec, len_v3v3(bezt2->vec[1], bezt2->vec[h1]));
1261 add_v3_v3v3(bezt2->vec[h1], bezt2->vec[1], handle_vec);
1262 }
1263 }
1264}
1265
1270{
1271 if (bezt->h1 != HD_FREE || bezt->h2 != HD_FREE) {
1272 bezt->h1 = bezt->h2 = HD_FREE;
1273 }
1274 else {
1275 bezt->h1 = bezt->h2 = HD_ALIGN;
1276 }
1277}
1278
1289
1293static bool delete_point_under_mouse(const ViewContext *vc, const wmEvent *event)
1294{
1295 BezTriple *bezt = nullptr;
1296 BPoint *bp = nullptr;
1297 Nurb *nu = nullptr;
1298 int temp = 0;
1299 Curve *cu = static_cast<Curve *>(vc->obedit->data);
1300 EditNurb *editnurb = cu->editnurb;
1301 ListBase *nurbs = BKE_curve_editNurbs_get(cu);
1302 const float mouse_point[2] = {float(event->mval[0]), float(event->mval[1])};
1303
1304 get_closest_vertex_to_point_in_nurbs(vc, nurbs, mouse_point, &nu, &bezt, &bp, &temp);
1305 const bool found_point = nu != nullptr;
1306
1307 bool deleted = false;
1308 if (found_point) {
1310 if (nu) {
1311 if (nu->type == CU_BEZIER) {
1312 BezTriple *next_bezt = BKE_nurb_bezt_get_next(nu, bezt);
1313 BezTriple *prev_bezt = BKE_nurb_bezt_get_prev(nu, bezt);
1314 if (next_bezt && prev_bezt) {
1315 const int bez_index = BKE_curve_nurb_vert_index_get(nu, bezt);
1316 const uint span_step[2] = {uint(bez_index), uint(bez_index)};
1317 ed_dissolve_bez_segment(prev_bezt, next_bezt, nu, cu, 1, span_step);
1318 }
1319 delete_bezt_from_nurb(bezt, nu, editnurb);
1320 }
1321 else {
1322 delete_bp_from_nurb(bp, nu, editnurb);
1323 }
1324
1325 if (nu->pntsu == 0) {
1326 delete_nurb(cu, nu);
1327 nu = nullptr;
1328 }
1329 deleted = true;
1330 cu->actvert = CU_ACT_NONE;
1331 }
1332 }
1333
1334 if (nu && nu->type == CU_BEZIER) {
1336 }
1337
1338 return deleted;
1339}
1340
1341static void move_adjacent_handle(const ViewContext *vc, const wmEvent *event, ListBase *nurbs)
1342{
1343 FOREACH_SELECTED_BEZT_BEGIN (bezt, nurbs) {
1344 BezTriple *adj_bezt;
1345 int bezt_idx;
1346 if (nu->pntsu == 1) {
1347 continue;
1348 }
1349 if (nu->bezt == bezt) {
1350 adj_bezt = BKE_nurb_bezt_get_next(nu, bezt);
1351 bezt_idx = 0;
1352 }
1353 else if (nu->bezt + nu->pntsu - 1 == bezt) {
1354 adj_bezt = BKE_nurb_bezt_get_prev(nu, bezt);
1355 bezt_idx = 2;
1356 }
1357 else {
1358 if (BEZT_ISSEL_IDX(bezt, 0)) {
1359 adj_bezt = BKE_nurb_bezt_get_prev(nu, bezt);
1360 bezt_idx = 2;
1361 }
1362 else if (BEZT_ISSEL_IDX(bezt, 2)) {
1363 adj_bezt = BKE_nurb_bezt_get_next(nu, bezt);
1364 bezt_idx = 0;
1365 }
1366 else {
1367 continue;
1368 }
1369 }
1370 adj_bezt->h1 = adj_bezt->h2 = HD_FREE;
1371
1372 int displacement[2];
1373 sub_v2_v2v2_int(displacement, event->xy, event->prev_xy);
1374 const float disp_fl[2] = {float(displacement[0]), float(displacement[1])};
1376 vc, adj_bezt, bezt_idx, disp_fl, 0.0f, false, false);
1378 }
1380}
1381
1386 Nurb *sel_nu,
1387 BezTriple *sel_bezt,
1388 BPoint *sel_bp)
1389{
1390 if (sel_bezt || (sel_bp && sel_nu->pntsu > 2)) {
1391 const bool is_bezt_endpoint = ((sel_nu->type == CU_BEZIER) &&
1392 ELEM(sel_bezt, sel_nu->bezt, sel_nu->bezt + sel_nu->pntsu - 1));
1393 const bool is_bp_endpoint = ((sel_nu->type != CU_BEZIER) &&
1394 ELEM(sel_bp, sel_nu->bp, sel_nu->bp + sel_nu->pntsu - 1));
1395 if (!(is_bezt_endpoint || is_bp_endpoint)) {
1396 return false;
1397 }
1398
1399 Nurb *nu = nullptr;
1400 BezTriple *bezt = nullptr;
1401 BPoint *bp = nullptr;
1402 Curve *cu = static_cast<Curve *>(vc->obedit->data);
1403 int bezt_idx;
1404 const float mval_fl[2] = {float(vc->mval[0]), float(vc->mval[1])};
1405
1407 vc, &(cu->editnurb->nurbs), mval_fl, &nu, &bezt, &bp, &bezt_idx);
1408
1409 if (nu == sel_nu &&
1410 ((nu->type == CU_BEZIER && bezt != sel_bezt &&
1411 ELEM(bezt, nu->bezt, nu->bezt + nu->pntsu - 1) && bezt_idx == 1) ||
1412 (nu->type != CU_BEZIER && bp != sel_bp && ELEM(bp, nu->bp, nu->bp + nu->pntsu - 1))))
1413 {
1414 View3D *v3d = vc->v3d;
1415 ListBase *nurbs = object_editcurve_get(vc->obedit);
1416 curve_toggle_cyclic(v3d, nurbs, 0);
1417 return true;
1418 }
1419 }
1420 return false;
1421}
1422
1424{
1425 FOREACH_SELECTED_BEZT_BEGIN (bezt, nurbs) {
1426 bezt->h1 = bezt->h2 = HD_ALIGN;
1427 copy_v3_v3(bezt->vec[0], bezt->vec[1]);
1428 copy_v3_v3(bezt->vec[2], bezt->vec[1]);
1429 BEZT_DESEL_ALL(bezt);
1430 BEZT_SEL_IDX(bezt, is_last_bezt(nu, bezt) ? 2 : 0);
1431 }
1433}
1434
1435static void toggle_select_bezt(BezTriple *bezt, const int bezt_idx, Curve *cu, Nurb *nu)
1436{
1437 if (BEZT_ISSEL_IDX(bezt, bezt_idx)) {
1438 BEZT_DESEL_IDX(bezt, bezt_idx);
1439 }
1440 else {
1441 BEZT_SEL_IDX(bezt, bezt_idx);
1442 }
1443
1444 if (BEZT_ISSEL_ANY(bezt)) {
1445 BKE_curve_nurb_vert_active_set(cu, nu, bezt);
1446 }
1447}
1448
1449static void toggle_select_bp(BPoint *bp, Curve *cu, Nurb *nu)
1450{
1451 if (bp->f1 & SELECT) {
1452 bp->f1 &= ~SELECT;
1453 }
1454 else {
1455 bp->f1 |= SELECT;
1457 }
1458}
1459
1460static void toggle_handle_types(BezTriple *bezt, int bezt_idx, CurvePenData *cpd)
1461{
1462 if (bezt_idx == 0) {
1463 if (bezt->h1 == HD_VECT) {
1464 bezt->h1 = bezt->h2 = HD_AUTO;
1465 }
1466 else {
1467 bezt->h1 = HD_VECT;
1468 if (bezt->h2 != HD_VECT) {
1469 bezt->h2 = HD_FREE;
1470 }
1471 }
1472 cpd->changed = true;
1473 }
1474 else if (bezt_idx == 2) {
1475 if (bezt->h2 == HD_VECT) {
1476 bezt->h1 = bezt->h2 = HD_AUTO;
1477 }
1478 else {
1479 bezt->h2 = HD_VECT;
1480 if (bezt->h1 != HD_VECT) {
1481 bezt->h1 = HD_FREE;
1482 }
1483 }
1484 cpd->changed = true;
1485 }
1486}
1487
1488static void cycle_handles(BezTriple *bezt)
1489{
1490 if (bezt->h1 == HD_AUTO) {
1491 bezt->h1 = bezt->h2 = HD_VECT;
1492 }
1493 else if (bezt->h1 == HD_VECT) {
1494 bezt->h1 = bezt->h2 = HD_ALIGN;
1495 }
1496 else if (bezt->h1 == HD_ALIGN) {
1497 bezt->h1 = bezt->h2 = HD_FREE;
1498 }
1499 else {
1500 bezt->h1 = bezt->h2 = HD_AUTO;
1501 }
1502}
1503
1504enum {
1511
1513{
1514 static const EnumPropertyItem modal_items[] = {
1516 "FREE_ALIGN_TOGGLE",
1517 0,
1518 "Free-Align Toggle",
1519 "Move handle of newly added point freely"},
1521 "MOVE_ADJACENT",
1522 0,
1523 "Move Adjacent Handle",
1524 "Move the closer handle of the adjacent vertex"},
1526 "MOVE_ENTIRE",
1527 0,
1528 "Move Entire Point",
1529 "Move the entire point using its handles"},
1531 "LINK_HANDLES",
1532 0,
1533 "Link Handles",
1534 "Mirror the movement of one handle onto the other"},
1536 "LOCK_ANGLE",
1537 0,
1538 "Lock Angle",
1539 "Move the handle along its current angle"},
1540 {0, nullptr, 0, nullptr, nullptr},
1541 };
1542
1543 wmKeyMap *keymap = WM_modalkeymap_find(keyconf, "Curve Pen Modal Map");
1544
1545 /* This function is called for each space-type, only needs to add map once. */
1546 if (keymap && keymap->modal_items) {
1547 return nullptr;
1548 }
1549
1550 keymap = WM_modalkeymap_ensure(keyconf, "Curve Pen Modal Map", modal_items);
1551
1552 WM_modalkeymap_assign(keymap, "CURVE_OT_pen");
1553
1554 return keymap;
1555}
1556
1557static int curve_pen_modal(bContext *C, wmOperator *op, const wmEvent *event)
1558{
1560 Object *obedit = CTX_data_edit_object(C);
1561
1563 Curve *cu = static_cast<Curve *>(vc.obedit->data);
1564 ListBase *nurbs = &cu->editnurb->nurbs;
1565 const float threshold_dist_px = ED_view3d_select_dist_px() * SEL_DIST_FACTOR;
1566
1567 BezTriple *bezt = nullptr;
1568 BPoint *bp = nullptr;
1569 Nurb *nu = nullptr;
1570
1572 params.sel_op = SEL_OP_SET;
1573 params.deselect_all = false;
1574
1576
1577 /* Distance threshold for mouse clicks to affect the spline or its points */
1578 const float mval_fl[2] = {float(event->mval[0]), float(event->mval[1])};
1579
1580 const bool extrude_point = RNA_boolean_get(op->ptr, "extrude_point");
1581 const bool delete_point = RNA_boolean_get(op->ptr, "delete_point");
1582 const bool insert_point = RNA_boolean_get(op->ptr, "insert_point");
1583 const bool move_seg = RNA_boolean_get(op->ptr, "move_segment");
1584 const bool select_point = RNA_boolean_get(op->ptr, "select_point");
1585 const bool move_point = RNA_boolean_get(op->ptr, "move_point");
1586 const bool close_spline = RNA_boolean_get(op->ptr, "close_spline");
1587 const bool toggle_vector = RNA_boolean_get(op->ptr, "toggle_vector");
1588 const bool cycle_handle_type = RNA_boolean_get(op->ptr, "cycle_handle_type");
1589 const int close_spline_method = RNA_enum_get(op->ptr, "close_spline_method");
1590 const int extrude_handle = RNA_enum_get(op->ptr, "extrude_handle");
1591
1592 CurvePenData *cpd;
1593 if (op->customdata == nullptr) {
1594 op->customdata = cpd = static_cast<CurvePenData *>(
1595 MEM_callocN(sizeof(CurvePenData), __func__));
1596 }
1597 else {
1598 cpd = (CurvePenData *)(op->customdata);
1599 cpd->select_multi = event->modifier == KM_SHIFT;
1600 }
1601
1602 if (event->type == EVT_MODAL_MAP) {
1603 if (cpd->msd == nullptr) {
1604 if (event->val == PEN_MODAL_FREE_ALIGN_TOGGLE) {
1606 cpd->link_handles = false;
1607 }
1608 else if (event->val == PEN_MODAL_LINK_HANDLES) {
1609 cpd->link_handles = !cpd->link_handles;
1610 if (cpd->link_handles) {
1611 move_all_selected_points(&vc, event, cpd, nurbs, false);
1612 }
1613 }
1614 else if (event->val == PEN_MODAL_MOVE_ENTIRE) {
1615 cpd->move_entire = !cpd->move_entire;
1616 }
1617 else if (event->val == PEN_MODAL_MOVE_ADJACENT) {
1618 cpd->move_adjacent = !cpd->move_adjacent;
1619 }
1620 else if (event->val == PEN_MODAL_LOCK_ANGLE) {
1621 cpd->lock_angle = !cpd->lock_angle;
1622 }
1623 }
1624 else {
1625 if (event->val == PEN_MODAL_FREE_ALIGN_TOGGLE) {
1626 BezTriple *bezt1 = cpd->msd->nu->bezt + cpd->msd->bezt_index;
1627 BezTriple *bezt2 = BKE_nurb_bezt_get_next(cpd->msd->nu, bezt1);
1630 }
1631 }
1632 }
1633
1634 if (ISMOUSE_MOTION(event->type)) {
1635 /* Check if dragging */
1636 if (!cpd->dragging && WM_event_drag_test(event, event->prev_press_xy)) {
1637 cpd->dragging = true;
1638
1639 if (cpd->new_point) {
1641 }
1642 }
1643
1644 if (cpd->dragging) {
1645 if (cpd->spline_nearby && move_seg && cpd->msd != nullptr) {
1646 MoveSegmentData *seg_data = cpd->msd;
1647 move_segment(&vc, seg_data, event);
1648 cpd->changed = true;
1649 if (seg_data->nu && seg_data->nu->type == CU_BEZIER) {
1650 BKE_nurb_handles_calc(seg_data->nu);
1651 }
1652 }
1653 else if (cpd->move_adjacent) {
1654 move_adjacent_handle(&vc, event, nurbs);
1655 cpd->changed = true;
1656 }
1657 else if (cpd->new_point || (move_point && !cpd->spline_nearby && cpd->found_point)) {
1658 /* Move only the bezt handles if it's a new point. */
1659 move_all_selected_points(&vc, event, cpd, nurbs, cpd->new_point);
1660 cpd->changed = true;
1661 }
1662 }
1663 }
1664 else if (ELEM(event->type, LEFTMOUSE)) {
1665 if (ELEM(event->val, KM_RELEASE, KM_DBL_CLICK)) {
1666 if (delete_point && !cpd->new_point && !cpd->dragging) {
1667 if (ED_curve_editnurb_select_pick(C, event->mval, threshold_dist_px, &params)) {
1668 cpd->changed = delete_point_under_mouse(&vc, event);
1669 }
1670 }
1671
1672 /* Close spline on Click, if enabled. */
1673 if (!cpd->changed && close_spline && close_spline_method == ON_CLICK && cpd->found_point &&
1674 !cpd->dragging)
1675 {
1676 if (cpd->nu && !is_cyclic(cpd->nu)) {
1677 copy_v2_v2_int(vc.mval, event->mval);
1678 cpd->changed = make_cyclic_if_endpoints(&vc, cpd->nu, cpd->bezt, cpd->bp);
1679 }
1680 }
1681
1682 if (!cpd->changed && (insert_point || extrude_point) && cpd->spline_nearby && !cpd->dragging)
1683 {
1684 if (insert_point) {
1685 insert_point_to_segment(&vc, event);
1686 cpd->new_point = true;
1687 cpd->changed = true;
1688 }
1689 else if (extrude_point) {
1690 extrude_points_from_selected_vertices(&vc, event, extrude_handle);
1691 cpd->changed = true;
1692 }
1693 }
1694
1695 if (!cpd->changed && toggle_vector) {
1696 int bezt_idx;
1697 get_closest_vertex_to_point_in_nurbs(&vc, nurbs, mval_fl, &nu, &bezt, &bp, &bezt_idx);
1698 if (bezt) {
1699 if (bezt_idx == 1 && cycle_handle_type) {
1700 cycle_handles(bezt);
1701 cpd->changed = true;
1702 }
1703 else {
1704 toggle_handle_types(bezt, bezt_idx, cpd);
1705 }
1706
1707 if (nu && nu->type == CU_BEZIER) {
1709 }
1710 }
1711 }
1712
1713 if (!cpd->selection_made && !cpd->changed) {
1714 if (cpd->select_multi) {
1715 int bezt_idx;
1716 get_closest_vertex_to_point_in_nurbs(&vc, nurbs, mval_fl, &nu, &bezt, &bp, &bezt_idx);
1717 if (bezt) {
1718 toggle_select_bezt(bezt, bezt_idx, cu, nu);
1719 }
1720 else if (bp) {
1721 toggle_select_bp(bp, cu, nu);
1722 }
1723 else {
1725 }
1726 }
1727 else if (select_point) {
1728 ED_curve_editnurb_select_pick(C, event->mval, threshold_dist_px, &params);
1729 }
1730 }
1731
1732 if (cpd->msd != nullptr) {
1733 MEM_freeN(cpd->msd);
1734 }
1735 MEM_freeN(cpd);
1737 }
1738 }
1739
1742 DEG_id_tag_update(static_cast<ID *>(obedit->data), 0);
1743
1744 return ret;
1745}
1746
1747static int curve_pen_invoke(bContext *C, wmOperator *op, const wmEvent *event)
1748{
1751 Curve *cu = static_cast<Curve *>(vc.obedit->data);
1752 ListBase *nurbs = &cu->editnurb->nurbs;
1753
1754 BezTriple *bezt = nullptr;
1755 BPoint *bp = nullptr;
1756 Nurb *nu = nullptr;
1757
1758 CurvePenData *cpd;
1759 op->customdata = cpd = static_cast<CurvePenData *>(MEM_callocN(sizeof(CurvePenData), __func__));
1760
1761 /* Distance threshold for mouse clicks to affect the spline or its points */
1762 const float mval_fl[2] = {float(event->mval[0]), float(event->mval[1])};
1763 const float threshold_dist_px = ED_view3d_select_dist_px() * SEL_DIST_FACTOR;
1764
1765 const bool extrude_point = RNA_boolean_get(op->ptr, "extrude_point");
1766 const bool insert_point = RNA_boolean_get(op->ptr, "insert_point");
1767 const bool move_seg = RNA_boolean_get(op->ptr, "move_segment");
1768 const bool move_point = RNA_boolean_get(op->ptr, "move_point");
1769 const bool close_spline = RNA_boolean_get(op->ptr, "close_spline");
1770 const int close_spline_method = RNA_enum_get(op->ptr, "close_spline_method");
1771 const int extrude_handle = RNA_enum_get(op->ptr, "extrude_handle");
1772
1773 if (ELEM(event->type, LEFTMOUSE) && ELEM(event->val, KM_PRESS, KM_DBL_CLICK)) {
1774 /* Get the details of points selected at the start of the operation.
1775 * Used for closing the spline when endpoints are clicked consecutively and for selecting a
1776 * single point. */
1777 get_first_selected_point(cu, vc.v3d, &nu, &bezt, &bp);
1778 cpd->nu = nu;
1779 cpd->bezt = bezt;
1780 cpd->bp = bp;
1781
1782 /* Get the details of the vertex closest to the mouse at the start of the operation. */
1783 Nurb *nu1;
1784 BezTriple *bezt1;
1785 BPoint *bp1;
1786 int bezt_idx = 0;
1788 &vc, nurbs, mval_fl, &nu1, &bezt1, &bp1, &bezt_idx);
1789
1790 if (move_point && nu1 && !nu1->hide &&
1791 (bezt || (bezt1 && !BEZT_ISSEL_IDX(bezt1, bezt_idx)) || (bp1 && !(bp1->f1 & SELECT))))
1792 {
1793 /* Select the closest bezt or bp. */
1795 if (bezt1) {
1796 BEZT_SEL_IDX(bezt1, bezt_idx);
1797 BKE_curve_nurb_vert_active_set(cu, nu1, bezt1);
1798 }
1799 else if (bp1) {
1800 bp1->f1 |= SELECT;
1801 BKE_curve_nurb_vert_active_set(cu, nu1, bp1);
1802 }
1803
1804 cpd->selection_made = true;
1805 }
1806 if (cpd->found_point) {
1807 /* Close the spline on press. */
1808 if (close_spline && close_spline_method == ON_PRESS && cpd->nu && !is_cyclic(cpd->nu)) {
1809 copy_v2_v2_int(vc.mval, event->mval);
1811 &vc, cpd->nu, cpd->bezt, cpd->bp);
1812 }
1813 }
1814 else if (!cpd->changed) {
1815 if (is_spline_nearby(&vc, op, event, threshold_dist_px)) {
1816 cpd->spline_nearby = true;
1817
1818 /* If move segment is disabled, then insert point on key press and set
1819 * "new_point" to true so that the new point's handles can be controlled. */
1820 if (insert_point && !move_seg) {
1821 insert_point_to_segment(&vc, event);
1822 cpd->new_point = cpd->changed = cpd->link_handles = true;
1823 }
1824 }
1825 else if (extrude_point) {
1826 extrude_points_from_selected_vertices(&vc, event, extrude_handle);
1827 cpd->new_point = cpd->changed = cpd->link_handles = true;
1828 }
1829 }
1830 }
1832
1834}
1835
1837{
1838 /* identifiers */
1839 ot->name = "Curve Pen";
1840 ot->idname = "CURVE_OT_pen";
1841 ot->description = "Construct and edit splines";
1842
1843 /* api callbacks */
1847
1848 /* flags */
1849 ot->flag = OPTYPE_UNDO;
1850
1851 /* properties */
1853
1855 "extrude_point",
1856 false,
1857 "Extrude Point",
1858 "Add a point connected to the last selected point");
1860 "extrude_handle",
1862 HD_VECT,
1863 "Extrude Handle Type",
1864 "Type of the extruded handle");
1865 RNA_def_boolean(ot->srna, "delete_point", false, "Delete Point", "Delete an existing point");
1867 ot->srna, "insert_point", false, "Insert Point", "Insert Point into a curve segment");
1868 RNA_def_boolean(ot->srna, "move_segment", false, "Move Segment", "Delete an existing point");
1870 ot->srna, "select_point", false, "Select Point", "Select a point or its handles");
1871 RNA_def_boolean(ot->srna, "move_point", false, "Move Point", "Move a point or its handles");
1873 "close_spline",
1874 true,
1875 "Close Spline",
1876 "Make a spline cyclic by clicking endpoints");
1878 "close_spline_method",
1880 OFF,
1881 "Close Spline Method",
1882 "The condition for close spline to activate");
1884 ot->srna, "toggle_vector", false, "Toggle Vector", "Toggle between Vector and Auto handles");
1886 "cycle_handle_type",
1887 false,
1888 "Cycle Handle Type",
1889 "Cycle between all four handle types");
1890}
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:3955
#define CU_IS_2D(cu)
Definition BKE_curve.hh:86
void BKE_nurb_free(Nurb *nu)
Definition curve.cc:569
void BKE_curve_nurb_vert_active_set(Curve *cu, const Nurb *nu, const void *vert)
Definition curve.cc:5021
void BKE_nurb_knot_calc_u(Nurb *nu)
Definition curve.cc:1181
void BKE_curve_forward_diff_bezier(float q0, float q1, float q2, float q3, float *p, int it, int stride)
Definition curve.cc:1663
ListBase * BKE_curve_editNurbs_get(Curve *cu)
Definition curve.cc:398
void BKE_curve_nurb_vert_active_validate(Curve *cu)
Definition curve.cc:5065
BezTriple * BKE_nurb_bezt_get_next(Nurb *nu, BezTriple *bezt)
Definition curve.cc:920
void BKE_curve_editNurb_keyIndex_delCV(GHash *keyindex, const void *cv)
Definition curve.cc:322
int BKE_curve_nurb_vert_index_get(const Nurb *nu, const void *vert)
Definition curve.cc:5010
BezTriple * BKE_nurb_bezt_get_prev(Nurb *nu, BezTriple *bezt)
Definition curve.cc:962
#define BLI_assert(a)
Definition BLI_assert.h:50
#define LISTBASE_FOREACH(type, var, list)
void BLI_remlink(struct ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition listbase.cc:130
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 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 void sub_v2_v2v2_int(int r[2], const int a[2], const int b[2])
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)
Definition math_vector.c:21
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)
Definition math_vector.c:36
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)
#define BEZT_DESEL_IDX(bezt, i)
#define BEZT_ISSEL_IDX(bezt, i)
#define BEZT_ISSEL_ANY_HIDDENHANDLES(v3d, bezt)
@ CU_BEZIER
@ CU_POLY
@ CU_NURBS
struct BPoint BPoint
#define BEZT_ISSEL_ANY(bezt)
#define CU_ACT_NONE
#define BEZT_DESEL_ALL(bezt)
@ HD_VECT
@ HD_FREE
@ HD_AUTO
@ HD_ALIGN
struct BezTriple BezTriple
@ CU_NURB_CYCLIC
#define BEZT_SEL_IDX(bezt, i)
@ CURVE_HANDLE_NONE
@ CURVE_HANDLE_SELECTED
@ 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:268
@ V3D_PROJ_TEST_CLIP_BB
Definition ED_view3d.hh:267
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:243
ViewContext ED_view3d_viewcontext_init(bContext *C, Depsgraph *depsgraph)
Read Guarded memory(de)allocation.
@ OPTYPE_UNDO
Definition WM_types.hh:162
#define NC_GEOM
Definition WM_types.hh:360
#define ND_DATA
Definition WM_types.hh:475
@ KM_PRESS
Definition WM_types.hh:284
@ KM_DBL_CLICK
Definition WM_types.hh:287
@ KM_RELEASE
Definition WM_types.hh:285
#define ND_SELECT
Definition WM_types.hh:474
@ KM_SHIFT
Definition WM_types.hh:255
#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])
const Depsgraph * depsgraph
#define sinf(x)
#define fabsf(x)
void ED_curve_beztcpy(EditNurb *editnurb, BezTriple *dst, BezTriple *src, int count)
ListBase * object_editcurve_get(Object *ob)
Definition editcurve.cc:88
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 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 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 bool is_spline_nearby(ViewContext *vc, wmOperator *op, const wmEvent *event, const float sel_dist)
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 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 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])
@ PEN_MODAL_LOCK_ANGLE
@ PEN_MODAL_LINK_HANDLES
@ PEN_MODAL_MOVE_ADJACENT
@ PEN_MODAL_MOVE_ENTIRE
@ PEN_MODAL_FREE_ALIGN_TOGGLE
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)
draw_view in_light_buf[] float
uiWidgetBaseParameters params[MAX_WIDGET_BASE_BATCH]
void *(* MEM_mallocN)(size_t len, const char *str)
Definition mallocn.cc:44
void MEM_freeN(void *vmemh)
Definition mallocn.cc:105
void *(* MEM_callocN)(size_t len, const char *str)
Definition mallocn.cc:42
const btScalar eps
Definition poly34.cpp:11
return ret
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:413
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:76
ARegion * region
Definition ED_view3d.hh:73
int mval[2]
Definition ED_view3d.hh:78
Scene * scene
Definition ED_view3d.hh:69
View3D * v3d
Definition ED_view3d.hh:74
Object * obedit
Definition ED_view3d.hh:72
short val
Definition WM_types.hh:724
int xy[2]
Definition WM_types.hh:726
int mval[2]
Definition WM_types.hh:728
int prev_xy[2]
Definition WM_types.hh:785
int prev_press_xy[2]
Definition WM_types.hh:795
short type
Definition WM_types.hh:722
const void * modal_items
const char * name
Definition WM_types.hh:990
bool(* poll)(bContext *C) ATTR_WARN_UNUSED_RESULT
Definition WM_types.hh:1042
const char * idname
Definition WM_types.hh:992
int(* modal)(bContext *C, wmOperator *op, const wmEvent *event) ATTR_WARN_UNUSED_RESULT
Definition WM_types.hh:1036
int(* invoke)(bContext *C, wmOperator *op, const wmEvent *event) ATTR_WARN_UNUSED_RESULT
Definition WM_types.hh:1022
const char * description
Definition WM_types.hh:996
StructRNA * srna
Definition WM_types.hh:1080
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.cc:4125
wmKeyMap * WM_modalkeymap_ensure(wmKeyConfig *keyconf, const char *idname, const EnumPropertyItem *items)
Definition wm_keymap.cc:933
void WM_modalkeymap_assign(wmKeyMap *km, const char *opname)
wmKeyMap * WM_modalkeymap_find(wmKeyConfig *keyconf, const char *idname)
Definition wm_keymap.cc:960
void WM_operator_properties_mouse_select(wmOperatorType *ot)