Blender V4.3
editcurve_select.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_object_types.h"
10#include "DNA_scene_types.h"
11
12#include "MEM_guardedalloc.h"
13
14#include "BLI_bitmap.h"
15#include "BLI_heap_simple.h"
16#include "BLI_kdtree.h"
17#include "BLI_listbase.h"
18#include "BLI_math_matrix.h"
19#include "BLI_math_vector.h"
20#include "BLI_rand.h"
21
22#include "BKE_context.hh"
23#include "BKE_curve.hh"
24#include "BKE_fcurve.hh"
25#include "BKE_layer.hh"
26#include "BKE_report.hh"
27
28#include "WM_api.hh"
29#include "WM_types.hh"
30
31#include "ED_curve.hh"
32#include "ED_object.hh"
33#include "ED_screen.hh"
34#include "ED_select_utils.hh"
35#include "ED_view3d.hh"
36
37#include "curve_intern.hh"
38
39#include "RNA_access.hh"
40#include "RNA_define.hh"
41
42#include "DEG_depsgraph.hh"
43
44using blender::Span;
45using blender::Vector;
46
47/* -------------------------------------------------------------------- */
51bool select_beztriple(BezTriple *bezt, bool selstatus, uint8_t flag, eVisible_Types hidden)
52{
53 if ((bezt->hide == 0) || (hidden == HIDDEN)) {
54 if (selstatus) { /* selects */
55 bezt->f1 |= flag;
56 bezt->f2 |= flag;
57 bezt->f3 |= flag;
58 return true;
59 }
60 /* deselects */
61 bezt->f1 &= ~flag;
62 bezt->f2 &= ~flag;
63 bezt->f3 &= ~flag;
64 return true;
65 }
66
67 return false;
68}
69
70bool select_bpoint(BPoint *bp, bool selstatus, uint8_t flag, bool hidden)
71{
72 if ((bp->hide == 0) || (hidden == 1)) {
73 if (selstatus) {
74 bp->f1 |= flag;
75 return true;
76 }
77 bp->f1 &= ~flag;
78 return true;
79 }
80
81 return false;
82}
83
85{
86 if (bezt->f2 & SELECT) {
87 return select_beztriple(bezt, false, SELECT, VISIBLE);
88 }
89 return select_beztriple(bezt, true, SELECT, VISIBLE);
90}
91
93{
94 if (bp->f1 & SELECT) {
95 return select_bpoint(bp, false, SELECT, VISIBLE);
96 }
97 return select_bpoint(bp, true, SELECT, VISIBLE);
98}
99
100bool ED_curve_nurb_select_check(const View3D *v3d, const Nurb *nu)
101{
102 if (nu->type == CU_BEZIER) {
103 const BezTriple *bezt;
104 int i;
105
106 for (i = nu->pntsu, bezt = nu->bezt; i--; bezt++) {
107 if (BEZT_ISSEL_ANY_HIDDENHANDLES(v3d, bezt)) {
108 return true;
109 }
110 }
111 }
112 else {
113 const BPoint *bp;
114 int i;
115
116 for (i = nu->pntsu * nu->pntsv, bp = nu->bp; i--; bp++) {
117 if (bp->f1 & SELECT) {
118 return true;
119 }
120 }
121 }
122 return false;
123}
124
125int ED_curve_nurb_select_count(const View3D *v3d, const Nurb *nu)
126{
127 int sel = 0;
128
129 if (nu->type == CU_BEZIER) {
130 const BezTriple *bezt;
131 int i;
132
133 for (i = nu->pntsu, bezt = nu->bezt; i--; bezt++) {
134 if (BEZT_ISSEL_ANY_HIDDENHANDLES(v3d, bezt)) {
135 sel++;
136 }
137 }
138 }
139 else {
140 const BPoint *bp;
141 int i;
142
143 for (i = nu->pntsu * nu->pntsv, bp = nu->bp; i--; bp++) {
144 if (bp->f1 & SELECT) {
145 sel++;
146 }
147 }
148 }
149
150 return sel;
151}
152
154{
155 bool changed = false;
156 int i;
157 if (nu->bezt) {
158 BezTriple *bezt;
159 for (i = nu->pntsu, bezt = nu->bezt; i--; bezt++) {
160 if (bezt->hide == 0) {
161 if (BEZT_ISSEL_ALL(bezt) == false) {
162 BEZT_SEL_ALL(bezt);
163 changed = true;
164 }
165 }
166 }
167 }
168 else if (nu->bp) {
169 BPoint *bp;
170 for (i = nu->pntsu * nu->pntsv, bp = nu->bp; i--; bp++) {
171 if (bp->hide == 0) {
172 if ((bp->f1 & SELECT) == 0) {
173 bp->f1 |= SELECT;
174 changed = true;
175 }
176 }
177 }
178 }
179 return changed;
180}
181
183{
184 bool changed = false;
185 LISTBASE_FOREACH (Nurb *, nu, &editnurb->nurbs) {
186 changed |= ED_curve_nurb_select_all(nu);
187 }
188 return changed;
189}
190
192{
193 bool changed = false;
194 int i;
195 if (nu->bezt) {
196 BezTriple *bezt;
197 for (i = nu->pntsu, bezt = nu->bezt; i--; bezt++) {
198 if (BEZT_ISSEL_ANY(bezt)) {
199 BEZT_DESEL_ALL(bezt);
200 changed = true;
201 }
202 }
203 }
204 else if (nu->bp) {
205 BPoint *bp;
206 for (i = nu->pntsu * nu->pntsv, bp = nu->bp; i--; bp++) {
207 if (bp->f1 & SELECT) {
208 bp->f1 &= ~SELECT;
209 changed = true;
210 }
211 }
212 }
213 return changed;
214}
215
216int ED_curve_select_count(const View3D *v3d, const EditNurb *editnurb)
217{
218 int sel = 0;
219
220 LISTBASE_FOREACH (Nurb *, nu, &editnurb->nurbs) {
221 sel += ED_curve_nurb_select_count(v3d, nu);
222 }
223
224 return sel;
225}
226
227bool ED_curve_select_check(const View3D *v3d, const EditNurb *editnurb)
228{
229 LISTBASE_FOREACH (const Nurb *, nu, &editnurb->nurbs) {
230 if (ED_curve_nurb_select_check(v3d, nu)) {
231 return true;
232 }
233 }
234
235 return false;
236}
237
239{
240 bool changed = false;
241 LISTBASE_FOREACH (Nurb *, nu, &editnurb->nurbs) {
242 changed |= ED_curve_nurb_deselect_all(nu);
243 }
244 return changed;
245}
246
248{
249 bool changed_multi = false;
250 for (Base *base : bases) {
251 Object *obedit = base->object;
252 Curve *cu = static_cast<Curve *>(obedit->data);
253 changed_multi |= ED_curve_deselect_all(cu->editnurb);
255 }
256 return changed_multi;
257}
258
267
268bool ED_curve_select_swap(EditNurb *editnurb, bool hide_handles)
269{
270 BPoint *bp;
271 BezTriple *bezt;
272 int a;
273 bool changed = false;
274
275 /* This could be an argument to swap individual handle selection.
276 * At the moment this is always used though. */
277 bool swap_handles = false;
278
279 /* When hiding handles, ignore handle selection. */
280 if (hide_handles) {
281 swap_handles = true;
282 }
283
284 LISTBASE_FOREACH (Nurb *, nu, &editnurb->nurbs) {
285 if (nu->type == CU_BEZIER) {
286 bezt = nu->bezt;
287 a = nu->pntsu;
288 while (a--) {
289 if (bezt->hide == 0) {
290 if (swap_handles) {
291 bezt->f2 ^= SELECT; /* always do the center point */
292 if (!hide_handles) {
293 bezt->f1 ^= SELECT;
294 bezt->f3 ^= SELECT;
295 }
296 }
297 else {
298 BLI_assert(!hide_handles);
299 if (BEZT_ISSEL_ANY(bezt)) {
300 BEZT_DESEL_ALL(bezt);
301 }
302 else {
303 BEZT_SEL_ALL(bezt);
304 }
305 }
306 changed = true;
307 }
308 bezt++;
309 }
310 }
311 else {
312 bp = nu->bp;
313 a = nu->pntsu * nu->pntsv;
314 while (a--) {
315 if (bp->hide == 0) {
317 changed = true;
318 }
319 bp++;
320 }
321 }
322 }
323 return changed;
324}
325
331static void select_adjacent_cp(ListBase *editnurb,
332 short next,
333 const bool cont,
334 const bool selstatus)
335{
336 BezTriple *bezt;
337 BPoint *bp;
338 int a;
339 bool lastsel = false;
340
341 if (next == 0) {
342 return;
343 }
344
345 LISTBASE_FOREACH (Nurb *, nu, editnurb) {
346 lastsel = false;
347 if (nu->type == CU_BEZIER) {
348 a = nu->pntsu;
349 bezt = nu->bezt;
350 if (next < 0) {
351 bezt = &nu->bezt[a - 1];
352 }
353 while (a--) {
354 if (a - abs(next) < 0) {
355 break;
356 }
357 if ((lastsel == false) && (bezt->hide == 0) &&
358 ((bezt->f2 & SELECT) || (selstatus == false)))
359 {
360 bezt += next;
361 if (!(bezt->f2 & SELECT) || (selstatus == false)) {
362 bool sel = select_beztriple(bezt, selstatus, SELECT, VISIBLE);
363 if (sel && !cont) {
364 lastsel = true;
365 }
366 }
367 }
368 else {
369 bezt += next;
370 lastsel = false;
371 }
372 /* move around in zigzag way so that we go through each */
373 bezt -= (next - next / abs(next));
374 }
375 }
376 else {
377 a = nu->pntsu * nu->pntsv;
378 bp = nu->bp;
379 if (next < 0) {
380 bp = &nu->bp[a - 1];
381 }
382 while (a--) {
383 if (a - abs(next) < 0) {
384 break;
385 }
386 if ((lastsel == false) && (bp->hide == 0) && ((bp->f1 & SELECT) || (selstatus == false))) {
387 bp += next;
388 if (!(bp->f1 & SELECT) || (selstatus == false)) {
389 bool sel = select_bpoint(bp, selstatus, SELECT, VISIBLE);
390 if (sel && !cont) {
391 lastsel = true;
392 }
393 }
394 }
395 else {
396 bp += next;
397 lastsel = false;
398 }
399 /* move around in zigzag way so that we go through each */
400 bp -= (next - next / abs(next));
401 }
402 }
403 }
404}
405
408/* -------------------------------------------------------------------- */
419static void selectend_nurb(Object *obedit, eEndPoint_Types selfirst, bool doswap, bool selstatus)
420{
421 ListBase *editnurb = object_editcurve_get(obedit);
422 BPoint *bp;
423 BezTriple *bezt;
424 Curve *cu;
425 int a;
426
427 if (obedit == nullptr) {
428 return;
429 }
430
431 cu = (Curve *)obedit->data;
432 cu->actvert = CU_ACT_NONE;
433
434 LISTBASE_FOREACH (Nurb *, nu, editnurb) {
435 if (nu->type == CU_BEZIER) {
436 a = nu->pntsu;
437
438 /* which point? */
439 if (selfirst == LAST) { /* select last */
440 bezt = &nu->bezt[a - 1];
441 }
442 else { /* select first */
443 bezt = nu->bezt;
444 }
445
446 while (a--) {
447 bool sel;
448 if (doswap) {
449 sel = swap_selection_beztriple(bezt);
450 }
451 else {
452 sel = select_beztriple(bezt, selstatus, SELECT, VISIBLE);
453 }
454
455 if (sel == true) {
456 break;
457 }
458 }
459 }
460 else {
461 a = nu->pntsu * nu->pntsv;
462
463 /* which point? */
464 if (selfirst == LAST) { /* select last */
465 bp = &nu->bp[a - 1];
466 }
467 else { /* select first */
468 bp = nu->bp;
469 }
470
471 while (a--) {
472 if (bp->hide == 0) {
473 bool sel;
474 if (doswap) {
475 sel = swap_selection_bpoint(bp);
476 }
477 else {
478 sel = select_bpoint(bp, selstatus, SELECT, VISIBLE);
479 }
480
481 if (sel == true) {
482 break;
483 }
484 }
485 }
486 }
487 }
488}
489
491{
492 const Scene *scene = CTX_data_scene(C);
493 ViewLayer *view_layer = CTX_data_view_layer(C);
495 scene, view_layer, CTX_wm_view3d(C));
496
497 for (Object *obedit : objects) {
498 selectend_nurb(obedit, FIRST, true, false);
499 DEG_id_tag_update(static_cast<ID *>(obedit->data), ID_RECALC_SELECT);
500 WM_event_add_notifier(C, NC_GEOM | ND_SELECT, obedit->data);
501 BKE_curve_nurb_vert_active_validate(static_cast<Curve *>(obedit->data));
502 }
503 return OPERATOR_FINISHED;
504}
505
507{
508 /* identifiers */
509 ot->name = "(De)select First";
510 ot->idname = "CURVE_OT_de_select_first";
511 ot->description = "(De)select first of visible part of each NURBS";
512
513 /* api callbacks */
516
517 /* flags */
519}
520
522{
523 Scene *scene = CTX_data_scene(C);
524 ViewLayer *view_layer = CTX_data_view_layer(C);
526 scene, view_layer, CTX_wm_view3d(C));
527
528 for (Object *obedit : objects) {
529 selectend_nurb(obedit, LAST, true, false);
530 DEG_id_tag_update(static_cast<ID *>(obedit->data), ID_RECALC_SELECT);
531 WM_event_add_notifier(C, NC_GEOM | ND_SELECT, obedit->data);
532 BKE_curve_nurb_vert_active_validate(static_cast<Curve *>(obedit->data));
533 }
534
535 return OPERATOR_FINISHED;
536}
537
539{
540 /* identifiers */
541 ot->name = "(De)select Last";
542 ot->idname = "CURVE_OT_de_select_last";
543 ot->description = "(De)select last of visible part of each NURBS";
544
545 /* api callbacks */
548
549 /* flags */
551}
552
555/* -------------------------------------------------------------------- */
560{
561 int action = RNA_enum_get(op->ptr, "action");
562
563 Scene *scene = CTX_data_scene(C);
564 ViewLayer *view_layer = CTX_data_view_layer(C);
565 View3D *v3d = CTX_wm_view3d(C);
567 scene, view_layer, CTX_wm_view3d(C));
568 if (action == SEL_TOGGLE) {
569 action = SEL_SELECT;
570 for (Object *obedit : objects) {
571 Curve *cu = static_cast<Curve *>(obedit->data);
572
573 if (ED_curve_select_check(v3d, cu->editnurb)) {
574 action = SEL_DESELECT;
575 break;
576 }
577 }
578 }
579
580 for (Object *obedit : objects) {
581 Curve *cu = static_cast<Curve *>(obedit->data);
582 bool changed = false;
583
584 switch (action) {
585 case SEL_SELECT:
586 changed = ED_curve_select_all(cu->editnurb);
587 break;
588 case SEL_DESELECT:
589 changed = ED_curve_deselect_all(cu->editnurb);
590 break;
591 case SEL_INVERT:
592 changed = ED_curve_select_swap(
593 cu->editnurb, (v3d && (v3d->overlay.handle_display == CURVE_HANDLE_NONE)));
594 break;
595 }
596
597 if (changed) {
598 DEG_id_tag_update(static_cast<ID *>(obedit->data), ID_RECALC_SELECT);
599 WM_event_add_notifier(C, NC_GEOM | ND_SELECT, obedit->data);
601 }
602 }
603
604 return OPERATOR_FINISHED;
605}
606
608{
609 /* identifiers */
610 ot->name = "(De)select All";
611 ot->idname = "CURVE_OT_select_all";
612 ot->description = "(De)select all control points";
613
614 /* api callbacks */
617
618 /* flags */
620
621 /* properties */
623}
624
627/* -------------------------------------------------------------------- */
631static int select_linked_exec(bContext *C, wmOperator * /*op*/)
632{
633 Scene *scene = CTX_data_scene(C);
634 ViewLayer *view_layer = CTX_data_view_layer(C);
635 View3D *v3d = CTX_wm_view3d(C);
636
638 scene, view_layer, CTX_wm_view3d(C));
639 for (Object *obedit : objects) {
640 Curve *cu = static_cast<Curve *>(obedit->data);
641 EditNurb *editnurb = cu->editnurb;
642 ListBase *nurbs = &editnurb->nurbs;
643 bool changed = false;
644
645 LISTBASE_FOREACH (Nurb *, nu, nurbs) {
646 if (ED_curve_nurb_select_check(v3d, nu)) {
647 changed |= ED_curve_nurb_select_all(nu);
648 }
649 }
650
651 if (changed) {
652 DEG_id_tag_update(static_cast<ID *>(obedit->data), ID_RECALC_SELECT);
653 WM_event_add_notifier(C, NC_GEOM | ND_SELECT, obedit->data);
654 }
655 }
656
657 return OPERATOR_FINISHED;
658}
659
660static int select_linked_invoke(bContext *C, wmOperator *op, const wmEvent * /*event*/)
661{
662 return select_linked_exec(C, op);
663}
664
666{
667 /* identifiers */
668 ot->name = "Select Linked All";
669 ot->idname = "CURVE_OT_select_linked";
670 ot->description = "Select all control points linked to the current selection";
671
672 /* api callbacks */
676
677 /* flags */
679
680 /* properties */
681}
682
685/* -------------------------------------------------------------------- */
689static int select_linked_pick_invoke(bContext *C, wmOperator *op, const wmEvent *event)
690{
692 Nurb *nu;
693 BezTriple *bezt;
694 BPoint *bp;
695 int a;
696 const bool select = !RNA_boolean_get(op->ptr, "deselect");
697 Base *basact = nullptr;
698
701 copy_v2_v2_int(vc.mval, event->mval);
702
703 if (!ED_curve_pick_vert(&vc, 1, &nu, &bezt, &bp, nullptr, &basact)) {
704 return OPERATOR_CANCELLED;
705 }
706
707 if (bezt) {
708 a = nu->pntsu;
709 bezt = nu->bezt;
710 while (a--) {
712 bezt++;
713 }
714 }
715 else if (bp) {
716 a = nu->pntsu * nu->pntsv;
717 bp = nu->bp;
718 while (a--) {
720 bp++;
721 }
722 }
723
724 Object *obedit = basact->object;
725
726 DEG_id_tag_update(static_cast<ID *>(obedit->data), ID_RECALC_SELECT);
728
729 if (!select) {
730 BKE_curve_nurb_vert_active_validate(static_cast<Curve *>(obedit->data));
731 }
732
733 return OPERATOR_FINISHED;
734}
735
737{
738 /* identifiers */
739 ot->name = "Select Linked";
740 ot->idname = "CURVE_OT_select_linked_pick";
741 ot->description = "Select all control points linked to already selected ones";
742
743 /* api callbacks */
746
747 /* flags */
749
750 /* properties */
752 "deselect",
753 false,
754 "Deselect",
755 "Deselect linked control points rather than selecting them");
756}
757
760/* -------------------------------------------------------------------- */
764static int select_row_exec(bContext *C, wmOperator * /*op*/)
765{
766 Object *obedit = CTX_data_edit_object(C);
767 Curve *cu = static_cast<Curve *>(obedit->data);
768 ListBase *editnurb = object_editcurve_get(obedit);
769 static BPoint *last = nullptr;
770 static int direction = 0;
771 Nurb *nu = nullptr;
772 BPoint *bp = nullptr;
773 int u = 0, v = 0, a, b;
774
775 if (!BKE_curve_nurb_vert_active_get(cu, &nu, static_cast<void **>((void *)&bp))) {
776 return OPERATOR_CANCELLED;
777 }
778
779 if (last == bp) {
780 direction = 1 - direction;
781 BKE_nurbList_flag_set(editnurb, SELECT, false);
782 }
783 last = bp;
784
785 u = cu->actvert % nu->pntsu;
786 v = cu->actvert / nu->pntsu;
787 bp = nu->bp;
788 for (a = 0; a < nu->pntsv; a++) {
789 for (b = 0; b < nu->pntsu; b++, bp++) {
790 if (direction) {
791 if (a == v) {
792 select_bpoint(bp, true, SELECT, VISIBLE);
793 }
794 }
795 else {
796 if (b == u) {
797 select_bpoint(bp, true, SELECT, VISIBLE);
798 }
799 }
800 }
801 }
802
803 DEG_id_tag_update(static_cast<ID *>(obedit->data), ID_RECALC_SELECT);
805
806 return OPERATOR_FINISHED;
807}
808
810{
811 /* identifiers */
812 ot->name = "Select Control Point Row";
813 ot->idname = "CURVE_OT_select_row";
814 ot->description = "Select a row of control points including active one";
815
816 /* api callbacks */
819
820 /* flags */
822}
823
826/* -------------------------------------------------------------------- */
830static int select_next_exec(bContext *C, wmOperator * /*op*/)
831{
832 Scene *scene = CTX_data_scene(C);
833 ViewLayer *view_layer = CTX_data_view_layer(C);
835 scene, view_layer, CTX_wm_view3d(C));
836
837 for (Object *obedit : objects) {
838
839 ListBase *editnurb = object_editcurve_get(obedit);
840 select_adjacent_cp(editnurb, 1, false, SELECT);
841
842 DEG_id_tag_update(static_cast<ID *>(obedit->data), ID_RECALC_SELECT);
843 WM_event_add_notifier(C, NC_GEOM | ND_SELECT, obedit->data);
844 }
845 return OPERATOR_FINISHED;
846}
847
849{
850 /* identifiers */
851 ot->name = "Select Next";
852 ot->idname = "CURVE_OT_select_next";
853 ot->description = "Select control points following already selected ones along the curves";
854
855 /* api callbacks */
858
859 /* flags */
861}
862
865/* -------------------------------------------------------------------- */
870{
871 Scene *scene = CTX_data_scene(C);
872 ViewLayer *view_layer = CTX_data_view_layer(C);
874 scene, view_layer, CTX_wm_view3d(C));
875
876 for (Object *obedit : objects) {
877
878 ListBase *editnurb = object_editcurve_get(obedit);
879 select_adjacent_cp(editnurb, -1, false, SELECT);
880
881 DEG_id_tag_update(static_cast<ID *>(obedit->data), ID_RECALC_SELECT);
882 WM_event_add_notifier(C, NC_GEOM | ND_SELECT, obedit->data);
883 }
884 return OPERATOR_FINISHED;
885}
886
888{
889 /* identifiers */
890 ot->name = "Select Previous";
891 ot->idname = "CURVE_OT_select_previous";
892 ot->description = "Select control points preceding already selected ones along the curves";
893
894 /* api callbacks */
897
898 /* flags */
900}
901
904/* -------------------------------------------------------------------- */
908static void curve_select_more(Object *obedit)
909{
910 ListBase *editnurb = object_editcurve_get(obedit);
911 BPoint *bp, *tempbp;
912 int a;
913 short sel = 0;
914
915 /* NOTE: NURBS surface is a special case because we mimic
916 * the behavior of "select more" of mesh tools.
917 * The algorithm is designed to work in planar cases so it
918 * may not be optimal always (example: end of NURBS sphere). */
919 if (obedit->type == OB_SURF) {
920 LISTBASE_FOREACH (Nurb *, nu, editnurb) {
921 BLI_bitmap *selbpoints;
922 a = nu->pntsu * nu->pntsv;
923 bp = nu->bp;
924 selbpoints = BLI_BITMAP_NEW(a, "selectlist");
925 while (a > 0) {
926 if (!BLI_BITMAP_TEST(selbpoints, a) && (bp->hide == 0) && (bp->f1 & SELECT)) {
927 /* upper control point */
928 if (a % nu->pntsu != 0) {
929 tempbp = bp - 1;
930 if (!(tempbp->f1 & SELECT)) {
931 select_bpoint(tempbp, true, SELECT, VISIBLE);
932 }
933 }
934
935 /* left control point. select only if it is not selected already */
936 if (a - nu->pntsu > 0) {
937 sel = 0;
938 tempbp = bp + nu->pntsu;
939 if (!(tempbp->f1 & SELECT)) {
940 sel = select_bpoint(tempbp, true, SELECT, VISIBLE);
941 }
942 /* make sure selected bpoint is discarded */
943 if (sel == 1) {
944 BLI_BITMAP_ENABLE(selbpoints, a - nu->pntsu);
945 }
946 }
947
948 /* right control point */
949 if (a + nu->pntsu < nu->pntsu * nu->pntsv) {
950 tempbp = bp - nu->pntsu;
951 if (!(tempbp->f1 & SELECT)) {
952 select_bpoint(tempbp, true, SELECT, VISIBLE);
953 }
954 }
955
956 /* lower control point. skip next bp in case selection was made */
957 if (a % nu->pntsu != 1) {
958 sel = 0;
959 tempbp = bp + 1;
960 if (!(tempbp->f1 & SELECT)) {
961 sel = select_bpoint(tempbp, true, SELECT, VISIBLE);
962 }
963 if (sel) {
964 bp++;
965 a--;
966 }
967 }
968 }
969
970 bp++;
971 a--;
972 }
973
974 MEM_freeN(selbpoints);
975 }
976 }
977 else {
978 select_adjacent_cp(editnurb, 1, false, SELECT);
979 select_adjacent_cp(editnurb, -1, false, SELECT);
980 }
981}
982
984{
985 Scene *scene = CTX_data_scene(C);
986 ViewLayer *view_layer = CTX_data_view_layer(C);
988 scene, view_layer, CTX_wm_view3d(C));
989 for (Object *obedit : objects) {
990 curve_select_more(obedit);
991 DEG_id_tag_update(static_cast<ID *>(obedit->data), ID_RECALC_SELECT);
992 WM_event_add_notifier(C, NC_GEOM | ND_SELECT, obedit->data);
993 }
994 return OPERATOR_FINISHED;
995}
996
998{
999 /* identifiers */
1000 ot->name = "Select More";
1001 ot->idname = "CURVE_OT_select_more";
1002 ot->description = "Select control points at the boundary of each selection region";
1003
1004 /* api callbacks */
1007
1008 /* flags */
1010}
1011
1014/* -------------------------------------------------------------------- */
1018/* basic method: deselect if control point doesn't have all neighbors selected */
1019static void curve_select_less(Object *obedit)
1020{
1021 ListBase *editnurb = object_editcurve_get(obedit);
1022 BPoint *bp;
1023 BezTriple *bezt;
1024 int a;
1025 int sel = 0;
1026 bool lastsel = false;
1027
1028 if (obedit->type == OB_SURF) {
1029 LISTBASE_FOREACH (Nurb *, nu, editnurb) {
1030 BLI_bitmap *selbpoints;
1031 a = nu->pntsu * nu->pntsv;
1032 bp = nu->bp;
1033 selbpoints = BLI_BITMAP_NEW(a, "selectlist");
1034 while (a--) {
1035 if ((bp->hide == 0) && (bp->f1 & SELECT)) {
1036 sel = 0;
1037
1038 /* check if neighbors have been selected */
1039 /* edges of surface are an exception */
1040 if ((a + 1) % nu->pntsu == 0) {
1041 sel++;
1042 }
1043 else {
1044 bp--;
1045 if (BLI_BITMAP_TEST(selbpoints, a + 1) || ((bp->hide == 0) && (bp->f1 & SELECT))) {
1046 sel++;
1047 }
1048 bp++;
1049 }
1050
1051 if ((a + 1) % nu->pntsu == 1) {
1052 sel++;
1053 }
1054 else {
1055 bp++;
1056 if ((bp->hide == 0) && (bp->f1 & SELECT)) {
1057 sel++;
1058 }
1059 bp--;
1060 }
1061
1062 if (a + 1 > nu->pntsu * nu->pntsv - nu->pntsu) {
1063 sel++;
1064 }
1065 else {
1066 bp -= nu->pntsu;
1067 if (BLI_BITMAP_TEST(selbpoints, a + nu->pntsu) ||
1068 ((bp->hide == 0) && (bp->f1 & SELECT)))
1069 {
1070 sel++;
1071 }
1072 bp += nu->pntsu;
1073 }
1074
1075 if (a < nu->pntsu) {
1076 sel++;
1077 }
1078 else {
1079 bp += nu->pntsu;
1080 if ((bp->hide == 0) && (bp->f1 & SELECT)) {
1081 sel++;
1082 }
1083 bp -= nu->pntsu;
1084 }
1085
1086 if (sel != 4) {
1087 select_bpoint(bp, false, SELECT, VISIBLE);
1088 BLI_BITMAP_ENABLE(selbpoints, a);
1089 }
1090 }
1091 else {
1092 lastsel = false;
1093 }
1094
1095 bp++;
1096 }
1097
1098 MEM_freeN(selbpoints);
1099 }
1100 }
1101 else {
1102 LISTBASE_FOREACH (Nurb *, nu, editnurb) {
1103 lastsel = false;
1104 /* check what type of curve/nurb it is */
1105 if (nu->type == CU_BEZIER) {
1106 a = nu->pntsu;
1107 bezt = nu->bezt;
1108 while (a--) {
1109 if ((bezt->hide == 0) && (bezt->f2 & SELECT)) {
1110 sel = (lastsel == 1);
1111
1112 /* check if neighbors have been selected */
1113 /* first and last are exceptions */
1114 if (a == nu->pntsu - 1) {
1115 sel++;
1116 }
1117 else {
1118 bezt--;
1119 if ((bezt->hide == 0) && (bezt->f2 & SELECT)) {
1120 sel++;
1121 }
1122 bezt++;
1123 }
1124
1125 if (a == 0) {
1126 sel++;
1127 }
1128 else {
1129 bezt++;
1130 if ((bezt->hide == 0) && (bezt->f2 & SELECT)) {
1131 sel++;
1132 }
1133 bezt--;
1134 }
1135
1136 if (sel != 2) {
1137 select_beztriple(bezt, false, SELECT, VISIBLE);
1138 lastsel = true;
1139 }
1140 else {
1141 lastsel = false;
1142 }
1143 }
1144 else {
1145 lastsel = false;
1146 }
1147
1148 bezt++;
1149 }
1150 }
1151 else {
1152 a = nu->pntsu * nu->pntsv;
1153 bp = nu->bp;
1154 while (a--) {
1155 if ((lastsel == false) && (bp->hide == 0) && (bp->f1 & SELECT)) {
1156 sel = 0;
1157
1158 /* first and last are exceptions */
1159 if (a == nu->pntsu * nu->pntsv - 1) {
1160 sel++;
1161 }
1162 else {
1163 bp--;
1164 if ((bp->hide == 0) && (bp->f1 & SELECT)) {
1165 sel++;
1166 }
1167 bp++;
1168 }
1169
1170 if (a == 0) {
1171 sel++;
1172 }
1173 else {
1174 bp++;
1175 if ((bp->hide == 0) && (bp->f1 & SELECT)) {
1176 sel++;
1177 }
1178 bp--;
1179 }
1180
1181 if (sel != 2) {
1182 select_bpoint(bp, false, SELECT, VISIBLE);
1183 lastsel = true;
1184 }
1185 else {
1186 lastsel = false;
1187 }
1188 }
1189 else {
1190 lastsel = false;
1191 }
1192
1193 bp++;
1194 }
1195 }
1196 }
1197 }
1198}
1199
1201{
1202 const Scene *scene = CTX_data_scene(C);
1203 ViewLayer *view_layer = CTX_data_view_layer(C);
1205 scene, view_layer, CTX_wm_view3d(C));
1206 for (Object *obedit : objects) {
1207 curve_select_less(obedit);
1208 DEG_id_tag_update(static_cast<ID *>(obedit->data), ID_RECALC_SELECT);
1209 WM_event_add_notifier(C, NC_GEOM | ND_SELECT, obedit->data);
1210 }
1211 return OPERATOR_FINISHED;
1212}
1213
1215{
1216 /* identifiers */
1217 ot->name = "Select Less";
1218 ot->idname = "CURVE_OT_select_less";
1219 ot->description = "Deselect control points at the boundary of each selection region";
1220
1221 /* api callbacks */
1224
1225 /* flags */
1227}
1228
1231/* -------------------------------------------------------------------- */
1236{
1237 const bool select = (RNA_enum_get(op->ptr, "action") == SEL_SELECT);
1238 const float randfac = RNA_float_get(op->ptr, "ratio");
1240
1241 const Scene *scene = CTX_data_scene(C);
1242 ViewLayer *view_layer = CTX_data_view_layer(C);
1244 scene, view_layer, CTX_wm_view3d(C));
1245
1246 for (const int ob_index : objects.index_range()) {
1247 Object *obedit = objects[ob_index];
1248 ListBase *editnurb = object_editcurve_get(obedit);
1249 int seed_iter = seed;
1250
1251 /* This gives a consistent result regardless of object order. */
1252 if (ob_index) {
1253 seed_iter += BLI_ghashutil_strhash_p(obedit->id.name);
1254 }
1255
1256 int totvert = 0;
1257 LISTBASE_FOREACH (Nurb *, nu, editnurb) {
1258 if (nu->type == CU_BEZIER) {
1259 int a = nu->pntsu;
1260 BezTriple *bezt = nu->bezt;
1261 while (a--) {
1262 if (!bezt->hide) {
1263 totvert++;
1264 }
1265 bezt++;
1266 }
1267 }
1268 else {
1269 int a = nu->pntsu * nu->pntsv;
1270 BPoint *bp = nu->bp;
1271 while (a--) {
1272 if (!bp->hide) {
1273 totvert++;
1274 }
1275 bp++;
1276 }
1277 }
1278 }
1279
1280 BLI_bitmap *verts_selection_mask = BLI_BITMAP_NEW(totvert, __func__);
1281 const int count_select = totvert * randfac;
1282 for (int i = 0; i < count_select; i++) {
1283 BLI_BITMAP_SET(verts_selection_mask, i, true);
1284 }
1285 BLI_bitmap_randomize(verts_selection_mask, totvert, seed_iter);
1286
1287 int bit_index = 0;
1288 LISTBASE_FOREACH (Nurb *, nu, editnurb) {
1289 if (nu->type == CU_BEZIER) {
1290 int a = nu->pntsu;
1291 BezTriple *bezt = nu->bezt;
1292
1293 while (a--) {
1294 if (!bezt->hide) {
1295 if (BLI_BITMAP_TEST(verts_selection_mask, bit_index)) {
1297 }
1298 bit_index++;
1299 }
1300 bezt++;
1301 }
1302 }
1303 else {
1304 int a = nu->pntsu * nu->pntsv;
1305 BPoint *bp = nu->bp;
1306
1307 while (a--) {
1308 if (!bp->hide) {
1309 if (BLI_BITMAP_TEST(verts_selection_mask, bit_index)) {
1311 }
1312 bit_index++;
1313 }
1314 bp++;
1315 }
1316 }
1317 }
1318
1319 MEM_freeN(verts_selection_mask);
1320 BKE_curve_nurb_vert_active_validate(static_cast<Curve *>(obedit->data));
1321 DEG_id_tag_update(static_cast<ID *>(obedit->data), ID_RECALC_SELECT);
1323 }
1324
1325 return OPERATOR_FINISHED;
1326}
1327
1329{
1330 /* identifiers */
1331 ot->name = "Select Random";
1332 ot->idname = "CURVE_OT_select_random";
1333 ot->description = "Randomly select some control points";
1334
1335 /* api callbacks */
1338
1339 /* flags */
1341
1342 /* properties */
1344}
1345
1348/* -------------------------------------------------------------------- */
1353{
1354 int a, start;
1355
1356 start = bezt - nu->bezt;
1357 a = nu->pntsu;
1358 bezt = &nu->bezt[a - 1];
1359
1360 while (a--) {
1361 const int depth = abs(start - a);
1363 select_beztriple(bezt, false, SELECT, HIDDEN);
1364 }
1365
1366 bezt--;
1367 }
1368}
1369
1371{
1372 int a, startrow, startpnt;
1373 int row, pnt;
1374
1375 startrow = (bp - nu->bp) / nu->pntsu;
1376 startpnt = (bp - nu->bp) % nu->pntsu;
1377
1378 a = nu->pntsu * nu->pntsv;
1379 bp = &nu->bp[a - 1];
1380 row = nu->pntsv - 1;
1381 pnt = nu->pntsu - 1;
1382
1383 while (a--) {
1384 const int depth = abs(pnt - startpnt) + abs(row - startrow);
1386 select_bpoint(bp, false, SELECT, HIDDEN);
1387 }
1388
1389 pnt--;
1390 if (pnt < 0) {
1391 pnt = nu->pntsu - 1;
1392 row--;
1393 }
1394
1395 bp--;
1396 }
1397}
1398
1400{
1401 Nurb *nu = nullptr;
1402 void *vert = nullptr;
1403
1404 if (!BKE_curve_nurb_vert_active_get(cu, &nu, &vert)) {
1405 return false;
1406 }
1407
1408 if (nu->bezt) {
1409 select_nth_bezt(nu, static_cast<BezTriple *>(vert), params);
1410 }
1411 else {
1412 select_nth_bp(nu, static_cast<BPoint *>(vert), params);
1413 }
1414
1415 return true;
1416}
1417
1419{
1420 const Scene *scene = CTX_data_scene(C);
1421 ViewLayer *view_layer = CTX_data_view_layer(C);
1422 Object *obact = CTX_data_edit_object(C);
1423 View3D *v3d = CTX_wm_view3d(C);
1424 bool changed = false;
1425
1426 CheckerIntervalParams op_params;
1428
1430 scene, view_layer, CTX_wm_view3d(C));
1431 for (Object *obedit : objects) {
1432 Curve *cu = static_cast<Curve *>(obedit->data);
1433
1434 if (!ED_curve_select_check(v3d, cu->editnurb)) {
1435 continue;
1436 }
1437
1438 if (ed_curve_select_nth(static_cast<Curve *>(obedit->data), &op_params) == true) {
1439 changed = true;
1440
1441 DEG_id_tag_update(static_cast<ID *>(obedit->data), ID_RECALC_SELECT);
1442 WM_event_add_notifier(C, NC_GEOM | ND_SELECT, obedit->data);
1443 }
1444 }
1445
1446 if (!changed) {
1447 if (obact->type == OB_SURF) {
1448 BKE_report(op->reports, RPT_ERROR, "Surface(s) have no active point");
1449 }
1450 else {
1451 BKE_report(op->reports, RPT_ERROR, "Curve(s) have no active point");
1452 }
1453 return OPERATOR_CANCELLED;
1454 }
1455 return OPERATOR_FINISHED;
1456}
1457
1459{
1460 /* identifiers */
1461 ot->name = "Checker Deselect";
1462 ot->description = "Deselect every Nth point starting from the active one";
1463 ot->idname = "CURVE_OT_select_nth";
1464
1465 /* api callbacks */
1468
1469 /* flags */
1471
1473}
1474
1477/* -------------------------------------------------------------------- */
1482 {SIM_CMP_EQ, "EQUAL", 0, "Equal", ""},
1483 {SIM_CMP_GT, "GREATER", 0, "Greater", ""},
1484 {SIM_CMP_LT, "LESS", 0, "Less", ""},
1485
1486 {0, nullptr, 0, nullptr, nullptr},
1487};
1488
1489enum {
1494};
1495
1497 {SIMCURHAND_TYPE, "TYPE", 0, "Type", ""},
1498 {SIMCURHAND_RADIUS, "RADIUS", 0, "Radius", ""},
1499 {SIMCURHAND_WEIGHT, "WEIGHT", 0, "Weight", ""},
1500 {SIMCURHAND_DIRECTION, "DIRECTION", 0, "Direction", ""},
1501 {0, nullptr, 0, nullptr, nullptr},
1502};
1503
1505 Nurb *nu,
1506 BezTriple *bezt,
1507 float r_dir[3])
1508{
1509 float rsmat[3][3];
1510 BKE_nurb_bezt_calc_normal(nu, bezt, r_dir);
1511 copy_m3_m4(rsmat, ob->object_to_world().ptr());
1512 mul_m3_v3(rsmat, r_dir);
1513 normalize_v3(r_dir);
1514}
1515
1516static void nurb_bpoint_direction_worldspace_get(Object *ob, Nurb *nu, BPoint *bp, float r_dir[3])
1517{
1518 float rsmat[3][3];
1519 BKE_nurb_bpoint_calc_normal(nu, bp, r_dir);
1520 copy_m3_m4(rsmat, ob->object_to_world().ptr());
1521 mul_m3_v3(rsmat, r_dir);
1522 normalize_v3(r_dir);
1523}
1524
1526 Object *ob, Nurb *nu, const int type, KDTree_1d *tree_1d, KDTree_3d *tree_3d)
1527{
1528 float tree_entry[3] = {0.0f, 0.0f, 0.0f};
1529
1530 if (nu->type == CU_BEZIER) {
1531 BezTriple *bezt;
1532 int i;
1533 int tree_index = 0;
1534
1535 for (i = nu->pntsu, bezt = nu->bezt; i--; bezt++) {
1536 if ((!bezt->hide) && (bezt->f1 & SELECT)) {
1537
1538 switch (type) {
1539 case SIMCURHAND_RADIUS: {
1540 float radius_ref = bezt->radius;
1541 tree_entry[0] = radius_ref;
1542 break;
1543 }
1544 case SIMCURHAND_WEIGHT: {
1545 float weight_ref = bezt->weight;
1546 tree_entry[0] = weight_ref;
1547 break;
1548 }
1549 case SIMCURHAND_DIRECTION: {
1550 nurb_bezt_direction_worldspace_get(ob, nu, bezt, tree_entry);
1551 break;
1552 }
1553 }
1554 if (tree_1d) {
1555 BLI_kdtree_1d_insert(tree_1d, tree_index++, tree_entry);
1556 }
1557 else {
1558 BLI_kdtree_3d_insert(tree_3d, tree_index++, tree_entry);
1559 }
1560 }
1561 }
1562 }
1563 else {
1564 BPoint *bp;
1565 int i;
1566 int tree_index = 0;
1567
1568 for (i = nu->pntsu * nu->pntsv, bp = nu->bp; i--; bp++) {
1569 if (!bp->hide && bp->f1 & SELECT) {
1570 switch (type) {
1571 case SIMCURHAND_RADIUS: {
1572 float radius_ref = bp->radius;
1573 tree_entry[0] = radius_ref;
1574 break;
1575 }
1576 case SIMCURHAND_WEIGHT: {
1577 float weight_ref = bp->weight;
1578 tree_entry[0] = weight_ref;
1579 break;
1580 }
1581 case SIMCURHAND_DIRECTION: {
1582 nurb_bpoint_direction_worldspace_get(ob, nu, bp, tree_entry);
1583 break;
1584 }
1585 }
1586 if (tree_1d) {
1587 BLI_kdtree_1d_insert(tree_1d, tree_index++, tree_entry);
1588 }
1589 else {
1590 BLI_kdtree_3d_insert(tree_3d, tree_index++, tree_entry);
1591 }
1592 }
1593 }
1594 }
1595}
1596
1598 Nurb *nu,
1599 const int type,
1600 const KDTree_1d *tree_1d,
1601 const KDTree_3d *tree_3d,
1602 const float thresh,
1603 const int compare)
1604{
1605 const float thresh_cos = cosf(thresh * float(M_PI_2));
1606 bool changed = false;
1607
1608 if (nu->type == CU_BEZIER) {
1609 BezTriple *bezt;
1610 int i;
1611
1612 for (i = nu->pntsu, bezt = nu->bezt; i--; bezt++) {
1613 if (!bezt->hide) {
1614 bool select = false;
1615
1616 switch (type) {
1617 case SIMCURHAND_RADIUS: {
1618 float radius_ref = bezt->radius;
1620 tree_1d, radius_ref, thresh, eSimilarCmp(compare)))
1621 {
1622 select = true;
1623 }
1624 break;
1625 }
1626 case SIMCURHAND_WEIGHT: {
1627 float weight_ref = bezt->weight;
1629 tree_1d, weight_ref, thresh, eSimilarCmp(compare)))
1630 {
1631 select = true;
1632 }
1633 break;
1634 }
1635 case SIMCURHAND_DIRECTION: {
1636 float dir[3];
1637 nurb_bezt_direction_worldspace_get(ob, nu, bezt, dir);
1638 KDTreeNearest_3d nearest;
1639 if (BLI_kdtree_3d_find_nearest(tree_3d, dir, &nearest) != -1) {
1640 float orient = angle_normalized_v3v3(dir, nearest.co);
1641 float delta = thresh_cos - fabsf(cosf(orient));
1642 if (ED_select_similar_compare_float(delta, thresh, eSimilarCmp(compare))) {
1643 select = true;
1644 }
1645 }
1646 break;
1647 }
1648 }
1649
1650 if (select) {
1651 select_beztriple(bezt, true, SELECT, VISIBLE);
1652 changed = true;
1653 }
1654 }
1655 }
1656 }
1657 else {
1658 BPoint *bp;
1659 int i;
1660
1661 for (i = nu->pntsu * nu->pntsv, bp = nu->bp; i--; bp++) {
1662 if (!bp->hide) {
1663 bool select = false;
1664
1665 switch (type) {
1666 case SIMCURHAND_RADIUS: {
1667 float radius_ref = bp->radius;
1669 tree_1d, radius_ref, thresh, eSimilarCmp(compare)))
1670 {
1671 select = true;
1672 }
1673 break;
1674 }
1675 case SIMCURHAND_WEIGHT: {
1676 float weight_ref = bp->weight;
1678 tree_1d, weight_ref, thresh, eSimilarCmp(compare)))
1679 {
1680 select = true;
1681 }
1682 break;
1683 }
1684 case SIMCURHAND_DIRECTION: {
1685 float dir[3];
1686 nurb_bpoint_direction_worldspace_get(ob, nu, bp, dir);
1687 KDTreeNearest_3d nearest;
1688 if (BLI_kdtree_3d_find_nearest(tree_3d, dir, &nearest) != -1) {
1689 float orient = angle_normalized_v3v3(dir, nearest.co);
1690 float delta = fabsf(cosf(orient)) - thresh_cos;
1691 if (ED_select_similar_compare_float(delta, thresh, eSimilarCmp(compare))) {
1692 select = true;
1693 }
1694 }
1695 break;
1696 }
1697 }
1698
1699 if (select) {
1700 select_bpoint(bp, true, SELECT, VISIBLE);
1701 changed = true;
1702 }
1703 }
1704 }
1705 }
1706 return changed;
1707}
1708
1710{
1711 /* Get props. */
1712 const int optype = RNA_enum_get(op->ptr, "type");
1713 const float thresh = RNA_float_get(op->ptr, "threshold");
1714 const int compare = RNA_enum_get(op->ptr, "compare");
1715
1716 const Scene *scene = CTX_data_scene(C);
1717 ViewLayer *view_layer = CTX_data_view_layer(C);
1718 View3D *v3d = CTX_wm_view3d(C);
1719 int tot_nurbs_selected_all = 0;
1721 scene, view_layer, CTX_wm_view3d(C));
1722
1723 for (Object *obedit : objects) {
1724 Curve *cu = static_cast<Curve *>(obedit->data);
1725 tot_nurbs_selected_all += ED_curve_select_count(v3d, cu->editnurb);
1726 }
1727
1728 if (tot_nurbs_selected_all == 0) {
1729 BKE_report(op->reports, RPT_ERROR, "No control point selected");
1730 return OPERATOR_CANCELLED;
1731 }
1732
1733 KDTree_1d *tree_1d = nullptr;
1734 KDTree_3d *tree_3d = nullptr;
1735 short type_ref = 0;
1736
1737 switch (optype) {
1738 case SIMCURHAND_RADIUS:
1739 case SIMCURHAND_WEIGHT:
1740 tree_1d = BLI_kdtree_1d_new(tot_nurbs_selected_all);
1741 break;
1743 tree_3d = BLI_kdtree_3d_new(tot_nurbs_selected_all);
1744 break;
1745 }
1746
1747 /* Get type of selected control points. */
1748 for (Object *obedit : objects) {
1749 Curve *cu = static_cast<Curve *>(obedit->data);
1750 EditNurb *editnurb = cu->editnurb;
1751
1752 LISTBASE_FOREACH (Nurb *, nu, &editnurb->nurbs) {
1753 if (!ED_curve_nurb_select_check(v3d, nu)) {
1754 continue;
1755 }
1756 switch (optype) {
1757 case SIMCURHAND_TYPE: {
1758 type_ref |= nu->type;
1759 break;
1760 }
1761 case SIMCURHAND_RADIUS:
1762 case SIMCURHAND_WEIGHT:
1764 curve_nurb_selected_type_get(obedit, nu, optype, tree_1d, tree_3d);
1765 break;
1766 }
1767 }
1768 }
1769
1770 if (tree_1d != nullptr) {
1771 BLI_kdtree_1d_deduplicate(tree_1d);
1772 BLI_kdtree_1d_balance(tree_1d);
1773 }
1774 if (tree_3d != nullptr) {
1775 BLI_kdtree_3d_deduplicate(tree_3d);
1776 BLI_kdtree_3d_balance(tree_3d);
1777 }
1778
1779 /* Select control points with desired type. */
1780 for (Object *obedit : objects) {
1781 Curve *cu = static_cast<Curve *>(obedit->data);
1782 EditNurb *editnurb = cu->editnurb;
1783 bool changed = false;
1784
1785 LISTBASE_FOREACH (Nurb *, nu, &editnurb->nurbs) {
1786 switch (optype) {
1787 case SIMCURHAND_TYPE: {
1788 if (nu->type & type_ref) {
1789 changed |= ED_curve_nurb_select_all(nu);
1790 }
1791 break;
1792 }
1793 case SIMCURHAND_RADIUS:
1794 case SIMCURHAND_WEIGHT:
1797 obedit, nu, optype, tree_1d, tree_3d, thresh, compare);
1798 break;
1799 }
1800 }
1801
1802 if (changed) {
1803 DEG_id_tag_update(static_cast<ID *>(obedit->data), ID_RECALC_SELECT);
1804 WM_event_add_notifier(C, NC_GEOM | ND_SELECT, obedit->data);
1805 }
1806 }
1807
1808 if (tree_1d != nullptr) {
1809 BLI_kdtree_1d_free(tree_1d);
1810 }
1811 if (tree_3d != nullptr) {
1812 BLI_kdtree_3d_free(tree_3d);
1813 }
1814 return OPERATOR_FINISHED;
1815}
1816
1818{
1819 /* identifiers */
1820 ot->name = "Select Similar";
1821 ot->idname = "CURVE_OT_select_similar";
1822 ot->description = "Select similar curve points by property type";
1823
1824 /* api callbacks */
1828
1829 /* flags */
1831
1832 /* properties */
1833 ot->prop = RNA_def_enum(
1834 ot->srna, "type", curve_prop_similar_types, SIMCURHAND_WEIGHT, "Type", "");
1835 RNA_def_enum(ot->srna, "compare", curve_prop_similar_compare_types, SIM_CMP_EQ, "Compare", "");
1836 RNA_def_float(ot->srna, "threshold", 0.1, 0.0, FLT_MAX, "Threshold", "", 0.0, 100.0);
1837}
1838
1841/* -------------------------------------------------------------------- */
1845static float curve_calc_dist_pair(const Nurb *nu, int a, int b)
1846{
1847 const float *a_fl, *b_fl;
1848
1849 if (nu->type == CU_BEZIER) {
1850 a_fl = nu->bezt[a].vec[1];
1851 b_fl = nu->bezt[b].vec[1];
1852 }
1853 else {
1854 a_fl = nu->bp[a].vec;
1855 b_fl = nu->bp[b].vec;
1856 }
1857
1858 return len_v3v3(a_fl, b_fl);
1859}
1860
1861static float curve_calc_dist_span(Nurb *nu, int vert_src, int vert_dst)
1862{
1863 const int u = nu->pntsu;
1864 int i_prev, i;
1865 float dist = 0.0f;
1866
1867 BLI_assert(nu->pntsv <= 1);
1868
1869 i_prev = vert_src;
1870 i = (i_prev + 1) % u;
1871
1872 while (true) {
1873 dist += curve_calc_dist_pair(nu, i_prev, i);
1874
1875 if (i == vert_dst) {
1876 break;
1877 }
1878 i = (i + 1) % u;
1879 }
1880 return dist;
1881}
1882
1883static void curve_select_shortest_path_curve(Nurb *nu, int vert_src, int vert_dst)
1884{
1885 const int u = nu->pntsu;
1886 int i;
1887
1888 if (vert_src > vert_dst) {
1889 std::swap(vert_src, vert_dst);
1890 }
1891
1892 if (nu->flagu & CU_NURB_CYCLIC) {
1893 if (curve_calc_dist_span(nu, vert_src, vert_dst) >
1894 curve_calc_dist_span(nu, vert_dst, vert_src))
1895 {
1896 std::swap(vert_src, vert_dst);
1897 }
1898 }
1899
1900 i = vert_src;
1901 while (true) {
1902 if (nu->type & CU_BEZIER) {
1903 select_beztriple(&nu->bezt[i], true, SELECT, HIDDEN);
1904 }
1905 else {
1906 select_bpoint(&nu->bp[i], true, SELECT, HIDDEN);
1907 }
1908
1909 if (i == vert_dst) {
1910 break;
1911 }
1912 i = (i + 1) % u;
1913 }
1914}
1915
1916static void curve_select_shortest_path_surf(Nurb *nu, int vert_src, int vert_dst)
1917{
1918 int totu = nu->pntsu;
1919 int totv = nu->pntsv;
1920 int vert_num = totu * totv;
1921
1922 /* custom data */
1923 struct PointAdj {
1924 int vert, vert_prev;
1925 float cost;
1926 } *data;
1927
1928 /* init connectivity data */
1929 data = static_cast<PointAdj *>(MEM_mallocN(sizeof(*data) * vert_num, __func__));
1930 for (int i = 0; i < vert_num; i++) {
1931 data[i].vert = i;
1932 data[i].vert_prev = -1;
1933 data[i].cost = FLT_MAX;
1934 }
1935
1936 /* init heap */
1938
1939 int vert_curr = data[vert_src].vert;
1940 BLI_heapsimple_insert(heap, 0.0f, &data[vert_src].vert);
1941 data[vert_src].cost = 0.0f;
1942 data[vert_src].vert_prev = vert_src; /* nop */
1943
1944 while (!BLI_heapsimple_is_empty(heap)) {
1945 vert_curr = *((int *)BLI_heapsimple_pop_min(heap));
1946 if (vert_curr == vert_dst) {
1947 break;
1948 }
1949
1950 int u, v;
1951 BKE_nurb_index_to_uv(nu, vert_curr, &u, &v);
1952
1953 /* loop over 4 adjacent verts */
1954 for (int sign = -1; sign != 3; sign += 2) {
1955 for (int axis = 0; axis != 2; axis += 1) {
1956 int uv_other[2] = {u, v};
1957 int vert_other;
1958
1959 uv_other[axis] += sign;
1960
1961 vert_other = BKE_nurb_index_from_uv(nu, uv_other[0], uv_other[1]);
1962 if (vert_other != -1) {
1963 const float dist = data[vert_curr].cost +
1964 curve_calc_dist_pair(nu, vert_curr, vert_other);
1965
1966 if (data[vert_other].cost > dist) {
1967 data[vert_other].cost = dist;
1968 if (data[vert_other].vert_prev == -1) {
1969 BLI_heapsimple_insert(heap, data[vert_other].cost, &data[vert_other].vert);
1970 }
1971 data[vert_other].vert_prev = vert_curr;
1972 }
1973 }
1974 }
1975 }
1976 }
1977
1978 BLI_heapsimple_free(heap, nullptr);
1979
1980 if (vert_curr == vert_dst) {
1981 int i = 0;
1982 while (vert_curr != vert_src && i++ < vert_num) {
1983 if (nu->type == CU_BEZIER) {
1984 select_beztriple(&nu->bezt[vert_curr], true, SELECT, HIDDEN);
1985 }
1986 else {
1987 select_bpoint(&nu->bp[vert_curr], true, SELECT, HIDDEN);
1988 }
1989 vert_curr = data[vert_curr].vert_prev;
1990 }
1991 }
1992
1993 MEM_freeN(data);
1994}
1995
1997{
1999 Nurb *nu_dst;
2000 BezTriple *bezt_dst;
2001 BPoint *bp_dst;
2002 int vert_dst;
2003 void *vert_dst_p;
2004 Base *basact = nullptr;
2005
2008 copy_v2_v2_int(vc.mval, event->mval);
2009
2010 if (!ED_curve_pick_vert(&vc, 1, &nu_dst, &bezt_dst, &bp_dst, nullptr, &basact)) {
2011 return OPERATOR_PASS_THROUGH;
2012 }
2013
2015 Object *obedit = basact->object;
2016 Curve *cu = static_cast<Curve *>(obedit->data);
2017 Nurb *nu_src = BKE_curve_nurb_active_get(cu);
2018 int vert_src = cu->actvert;
2019
2020 if (vert_src == CU_ACT_NONE) {
2021 return OPERATOR_PASS_THROUGH;
2022 }
2023
2024 if (nu_src != nu_dst) {
2025 BKE_report(op->reports, RPT_ERROR, "Control point belongs to another spline");
2026 return OPERATOR_CANCELLED;
2027 }
2028
2029 vert_dst_p = bezt_dst ? (void *)bezt_dst : (void *)bp_dst;
2030 vert_dst = BKE_curve_nurb_vert_index_get(nu_dst, vert_dst_p);
2031 if (vert_src == vert_dst) {
2032 return OPERATOR_CANCELLED;
2033 }
2034
2035 if ((obedit->type == OB_SURF) && (nu_src->pntsv > 1)) {
2036 curve_select_shortest_path_surf(nu_src, vert_src, vert_dst);
2037 }
2038 else {
2039 curve_select_shortest_path_curve(nu_src, vert_src, vert_dst);
2040 }
2041
2042 BKE_curve_nurb_vert_active_set(cu, nu_dst, vert_dst_p);
2043
2045 if (BKE_view_layer_active_base_get(vc.view_layer) != basact) {
2047 }
2048
2051 return OPERATOR_FINISHED;
2052}
2053
2055{
2056 /* identifiers */
2057 ot->name = "Pick Shortest Path";
2058 ot->idname = "CURVE_OT_shortest_path_pick";
2059 ot->description = "Select shortest path between two selections";
2060
2061 /* api callbacks */
2064
2065 /* flags */
2067}
2068
Depsgraph * CTX_data_ensure_evaluated_depsgraph(const bContext *C)
Scene * CTX_data_scene(const bContext *C)
Object * CTX_data_edit_object(const bContext *C)
View3D * CTX_wm_view3d(const bContext *C)
ViewLayer * CTX_data_view_layer(const bContext *C)
void BKE_nurb_index_to_uv(Nurb *nu, int index, int *r_u, int *r_v)
Definition curve.cc:911
Nurb * BKE_curve_nurb_active_get(Curve *cu)
Definition curve.cc:4995
void BKE_curve_nurb_vert_active_set(Curve *cu, const Nurb *nu, const void *vert)
Definition curve.cc:5021
int BKE_nurb_index_from_uv(Nurb *nu, int u, int v)
Definition curve.cc:889
void BKE_nurb_bezt_calc_normal(Nurb *nu, BezTriple *bezt, float r_normal[3])
Definition curve.cc:1006
void BKE_curve_nurb_vert_active_validate(Curve *cu)
Definition curve.cc:5065
bool BKE_curve_nurb_vert_active_get(Curve *cu, Nurb **r_nu, void **r_vert)
Definition curve.cc:5038
void BKE_nurbList_flag_set(ListBase *editnurb, uint8_t flag, bool set)
Definition curve.cc:4339
void BKE_nurb_bpoint_calc_normal(Nurb *nu, BPoint *bp, float r_normal[3])
Definition curve.cc:1058
int BKE_curve_nurb_vert_index_get(const Nurb *nu, const void *vert)
Definition curve.cc:5010
void BKE_view_layer_synced_ensure(const Scene *scene, ViewLayer *view_layer)
Base * BKE_view_layer_active_base_get(ViewLayer *view_layer)
blender::Vector< Base * > BKE_view_layer_array_from_bases_in_edit_mode_unique_data(const Scene *scene, ViewLayer *view_layer, const View3D *v3d)
blender::Vector< Object * > BKE_view_layer_array_from_objects_in_edit_mode_unique_data(const Scene *scene, ViewLayer *view_layer, const View3D *v3d)
void BKE_report(ReportList *reports, eReportType type, const char *message)
Definition report.cc:125
#define BLI_assert(a)
Definition BLI_assert.h:50
#define BLI_BITMAP_NEW(_num, _alloc_string)
Definition BLI_bitmap.h:41
#define BLI_BITMAP_TEST(_bitmap, _index)
Definition BLI_bitmap.h:65
#define BLI_BITMAP_ENABLE(_bitmap, _index)
Definition BLI_bitmap.h:82
#define BLI_BITMAP_SET(_bitmap, _index, _set)
Definition BLI_bitmap.h:103
unsigned int BLI_bitmap
Definition BLI_bitmap.h:17
unsigned int BLI_ghashutil_strhash_p(const void *ptr)
A min-heap / priority queue ADT.
HeapSimple * BLI_heapsimple_new(void) ATTR_WARN_UNUSED_RESULT
void BLI_heapsimple_free(HeapSimple *heap, HeapSimpleFreeFP ptrfreefp) ATTR_NONNULL(1)
void * BLI_heapsimple_pop_min(HeapSimple *heap) ATTR_NONNULL(1)
bool BLI_heapsimple_is_empty(const HeapSimple *heap) ATTR_NONNULL(1)
void BLI_heapsimple_insert(HeapSimple *heap, float value, void *ptr) ATTR_NONNULL(1)
A KD-tree for nearest neighbor search.
#define LISTBASE_FOREACH(type, var, list)
#define M_PI_2
void mul_m3_v3(const float M[3][3], float r[3])
void copy_m3_m4(float m1[3][3], const float m2[4][4])
MINLINE float len_v3v3(const float a[3], const float b[3]) ATTR_WARN_UNUSED_RESULT
MINLINE void copy_v2_v2_int(int r[2], const int a[2])
float angle_normalized_v3v3(const float v1[3], const float v2[3]) ATTR_WARN_UNUSED_RESULT
MINLINE float normalize_v3(float n[3])
Random number functions.
void BLI_bitmap_randomize(unsigned int *bitmap, unsigned int bits_num, unsigned int seed) ATTR_NONNULL(1)
Definition rand.cc:196
void DEG_id_tag_update(ID *id, unsigned int flags)
@ ID_RECALC_SELECT
Definition DNA_ID.h:1068
@ ID_RECALC_SYNC_TO_EVAL
Definition DNA_ID.h:1085
#define BEZT_ISSEL_ANY_HIDDENHANDLES(v3d, bezt)
@ CU_BEZIER
#define BEZT_SEL_ALL(bezt)
#define BEZT_ISSEL_ANY(bezt)
#define CU_ACT_NONE
#define BEZT_DESEL_ALL(bezt)
#define BEZT_ISSEL_ALL(bezt)
@ CU_NURB_CYCLIC
Object is a sort of wrapper for general info.
@ OB_SURF
@ CURVE_HANDLE_NONE
@ OPERATOR_PASS_THROUGH
bool ED_operator_editsurfcurve_region_view3d(bContext *C)
bool ED_operator_editsurfcurve(bContext *C)
bool ED_operator_editcurve(bContext *C)
bool ED_operator_editsurf(bContext *C)
@ SEL_SELECT
@ SEL_INVERT
@ SEL_DESELECT
@ SEL_TOGGLE
bool ED_select_similar_compare_float(float delta, float thresh, eSimilarCmp compare)
eSimilarCmp
@ SIM_CMP_LT
@ SIM_CMP_GT
@ SIM_CMP_EQ
bool ED_select_similar_compare_float_tree(const KDTree_1d *tree, float length, float thresh, eSimilarCmp compare)
ViewContext ED_view3d_viewcontext_init(bContext *C, Depsgraph *depsgraph)
void view3d_operator_needs_opengl(const bContext *C)
void ED_view3d_viewcontext_init_object(ViewContext *vc, Object *obact)
Read Guarded memory(de)allocation.
@ OPTYPE_UNDO
Definition WM_types.hh:162
@ OPTYPE_REGISTER
Definition WM_types.hh:160
#define NC_GEOM
Definition WM_types.hh:360
#define ND_SELECT
Definition WM_types.hh:474
ATTR_WARN_UNUSED_RESULT const BMVert * v
static unsigned long seed
Definition btSoftBody.h:39
local_group_size(16, 16) .push_constant(Type b
#define SELECT
eEndPoint_Types
@ FIRST
@ LAST
bool ED_curve_pick_vert(ViewContext *vc, short sel, Nurb **r_nurb, BezTriple **r_bezt, BPoint **r_bp, short *r_handle, Base **r_base)
eVisible_Types
@ HIDDEN
@ VISIBLE
const Depsgraph * depsgraph
#define cosf(x)
#define fabsf(x)
ListBase * object_editcurve_get(Object *ob)
Definition editcurve.cc:88
void CURVE_OT_select_random(wmOperatorType *ot)
bool select_bpoint(BPoint *bp, bool selstatus, uint8_t flag, bool hidden)
static void select_nth_bezt(Nurb *nu, BezTriple *bezt, const CheckerIntervalParams *params)
bool select_beztriple(BezTriple *bezt, bool selstatus, uint8_t flag, eVisible_Types hidden)
bool ED_curve_select_check(const View3D *v3d, const EditNurb *editnurb)
bool ED_curve_deselect_all_multi_ex(Span< Base * > bases)
bool ED_curve_nurb_select_check(const View3D *v3d, const Nurb *nu)
void CURVE_OT_select_less(wmOperatorType *ot)
static bool ed_curve_select_nth(Curve *cu, const CheckerIntervalParams *params)
void CURVE_OT_select_similar(wmOperatorType *ot)
static int curve_select_more_exec(bContext *C, wmOperator *)
static void curve_select_more(Object *obedit)
static void curve_nurb_selected_type_get(Object *ob, Nurb *nu, const int type, KDTree_1d *tree_1d, KDTree_3d *tree_3d)
bool ED_curve_select_all(EditNurb *editnurb)
void CURVE_OT_select_previous(wmOperatorType *ot)
static const EnumPropertyItem curve_prop_similar_types[]
static int de_select_first_exec(bContext *C, wmOperator *)
static void nurb_bezt_direction_worldspace_get(Object *ob, Nurb *nu, BezTriple *bezt, float r_dir[3])
static const EnumPropertyItem curve_prop_similar_compare_types[]
static int select_linked_pick_invoke(bContext *C, wmOperator *op, const wmEvent *event)
void CURVE_OT_shortest_path_pick(wmOperatorType *ot)
bool ED_curve_deselect_all_multi(bContext *C)
void CURVE_OT_select_linked(wmOperatorType *ot)
bool ED_curve_nurb_deselect_all(const Nurb *nu)
static void selectend_nurb(Object *obedit, eEndPoint_Types selfirst, bool doswap, bool selstatus)
static void select_adjacent_cp(ListBase *editnurb, short next, const bool cont, const bool selstatus)
bool ED_curve_select_swap(EditNurb *editnurb, bool hide_handles)
static void curve_select_less(Object *obedit)
static int curve_select_random_exec(bContext *C, wmOperator *op)
static int select_row_exec(bContext *C, wmOperator *)
static void select_nth_bp(Nurb *nu, BPoint *bp, const CheckerIntervalParams *params)
void CURVE_OT_select_next(wmOperatorType *ot)
int ED_curve_select_count(const View3D *v3d, const EditNurb *editnurb)
static int select_next_exec(bContext *C, wmOperator *)
static int edcu_shortest_path_pick_invoke(bContext *C, wmOperator *op, const wmEvent *event)
static int de_select_last_exec(bContext *C, wmOperator *)
static bool curve_nurb_select_similar_type(Object *ob, Nurb *nu, const int type, const KDTree_1d *tree_1d, const KDTree_3d *tree_3d, const float thresh, const int compare)
static bool swap_selection_bpoint(BPoint *bp)
bool ED_curve_nurb_select_all(const Nurb *nu)
static int select_nth_exec(bContext *C, wmOperator *op)
void CURVE_OT_select_all(wmOperatorType *ot)
bool ED_curve_deselect_all(EditNurb *editnurb)
static bool swap_selection_beztriple(BezTriple *bezt)
void CURVE_OT_select_nth(wmOperatorType *ot)
@ SIMCURHAND_WEIGHT
@ SIMCURHAND_DIRECTION
@ SIMCURHAND_RADIUS
@ SIMCURHAND_TYPE
void CURVE_OT_de_select_first(wmOperatorType *ot)
static int curve_select_similar_exec(bContext *C, wmOperator *op)
void CURVE_OT_select_more(wmOperatorType *ot)
static void nurb_bpoint_direction_worldspace_get(Object *ob, Nurb *nu, BPoint *bp, float r_dir[3])
static int select_previous_exec(bContext *C, wmOperator *)
static int select_linked_invoke(bContext *C, wmOperator *op, const wmEvent *)
void CURVE_OT_select_row(wmOperatorType *ot)
int ED_curve_nurb_select_count(const View3D *v3d, const Nurb *nu)
void CURVE_OT_de_select_last(wmOperatorType *ot)
static float curve_calc_dist_span(Nurb *nu, int vert_src, int vert_dst)
static int select_linked_exec(bContext *C, wmOperator *)
void CURVE_OT_select_linked_pick(wmOperatorType *ot)
static void curve_select_shortest_path_curve(Nurb *nu, int vert_src, int vert_dst)
static void curve_select_shortest_path_surf(Nurb *nu, int vert_src, int vert_dst)
static int curve_select_less_exec(bContext *C, wmOperator *)
static float curve_calc_dist_pair(const Nurb *nu, int a, int b)
static int de_select_all_exec(bContext *C, wmOperator *op)
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
ccl_device_inline float4 select(const int4 mask, const float4 a, const float4 b)
static ulong * next
void base_activate(bContext *C, Base *base)
float RNA_float_get(PointerRNA *ptr, const char *name)
bool RNA_boolean_get(PointerRNA *ptr, const char *name)
int RNA_enum_get(PointerRNA *ptr, const char *name)
PropertyRNA * RNA_def_float(StructOrFunctionRNA *cont_, const char *identifier, const float default_value, const float hardmin, const float hardmax, const char *ui_name, const char *ui_description, const float softmin, const float softmax)
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
unsigned char uint8_t
Definition stdint.h:78
uint8_t f1
float vec[4]
struct Object * object
float vec[3][3]
EditNurb * editnurb
ListBase nurbs
Definition DNA_ID.h:413
char name[66]
Definition DNA_ID.h:425
short flagu
short type
BezTriple * bezt
BPoint * bp
View3DOverlay overlay
int mval[2]
Definition ED_view3d.hh:78
Scene * scene
Definition ED_view3d.hh:69
ViewLayer * view_layer
Definition ED_view3d.hh:70
View3D * v3d
Definition ED_view3d.hh:74
int mval[2]
Definition WM_types.hh:728
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(* invoke)(bContext *C, wmOperator *op, const wmEvent *event) ATTR_WARN_UNUSED_RESULT
Definition WM_types.hh:1022
int(* exec)(bContext *C, wmOperator *op) ATTR_WARN_UNUSED_RESULT
Definition WM_types.hh:1006
const char * description
Definition WM_types.hh:996
PropertyRNA * prop
Definition WM_types.hh:1092
StructRNA * srna
Definition WM_types.hh:1080
struct ReportList * reports
struct PointerRNA * ptr
ccl_device_inline int abs(int x)
Definition util/math.h:120
void WM_event_add_notifier(const bContext *C, uint type, void *reference)
wmOperatorType * ot
Definition wm_files.cc:4125
void WM_operator_properties_checker_interval_from_op(wmOperator *op, CheckerIntervalParams *op_params)
int WM_operator_properties_select_random_seed_increment_get(wmOperator *op)
void WM_operator_properties_select_random(wmOperatorType *ot)
void WM_operator_properties_select_all(wmOperatorType *ot)
void WM_operator_properties_checker_interval(wmOperatorType *ot, bool nth_can_disable)
bool WM_operator_properties_checker_interval_test(const CheckerIntervalParams *op_params, int depth)
int WM_menu_invoke(bContext *C, wmOperator *op, const wmEvent *)
uint8_t flag
Definition wm_window.cc:138