Blender V5.0
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
8
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/* -------------------------------------------------------------------- */
50
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
407
408/* -------------------------------------------------------------------- */
411
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. */
514 ot->exec = de_select_first_exec;
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. */
546 ot->exec = de_select_last_exec;
548
549 /* flags */
551}
552
554
555/* -------------------------------------------------------------------- */
558
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. */
615 ot->exec = de_select_all_exec;
617
618 /* flags */
620
621 /* properties */
623}
624
626
627/* -------------------------------------------------------------------- */
630
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
661 wmOperator *op,
662 const wmEvent * /*event*/)
663{
664 return select_linked_exec(C, op);
665}
666
668{
669 /* identifiers */
670 ot->name = "Select Linked All";
671 ot->idname = "CURVE_OT_select_linked";
672 ot->description = "Select all control points linked to the current selection";
673
674 /* API callbacks. */
675 ot->exec = select_linked_exec;
676 ot->invoke = select_linked_invoke;
678
679 /* flags */
681
682 /* properties */
683}
684
686
687/* -------------------------------------------------------------------- */
690
692 wmOperator *op,
693 const wmEvent *event)
694{
696 Nurb *nu;
697 BezTriple *bezt;
698 BPoint *bp;
699 int a;
700 const bool select = !RNA_boolean_get(op->ptr, "deselect");
701 Base *basact = nullptr;
702
705 copy_v2_v2_int(vc.mval, event->mval);
706
707 if (!ED_curve_pick_vert(&vc, 1, &nu, &bezt, &bp, nullptr, &basact)) {
708 return OPERATOR_CANCELLED;
709 }
710
711 if (bezt) {
712 a = nu->pntsu;
713 bezt = nu->bezt;
714 while (a--) {
716 bezt++;
717 }
718 }
719 else if (bp) {
720 a = nu->pntsu * nu->pntsv;
721 bp = nu->bp;
722 while (a--) {
724 bp++;
725 }
726 }
727
728 Object *obedit = basact->object;
729
730 DEG_id_tag_update(static_cast<ID *>(obedit->data), ID_RECALC_SELECT);
732
733 if (!select) {
734 BKE_curve_nurb_vert_active_validate(static_cast<Curve *>(obedit->data));
735 }
736
737 return OPERATOR_FINISHED;
738}
739
741{
742 /* identifiers */
743 ot->name = "Select Linked";
744 ot->idname = "CURVE_OT_select_linked_pick";
745 ot->description = "Select all control points linked to already selected ones";
746
747 /* API callbacks. */
750
751 /* flags */
753
754 /* properties */
755 RNA_def_boolean(ot->srna,
756 "deselect",
757 false,
758 "Deselect",
759 "Deselect linked control points rather than selecting them");
760}
761
763
764/* -------------------------------------------------------------------- */
767
769{
770 Object *obedit = CTX_data_edit_object(C);
771 Curve *cu = static_cast<Curve *>(obedit->data);
772 ListBase *editnurb = object_editcurve_get(obedit);
773 static BPoint *last = nullptr;
774 static int direction = 0;
775 Nurb *nu = nullptr;
776 BPoint *bp = nullptr;
777 int u = 0, v = 0, a, b;
778
779 if (!BKE_curve_nurb_vert_active_get(cu, &nu, static_cast<void **>((void *)&bp))) {
780 return OPERATOR_CANCELLED;
781 }
782
783 if (last == bp) {
784 direction = 1 - direction;
785 BKE_nurbList_flag_set(editnurb, SELECT, false);
786 }
787 last = bp;
788
789 u = cu->actvert % nu->pntsu;
790 v = cu->actvert / nu->pntsu;
791 bp = nu->bp;
792 for (a = 0; a < nu->pntsv; a++) {
793 for (b = 0; b < nu->pntsu; b++, bp++) {
794 if (direction) {
795 if (a == v) {
796 select_bpoint(bp, true, SELECT, VISIBLE);
797 }
798 }
799 else {
800 if (b == u) {
801 select_bpoint(bp, true, SELECT, VISIBLE);
802 }
803 }
804 }
805 }
806
807 DEG_id_tag_update(static_cast<ID *>(obedit->data), ID_RECALC_SELECT);
809
810 return OPERATOR_FINISHED;
811}
812
814{
815 /* identifiers */
816 ot->name = "Select Control Point Row";
817 ot->idname = "CURVE_OT_select_row";
818 ot->description =
819 "Select a row of control points including active one. "
820 "Successive use on the same point switches between U/V directions";
821
822 /* API callbacks. */
823 ot->exec = select_row_exec;
824 ot->poll = ED_operator_editsurf;
825
826 /* flags */
828}
829
831
832/* -------------------------------------------------------------------- */
835
837{
838 Scene *scene = CTX_data_scene(C);
839 ViewLayer *view_layer = CTX_data_view_layer(C);
841 scene, view_layer, CTX_wm_view3d(C));
842
843 for (Object *obedit : objects) {
844
845 ListBase *editnurb = object_editcurve_get(obedit);
846 select_adjacent_cp(editnurb, 1, false, SELECT);
847
848 DEG_id_tag_update(static_cast<ID *>(obedit->data), ID_RECALC_SELECT);
849 WM_event_add_notifier(C, NC_GEOM | ND_SELECT, obedit->data);
850 }
851 return OPERATOR_FINISHED;
852}
853
855{
856 /* identifiers */
857 ot->name = "Select Next";
858 ot->idname = "CURVE_OT_select_next";
859 ot->description = "Select control points following already selected ones along the curves";
860
861 /* API callbacks. */
862 ot->exec = select_next_exec;
864
865 /* flags */
867}
868
870
871/* -------------------------------------------------------------------- */
874
876{
877 Scene *scene = CTX_data_scene(C);
878 ViewLayer *view_layer = CTX_data_view_layer(C);
880 scene, view_layer, CTX_wm_view3d(C));
881
882 for (Object *obedit : objects) {
883
884 ListBase *editnurb = object_editcurve_get(obedit);
885 select_adjacent_cp(editnurb, -1, false, SELECT);
886
887 DEG_id_tag_update(static_cast<ID *>(obedit->data), ID_RECALC_SELECT);
888 WM_event_add_notifier(C, NC_GEOM | ND_SELECT, obedit->data);
889 }
890 return OPERATOR_FINISHED;
891}
892
894{
895 /* identifiers */
896 ot->name = "Select Previous";
897 ot->idname = "CURVE_OT_select_previous";
898 ot->description = "Select control points preceding already selected ones along the curves";
899
900 /* API callbacks. */
901 ot->exec = select_previous_exec;
903
904 /* flags */
906}
907
909
910/* -------------------------------------------------------------------- */
913
914static void curve_select_more(Object *obedit)
915{
916 ListBase *editnurb = object_editcurve_get(obedit);
917 BPoint *bp, *tempbp;
918 int a;
919 short sel = 0;
920
921 /* NOTE: NURBS surface is a special case because we mimic
922 * the behavior of "select more" of mesh tools.
923 * The algorithm is designed to work in planar cases so it
924 * may not be optimal always (example: end of NURBS sphere). */
925 if (obedit->type == OB_SURF) {
926 LISTBASE_FOREACH (Nurb *, nu, editnurb) {
927 BLI_bitmap *selbpoints;
928 a = nu->pntsu * nu->pntsv;
929 bp = nu->bp;
930 selbpoints = BLI_BITMAP_NEW(a, "selectlist");
931 while (a > 0) {
932 if (!BLI_BITMAP_TEST(selbpoints, a) && (bp->hide == 0) && (bp->f1 & SELECT)) {
933 /* upper control point */
934 if (a % nu->pntsu != 0) {
935 tempbp = bp - 1;
936 if (!(tempbp->f1 & SELECT)) {
937 select_bpoint(tempbp, true, SELECT, VISIBLE);
938 }
939 }
940
941 /* left control point. select only if it is not selected already */
942 if (a - nu->pntsu > 0) {
943 sel = 0;
944 tempbp = bp + nu->pntsu;
945 if (!(tempbp->f1 & SELECT)) {
946 sel = select_bpoint(tempbp, true, SELECT, VISIBLE);
947 }
948 /* make sure selected bpoint is discarded */
949 if (sel == 1) {
950 BLI_BITMAP_ENABLE(selbpoints, a - nu->pntsu);
951 }
952 }
953
954 /* right control point */
955 if (a + nu->pntsu < nu->pntsu * nu->pntsv) {
956 tempbp = bp - nu->pntsu;
957 if (!(tempbp->f1 & SELECT)) {
958 select_bpoint(tempbp, true, SELECT, VISIBLE);
959 }
960 }
961
962 /* lower control point. skip next bp in case selection was made */
963 if (a % nu->pntsu != 1) {
964 sel = 0;
965 tempbp = bp + 1;
966 if (!(tempbp->f1 & SELECT)) {
967 sel = select_bpoint(tempbp, true, SELECT, VISIBLE);
968 }
969 if (sel) {
970 bp++;
971 a--;
972 }
973 }
974 }
975
976 bp++;
977 a--;
978 }
979
980 MEM_freeN(selbpoints);
981 }
982 }
983 else {
984 select_adjacent_cp(editnurb, 1, false, SELECT);
985 select_adjacent_cp(editnurb, -1, false, SELECT);
986 }
987}
988
990{
991 Scene *scene = CTX_data_scene(C);
992 ViewLayer *view_layer = CTX_data_view_layer(C);
994 scene, view_layer, CTX_wm_view3d(C));
995 for (Object *obedit : objects) {
996 curve_select_more(obedit);
997 DEG_id_tag_update(static_cast<ID *>(obedit->data), ID_RECALC_SELECT);
998 WM_event_add_notifier(C, NC_GEOM | ND_SELECT, obedit->data);
999 }
1000 return OPERATOR_FINISHED;
1001}
1002
1004{
1005 /* identifiers */
1006 ot->name = "Select More";
1007 ot->idname = "CURVE_OT_select_more";
1008 ot->description = "Select control points at the boundary of each selection region";
1009
1010 /* API callbacks. */
1011 ot->exec = curve_select_more_exec;
1013
1014 /* flags */
1015 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1016}
1017
1019
1020/* -------------------------------------------------------------------- */
1023
1024/* basic method: deselect if control point doesn't have all neighbors selected */
1025static void curve_select_less(Object *obedit)
1026{
1027 ListBase *editnurb = object_editcurve_get(obedit);
1028 BPoint *bp;
1029 BezTriple *bezt;
1030 int a;
1031 int sel = 0;
1032 bool lastsel = false;
1033
1034 if (obedit->type == OB_SURF) {
1035 LISTBASE_FOREACH (Nurb *, nu, editnurb) {
1036 BLI_bitmap *selbpoints;
1037 a = nu->pntsu * nu->pntsv;
1038 bp = nu->bp;
1039 selbpoints = BLI_BITMAP_NEW(a, "selectlist");
1040 while (a--) {
1041 if ((bp->hide == 0) && (bp->f1 & SELECT)) {
1042 sel = 0;
1043
1044 /* check if neighbors have been selected */
1045 /* edges of surface are an exception */
1046 if ((a + 1) % nu->pntsu == 0) {
1047 sel++;
1048 }
1049 else {
1050 bp--;
1051 if (BLI_BITMAP_TEST(selbpoints, a + 1) || ((bp->hide == 0) && (bp->f1 & SELECT))) {
1052 sel++;
1053 }
1054 bp++;
1055 }
1056
1057 if ((a + 1) % nu->pntsu == 1) {
1058 sel++;
1059 }
1060 else {
1061 bp++;
1062 if ((bp->hide == 0) && (bp->f1 & SELECT)) {
1063 sel++;
1064 }
1065 bp--;
1066 }
1067
1068 if (a + 1 > nu->pntsu * nu->pntsv - nu->pntsu) {
1069 sel++;
1070 }
1071 else {
1072 bp -= nu->pntsu;
1073 if (BLI_BITMAP_TEST(selbpoints, a + nu->pntsu) ||
1074 ((bp->hide == 0) && (bp->f1 & SELECT)))
1075 {
1076 sel++;
1077 }
1078 bp += nu->pntsu;
1079 }
1080
1081 if (a < nu->pntsu) {
1082 sel++;
1083 }
1084 else {
1085 bp += nu->pntsu;
1086 if ((bp->hide == 0) && (bp->f1 & SELECT)) {
1087 sel++;
1088 }
1089 bp -= nu->pntsu;
1090 }
1091
1092 if (sel != 4) {
1093 select_bpoint(bp, false, SELECT, VISIBLE);
1094 BLI_BITMAP_ENABLE(selbpoints, a);
1095 }
1096 }
1097 else {
1098 lastsel = false;
1099 }
1100
1101 bp++;
1102 }
1103
1104 MEM_freeN(selbpoints);
1105 }
1106 }
1107 else {
1108 LISTBASE_FOREACH (Nurb *, nu, editnurb) {
1109 lastsel = false;
1110 /* check what type of curve/nurb it is */
1111 if (nu->type == CU_BEZIER) {
1112 a = nu->pntsu;
1113 bezt = nu->bezt;
1114 while (a--) {
1115 if ((bezt->hide == 0) && (bezt->f2 & SELECT)) {
1116 sel = (lastsel == 1);
1117
1118 /* check if neighbors have been selected */
1119 /* first and last are exceptions */
1120 if (a == nu->pntsu - 1) {
1121 sel++;
1122 }
1123 else {
1124 bezt--;
1125 if ((bezt->hide == 0) && (bezt->f2 & SELECT)) {
1126 sel++;
1127 }
1128 bezt++;
1129 }
1130
1131 if (a == 0) {
1132 sel++;
1133 }
1134 else {
1135 bezt++;
1136 if ((bezt->hide == 0) && (bezt->f2 & SELECT)) {
1137 sel++;
1138 }
1139 bezt--;
1140 }
1141
1142 if (sel != 2) {
1143 select_beztriple(bezt, false, SELECT, VISIBLE);
1144 lastsel = true;
1145 }
1146 else {
1147 lastsel = false;
1148 }
1149 }
1150 else {
1151 lastsel = false;
1152 }
1153
1154 bezt++;
1155 }
1156 }
1157 else {
1158 a = nu->pntsu * nu->pntsv;
1159 bp = nu->bp;
1160 while (a--) {
1161 if ((lastsel == false) && (bp->hide == 0) && (bp->f1 & SELECT)) {
1162 sel = 0;
1163
1164 /* first and last are exceptions */
1165 if (a == nu->pntsu * nu->pntsv - 1) {
1166 sel++;
1167 }
1168 else {
1169 bp--;
1170 if ((bp->hide == 0) && (bp->f1 & SELECT)) {
1171 sel++;
1172 }
1173 bp++;
1174 }
1175
1176 if (a == 0) {
1177 sel++;
1178 }
1179 else {
1180 bp++;
1181 if ((bp->hide == 0) && (bp->f1 & SELECT)) {
1182 sel++;
1183 }
1184 bp--;
1185 }
1186
1187 if (sel != 2) {
1188 select_bpoint(bp, false, SELECT, VISIBLE);
1189 lastsel = true;
1190 }
1191 else {
1192 lastsel = false;
1193 }
1194 }
1195 else {
1196 lastsel = false;
1197 }
1198
1199 bp++;
1200 }
1201 }
1202 }
1203 }
1204}
1205
1207{
1208 const Scene *scene = CTX_data_scene(C);
1209 ViewLayer *view_layer = CTX_data_view_layer(C);
1211 scene, view_layer, CTX_wm_view3d(C));
1212 for (Object *obedit : objects) {
1213 curve_select_less(obedit);
1214 DEG_id_tag_update(static_cast<ID *>(obedit->data), ID_RECALC_SELECT);
1215 WM_event_add_notifier(C, NC_GEOM | ND_SELECT, obedit->data);
1216 }
1217 return OPERATOR_FINISHED;
1218}
1219
1221{
1222 /* identifiers */
1223 ot->name = "Select Less";
1224 ot->idname = "CURVE_OT_select_less";
1225 ot->description = "Deselect control points at the boundary of each selection region";
1226
1227 /* API callbacks. */
1228 ot->exec = curve_select_less_exec;
1230
1231 /* flags */
1232 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1233}
1234
1236
1237/* -------------------------------------------------------------------- */
1240
1242{
1243 const bool select = (RNA_enum_get(op->ptr, "action") == SEL_SELECT);
1244 const float randfac = RNA_float_get(op->ptr, "ratio");
1246
1247 const Scene *scene = CTX_data_scene(C);
1248 ViewLayer *view_layer = CTX_data_view_layer(C);
1250 scene, view_layer, CTX_wm_view3d(C));
1251
1252 for (const int ob_index : objects.index_range()) {
1253 Object *obedit = objects[ob_index];
1254 ListBase *editnurb = object_editcurve_get(obedit);
1255 int seed_iter = seed;
1256
1257 /* This gives a consistent result regardless of object order. */
1258 if (ob_index) {
1259 seed_iter += BLI_ghashutil_strhash_p(obedit->id.name);
1260 }
1261
1262 int totvert = 0;
1263 LISTBASE_FOREACH (Nurb *, nu, editnurb) {
1264 if (nu->type == CU_BEZIER) {
1265 int a = nu->pntsu;
1266 BezTriple *bezt = nu->bezt;
1267 while (a--) {
1268 if (!bezt->hide) {
1269 totvert++;
1270 }
1271 bezt++;
1272 }
1273 }
1274 else {
1275 int a = nu->pntsu * nu->pntsv;
1276 BPoint *bp = nu->bp;
1277 while (a--) {
1278 if (!bp->hide) {
1279 totvert++;
1280 }
1281 bp++;
1282 }
1283 }
1284 }
1285
1286 BLI_bitmap *verts_selection_mask = BLI_BITMAP_NEW(totvert, __func__);
1287 const int count_select = totvert * randfac;
1288 for (int i = 0; i < count_select; i++) {
1289 BLI_BITMAP_SET(verts_selection_mask, i, true);
1290 }
1291 BLI_bitmap_randomize(verts_selection_mask, totvert, seed_iter);
1292
1293 int bit_index = 0;
1294 LISTBASE_FOREACH (Nurb *, nu, editnurb) {
1295 if (nu->type == CU_BEZIER) {
1296 int a = nu->pntsu;
1297 BezTriple *bezt = nu->bezt;
1298
1299 while (a--) {
1300 if (!bezt->hide) {
1301 if (BLI_BITMAP_TEST(verts_selection_mask, bit_index)) {
1303 }
1304 bit_index++;
1305 }
1306 bezt++;
1307 }
1308 }
1309 else {
1310 int a = nu->pntsu * nu->pntsv;
1311 BPoint *bp = nu->bp;
1312
1313 while (a--) {
1314 if (!bp->hide) {
1315 if (BLI_BITMAP_TEST(verts_selection_mask, bit_index)) {
1317 }
1318 bit_index++;
1319 }
1320 bp++;
1321 }
1322 }
1323 }
1324
1325 MEM_freeN(verts_selection_mask);
1326 BKE_curve_nurb_vert_active_validate(static_cast<Curve *>(obedit->data));
1327 DEG_id_tag_update(static_cast<ID *>(obedit->data), ID_RECALC_SELECT);
1329 }
1330
1331 return OPERATOR_FINISHED;
1332}
1333
1335{
1336 /* identifiers */
1337 ot->name = "Select Random";
1338 ot->idname = "CURVE_OT_select_random";
1339 ot->description = "Randomly select some control points";
1340
1341 /* API callbacks. */
1344
1345 /* flags */
1346 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1347
1348 /* properties */
1350}
1351
1353
1354/* -------------------------------------------------------------------- */
1357
1359{
1360 int a, start;
1361
1362 start = bezt - nu->bezt;
1363 a = nu->pntsu;
1364 bezt = &nu->bezt[a - 1];
1365
1366 while (a--) {
1367 const int depth = abs(start - a);
1369 select_beztriple(bezt, false, SELECT, HIDDEN);
1370 }
1371
1372 bezt--;
1373 }
1374}
1375
1377{
1378 int a, startrow, startpnt;
1379 int row, pnt;
1380
1381 startrow = (bp - nu->bp) / nu->pntsu;
1382 startpnt = (bp - nu->bp) % nu->pntsu;
1383
1384 a = nu->pntsu * nu->pntsv;
1385 bp = &nu->bp[a - 1];
1386 row = nu->pntsv - 1;
1387 pnt = nu->pntsu - 1;
1388
1389 while (a--) {
1390 const int depth = abs(pnt - startpnt) + abs(row - startrow);
1392 select_bpoint(bp, false, SELECT, HIDDEN);
1393 }
1394
1395 pnt--;
1396 if (pnt < 0) {
1397 pnt = nu->pntsu - 1;
1398 row--;
1399 }
1400
1401 bp--;
1402 }
1403}
1404
1406{
1407 Nurb *nu = nullptr;
1408 void *vert = nullptr;
1409
1410 if (!BKE_curve_nurb_vert_active_get(cu, &nu, &vert)) {
1411 return false;
1412 }
1413
1414 if (nu->bezt) {
1415 select_nth_bezt(nu, static_cast<BezTriple *>(vert), params);
1416 }
1417 else {
1418 select_nth_bp(nu, static_cast<BPoint *>(vert), params);
1419 }
1420
1421 return true;
1422}
1423
1425{
1426 const Scene *scene = CTX_data_scene(C);
1427 ViewLayer *view_layer = CTX_data_view_layer(C);
1428 Object *obact = CTX_data_edit_object(C);
1429 View3D *v3d = CTX_wm_view3d(C);
1430 bool changed = false;
1431
1432 CheckerIntervalParams op_params;
1434
1436 scene, view_layer, CTX_wm_view3d(C));
1437 for (Object *obedit : objects) {
1438 Curve *cu = static_cast<Curve *>(obedit->data);
1439
1440 if (!ED_curve_select_check(v3d, cu->editnurb)) {
1441 continue;
1442 }
1443
1444 if (ed_curve_select_nth(static_cast<Curve *>(obedit->data), &op_params) == true) {
1445 changed = true;
1446
1447 DEG_id_tag_update(static_cast<ID *>(obedit->data), ID_RECALC_SELECT);
1448 WM_event_add_notifier(C, NC_GEOM | ND_SELECT, obedit->data);
1449 }
1450 }
1451
1452 if (!changed) {
1453 if (obact->type == OB_SURF) {
1454 BKE_report(op->reports, RPT_ERROR, "Surface(s) have no active point");
1455 }
1456 else {
1457 BKE_report(op->reports, RPT_ERROR, "Curve(s) have no active point");
1458 }
1459 return OPERATOR_CANCELLED;
1460 }
1461 return OPERATOR_FINISHED;
1462}
1463
1465{
1466 /* identifiers */
1467 ot->name = "Checker Deselect";
1468 ot->description = "Deselect every Nth point starting from the active one";
1469 ot->idname = "CURVE_OT_select_nth";
1470
1471 /* API callbacks. */
1472 ot->exec = select_nth_exec;
1474
1475 /* flags */
1476 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1477
1479}
1480
1482
1483/* -------------------------------------------------------------------- */
1486
1488 {SIM_CMP_EQ, "EQUAL", 0, "Equal", ""},
1489 {SIM_CMP_GT, "GREATER", 0, "Greater", ""},
1490 {SIM_CMP_LT, "LESS", 0, "Less", ""},
1491
1492 {0, nullptr, 0, nullptr, nullptr},
1493};
1494
1495enum {
1500};
1501
1503 {SIMCURHAND_TYPE, "TYPE", 0, "Type", ""},
1504 {SIMCURHAND_RADIUS, "RADIUS", 0, "Radius", ""},
1505 {SIMCURHAND_WEIGHT, "WEIGHT", 0, "Weight", ""},
1506 {SIMCURHAND_DIRECTION, "DIRECTION", 0, "Direction", ""},
1507 {0, nullptr, 0, nullptr, nullptr},
1508};
1509
1511 Nurb *nu,
1512 BezTriple *bezt,
1513 float r_dir[3])
1514{
1515 float rsmat[3][3];
1516 BKE_nurb_bezt_calc_normal(nu, bezt, r_dir);
1517 copy_m3_m4(rsmat, ob->object_to_world().ptr());
1518 mul_m3_v3(rsmat, r_dir);
1519 normalize_v3(r_dir);
1520}
1521
1522static void nurb_bpoint_direction_worldspace_get(Object *ob, Nurb *nu, BPoint *bp, float r_dir[3])
1523{
1524 float rsmat[3][3];
1525 BKE_nurb_bpoint_calc_normal(nu, bp, r_dir);
1526 copy_m3_m4(rsmat, ob->object_to_world().ptr());
1527 mul_m3_v3(rsmat, r_dir);
1528 normalize_v3(r_dir);
1529}
1530
1532 Object *ob, Nurb *nu, const int type, KDTree_1d *tree_1d, KDTree_3d *tree_3d)
1533{
1534 float tree_entry[3] = {0.0f, 0.0f, 0.0f};
1535
1536 if (nu->type == CU_BEZIER) {
1537 BezTriple *bezt;
1538 int i;
1539 int tree_index = 0;
1540
1541 for (i = nu->pntsu, bezt = nu->bezt; i--; bezt++) {
1542 if ((!bezt->hide) && (bezt->f1 & SELECT)) {
1543
1544 switch (type) {
1545 case SIMCURHAND_RADIUS: {
1546 float radius_ref = bezt->radius;
1547 tree_entry[0] = radius_ref;
1548 break;
1549 }
1550 case SIMCURHAND_WEIGHT: {
1551 float weight_ref = bezt->weight;
1552 tree_entry[0] = weight_ref;
1553 break;
1554 }
1555 case SIMCURHAND_DIRECTION: {
1556 nurb_bezt_direction_worldspace_get(ob, nu, bezt, tree_entry);
1557 break;
1558 }
1559 }
1560 if (tree_1d) {
1561 BLI_kdtree_1d_insert(tree_1d, tree_index++, tree_entry);
1562 }
1563 else {
1564 BLI_kdtree_3d_insert(tree_3d, tree_index++, tree_entry);
1565 }
1566 }
1567 }
1568 }
1569 else {
1570 BPoint *bp;
1571 int i;
1572 int tree_index = 0;
1573
1574 for (i = nu->pntsu * nu->pntsv, bp = nu->bp; i--; bp++) {
1575 if (!bp->hide && bp->f1 & SELECT) {
1576 switch (type) {
1577 case SIMCURHAND_RADIUS: {
1578 float radius_ref = bp->radius;
1579 tree_entry[0] = radius_ref;
1580 break;
1581 }
1582 case SIMCURHAND_WEIGHT: {
1583 float weight_ref = bp->weight;
1584 tree_entry[0] = weight_ref;
1585 break;
1586 }
1587 case SIMCURHAND_DIRECTION: {
1588 nurb_bpoint_direction_worldspace_get(ob, nu, bp, tree_entry);
1589 break;
1590 }
1591 }
1592 if (tree_1d) {
1593 BLI_kdtree_1d_insert(tree_1d, tree_index++, tree_entry);
1594 }
1595 else {
1596 BLI_kdtree_3d_insert(tree_3d, tree_index++, tree_entry);
1597 }
1598 }
1599 }
1600 }
1601}
1602
1604 Nurb *nu,
1605 const int type,
1606 const KDTree_1d *tree_1d,
1607 const KDTree_3d *tree_3d,
1608 const float thresh,
1609 const int compare)
1610{
1611 const float thresh_cos = cosf(thresh * float(M_PI_2));
1612 bool changed = false;
1613
1614 if (nu->type == CU_BEZIER) {
1615 BezTriple *bezt;
1616 int i;
1617
1618 for (i = nu->pntsu, bezt = nu->bezt; i--; bezt++) {
1619 if (!bezt->hide) {
1620 bool select = false;
1621
1622 switch (type) {
1623 case SIMCURHAND_RADIUS: {
1624 float radius_ref = bezt->radius;
1626 tree_1d, radius_ref, thresh, eSimilarCmp(compare)))
1627 {
1628 select = true;
1629 }
1630 break;
1631 }
1632 case SIMCURHAND_WEIGHT: {
1633 float weight_ref = bezt->weight;
1635 tree_1d, weight_ref, thresh, eSimilarCmp(compare)))
1636 {
1637 select = true;
1638 }
1639 break;
1640 }
1641 case SIMCURHAND_DIRECTION: {
1642 float dir[3];
1643 nurb_bezt_direction_worldspace_get(ob, nu, bezt, dir);
1644 KDTreeNearest_3d nearest;
1645 if (BLI_kdtree_3d_find_nearest(tree_3d, dir, &nearest) != -1) {
1646 float orient = angle_normalized_v3v3(dir, nearest.co);
1647 float delta = thresh_cos - fabsf(cosf(orient));
1648 if (ED_select_similar_compare_float(delta, thresh, eSimilarCmp(compare))) {
1649 select = true;
1650 }
1651 }
1652 break;
1653 }
1654 }
1655
1656 if (select) {
1657 select_beztriple(bezt, true, SELECT, VISIBLE);
1658 changed = true;
1659 }
1660 }
1661 }
1662 }
1663 else {
1664 BPoint *bp;
1665 int i;
1666
1667 for (i = nu->pntsu * nu->pntsv, bp = nu->bp; i--; bp++) {
1668 if (!bp->hide) {
1669 bool select = false;
1670
1671 switch (type) {
1672 case SIMCURHAND_RADIUS: {
1673 float radius_ref = bp->radius;
1675 tree_1d, radius_ref, thresh, eSimilarCmp(compare)))
1676 {
1677 select = true;
1678 }
1679 break;
1680 }
1681 case SIMCURHAND_WEIGHT: {
1682 float weight_ref = bp->weight;
1684 tree_1d, weight_ref, thresh, eSimilarCmp(compare)))
1685 {
1686 select = true;
1687 }
1688 break;
1689 }
1690 case SIMCURHAND_DIRECTION: {
1691 float dir[3];
1692 nurb_bpoint_direction_worldspace_get(ob, nu, bp, dir);
1693 KDTreeNearest_3d nearest;
1694 if (BLI_kdtree_3d_find_nearest(tree_3d, dir, &nearest) != -1) {
1695 float orient = angle_normalized_v3v3(dir, nearest.co);
1696 float delta = fabsf(cosf(orient)) - thresh_cos;
1697 if (ED_select_similar_compare_float(delta, thresh, eSimilarCmp(compare))) {
1698 select = true;
1699 }
1700 }
1701 break;
1702 }
1703 }
1704
1705 if (select) {
1706 select_bpoint(bp, true, SELECT, VISIBLE);
1707 changed = true;
1708 }
1709 }
1710 }
1711 }
1712 return changed;
1713}
1714
1716{
1717 /* Get props. */
1718 const int optype = RNA_enum_get(op->ptr, "type");
1719 const float thresh = RNA_float_get(op->ptr, "threshold");
1720 const int compare = RNA_enum_get(op->ptr, "compare");
1721
1722 const Scene *scene = CTX_data_scene(C);
1723 ViewLayer *view_layer = CTX_data_view_layer(C);
1724 View3D *v3d = CTX_wm_view3d(C);
1725 int tot_nurbs_selected_all = 0;
1727 scene, view_layer, CTX_wm_view3d(C));
1728
1729 for (Object *obedit : objects) {
1730 Curve *cu = static_cast<Curve *>(obedit->data);
1731 tot_nurbs_selected_all += ED_curve_select_count(v3d, cu->editnurb);
1732 }
1733
1734 if (tot_nurbs_selected_all == 0) {
1735 BKE_report(op->reports, RPT_ERROR, "No control point selected");
1736 return OPERATOR_CANCELLED;
1737 }
1738
1739 KDTree_1d *tree_1d = nullptr;
1740 KDTree_3d *tree_3d = nullptr;
1741 short type_ref = 0;
1742
1743 switch (optype) {
1744 case SIMCURHAND_RADIUS:
1745 case SIMCURHAND_WEIGHT:
1746 tree_1d = BLI_kdtree_1d_new(tot_nurbs_selected_all);
1747 break;
1749 tree_3d = BLI_kdtree_3d_new(tot_nurbs_selected_all);
1750 break;
1751 }
1752
1753 /* Get type of selected control points. */
1754 for (Object *obedit : objects) {
1755 Curve *cu = static_cast<Curve *>(obedit->data);
1756 EditNurb *editnurb = cu->editnurb;
1757
1758 LISTBASE_FOREACH (Nurb *, nu, &editnurb->nurbs) {
1759 if (!ED_curve_nurb_select_check(v3d, nu)) {
1760 continue;
1761 }
1762 switch (optype) {
1763 case SIMCURHAND_TYPE: {
1764 type_ref |= nu->type;
1765 break;
1766 }
1767 case SIMCURHAND_RADIUS:
1768 case SIMCURHAND_WEIGHT:
1770 curve_nurb_selected_type_get(obedit, nu, optype, tree_1d, tree_3d);
1771 break;
1772 }
1773 }
1774 }
1775
1776 if (tree_1d != nullptr) {
1777 BLI_kdtree_1d_deduplicate(tree_1d);
1778 BLI_kdtree_1d_balance(tree_1d);
1779 }
1780 if (tree_3d != nullptr) {
1781 BLI_kdtree_3d_deduplicate(tree_3d);
1782 BLI_kdtree_3d_balance(tree_3d);
1783 }
1784
1785 /* Select control points with desired type. */
1786 for (Object *obedit : objects) {
1787 Curve *cu = static_cast<Curve *>(obedit->data);
1788 EditNurb *editnurb = cu->editnurb;
1789 bool changed = false;
1790
1791 LISTBASE_FOREACH (Nurb *, nu, &editnurb->nurbs) {
1792 switch (optype) {
1793 case SIMCURHAND_TYPE: {
1794 if (nu->type & type_ref) {
1795 changed |= ED_curve_nurb_select_all(nu);
1796 }
1797 break;
1798 }
1799 case SIMCURHAND_RADIUS:
1800 case SIMCURHAND_WEIGHT:
1803 obedit, nu, optype, tree_1d, tree_3d, thresh, compare);
1804 break;
1805 }
1806 }
1807
1808 if (changed) {
1809 DEG_id_tag_update(static_cast<ID *>(obedit->data), ID_RECALC_SELECT);
1810 WM_event_add_notifier(C, NC_GEOM | ND_SELECT, obedit->data);
1811 }
1812 }
1813
1814 if (tree_1d != nullptr) {
1815 BLI_kdtree_1d_free(tree_1d);
1816 }
1817 if (tree_3d != nullptr) {
1818 BLI_kdtree_3d_free(tree_3d);
1819 }
1820 return OPERATOR_FINISHED;
1821}
1822
1824{
1825 /* identifiers */
1826 ot->name = "Select Similar";
1827 ot->idname = "CURVE_OT_select_similar";
1828 ot->description = "Select similar curve points by property type";
1829
1830 /* API callbacks. */
1831 ot->invoke = WM_menu_invoke;
1834
1835 /* flags */
1836 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1837
1838 /* properties */
1839 ot->prop = RNA_def_enum(
1840 ot->srna, "type", curve_prop_similar_types, SIMCURHAND_WEIGHT, "Type", "");
1841 RNA_def_enum(ot->srna, "compare", curve_prop_similar_compare_types, SIM_CMP_EQ, "Compare", "");
1842 RNA_def_float(ot->srna, "threshold", 0.1, 0.0, FLT_MAX, "Threshold", "", 0.0, 100.0);
1843}
1844
1846
1847/* -------------------------------------------------------------------- */
1850
1851static float curve_calc_dist_pair(const Nurb *nu, int a, int b)
1852{
1853 const float *a_fl, *b_fl;
1854
1855 if (nu->type == CU_BEZIER) {
1856 a_fl = nu->bezt[a].vec[1];
1857 b_fl = nu->bezt[b].vec[1];
1858 }
1859 else {
1860 a_fl = nu->bp[a].vec;
1861 b_fl = nu->bp[b].vec;
1862 }
1863
1864 return len_v3v3(a_fl, b_fl);
1865}
1866
1867static float curve_calc_dist_span(Nurb *nu, int vert_src, int vert_dst)
1868{
1869 const int u = nu->pntsu;
1870 int i_prev, i;
1871 float dist = 0.0f;
1872
1873 BLI_assert(nu->pntsv <= 1);
1874
1875 i_prev = vert_src;
1876 i = (i_prev + 1) % u;
1877
1878 while (true) {
1879 dist += curve_calc_dist_pair(nu, i_prev, i);
1880
1881 if (i == vert_dst) {
1882 break;
1883 }
1884 i = (i + 1) % u;
1885 }
1886 return dist;
1887}
1888
1889static void curve_select_shortest_path_curve(Nurb *nu, int vert_src, int vert_dst)
1890{
1891 const int u = nu->pntsu;
1892 int i;
1893
1894 if (vert_src > vert_dst) {
1895 std::swap(vert_src, vert_dst);
1896 }
1897
1898 if (nu->flagu & CU_NURB_CYCLIC) {
1899 if (curve_calc_dist_span(nu, vert_src, vert_dst) >
1900 curve_calc_dist_span(nu, vert_dst, vert_src))
1901 {
1902 std::swap(vert_src, vert_dst);
1903 }
1904 }
1905
1906 i = vert_src;
1907 while (true) {
1908 if (nu->type & CU_BEZIER) {
1909 select_beztriple(&nu->bezt[i], true, SELECT, HIDDEN);
1910 }
1911 else {
1912 select_bpoint(&nu->bp[i], true, SELECT, HIDDEN);
1913 }
1914
1915 if (i == vert_dst) {
1916 break;
1917 }
1918 i = (i + 1) % u;
1919 }
1920}
1921
1922static void curve_select_shortest_path_surf(Nurb *nu, int vert_src, int vert_dst)
1923{
1924 int totu = nu->pntsu;
1925 int totv = nu->pntsv;
1926 int vert_num = totu * totv;
1927
1928 /* custom data */
1929 struct PointAdj {
1930 int vert, vert_prev;
1931 float cost;
1932 } *data;
1933
1934 /* init connectivity data */
1935 data = MEM_malloc_arrayN<PointAdj>(vert_num, __func__);
1936 for (int i = 0; i < vert_num; i++) {
1937 data[i].vert = i;
1938 data[i].vert_prev = -1;
1939 data[i].cost = FLT_MAX;
1940 }
1941
1942 /* init heap */
1944
1945 int vert_curr = data[vert_src].vert;
1946 BLI_heapsimple_insert(heap, 0.0f, &data[vert_src].vert);
1947 data[vert_src].cost = 0.0f;
1948 data[vert_src].vert_prev = vert_src; /* nop */
1949
1950 while (!BLI_heapsimple_is_empty(heap)) {
1951 vert_curr = *((int *)BLI_heapsimple_pop_min(heap));
1952 if (vert_curr == vert_dst) {
1953 break;
1954 }
1955
1956 int u, v;
1957 BKE_nurb_index_to_uv(nu, vert_curr, &u, &v);
1958
1959 /* loop over 4 adjacent verts */
1960 for (int sign = -1; sign != 3; sign += 2) {
1961 for (int axis = 0; axis != 2; axis += 1) {
1962 int uv_other[2] = {u, v};
1963 int vert_other;
1964
1965 uv_other[axis] += sign;
1966
1967 vert_other = BKE_nurb_index_from_uv(nu, uv_other[0], uv_other[1]);
1968 if (vert_other != -1) {
1969 const float dist = data[vert_curr].cost +
1970 curve_calc_dist_pair(nu, vert_curr, vert_other);
1971
1972 if (data[vert_other].cost > dist) {
1973 data[vert_other].cost = dist;
1974 if (data[vert_other].vert_prev == -1) {
1975 BLI_heapsimple_insert(heap, data[vert_other].cost, &data[vert_other].vert);
1976 }
1977 data[vert_other].vert_prev = vert_curr;
1978 }
1979 }
1980 }
1981 }
1982 }
1983
1984 BLI_heapsimple_free(heap, nullptr);
1985
1986 if (vert_curr == vert_dst) {
1987 int i = 0;
1988 while (vert_curr != vert_src && i++ < vert_num) {
1989 if (nu->type == CU_BEZIER) {
1990 select_beztriple(&nu->bezt[vert_curr], true, SELECT, HIDDEN);
1991 }
1992 else {
1993 select_bpoint(&nu->bp[vert_curr], true, SELECT, HIDDEN);
1994 }
1995 vert_curr = data[vert_curr].vert_prev;
1996 }
1997 }
1998
1999 MEM_freeN(data);
2000}
2001
2003 wmOperator *op,
2004 const wmEvent *event)
2005{
2007 Nurb *nu_dst;
2008 BezTriple *bezt_dst;
2009 BPoint *bp_dst;
2010 int vert_dst;
2011 void *vert_dst_p;
2012 Base *basact = nullptr;
2013
2016 copy_v2_v2_int(vc.mval, event->mval);
2017
2018 if (!ED_curve_pick_vert(&vc, 1, &nu_dst, &bezt_dst, &bp_dst, nullptr, &basact)) {
2019 return OPERATOR_PASS_THROUGH;
2020 }
2021
2023 Object *obedit = basact->object;
2024 Curve *cu = static_cast<Curve *>(obedit->data);
2025 Nurb *nu_src = BKE_curve_nurb_active_get(cu);
2026 int vert_src = cu->actvert;
2027
2028 if (vert_src == CU_ACT_NONE) {
2029 return OPERATOR_PASS_THROUGH;
2030 }
2031
2032 if (nu_src != nu_dst) {
2033 BKE_report(op->reports, RPT_ERROR, "Control point belongs to another spline");
2034 return OPERATOR_CANCELLED;
2035 }
2036
2037 vert_dst_p = bezt_dst ? (void *)bezt_dst : (void *)bp_dst;
2038 vert_dst = BKE_curve_nurb_vert_index_get(nu_dst, vert_dst_p);
2039 if (vert_src == vert_dst) {
2040 return OPERATOR_CANCELLED;
2041 }
2042
2043 if ((obedit->type == OB_SURF) && (nu_src->pntsv > 1)) {
2044 curve_select_shortest_path_surf(nu_src, vert_src, vert_dst);
2045 }
2046 else {
2047 curve_select_shortest_path_curve(nu_src, vert_src, vert_dst);
2048 }
2049
2050 BKE_curve_nurb_vert_active_set(cu, nu_dst, vert_dst_p);
2051
2053 if (BKE_view_layer_active_base_get(vc.view_layer) != basact) {
2055 }
2056
2059 return OPERATOR_FINISHED;
2060}
2061
2063{
2064 /* identifiers */
2065 ot->name = "Pick Shortest Path";
2066 ot->idname = "CURVE_OT_shortest_path_pick";
2067 ot->description = "Select shortest path between two selections";
2068
2069 /* API callbacks. */
2072
2073 /* flags */
2074 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
2075}
2076
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:912
Nurb * BKE_curve_nurb_active_get(Curve *cu)
Definition curve.cc:4988
void BKE_curve_nurb_vert_active_set(Curve *cu, const Nurb *nu, const void *vert)
Definition curve.cc:5014
int BKE_nurb_index_from_uv(Nurb *nu, int u, int v)
Definition curve.cc:890
void BKE_nurb_bezt_calc_normal(Nurb *nu, BezTriple *bezt, float r_normal[3])
Definition curve.cc:1007
void BKE_curve_nurb_vert_active_validate(Curve *cu)
Definition curve.cc:5058
bool BKE_curve_nurb_vert_active_get(Curve *cu, Nurb **r_nu, void **r_vert)
Definition curve.cc:5031
void BKE_nurbList_flag_set(ListBase *editnurb, uint8_t flag, bool set)
Definition curve.cc:4341
void BKE_nurb_bpoint_calc_normal(Nurb *nu, BPoint *bp, float r_normal[3])
Definition curve.cc:1059
int BKE_curve_nurb_vert_index_get(const Nurb *nu, const void *vert)
Definition curve.cc:5003
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)
@ RPT_ERROR
Definition BKE_report.hh:39
void BKE_report(ReportList *reports, eReportType type, const char *message)
Definition report.cc:153
#define BLI_assert(a)
Definition BLI_assert.h:46
#define BLI_BITMAP_NEW(_num, _alloc_string)
Definition BLI_bitmap.h:37
#define BLI_BITMAP_TEST(_bitmap, _index)
Definition BLI_bitmap.h:61
#define BLI_BITMAP_ENABLE(_bitmap, _index)
Definition BLI_bitmap.h:78
#define BLI_BITMAP_SET(_bitmap, _index, _set)
Definition BLI_bitmap.h:99
unsigned int BLI_bitmap
Definition BLI_bitmap.h:13
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:164
void DEG_id_tag_update(ID *id, unsigned int flags)
@ ID_RECALC_SELECT
Definition DNA_ID.h:1101
@ ID_RECALC_SYNC_TO_EVAL
Definition DNA_ID.h:1118
@ CU_NURB_CYCLIC
#define CU_ACT_NONE
@ CU_BEZIER
#define BEZT_ISSEL_ANY_HIDDENHANDLES(v3d, bezt)
#define BEZT_SEL_ALL(bezt)
#define BEZT_ISSEL_ANY(bezt)
#define BEZT_DESEL_ALL(bezt)
#define BEZT_ISSEL_ALL(bezt)
Object is a sort of wrapper for general info.
@ OB_SURF
@ CURVE_HANDLE_NONE
@ OPERATOR_CANCELLED
@ OPERATOR_FINISHED
@ 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_gpu(const bContext *C)
void ED_view3d_viewcontext_init_object(ViewContext *vc, Object *obact)
Read Guarded memory(de)allocation.
#define C
Definition RandGen.cpp:29
#define NC_GEOM
Definition WM_types.hh:393
@ OPTYPE_UNDO
Definition WM_types.hh:182
@ OPTYPE_REGISTER
Definition WM_types.hh:180
#define ND_SELECT
Definition WM_types.hh:508
BMesh const char void * data
ATTR_WARN_UNUSED_RESULT const BMVert * v
BPy_StructRNA * depsgraph
static unsigned long seed
Definition btSoftBody.h:39
IndexRange index_range() const
#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
ListBase * object_editcurve_get(Object *ob)
Definition editcurve.cc:92
void CURVE_OT_select_random(wmOperatorType *ot)
bool select_bpoint(BPoint *bp, bool selstatus, uint8_t flag, bool hidden)
@ SIMCURHAND_WEIGHT
@ SIMCURHAND_DIRECTION
@ SIMCURHAND_RADIUS
@ SIMCURHAND_TYPE
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 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 void nurb_bezt_direction_worldspace_get(Object *ob, Nurb *nu, BezTriple *bezt, float r_dir[3])
static const EnumPropertyItem curve_prop_similar_compare_types[]
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 wmOperatorStatus curve_select_similar_exec(bContext *C, wmOperator *op)
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 wmOperatorStatus select_linked_invoke(bContext *C, wmOperator *op, const wmEvent *)
static void select_nth_bp(Nurb *nu, BPoint *bp, const CheckerIntervalParams *params)
void CURVE_OT_select_next(wmOperatorType *ot)
static wmOperatorStatus de_select_last_exec(bContext *C, wmOperator *)
static wmOperatorStatus select_linked_exec(bContext *C, wmOperator *)
int ED_curve_select_count(const View3D *v3d, const EditNurb *editnurb)
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)
static wmOperatorStatus curve_select_more_exec(bContext *C, wmOperator *)
bool ED_curve_nurb_select_all(const Nurb *nu)
static wmOperatorStatus select_nth_exec(bContext *C, wmOperator *op)
static wmOperatorStatus curve_select_random_exec(bContext *C, wmOperator *op)
void CURVE_OT_select_all(wmOperatorType *ot)
static wmOperatorStatus de_select_first_exec(bContext *C, wmOperator *)
bool ED_curve_deselect_all(EditNurb *editnurb)
static bool swap_selection_beztriple(BezTriple *bezt)
void CURVE_OT_select_nth(wmOperatorType *ot)
static wmOperatorStatus select_row_exec(bContext *C, wmOperator *)
static wmOperatorStatus select_next_exec(bContext *C, wmOperator *)
static wmOperatorStatus select_linked_pick_invoke(bContext *C, wmOperator *op, const wmEvent *event)
void CURVE_OT_de_select_first(wmOperatorType *ot)
void CURVE_OT_select_more(wmOperatorType *ot)
static wmOperatorStatus select_previous_exec(bContext *C, wmOperator *)
static void nurb_bpoint_direction_worldspace_get(Object *ob, Nurb *nu, BPoint *bp, float r_dir[3])
static wmOperatorStatus de_select_all_exec(bContext *C, wmOperator *op)
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)
void CURVE_OT_select_linked_pick(wmOperatorType *ot)
static wmOperatorStatus curve_select_less_exec(bContext *C, wmOperator *)
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 wmOperatorStatus edcu_shortest_path_pick_invoke(bContext *C, wmOperator *op, const wmEvent *event)
static float curve_calc_dist_pair(const Nurb *nu, int a, int b)
constexpr T sign(T) RET
#define abs
#define select(A, B, C)
uiWidgetBaseParameters params[MAX_WIDGET_BASE_BATCH]
void * MEM_malloc_arrayN(size_t len, size_t size, const char *str)
Definition mallocn.cc:133
void MEM_freeN(void *vmemh)
Definition mallocn.cc:113
static ulong * next
void base_activate(bContext *C, Base *base)
#define fabsf
#define cosf
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
uint8_t f1
float vec[4]
struct Object * object
float vec[3][3]
EditNurb * editnurb
ListBase nurbs
Definition DNA_ID.h:414
char name[258]
Definition DNA_ID.h:432
short flagu
short type
BezTriple * bezt
BPoint * bp
View3DOverlay overlay
int mval[2]
Definition ED_view3d.hh:82
Scene * scene
Definition ED_view3d.hh:73
ViewLayer * view_layer
Definition ED_view3d.hh:74
View3D * v3d
Definition ED_view3d.hh:78
int mval[2]
Definition WM_types.hh:763
struct ReportList * reports
struct PointerRNA * ptr
i
Definition text_draw.cc:230
void WM_event_add_notifier(const bContext *C, uint type, void *reference)
wmOperatorType * ot
Definition wm_files.cc:4237
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)
wmOperatorStatus WM_menu_invoke(bContext *C, wmOperator *op, const wmEvent *)
uint8_t flag
Definition wm_window.cc:145