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"
38 #include "BKE_context.h"
41 #include "DNA_mask_types.h"
42 #include "DNA_object_types.h" /* SELECT */
47 #include "ED_screen.h"
49 #include "ED_mask.h" /* own include */
51 #include "RNA_access.h"
52 #include "RNA_define.h"
54 #include "mask_intern.h" /* own include */
57 int ED_mask_spline_select_check(MaskSpline *spline)
61 for (i = 0; i < spline->tot_point; i++) {
62 MaskSplinePoint *point = &spline->points[i];
64 if (MASKPOINT_ISSEL_ANY(point))
71 int ED_mask_layer_select_check(MaskLayer *masklay)
75 if (masklay->restrictflag & (MASK_RESTRICT_VIEW | MASK_RESTRICT_SELECT)) {
79 for (spline = masklay->splines.first; spline; spline = spline->next) {
80 if (ED_mask_spline_select_check(spline)) {
88 int ED_mask_select_check(Mask *mask)
92 for (masklay = mask->masklayers.first; masklay; masklay = masklay->next) {
93 if (ED_mask_layer_select_check(masklay)) {
102 void ED_mask_spline_select_set(MaskSpline *spline, const short do_select)
107 spline->flag |= SELECT;
109 spline->flag &= ~SELECT;
111 for (i = 0; i < spline->tot_point; i++) {
112 MaskSplinePoint *point = &spline->points[i];
114 BKE_mask_point_select_set(point, do_select);
118 void ED_mask_layer_select_set(MaskLayer *masklay, const short do_select)
122 if (masklay->restrictflag & MASK_RESTRICT_SELECT) {
123 if (do_select == TRUE) {
128 for (spline = masklay->splines.first; spline; spline = spline->next) {
129 ED_mask_spline_select_set(spline, do_select);
133 void ED_mask_select_toggle_all(Mask *mask, int action)
137 if (action == SEL_TOGGLE) {
138 if (ED_mask_select_check(mask))
139 action = SEL_DESELECT;
144 for (masklay = mask->masklayers.first; masklay; masklay = masklay->next) {
146 if (masklay->restrictflag & MASK_RESTRICT_VIEW) {
150 ED_mask_layer_select_set(masklay, (action == SEL_SELECT) ? TRUE : FALSE);
154 void ED_mask_select_flush_all(Mask *mask)
158 for (masklay = mask->masklayers.first; masklay; masklay = masklay->next) {
161 for (spline = masklay->splines.first; spline; spline = spline->next) {
164 spline->flag &= ~SELECT;
166 /* intentionally _dont_ do this in the masklay loop
167 * so we clear flags on all splines */
168 if (masklay->restrictflag & MASK_RESTRICT_VIEW) {
172 for (i = 0; i < spline->tot_point; i++) {
173 MaskSplinePoint *cur_point = &spline->points[i];
175 if (MASKPOINT_ISSEL_ANY(cur_point)) {
176 spline->flag |= SELECT;
181 for (j = 0; j < cur_point->tot_uw; j++) {
182 if (cur_point->uw[j].flag & SELECT) {
183 spline->flag |= SELECT;
193 /******************** toggle selection *********************/
195 static int select_all_exec(bContext *C, wmOperator *op)
197 Mask *mask = CTX_data_edit_mask(C);
198 int action = RNA_enum_get(op->ptr, "action");
200 ED_mask_select_toggle_all(mask, action);
201 ED_mask_select_flush_all(mask);
203 WM_event_add_notifier(C, NC_MASK | ND_SELECT, mask);
205 return OPERATOR_FINISHED;
208 void MASK_OT_select_all(wmOperatorType *ot)
211 ot->name = "(De)select All";
212 ot->description = "Change selection of all curve points";
213 ot->idname = "MASK_OT_select_all";
216 ot->exec = select_all_exec;
217 ot->poll = ED_maskedit_mask_poll;
220 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
223 WM_operator_properties_select_all(ot);
226 /******************** select *********************/
228 static int select_exec(bContext *C, wmOperator *op)
230 Mask *mask = CTX_data_edit_mask(C);
233 MaskSplinePoint *point = NULL;
235 short extend = RNA_boolean_get(op->ptr, "extend");
236 short deselect = RNA_boolean_get(op->ptr, "deselect");
237 short toggle = RNA_boolean_get(op->ptr, "toggle");
240 const float threshold = 19;
242 RNA_float_get_array(op->ptr, "location", co);
244 point = ED_mask_point_find_nearest(C, mask, co, threshold, &masklay, &spline, &is_handle, NULL);
246 if (extend == 0 && deselect == 0 && toggle == 0)
247 ED_mask_select_toggle_all(mask, SEL_DESELECT);
253 masklay->act_spline = spline;
254 masklay->act_point = point;
256 BKE_mask_point_select_set_handle(point, TRUE);
259 BKE_mask_point_select_set_handle(point, FALSE);
262 masklay->act_spline = spline;
263 masklay->act_point = point;
265 if (!MASKPOINT_ISSEL_HANDLE(point)) {
266 BKE_mask_point_select_set_handle(point, TRUE);
269 BKE_mask_point_select_set_handle(point, FALSE);
275 masklay->act_spline = spline;
276 masklay->act_point = point;
278 BKE_mask_point_select_set(point, TRUE);
281 BKE_mask_point_select_set(point, FALSE);
284 masklay->act_spline = spline;
285 masklay->act_point = point;
287 if (!MASKPOINT_ISSEL_ANY(point)) {
288 BKE_mask_point_select_set(point, TRUE);
291 BKE_mask_point_select_set(point, FALSE);
296 masklay->act_spline = spline;
297 masklay->act_point = point;
299 ED_mask_select_flush_all(mask);
301 WM_event_add_notifier(C, NC_MASK | ND_SELECT, mask);
303 return OPERATOR_FINISHED;
306 MaskSplinePointUW *uw;
308 if (ED_mask_feather_find_nearest(C, mask, co, threshold, &masklay, &spline, &point, &uw, NULL)) {
311 masklay->act_spline = spline;
312 masklay->act_point = point;
314 if (uw) uw->flag |= SELECT;
317 if (uw) uw->flag &= ~SELECT;
320 masklay->act_spline = spline;
321 masklay->act_point = point;
324 if (!(uw->flag & SELECT)) {
333 ED_mask_select_flush_all(mask);
335 WM_event_add_notifier(C, NC_MASK | ND_SELECT, mask);
337 return OPERATOR_FINISHED;
341 return OPERATOR_PASS_THROUGH;
344 static int select_invoke(bContext *C, wmOperator *op, wmEvent *event)
348 ED_mask_mouse_pos(C, event, co);
350 RNA_float_set_array(op->ptr, "location", co);
352 return select_exec(C, op);
355 void MASK_OT_select(wmOperatorType *ot)
359 ot->description = "Select spline points";
360 ot->idname = "MASK_OT_select";
363 ot->exec = select_exec;
364 ot->invoke = select_invoke;
365 ot->poll = ED_maskedit_mask_poll;
368 ot->flag = OPTYPE_UNDO;
371 WM_operator_properties_mouse_select(ot);
373 RNA_def_float_vector(ot->srna, "location", 2, NULL, -FLT_MIN, FLT_MAX,
374 "Location", "Location of vertex in normalized space", -1.0f, 1.0f);
379 /********************** border select operator *********************/
381 static int border_select_exec(bContext *C, wmOperator *op)
383 Mask *mask = CTX_data_edit_mask(C);
389 int change = FALSE, mode, extend;
391 /* get rectangle from operator */
392 rect.xmin = RNA_int_get(op->ptr, "xmin");
393 rect.ymin = RNA_int_get(op->ptr, "ymin");
394 rect.xmax = RNA_int_get(op->ptr, "xmax");
395 rect.ymax = RNA_int_get(op->ptr, "ymax");
397 ED_mask_point_pos(C, rect.xmin, rect.ymin, &rectf.xmin, &rectf.ymin);
398 ED_mask_point_pos(C, rect.xmax, rect.ymax, &rectf.xmax, &rectf.ymax);
400 mode = RNA_int_get(op->ptr, "gesture_mode");
401 extend = RNA_boolean_get(op->ptr, "extend");
403 /* do actual selection */
404 for (masklay = mask->masklayers.first; masklay; masklay = masklay->next) {
407 if (masklay->restrictflag & (MASK_RESTRICT_VIEW | MASK_RESTRICT_SELECT)) {
411 for (spline = masklay->splines.first; spline; spline = spline->next) {
412 MaskSplinePoint *points_array = BKE_mask_spline_point_array(spline);
414 for (i = 0; i < spline->tot_point; i++) {
415 MaskSplinePoint *point = &spline->points[i];
416 MaskSplinePoint *point_deform = &points_array[i];
421 if (BLI_in_rctf_v(&rectf, point_deform->bezt.vec[1])) {
422 BKE_mask_point_select_set(point, mode == GESTURE_MODAL_SELECT);
423 BKE_mask_point_select_set_handle(point, mode == GESTURE_MODAL_SELECT);
426 BKE_mask_point_select_set(point, FALSE);
427 BKE_mask_point_select_set_handle(point, FALSE);
436 ED_mask_select_flush_all(mask);
438 WM_event_add_notifier(C, NC_MASK | ND_SELECT, mask);
440 return OPERATOR_FINISHED;
443 return OPERATOR_CANCELLED;
446 void MASK_OT_select_border(wmOperatorType *ot)
449 ot->name = "Border Select";
450 ot->description = "Select markers using border selection";
451 ot->idname = "MASK_OT_select_border";
454 ot->invoke = WM_border_select_invoke;
455 ot->exec = border_select_exec;
456 ot->modal = WM_border_select_modal;
457 ot->poll = ED_maskedit_mask_poll;
460 ot->flag = OPTYPE_UNDO;
463 WM_operator_properties_gesture_border(ot, TRUE);
466 static int do_lasso_select_mask(bContext *C, int mcords[][2], short moves, short select)
468 Mask *mask = CTX_data_edit_mask(C);
475 /* get rectangle from operator */
476 BLI_lasso_boundbox(&rect, mcords, moves);
478 /* do actual selection */
479 for (masklay = mask->masklayers.first; masklay; masklay = masklay->next) {
482 if (masklay->restrictflag & (MASK_RESTRICT_VIEW | MASK_RESTRICT_SELECT)) {
486 for (spline = masklay->splines.first; spline; spline = spline->next) {
487 MaskSplinePoint *points_array = BKE_mask_spline_point_array(spline);
489 for (i = 0; i < spline->tot_point; i++) {
490 MaskSplinePoint *point = &spline->points[i];
491 MaskSplinePoint *point_deform = &points_array[i];
498 /* marker in screen coords */
499 ED_mask_point_pos__reverse(C,
500 point_deform->bezt.vec[1][0], point_deform->bezt.vec[1][1],
501 &screen_co[0], &screen_co[1]);
503 if (BLI_in_rcti(&rect, screen_co[0], screen_co[1]) &&
504 BLI_lasso_is_point_inside(mcords, moves, screen_co[0], screen_co[1], INT_MAX))
506 BKE_mask_point_select_set(point, select);
507 BKE_mask_point_select_set_handle(point, select);
516 ED_mask_select_flush_all(mask);
518 WM_event_add_notifier(C, NC_MASK | ND_SELECT, mask);
524 static int clip_lasso_select_exec(bContext *C, wmOperator *op)
527 int (*mcords)[2] = WM_gesture_lasso_path_to_array(C, op, &mcords_tot);
532 select = !RNA_boolean_get(op->ptr, "deselect");
533 do_lasso_select_mask(C, mcords, mcords_tot, select);
537 return OPERATOR_FINISHED;
539 return OPERATOR_PASS_THROUGH;
542 /* MASKTODO - image space */
543 void MASK_OT_select_lasso(wmOperatorType *ot)
546 ot->name = "Lasso Select";
547 ot->description = "Select markers using lasso selection";
548 ot->idname = "MASK_OT_select_lasso";
551 ot->invoke = WM_gesture_lasso_invoke;
552 ot->modal = WM_gesture_lasso_modal;
553 ot->exec = clip_lasso_select_exec;
554 ot->poll = ED_maskedit_mask_poll;
555 ot->cancel = WM_gesture_lasso_cancel;
558 ot->flag = OPTYPE_UNDO;
561 RNA_def_collection_runtime(ot->srna, "path", &RNA_OperatorMousePath, "Path", "");
562 RNA_def_boolean(ot->srna, "deselect", 0, "Deselect", "Deselect rather than select items");
563 RNA_def_boolean(ot->srna, "extend", 1, "Extend", "Extend selection instead of deselecting everything first");
566 /********************** circle select operator *********************/
568 static int mask_spline_point_inside_ellipse(BezTriple *bezt, float offset[2], float ellipse[2])
570 /* normalized ellipse: ell[0] = scaleX, ell[1] = scaleY */
573 x = (bezt->vec[1][0] - offset[0]) * ellipse[0];
574 y = (bezt->vec[1][1] - offset[1]) * ellipse[1];
576 return x * x + y * y < 1.0f;
579 static int circle_select_exec(bContext *C, wmOperator *op)
581 Mask *mask = CTX_data_edit_mask(C);
585 int x, y, radius, width, height, mode, change = FALSE;
586 float zoomx, zoomy, offset[2], ellipse[2];
588 /* get operator properties */
589 x = RNA_int_get(op->ptr, "x");
590 y = RNA_int_get(op->ptr, "y");
591 radius = RNA_int_get(op->ptr, "radius");
593 mode = RNA_int_get(op->ptr, "gesture_mode");
595 /* compute ellipse and position in unified coordinates */
596 ED_mask_size(C, &width, &height);
597 ED_mask_zoom(C, &zoomx, &zoomy);
598 width = height = MAX2(width, height);
600 ellipse[0] = width * zoomx / radius;
601 ellipse[1] = height * zoomy / radius;
603 ED_mask_point_pos(C, x, y, &offset[0], &offset[1]);
605 /* do actual selection */
606 for (masklay = mask->masklayers.first; masklay; masklay = masklay->next) {
609 if (masklay->restrictflag & (MASK_RESTRICT_VIEW | MASK_RESTRICT_SELECT)) {
613 for (spline = masklay->splines.first; spline; spline = spline->next) {
614 MaskSplinePoint *points_array = BKE_mask_spline_point_array(spline);
616 for (i = 0; i < spline->tot_point; i++) {
617 MaskSplinePoint *point = &spline->points[i];
618 MaskSplinePoint *point_deform = &points_array[i];
620 if (mask_spline_point_inside_ellipse(&point_deform->bezt, offset, ellipse)) {
621 BKE_mask_point_select_set(point, mode == GESTURE_MODAL_SELECT);
622 BKE_mask_point_select_set_handle(point, mode == GESTURE_MODAL_SELECT);
631 ED_mask_select_flush_all(mask);
633 WM_event_add_notifier(C, NC_MASK | ND_SELECT, mask);
635 return OPERATOR_FINISHED;
638 return OPERATOR_CANCELLED;
641 void MASK_OT_select_circle(wmOperatorType *ot)
644 ot->name = "Circle Select";
645 ot->description = "Select markers using circle selection";
646 ot->idname = "MASK_OT_select_circle";
649 ot->invoke = WM_gesture_circle_invoke;
650 ot->modal = WM_gesture_circle_modal;
651 ot->exec = circle_select_exec;
652 ot->poll = ED_maskedit_mask_poll;
655 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
658 RNA_def_int(ot->srna, "x", 0, INT_MIN, INT_MAX, "X", "", INT_MIN, INT_MAX);
659 RNA_def_int(ot->srna, "y", 0, INT_MIN, INT_MAX, "Y", "", INT_MIN, INT_MAX);
660 RNA_def_int(ot->srna, "radius", 0, INT_MIN, INT_MAX, "Radius", "", INT_MIN, INT_MAX);
661 RNA_def_int(ot->srna, "gesture_mode", 0, INT_MIN, INT_MAX, "Gesture Mode", "", INT_MIN, INT_MAX);
664 static int mask_select_linked_pick_invoke(bContext *C, wmOperator *op, wmEvent *event)
666 Mask *mask = CTX_data_edit_mask(C);
669 MaskSplinePoint *point = NULL;
671 int do_select = !RNA_boolean_get(op->ptr, "deselect");
674 const float threshold = 19;
677 ED_mask_mouse_pos(C, event, co);
679 point = ED_mask_point_find_nearest(C, mask, co, threshold, &masklay, &spline, &is_handle, NULL);
682 ED_mask_spline_select_set(spline, do_select);
683 masklay->act_spline = spline;
684 masklay->act_point = point;
690 ED_mask_select_flush_all(mask);
692 WM_event_add_notifier(C, NC_MASK | ND_SELECT, mask);
694 return OPERATOR_FINISHED;
697 return OPERATOR_CANCELLED;
700 void MASK_OT_select_linked_pick(wmOperatorType *ot)
703 ot->name = "Select Linked";
704 ot->idname = "MASK_OT_select_linked_pick";
705 ot->description = "(De)select all points linked to the curve under the mouse cursor";
708 ot->invoke = mask_select_linked_pick_invoke;
709 ot->poll = ED_maskedit_mask_poll;
712 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
714 RNA_def_boolean(ot->srna, "deselect", 0, "Deselect", "");
717 static int mask_select_linked_exec(bContext *C, wmOperator *UNUSED(op))
719 Mask *mask = CTX_data_edit_mask(C);
724 /* do actual selection */
725 for (masklay = mask->masklayers.first; masklay; masklay = masklay->next) {
728 if (masklay->restrictflag & (MASK_RESTRICT_VIEW | MASK_RESTRICT_SELECT)) {
732 for (spline = masklay->splines.first; spline; spline = spline->next) {
733 if (ED_mask_spline_select_check(spline)) {
734 ED_mask_spline_select_set(spline, TRUE);
741 ED_mask_select_flush_all(mask);
743 WM_event_add_notifier(C, NC_MASK | ND_SELECT, mask);
745 return OPERATOR_FINISHED;
748 return OPERATOR_CANCELLED;
751 void MASK_OT_select_linked(wmOperatorType *ot)
754 ot->name = "Select Linked All";
755 ot->idname = "MASK_OT_select_linked";
756 ot->description = "Select all vertices linked to the active mesh";
759 ot->exec = mask_select_linked_exec;
760 ot->poll = ED_maskedit_mask_poll;
763 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;