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
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 = (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 {
1510};
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 */
1844 ot->invoke = curve_pen_invoke;
1845 ot->modal = curve_pen_modal;
1846 ot->poll = ED_operator_editcurve;
1847
1848 /* flags */
1849 ot->flag = OPTYPE_UNDO;
1850
1851 /* properties */
1853
1854 RNA_def_boolean(ot->srna,
1855 "extrude_point",
1856 false,
1857 "Extrude Point",
1858 "Add a point connected to the last selected point");
1859 RNA_def_enum(ot->srna,
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");
1872 RNA_def_boolean(ot->srna,
1873 "close_spline",
1874 true,
1875 "Close Spline",
1876 "Make a spline cyclic by clicking endpoints");
1877 RNA_def_enum(ot->srna,
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");
1885 RNA_def_boolean(ot->srna,
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)
@ CU_BEZIER
@ CU_POLY
@ CU_NURBS
#define BEZT_ISSEL_ANY_HIDDENHANDLES(v3d, bezt)
#define BEZT_ISSEL_ANY(bezt)
#define CU_ACT_NONE
#define BEZT_DESEL_ALL(bezt)
@ HD_VECT
@ HD_FREE
@ HD_AUTO
@ HD_ALIGN
#define BEZT_SEL_IDX(bezt, i)
@ CU_NURB_CYCLIC
@ 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:277
@ V3D_PROJ_TEST_CLIP_BB
Definition ED_view3d.hh:276
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:252
ViewContext ED_view3d_viewcontext_init(bContext *C, Depsgraph *depsgraph)
Read Guarded memory(de)allocation.
in reality light always falls off quadratically Particle Retrieve the data of the particle that spawned the object for example to give variation to multiple instances of an object Point Retrieve information about points in a point cloud Retrieve the edges of an object as it appears to Cycles topology will always appear triangulated Convert a blackbody temperature to an RGB value Normal Generate a perturbed normal from an RGB normal map image Typically used for faking highly detailed surfaces Generate an OSL shader from a file or text data block Image Sample an image file as a texture Gabor Generate Gabor noise Gradient Generate interpolated color and intensity values based on the input vector Magic Generate a psychedelic color texture Voronoi Generate Worley noise based on the distance to random points Typically used to generate textures such as or biological cells Brick Generate a procedural texture producing bricks Texture Retrieve multiple types of texture coordinates nTypically used as inputs for texture nodes Vector Convert a point
#define C
Definition RandGen.cpp:29
#define NC_GEOM
Definition WM_types.hh:360
#define ND_DATA
Definition WM_types.hh:475
@ OPTYPE_UNDO
Definition WM_types.hh:162
#define ND_SELECT
Definition WM_types.hh:474
@ KM_PRESS
Definition WM_types.hh:284
@ KM_DBL_CLICK
Definition WM_types.hh:287
@ KM_RELEASE
Definition WM_types.hh:285
@ KM_SHIFT
Definition WM_types.hh:255
BPy_StructRNA * depsgraph
#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])
#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])
@ PEN_MODAL_LOCK_ANGLE
@ PEN_MODAL_LINK_HANDLES
@ PEN_MODAL_MOVE_ADJACENT
@ PEN_MODAL_MOVE_ENTIRE
@ PEN_MODAL_FREE_ALIGN_TOGGLE
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])
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
float distance(float a, float b)
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
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)