mask - border & lasso select (lasso uses Ctrl+Alt - as with clip view)
[blender.git] / source / blender / editors / mask / mask_select.c
1 /*
2  * ***** BEGIN GPL LICENSE BLOCK *****
3  *
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.
8  *
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.
13  *
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.
17  *
18  * The Original Code is Copyright (C) 2012 Blender Foundation.
19  * All rights reserved.
20  *
21  *
22  * Contributor(s): Blender Foundation,
23  *                 Sergey Sharybin
24  *
25  * ***** END GPL LICENSE BLOCK *****
26  */
27
28 /** \file blender/editors/mask/mask_select.c
29  *  \ingroup edmask
30  */
31
32 #include "MEM_guardedalloc.h"
33
34 #include "BLI_utildefines.h"
35 #include "BLI_listbase.h"
36 #include "BLI_rect.h"
37 #include "BLI_lasso.h"
38 #include "BLI_math.h"
39
40 #include "BKE_context.h"
41 #include "BKE_curve.h"
42 #include "BKE_depsgraph.h"
43 #include "BKE_mask.h"
44
45 #include "DNA_mask_types.h"
46 #include "DNA_object_types.h"  /* SELECT */
47
48 #include "WM_api.h"
49 #include "WM_types.h"
50
51 #include "ED_screen.h"
52 #include "ED_mask.h"
53 #include "ED_clip.h"
54
55 #include "RNA_access.h"
56 #include "RNA_define.h"
57
58 #include "mask_intern.h"  /* own include */
59
60 int ED_mask_spline_select_check(MaskSplinePoint *points, int tot_point)
61 {
62         int i;
63
64         for (i = 0; i < tot_point; i++) {
65                 MaskSplinePoint *point = &points[i];
66
67                 if (MASKPOINT_ISSEL(point))
68                         return TRUE;
69         }
70
71         return FALSE;
72 }
73
74 int ED_mask_select_check(Mask *mask)
75 {
76         MaskObject *maskobj;
77
78         for (maskobj = mask->maskobjs.first; maskobj; maskobj = maskobj->next) {
79                 MaskSpline *spline;
80
81                 for (spline = maskobj->splines.first; spline; spline = spline->next) {
82                         if (ED_mask_spline_select_check(spline->points, spline->tot_point)) {
83                                 return TRUE;
84                         }
85                 }
86         }
87
88         return FALSE;
89 }
90
91 void ED_mask_select_toggle_all(Mask *mask, int action)
92 {
93         MaskObject *maskobj;
94
95         if (action == SEL_TOGGLE) {
96                 if (ED_mask_select_check(mask))
97                         action = SEL_DESELECT;
98                 else
99                         action = SEL_SELECT;
100         }
101
102         for (maskobj = mask->maskobjs.first; maskobj; maskobj = maskobj->next) {
103                 MaskSpline *spline;
104
105                 for (spline = maskobj->splines.first; spline; spline = spline->next) {
106                         int i;
107
108                         for (i = 0; i < spline->tot_point; i++) {
109                                 MaskSplinePoint *point = &spline->points[i];
110
111                                 BKE_mask_point_select_set(point, (action == SEL_SELECT) ? TRUE : FALSE);
112                         }
113                 }
114         }
115 }
116
117 void ED_mask_select_flush_all(Mask *mask)
118 {
119         MaskObject *maskobj;
120
121         for (maskobj = mask->maskobjs.first; maskobj; maskobj = maskobj->next) {
122                 MaskSpline *spline;
123
124                 for (spline = maskobj->splines.first; spline; spline = spline->next) {
125                         int i;
126
127                         spline->flag &= ~SELECT;
128
129                         for (i = 0; i < spline->tot_point; i++) {
130                                 MaskSplinePoint *cur_point = &spline->points[i];
131
132                                 if (MASKPOINT_ISSEL(cur_point)) {
133                                         spline->flag |= SELECT;
134                                 }
135                                 else {
136                                         int j;
137
138                                         for (j = 0; j < cur_point->tot_uw; j++) {
139                                                 if (cur_point->uw[j].flag & SELECT) {
140                                                         spline->flag |= SELECT;
141                                                         break;
142                                                 }
143                                         }
144                                 }
145                         }
146                 }
147         }
148 }
149
150 /******************** toggle selection *********************/
151
152 static int select_all_exec(bContext *C, wmOperator *op)
153 {
154         Mask *mask = CTX_data_edit_mask(C);
155         int action = RNA_enum_get(op->ptr, "action");
156
157         ED_mask_select_toggle_all(mask, action);
158         ED_mask_select_flush_all(mask);
159
160         WM_event_add_notifier(C, NC_MASK | ND_SELECT, mask);
161
162         return OPERATOR_FINISHED;
163 }
164
165 void MASK_OT_select_all(wmOperatorType *ot)
166 {
167         /* identifiers */
168         ot->name = "Select or Deselect All";
169         ot->description = "Change selection of all curve points";
170         ot->idname = "MASK_OT_select_all";
171
172         /* api callbacks */
173         ot->exec = select_all_exec;
174         ot->poll = ED_maskediting_mask_poll;
175
176         /* flags */
177         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
178
179         /* properties */
180         WM_operator_properties_select_all(ot);
181 }
182
183 /******************** select *********************/
184
185 static int select_exec(bContext *C, wmOperator *op)
186 {
187         Mask *mask = CTX_data_edit_mask(C);
188         MaskObject *maskobj;
189         MaskSpline *spline;
190         MaskSplinePoint *point = NULL;
191         float co[2];
192         short extend = RNA_boolean_get(op->ptr, "extend");
193         short deselect = RNA_boolean_get(op->ptr, "deselect");
194         short toggle = RNA_boolean_get(op->ptr, "toggle");
195
196         int is_handle = 0;
197         const float threshold = 19;
198
199         RNA_float_get_array(op->ptr, "location", co);
200
201         point = ED_mask_point_find_nearest(C, mask, co, threshold, &maskobj, &spline, &is_handle, NULL);
202
203         if (point) {
204                 if (extend == 0 && deselect == 0 && toggle == 0)
205                         ED_mask_select_toggle_all(mask, SEL_DESELECT);
206
207                 if (is_handle) {
208                         if (extend) {
209                                 maskobj->act_spline = spline;
210                                 maskobj->act_point = point;
211
212                                 BKE_mask_point_select_set_handle(point, TRUE);
213                         }
214                         else if (deselect) {
215                                 BKE_mask_point_select_set_handle(point, FALSE);
216                         }
217                         else {
218                                 maskobj->act_spline = spline;
219                                 maskobj->act_point = point;
220
221                                 if (!MASKPOINT_HANDLE_ISSEL(point)) {
222                                         BKE_mask_point_select_set_handle(point, TRUE);
223                                 }
224                                 else if (toggle) {
225                                         BKE_mask_point_select_set_handle(point, FALSE);
226                                 }
227                         }
228                 }
229                 else {
230                         if (extend) {
231                                 maskobj->act_spline = spline;
232                                 maskobj->act_point = point;
233
234                                 BKE_mask_point_select_set(point, TRUE);
235                         }
236                         else if (deselect) {
237                                 BKE_mask_point_select_set(point, FALSE);
238                         }
239                         else {
240                                 maskobj->act_spline = spline;
241                                 maskobj->act_point = point;
242
243                                 if (!MASKPOINT_ISSEL(point)) {
244                                         BKE_mask_point_select_set(point, TRUE);
245                                 }
246                                 else if (toggle) {
247                                         BKE_mask_point_select_set(point, FALSE);
248                                 }
249                         }
250                 }
251
252                 maskobj->act_spline = spline;
253                 maskobj->act_point = point;
254
255                 ED_mask_select_flush_all(mask);
256
257                 WM_event_add_notifier(C, NC_MASK | ND_SELECT, mask);
258
259                 return OPERATOR_FINISHED;
260         }
261         else {
262                 MaskSplinePointUW *uw;
263
264                 if (ED_mask_feather_find_nearest(C, mask, co, threshold, &maskobj, &spline, &point, &uw, NULL)) {
265                         if (!extend)
266                                 ED_mask_select_toggle_all(mask, SEL_DESELECT);
267
268                         if (uw)
269                                 uw->flag |= SELECT;
270
271                         maskobj->act_spline = spline;
272                         maskobj->act_point = point;
273
274                         ED_mask_select_flush_all(mask);
275
276                         WM_event_add_notifier(C, NC_MASK | ND_SELECT, mask);
277
278                         return OPERATOR_FINISHED;
279                 }
280         }
281
282         return OPERATOR_PASS_THROUGH;
283 }
284
285 static int select_invoke(bContext *C, wmOperator *op, wmEvent *event)
286 {
287         float co[2];
288
289         ED_mask_mouse_pos(C, event, co);
290
291         RNA_float_set_array(op->ptr, "location", co);
292
293         return select_exec(C, op);
294 }
295
296 void MASK_OT_select(wmOperatorType *ot)
297 {
298         /* identifiers */
299         ot->name = "Select";
300         ot->description = "Select spline points";
301         ot->idname = "MASK_OT_select";
302
303         /* api callbacks */
304         ot->exec = select_exec;
305         ot->invoke = select_invoke;
306         ot->poll = ED_maskediting_mask_poll;
307
308         /* flags */
309         ot->flag = OPTYPE_UNDO;
310
311         /* properties */
312         WM_operator_properties_mouse_select(ot);
313
314         RNA_def_float_vector(ot->srna, "location", 2, NULL, -FLT_MIN, FLT_MAX,
315                              "Location", "Location of vertex in normalized space", -1.0f, 1.0f);
316 }
317
318
319
320 /********************** border select operator *********************/
321
322 static int border_select_exec(bContext *C, wmOperator *op)
323 {
324         Mask *mask = CTX_data_edit_mask(C);
325         MaskObject *maskobj;
326         int i;
327
328         rcti rect;
329         rctf rectf;
330         int change = FALSE, mode, extend;
331
332         /* get rectangle from operator */
333         rect.xmin = RNA_int_get(op->ptr, "xmin");
334         rect.ymin = RNA_int_get(op->ptr, "ymin");
335         rect.xmax = RNA_int_get(op->ptr, "xmax");
336         rect.ymax = RNA_int_get(op->ptr, "ymax");
337
338         ED_mask_point_pos(C, rect.xmin, rect.ymin, &rectf.xmin, &rectf.ymin);
339         ED_mask_point_pos(C, rect.xmax, rect.ymax, &rectf.xmax, &rectf.ymax);
340
341         mode = RNA_int_get(op->ptr, "gesture_mode");
342         extend = RNA_boolean_get(op->ptr, "extend");
343
344         /* do actual selection */
345         for (maskobj = mask->maskobjs.first; maskobj; maskobj = maskobj->next) {
346                 MaskSpline *spline;
347
348                 for (spline = maskobj->splines.first; spline; spline = spline->next) {
349                         for (i = 0; i < spline->tot_point; i++) {
350                                 MaskSplinePoint *point = &spline->points[i];
351
352                                 /* TODO: handles? */
353                                 /* TODO: uw? */
354
355                                 if (1) { /* can the point be selected? */
356                                         if (BLI_in_rctf(&rectf, point->bezt.vec[1][0], point->bezt.vec[1][1])) {
357                                                 BKE_mask_point_select_set(point, mode == GESTURE_MODAL_SELECT);
358                                                 BKE_mask_point_select_set_handle(point, mode == GESTURE_MODAL_SELECT);
359                                         }
360                                         else if (!extend) {
361                                                 BKE_mask_point_select_set(point, FALSE);
362                                                 BKE_mask_point_select_set_handle(point, FALSE);
363                                         }
364
365                                         change = TRUE;
366                                 }
367                         }
368                 }
369         }
370
371         if (change) {
372                 ED_mask_select_flush_all(mask);
373
374                 WM_event_add_notifier(C, NC_MASK | ND_SELECT, mask);
375
376                 return OPERATOR_FINISHED;
377         }
378
379         return OPERATOR_CANCELLED;
380 }
381
382 void MASK_OT_select_border(wmOperatorType *ot)
383 {
384         /* identifiers */
385         ot->name = "Border Select";
386         ot->description = "Select markers using border selection";
387         ot->idname = "MASK_OT_select_border";
388
389         /* api callbacks */
390         ot->invoke = WM_border_select_invoke;
391         ot->exec = border_select_exec;
392         ot->modal = WM_border_select_modal;
393         ot->poll = ED_maskediting_mask_poll;
394
395         /* flags */
396         ot->flag = OPTYPE_UNDO;
397
398         /* properties */
399         WM_operator_properties_gesture_border(ot, TRUE);
400 }
401
402 static int do_lasso_select_mask(bContext *C, int mcords[][2], short moves, short select)
403 {
404         Mask *mask = CTX_data_edit_mask(C);
405         MaskObject *maskobj;
406         int i;
407
408         rcti rect;
409         int change = FALSE;
410
411         /* get rectangle from operator */
412         BLI_lasso_boundbox(&rect, mcords, moves);
413
414         /* do actual selection */
415         for (maskobj = mask->maskobjs.first; maskobj; maskobj = maskobj->next) {
416                 MaskSpline *spline;
417
418                 for (spline = maskobj->splines.first; spline; spline = spline->next) {
419                         for (i = 0; i < spline->tot_point; i++) {
420                                 MaskSplinePoint *point = &spline->points[i];
421
422                                 /* TODO: handles? */
423                                 /* TODO: uw? */
424
425                                 float screen_co[2];
426
427                                 /* marker in screen coords */
428                                 ED_mask_point_pos__reverse(C,
429                                                            point->bezt.vec[1][0], point->bezt.vec[1][1],
430                                                            &screen_co[0], &screen_co[1]);
431
432                                 if (BLI_in_rcti(&rect, screen_co[0], screen_co[1]) &&
433                                     BLI_lasso_is_point_inside(mcords, moves, screen_co[0], screen_co[1], INT_MAX))
434                                 {
435                                         BKE_mask_point_select_set(point, select);
436                                         BKE_mask_point_select_set_handle(point, select);
437                                 }
438
439                                 change = TRUE;
440                         }
441                 }
442         }
443
444         if (change) {
445                 ED_mask_select_flush_all(mask);
446
447                 WM_event_add_notifier(C, NC_MASK | ND_SELECT, mask);
448         }
449
450         return change;
451 }
452
453 static int clip_lasso_select_exec(bContext *C, wmOperator *op)
454 {
455         int mcords_tot;
456         int (*mcords)[2] = WM_gesture_lasso_path_to_array(C, op, &mcords_tot);
457
458         if (mcords) {
459                 short select;
460
461                 select = !RNA_boolean_get(op->ptr, "deselect");
462                 do_lasso_select_mask(C, mcords, mcords_tot, select);
463
464                 MEM_freeN(mcords);
465
466                 return OPERATOR_FINISHED;
467         }
468         return OPERATOR_PASS_THROUGH;
469 }
470
471 void MASK_OT_select_lasso(wmOperatorType *ot)
472 {
473         /* identifiers */
474         ot->name = "Lasso Select";
475         ot->description = "Select markers using lasso selection";
476         ot->idname = "MASK_OT_select_lasso";
477
478         /* api callbacks */
479         ot->invoke = WM_gesture_lasso_invoke;
480         ot->modal = WM_gesture_lasso_modal;
481         ot->exec = clip_lasso_select_exec;
482         ot->poll = ED_maskediting_mask_poll;
483         ot->cancel = WM_gesture_lasso_cancel;
484
485         /* flags */
486         ot->flag = OPTYPE_UNDO;
487
488         /* properties */
489         RNA_def_collection_runtime(ot->srna, "path", &RNA_OperatorMousePath, "Path", "");
490         RNA_def_boolean(ot->srna, "deselect", 0, "Deselect", "Deselect rather than select items");
491         RNA_def_boolean(ot->srna, "extend", 1, "Extend", "Extend selection instead of deselecting everything first");
492 }
493
494 #if 0
495 /********************** circle select operator *********************/
496
497 static int marker_inside_ellipse(MovieTrackingMarker *marker, float offset[2], float ellipse[2])
498 {
499         /* normalized ellipse: ell[0] = scaleX, ell[1] = scaleY */
500         float x, y;
501
502         x = (marker->pos[0] - offset[0])*ellipse[0];
503         y = (marker->pos[1] - offset[1])*ellipse[1];
504
505         return x*x + y*y < 1.0f;
506 }
507
508 static int circle_select_exec(bContext *C, wmOperator *op)
509 {
510         SpaceClip *sc = CTX_wm_space_clip(C);
511         MovieClip *clip = ED_space_clip(sc);
512         ARegion *ar = CTX_wm_region(C);
513         MovieTracking *tracking = &clip->tracking;
514         MovieTrackingTrack *track;
515         ListBase *tracksbase = BKE_tracking_get_tracks(tracking);
516         int x, y, radius, width, height, mode, change = FALSE;
517         float zoomx, zoomy, offset[2], ellipse[2];
518
519         /* get operator properties */
520         x = RNA_int_get(op->ptr, "x");
521         y = RNA_int_get(op->ptr, "y");
522         radius = RNA_int_get(op->ptr, "radius");
523
524         mode = RNA_int_get(op->ptr, "gesture_mode");
525
526         /* compute ellipse and position in unified coordinates */
527         ED_space_clip_size(sc, &width, &height);
528         ED_space_clip_zoom(sc, ar, &zoomx, &zoomy);
529
530         ellipse[0] = width * zoomx / radius;
531         ellipse[1] = height * zoomy / radius;
532
533         ED_clip_point_stable_pos(C, x, y, &offset[0], &offset[1]);
534
535         /* do selection */
536         track = tracksbase->first;
537         while (track) {
538                 if ((track->flag & TRACK_HIDDEN) == 0) {
539                         MovieTrackingMarker *marker = BKE_tracking_get_marker(track, sc->user.framenr);
540
541                         if (MARKER_VISIBLE(sc, track, marker) && marker_inside_ellipse(marker, offset, ellipse)) {
542                                 BKE_tracking_track_flag(track, TRACK_AREA_ALL, SELECT, mode != GESTURE_MODAL_SELECT);
543
544                                 change = TRUE;
545                         }
546                 }
547
548                 track = track->next;
549         }
550
551         if (change) {
552                 ED_mask_select_flush_all(mask);
553
554                 WM_event_add_notifier(C, NC_MASK | ND_SELECT, mask);
555
556                 return OPERATOR_FINISHED;
557         }
558
559         return OPERATOR_CANCELLED;
560 }
561
562 void MASK_OT_select_circle(wmOperatorType *ot)
563 {
564         /* identifiers */
565         ot->name = "Circle Select";
566         ot->description = "Select markers using circle selection";
567         ot->idname = "MASK_OT_select_circle";
568
569         /* api callbacks */
570         ot->invoke = WM_gesture_circle_invoke;
571         ot->modal = WM_gesture_circle_modal;
572         ot->exec = circle_select_exec;
573         ot->poll = ED_maskediting_mask_poll;
574
575         /* flags */
576         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
577
578         /* properties */
579         RNA_def_int(ot->srna, "x", 0, INT_MIN, INT_MAX, "X", "", INT_MIN, INT_MAX);
580         RNA_def_int(ot->srna, "y", 0, INT_MIN, INT_MAX, "Y", "", INT_MIN, INT_MAX);
581         RNA_def_int(ot->srna, "radius", 0, INT_MIN, INT_MAX, "Radius", "", INT_MIN, INT_MAX);
582         RNA_def_int(ot->srna, "gesture_mode", 0, INT_MIN, INT_MAX, "Gesture Mode", "", INT_MIN, INT_MAX);
583 }
584
585 #endif