2 * ***** BEGIN GPL LICENSE BLOCK *****
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License
6 * as published by the Free Software Foundation; either version 2
7 * of the License, or (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software Foundation,
16 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18 * The Original Code is Copyright (C) 2012 Blender Foundation.
19 * All rights reserved.
22 * Contributor(s): Blender Foundation,
25 * ***** END GPL LICENSE BLOCK *****
28 /** \file blender/editors/mask/mask_select.c
32 #include "MEM_guardedalloc.h"
34 #include "BLI_utildefines.h"
36 #include "BLI_lasso.h"
39 #include "BKE_context.h"
42 #include "DNA_mask_types.h"
43 #include "DNA_object_types.h" /* SELECT */
48 #include "ED_screen.h"
50 #include "ED_mask.h" /* own include */
52 #include "RNA_access.h"
53 #include "RNA_define.h"
55 #include "mask_intern.h" /* own include */
58 bool ED_mask_spline_select_check(MaskSpline *spline)
62 for (i = 0; i < spline->tot_point; i++) {
63 MaskSplinePoint *point = &spline->points[i];
65 if (MASKPOINT_ISSEL_ANY(point))
72 bool ED_mask_layer_select_check(MaskLayer *masklay)
76 if (masklay->restrictflag & (MASK_RESTRICT_VIEW | MASK_RESTRICT_SELECT)) {
80 for (spline = masklay->splines.first; spline; spline = spline->next) {
81 if (ED_mask_spline_select_check(spline)) {
89 bool ED_mask_select_check(Mask *mask)
93 for (masklay = mask->masklayers.first; masklay; masklay = masklay->next) {
94 if (ED_mask_layer_select_check(masklay)) {
103 void ED_mask_spline_select_set(MaskSpline *spline, const short do_select)
108 spline->flag |= SELECT;
110 spline->flag &= ~SELECT;
112 for (i = 0; i < spline->tot_point; i++) {
113 MaskSplinePoint *point = &spline->points[i];
115 BKE_mask_point_select_set(point, do_select);
119 void ED_mask_layer_select_set(MaskLayer *masklay, const short do_select)
123 if (masklay->restrictflag & MASK_RESTRICT_SELECT) {
124 if (do_select == TRUE) {
129 for (spline = masklay->splines.first; spline; spline = spline->next) {
130 ED_mask_spline_select_set(spline, do_select);
134 void ED_mask_select_toggle_all(Mask *mask, int action)
138 if (action == SEL_TOGGLE) {
139 if (ED_mask_select_check(mask))
140 action = SEL_DESELECT;
145 for (masklay = mask->masklayers.first; masklay; masklay = masklay->next) {
147 if (masklay->restrictflag & MASK_RESTRICT_VIEW) {
151 if (action == SEL_INVERT) {
152 /* we don't have generic functions for this, its restricted to this operator
153 * if one day we need to re-use such functionality, they can be split out */
156 if (masklay->restrictflag & MASK_RESTRICT_SELECT) {
159 for (spline = masklay->splines.first; spline; spline = spline->next) {
161 for (i = 0; i < spline->tot_point; i++) {
162 MaskSplinePoint *point = &spline->points[i];
163 BKE_mask_point_select_set(point, !MASKPOINT_ISSEL_ANY(point));
169 ED_mask_layer_select_set(masklay, (action == SEL_SELECT) ? TRUE : FALSE);
174 void ED_mask_select_flush_all(Mask *mask)
178 for (masklay = mask->masklayers.first; masklay; masklay = masklay->next) {
181 for (spline = masklay->splines.first; spline; spline = spline->next) {
184 spline->flag &= ~SELECT;
186 /* intentionally _dont_ do this in the masklay loop
187 * so we clear flags on all splines */
188 if (masklay->restrictflag & MASK_RESTRICT_VIEW) {
192 for (i = 0; i < spline->tot_point; i++) {
193 MaskSplinePoint *cur_point = &spline->points[i];
195 if (MASKPOINT_ISSEL_ANY(cur_point)) {
196 spline->flag |= SELECT;
201 for (j = 0; j < cur_point->tot_uw; j++) {
202 if (cur_point->uw[j].flag & SELECT) {
203 spline->flag |= SELECT;
213 /******************** toggle selection *********************/
215 static int select_all_exec(bContext *C, wmOperator *op)
217 Mask *mask = CTX_data_edit_mask(C);
218 int action = RNA_enum_get(op->ptr, "action");
220 ED_mask_select_toggle_all(mask, action);
221 ED_mask_select_flush_all(mask);
223 WM_event_add_notifier(C, NC_MASK | ND_SELECT, mask);
225 return OPERATOR_FINISHED;
228 void MASK_OT_select_all(wmOperatorType *ot)
231 ot->name = "(De)select All";
232 ot->description = "Change selection of all curve points";
233 ot->idname = "MASK_OT_select_all";
236 ot->exec = select_all_exec;
237 ot->poll = ED_maskedit_mask_poll;
240 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
243 WM_operator_properties_select_all(ot);
246 /******************** select *********************/
248 static int select_exec(bContext *C, wmOperator *op)
250 Mask *mask = CTX_data_edit_mask(C);
253 MaskSplinePoint *point = NULL;
255 bool extend = RNA_boolean_get(op->ptr, "extend");
256 bool deselect = RNA_boolean_get(op->ptr, "deselect");
257 bool toggle = RNA_boolean_get(op->ptr, "toggle");
258 eMaskWhichHandle which_handle;
259 const float threshold = 19;
261 RNA_float_get_array(op->ptr, "location", co);
263 point = ED_mask_point_find_nearest(C, mask, co, threshold, &masklay, &spline, &which_handle, NULL);
265 if (extend == false && deselect == false && toggle == false)
266 ED_mask_select_toggle_all(mask, SEL_DESELECT);
269 if (which_handle != MASK_WHICH_HANDLE_NONE) {
271 masklay->act_spline = spline;
272 masklay->act_point = point;
274 BKE_mask_point_select_set_handle(point, which_handle, TRUE);
277 BKE_mask_point_select_set_handle(point, which_handle, FALSE);
280 masklay->act_spline = spline;
281 masklay->act_point = point;
283 if (!MASKPOINT_ISSEL_HANDLE(point, which_handle)) {
284 BKE_mask_point_select_set_handle(point, which_handle, TRUE);
287 BKE_mask_point_select_set_handle(point, which_handle, FALSE);
293 masklay->act_spline = spline;
294 masklay->act_point = point;
296 BKE_mask_point_select_set(point, TRUE);
299 BKE_mask_point_select_set(point, FALSE);
302 masklay->act_spline = spline;
303 masklay->act_point = point;
305 if (!MASKPOINT_ISSEL_ANY(point)) {
306 BKE_mask_point_select_set(point, TRUE);
309 BKE_mask_point_select_set(point, FALSE);
314 masklay->act_spline = spline;
315 masklay->act_point = point;
317 ED_mask_select_flush_all(mask);
319 WM_event_add_notifier(C, NC_MASK | ND_SELECT, mask);
321 return OPERATOR_FINISHED;
324 MaskSplinePointUW *uw;
326 if (ED_mask_feather_find_nearest(C, mask, co, threshold, &masklay, &spline, &point, &uw, NULL)) {
329 masklay->act_spline = spline;
330 masklay->act_point = point;
332 if (uw) uw->flag |= SELECT;
335 if (uw) uw->flag &= ~SELECT;
338 masklay->act_spline = spline;
339 masklay->act_point = point;
342 if (!(uw->flag & SELECT)) {
351 ED_mask_select_flush_all(mask);
353 WM_event_add_notifier(C, NC_MASK | ND_SELECT, mask);
355 return OPERATOR_FINISHED;
359 return OPERATOR_PASS_THROUGH;
362 static int select_invoke(bContext *C, wmOperator *op, const wmEvent *event)
364 ScrArea *sa = CTX_wm_area(C);
365 ARegion *ar = CTX_wm_region(C);
369 ED_mask_mouse_pos(sa, ar, event->mval, co);
371 RNA_float_set_array(op->ptr, "location", co);
373 return select_exec(C, op);
376 void MASK_OT_select(wmOperatorType *ot)
380 ot->description = "Select spline points";
381 ot->idname = "MASK_OT_select";
384 ot->exec = select_exec;
385 ot->invoke = select_invoke;
386 ot->poll = ED_maskedit_mask_poll;
389 ot->flag = OPTYPE_UNDO;
392 WM_operator_properties_mouse_select(ot);
394 RNA_def_float_vector(ot->srna, "location", 2, NULL, -FLT_MAX, FLT_MAX,
395 "Location", "Location of vertex in normalized space", -1.0f, 1.0f);
400 /********************** border select operator *********************/
402 static int border_select_exec(bContext *C, wmOperator *op)
404 ScrArea *sa = CTX_wm_area(C);
405 ARegion *ar = CTX_wm_region(C);
407 Mask *mask = CTX_data_edit_mask(C);
414 bool changed = false, extend;
416 /* get rectangle from operator */
417 WM_operator_properties_border_to_rcti(op, &rect);
419 ED_mask_point_pos(sa, ar, rect.xmin, rect.ymin, &rectf.xmin, &rectf.ymin);
420 ED_mask_point_pos(sa, ar, rect.xmax, rect.ymax, &rectf.xmax, &rectf.ymax);
422 mode = RNA_int_get(op->ptr, "gesture_mode");
423 extend = RNA_boolean_get(op->ptr, "extend");
425 /* do actual selection */
426 for (masklay = mask->masklayers.first; masklay; masklay = masklay->next) {
429 if (masklay->restrictflag & (MASK_RESTRICT_VIEW | MASK_RESTRICT_SELECT)) {
433 for (spline = masklay->splines.first; spline; spline = spline->next) {
434 MaskSplinePoint *points_array = BKE_mask_spline_point_array(spline);
436 for (i = 0; i < spline->tot_point; i++) {
437 MaskSplinePoint *point = &spline->points[i];
438 MaskSplinePoint *point_deform = &points_array[i];
443 if (BLI_rctf_isect_pt_v(&rectf, point_deform->bezt.vec[1])) {
444 BKE_mask_point_select_set(point, mode == GESTURE_MODAL_SELECT);
445 BKE_mask_point_select_set_handle(point, MASK_WHICH_HANDLE_BOTH, mode == GESTURE_MODAL_SELECT);
448 BKE_mask_point_select_set(point, FALSE);
449 BKE_mask_point_select_set_handle(point, MASK_WHICH_HANDLE_BOTH, FALSE);
458 ED_mask_select_flush_all(mask);
460 WM_event_add_notifier(C, NC_MASK | ND_SELECT, mask);
462 return OPERATOR_FINISHED;
465 return OPERATOR_CANCELLED;
468 void MASK_OT_select_border(wmOperatorType *ot)
471 ot->name = "Border Select";
472 ot->description = "Select curve points using border selection";
473 ot->idname = "MASK_OT_select_border";
476 ot->invoke = WM_border_select_invoke;
477 ot->exec = border_select_exec;
478 ot->modal = WM_border_select_modal;
479 ot->poll = ED_maskedit_mask_poll;
482 ot->flag = OPTYPE_UNDO;
485 WM_operator_properties_gesture_border(ot, TRUE);
488 static bool do_lasso_select_mask(bContext *C, const int mcords[][2], short moves, short select)
490 ScrArea *sa = CTX_wm_area(C);
491 ARegion *ar = CTX_wm_region(C);
493 Mask *mask = CTX_data_edit_mask(C);
498 bool changed = false;
500 /* get rectangle from operator */
501 BLI_lasso_boundbox(&rect, mcords, moves);
503 /* do actual selection */
504 for (masklay = mask->masklayers.first; masklay; masklay = masklay->next) {
507 if (masklay->restrictflag & (MASK_RESTRICT_VIEW | MASK_RESTRICT_SELECT)) {
511 for (spline = masklay->splines.first; spline; spline = spline->next) {
512 MaskSplinePoint *points_array = BKE_mask_spline_point_array(spline);
514 for (i = 0; i < spline->tot_point; i++) {
515 MaskSplinePoint *point = &spline->points[i];
516 MaskSplinePoint *point_deform = &points_array[i];
523 /* point in screen coords */
524 ED_mask_point_pos__reverse(sa, ar,
525 point_deform->bezt.vec[1][0], point_deform->bezt.vec[1][1],
526 &screen_co[0], &screen_co[1]);
528 if (BLI_rcti_isect_pt(&rect, screen_co[0], screen_co[1]) &&
529 BLI_lasso_is_point_inside(mcords, moves, screen_co[0], screen_co[1], INT_MAX))
531 BKE_mask_point_select_set(point, select);
532 BKE_mask_point_select_set_handle(point, MASK_WHICH_HANDLE_BOTH, select);
541 ED_mask_select_flush_all(mask);
543 WM_event_add_notifier(C, NC_MASK | ND_SELECT, mask);
549 static int clip_lasso_select_exec(bContext *C, wmOperator *op)
552 const int (*mcords)[2] = WM_gesture_lasso_path_to_array(C, op, &mcords_tot);
557 select = !RNA_boolean_get(op->ptr, "deselect");
558 do_lasso_select_mask(C, mcords, mcords_tot, select);
560 MEM_freeN((void *)mcords);
562 return OPERATOR_FINISHED;
564 return OPERATOR_PASS_THROUGH;
567 void MASK_OT_select_lasso(wmOperatorType *ot)
570 ot->name = "Lasso Select";
571 ot->description = "Select curve points using lasso selection";
572 ot->idname = "MASK_OT_select_lasso";
575 ot->invoke = WM_gesture_lasso_invoke;
576 ot->modal = WM_gesture_lasso_modal;
577 ot->exec = clip_lasso_select_exec;
578 ot->poll = ED_maskedit_mask_poll;
579 ot->cancel = WM_gesture_lasso_cancel;
582 ot->flag = OPTYPE_UNDO;
585 RNA_def_collection_runtime(ot->srna, "path", &RNA_OperatorMousePath, "Path", "");
586 RNA_def_boolean(ot->srna, "deselect", 0, "Deselect", "Deselect rather than select items");
587 RNA_def_boolean(ot->srna, "extend", 1, "Extend", "Extend selection instead of deselecting everything first");
590 /********************** circle select operator *********************/
592 static int mask_spline_point_inside_ellipse(BezTriple *bezt, const float offset[2], const float ellipse[2])
594 /* normalized ellipse: ell[0] = scaleX, ell[1] = scaleY */
597 x = (bezt->vec[1][0] - offset[0]) * ellipse[0];
598 y = (bezt->vec[1][1] - offset[1]) * ellipse[1];
600 return x * x + y * y < 1.0f;
603 static int circle_select_exec(bContext *C, wmOperator *op)
605 ScrArea *sa = CTX_wm_area(C);
606 ARegion *ar = CTX_wm_region(C);
608 Mask *mask = CTX_data_edit_mask(C);
612 float zoomx, zoomy, offset[2], ellipse[2];
613 int x, y, radius, width, height, mode;
614 bool changed = false;
616 /* get operator properties */
617 x = RNA_int_get(op->ptr, "x");
618 y = RNA_int_get(op->ptr, "y");
619 radius = RNA_int_get(op->ptr, "radius");
621 mode = RNA_int_get(op->ptr, "gesture_mode");
623 /* compute ellipse and position in unified coordinates */
624 ED_mask_get_size(sa, &width, &height);
625 ED_mask_zoom(sa, ar, &zoomx, &zoomy);
626 width = height = max_ii(width, height);
628 ellipse[0] = width * zoomx / radius;
629 ellipse[1] = height * zoomy / radius;
631 ED_mask_point_pos(sa, ar, x, y, &offset[0], &offset[1]);
633 /* do actual selection */
634 for (masklay = mask->masklayers.first; masklay; masklay = masklay->next) {
637 if (masklay->restrictflag & (MASK_RESTRICT_VIEW | MASK_RESTRICT_SELECT)) {
641 for (spline = masklay->splines.first; spline; spline = spline->next) {
642 MaskSplinePoint *points_array = BKE_mask_spline_point_array(spline);
644 for (i = 0; i < spline->tot_point; i++) {
645 MaskSplinePoint *point = &spline->points[i];
646 MaskSplinePoint *point_deform = &points_array[i];
648 if (mask_spline_point_inside_ellipse(&point_deform->bezt, offset, ellipse)) {
649 BKE_mask_point_select_set(point, mode == GESTURE_MODAL_SELECT);
650 BKE_mask_point_select_set_handle(point, MASK_WHICH_HANDLE_BOTH, mode == GESTURE_MODAL_SELECT);
659 ED_mask_select_flush_all(mask);
661 WM_event_add_notifier(C, NC_MASK | ND_SELECT, mask);
663 return OPERATOR_FINISHED;
666 return OPERATOR_CANCELLED;
669 void MASK_OT_select_circle(wmOperatorType *ot)
672 ot->name = "Circle Select";
673 ot->description = "Select curve points using circle selection";
674 ot->idname = "MASK_OT_select_circle";
677 ot->invoke = WM_gesture_circle_invoke;
678 ot->modal = WM_gesture_circle_modal;
679 ot->exec = circle_select_exec;
680 ot->poll = ED_maskedit_mask_poll;
683 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
686 RNA_def_int(ot->srna, "x", 0, INT_MIN, INT_MAX, "X", "", INT_MIN, INT_MAX);
687 RNA_def_int(ot->srna, "y", 0, INT_MIN, INT_MAX, "Y", "", INT_MIN, INT_MAX);
688 RNA_def_int(ot->srna, "radius", 0, INT_MIN, INT_MAX, "Radius", "", INT_MIN, INT_MAX);
689 RNA_def_int(ot->srna, "gesture_mode", 0, INT_MIN, INT_MAX, "Gesture Mode", "", INT_MIN, INT_MAX);
692 static int mask_select_linked_pick_invoke(bContext *C, wmOperator *op, const wmEvent *event)
694 ScrArea *sa = CTX_wm_area(C);
695 ARegion *ar = CTX_wm_region(C);
697 Mask *mask = CTX_data_edit_mask(C);
700 MaskSplinePoint *point = NULL;
702 int do_select = !RNA_boolean_get(op->ptr, "deselect");
703 const float threshold = 19;
704 bool changed = false;
706 ED_mask_mouse_pos(sa, ar, event->mval, co);
708 point = ED_mask_point_find_nearest(C, mask, co, threshold, &masklay, &spline, NULL, NULL);
711 ED_mask_spline_select_set(spline, do_select);
712 masklay->act_spline = spline;
713 masklay->act_point = point;
719 ED_mask_select_flush_all(mask);
721 WM_event_add_notifier(C, NC_MASK | ND_SELECT, mask);
723 return OPERATOR_FINISHED;
726 return OPERATOR_CANCELLED;
729 void MASK_OT_select_linked_pick(wmOperatorType *ot)
732 ot->name = "Select Linked";
733 ot->idname = "MASK_OT_select_linked_pick";
734 ot->description = "(De)select all points linked to the curve under the mouse cursor";
737 ot->invoke = mask_select_linked_pick_invoke;
738 ot->poll = ED_maskedit_mask_poll;
741 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
743 RNA_def_boolean(ot->srna, "deselect", 0, "Deselect", "");
746 static int mask_select_linked_exec(bContext *C, wmOperator *UNUSED(op))
748 Mask *mask = CTX_data_edit_mask(C);
751 bool changed = false;
753 /* do actual selection */
754 for (masklay = mask->masklayers.first; masklay; masklay = masklay->next) {
757 if (masklay->restrictflag & (MASK_RESTRICT_VIEW | MASK_RESTRICT_SELECT)) {
761 for (spline = masklay->splines.first; spline; spline = spline->next) {
762 if (ED_mask_spline_select_check(spline)) {
763 ED_mask_spline_select_set(spline, TRUE);
770 ED_mask_select_flush_all(mask);
772 WM_event_add_notifier(C, NC_MASK | ND_SELECT, mask);
774 return OPERATOR_FINISHED;
777 return OPERATOR_CANCELLED;
780 void MASK_OT_select_linked(wmOperatorType *ot)
783 ot->name = "Select Linked All";
784 ot->idname = "MASK_OT_select_linked";
785 ot->description = "Select all curve points linked to already selected ones";
788 ot->exec = mask_select_linked_exec;
789 ot->poll = ED_maskedit_mask_poll;
792 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
795 /**************** Select more/less **************/
797 static int mask_select_more_less(bContext *C, bool more)
799 Mask *mask = CTX_data_edit_mask(C);
802 for (masklay = mask->masklayers.first; masklay; masklay = masklay->next) {
805 if (masklay->restrictflag & (MASK_RESTRICT_VIEW | MASK_RESTRICT_SELECT)) {
809 for (spline = masklay->splines.first; spline; spline = spline->next) {
810 const bool cyclic = (spline->flag & MASK_SPLINE_CYCLIC) != 0;
811 bool start_sel, end_sel, prev_sel, cur_sel;
814 /* reselect point if any handle is selected to make the result more predictable */
815 for (i = 0; i < spline->tot_point; i++) {
816 BKE_mask_point_select_set(spline->points + i, MASKPOINT_ISSEL_ANY(spline->points + i));
819 /* select more/less does not affect empty/single point splines */
820 if (spline->tot_point < 2) {
825 start_sel = !!MASKPOINT_ISSEL_KNOT(spline->points);
826 end_sel = !!MASKPOINT_ISSEL_KNOT(&spline->points[spline->tot_point - 1]);
833 for (i = 0; i < spline->tot_point; i++) {
834 if (i == 0 && !cyclic) {
838 prev_sel = (i > 0) ? !!MASKPOINT_ISSEL_KNOT(&spline->points[i - 1]) : end_sel;
839 cur_sel = !!MASKPOINT_ISSEL_KNOT(&spline->points[i]);
841 if (cur_sel != more) {
842 if (prev_sel == more) {
843 BKE_mask_point_select_set(&spline->points[i], more);
849 for (i = spline->tot_point - 1; i >= 0; i--) {
850 if (i == spline->tot_point - 1 && !cyclic) {
854 prev_sel = (i < spline->tot_point - 1) ? !!MASKPOINT_ISSEL_KNOT(&spline->points[i + 1]) : start_sel;
855 cur_sel = !!MASKPOINT_ISSEL_KNOT(&spline->points[i]);
857 if (cur_sel != more) {
858 if (prev_sel == more) {
859 BKE_mask_point_select_set(&spline->points[i], more);
867 WM_event_add_notifier(C, NC_MASK | ND_SELECT, mask);
869 return OPERATOR_FINISHED;
872 static int mask_select_more_exec(bContext *C, wmOperator *UNUSED(op))
874 return mask_select_more_less(C, true);
877 void MASK_OT_select_more(wmOperatorType *ot)
880 ot->name = "Select More";
881 ot->idname = "MASK_OT_select_more";
882 ot->description = "Select more spline points connected to initial selection";
885 ot->exec = mask_select_more_exec;
886 ot->poll = ED_maskedit_mask_poll;
889 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
892 static int mask_select_less_exec(bContext *C, wmOperator *UNUSED(op))
894 return mask_select_more_less(C, false);
897 void MASK_OT_select_less(wmOperatorType *ot)
900 ot->name = "Select Less";
901 ot->idname = "MASK_OT_select_less";
902 ot->description = "Deselect spline points at the boundary of each selection region";
905 ot->exec = mask_select_less_exec;
906 ot->poll = ED_maskedit_mask_poll;
909 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;