Merge plane track feature from tomato branch
[blender.git] / source / blender / editors / space_clip / tracking_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) 2011 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/space_clip/tracking_select.c
29  *  \ingroup spclip
30  */
31
32 #include "MEM_guardedalloc.h"
33
34 #include "DNA_camera_types.h"
35 #include "DNA_constraint_types.h"
36 #include "DNA_gpencil_types.h"
37 #include "DNA_movieclip_types.h"
38 #include "DNA_object_types.h"   /* SELECT */
39 #include "DNA_scene_types.h"
40
41 #include "BLI_utildefines.h"
42 #include "BLI_math.h"
43 #include "BLI_listbase.h"
44 #include "BLI_rect.h"
45 #include "BLI_lasso.h"
46 #include "BLI_blenlib.h"
47
48 #include "BKE_main.h"
49 #include "BKE_context.h"
50 #include "BKE_constraint.h"
51 #include "BKE_movieclip.h"
52 #include "BKE_tracking.h"
53 #include "BKE_global.h"
54 #include "BKE_depsgraph.h"
55 #include "BKE_object.h"
56 #include "BKE_report.h"
57 #include "BKE_scene.h"
58 #include "BKE_library.h"
59 #include "BKE_sound.h"
60
61 #include "WM_api.h"
62 #include "WM_types.h"
63
64 #include "ED_screen.h"
65 #include "ED_clip.h"
66 #include "ED_keyframing.h"
67
68 #include "IMB_imbuf_types.h"
69 #include "IMB_imbuf.h"
70
71 #include "UI_interface.h"
72
73 #include "RNA_access.h"
74 #include "RNA_define.h"
75
76 #include "PIL_time.h"
77
78 #include "UI_view2d.h"
79
80 #include "clip_intern.h"    // own include
81
82 static float dist_to_crns(float co[2], float pos[2], float crns[4][2]);
83
84 /********************** mouse select operator *********************/
85
86 static int mouse_on_side(float co[2], float x1, float y1, float x2, float y2, float epsx, float epsy)
87 {
88         if (x1 > x2)
89
90                 SWAP(float, x1, x2);
91
92         if (y1 > y2)
93                 SWAP(float, y1, y2);
94
95         return (co[0] >= x1 - epsx && co[0] <= x2 + epsx) && (co[1] >= y1 - epsy && co[1] <= y2 + epsy);
96 }
97
98 static int mouse_on_rect(float co[2], float pos[2], float min[2], float max[2], float epsx, float epsy)
99 {
100         return mouse_on_side(co, pos[0] + min[0], pos[1] + min[1], pos[0] + max[0], pos[1] + min[1], epsx, epsy) ||
101                mouse_on_side(co, pos[0] + min[0], pos[1] + min[1], pos[0] + min[0], pos[1] + max[1], epsx, epsy) ||
102                mouse_on_side(co, pos[0] + min[0], pos[1] + max[1], pos[0] + max[0], pos[1] + max[1], epsx, epsy) ||
103                mouse_on_side(co, pos[0] + max[0], pos[1] + min[1], pos[0] + max[0], pos[1] + max[1], epsx, epsy);
104 }
105
106 static int mouse_on_crns(float co[2], float pos[2], float crns[4][2], float epsx, float epsy)
107 {
108         float dist = dist_to_crns(co, pos, crns);
109
110         return dist < max_ff(epsx, epsy);
111 }
112
113 static int track_mouse_area(const bContext *C, float co[2], MovieTrackingTrack *track)
114 {
115         SpaceClip *sc = CTX_wm_space_clip(C);
116         int framenr = ED_space_clip_get_clip_frame_number(sc);
117         MovieTrackingMarker *marker = BKE_tracking_marker_get(track, framenr);
118         float pat_min[2], pat_max[2];
119         float epsx, epsy;
120         int width, height;
121
122         ED_space_clip_get_size(sc, &width, &height);
123
124         BKE_tracking_marker_pattern_minmax(marker, pat_min, pat_max);
125
126         epsx = min_ffff(pat_min[0] - marker->search_min[0], marker->search_max[0] - pat_max[0],
127                         fabsf(pat_min[0]), fabsf(pat_max[0])) / 2;
128         epsy = min_ffff(pat_min[1] - marker->search_min[1], marker->search_max[1] - pat_max[1],
129                         fabsf(pat_min[1]), fabsf(pat_max[1])) / 2;
130
131         epsx = max_ff(epsx, 2.0f / width);
132         epsy = max_ff(epsy, 2.0f / height);
133
134         if (sc->flag & SC_SHOW_MARKER_SEARCH) {
135                 if (mouse_on_rect(co, marker->pos, marker->search_min, marker->search_max, epsx, epsy))
136                         return TRACK_AREA_SEARCH;
137         }
138
139         if ((marker->flag & MARKER_DISABLED) == 0) {
140                 if (sc->flag & SC_SHOW_MARKER_PATTERN)
141                         if (mouse_on_crns(co, marker->pos, marker->pattern_corners, epsx, epsy))
142                                 return TRACK_AREA_PAT;
143
144                 epsx = 12.0f / width;
145                 epsy = 12.0f / height;
146
147                 if (fabsf(co[0] - marker->pos[0] - track->offset[0]) < epsx &&
148                     fabsf(co[1] - marker->pos[1] - track->offset[1]) <= epsy)
149                 {
150                         return TRACK_AREA_POINT;
151                 }
152         }
153
154         return TRACK_AREA_NONE;
155 }
156
157 static float dist_to_rect(float co[2], float pos[2], float min[2], float max[2])
158 {
159         float d1, d2, d3, d4;
160         float p[2] = {co[0] - pos[0], co[1] - pos[1]};
161         float v1[2] = {min[0], min[1]}, v2[2] = {max[0], min[1]};
162         float v3[2] = {max[0], max[1]}, v4[2] = {min[0], max[1]};
163
164         d1 = dist_squared_to_line_segment_v2(p, v1, v2);
165         d2 = dist_squared_to_line_segment_v2(p, v2, v3);
166         d3 = dist_squared_to_line_segment_v2(p, v3, v4);
167         d4 = dist_squared_to_line_segment_v2(p, v4, v1);
168
169         return sqrtf(min_ffff(d1, d2, d3, d4));
170 }
171
172 /* Distance to quad defined by it's corners, corners are relative to pos */
173 static float dist_to_crns(float co[2], float pos[2], float crns[4][2])
174 {
175         float d1, d2, d3, d4;
176         float p[2] = {co[0] - pos[0], co[1] - pos[1]};
177         float *v1 = crns[0], *v2 = crns[1];
178         float *v3 = crns[2], *v4 = crns[3];
179
180         d1 = dist_squared_to_line_segment_v2(p, v1, v2);
181         d2 = dist_squared_to_line_segment_v2(p, v2, v3);
182         d3 = dist_squared_to_line_segment_v2(p, v3, v4);
183         d4 = dist_squared_to_line_segment_v2(p, v4, v1);
184
185         return sqrtf(min_ffff(d1, d2, d3, d4));
186 }
187
188 /* Same as above, but all the coordinates are absolute */
189 static float dist_to_crns_abs(float co[2], float corners[4][2])
190 {
191         float d1, d2, d3, d4;
192         float *v1 = corners[0], *v2 = corners[1];
193         float *v3 = corners[2], *v4 = corners[3];
194
195         d1 = dist_squared_to_line_segment_v2(co, v1, v2);
196         d2 = dist_squared_to_line_segment_v2(co, v2, v3);
197         d3 = dist_squared_to_line_segment_v2(co, v3, v4);
198         d4 = dist_squared_to_line_segment_v2(co, v4, v1);
199
200         return sqrtf(min_ffff(d1, d2, d3, d4));
201 }
202
203 static MovieTrackingTrack *find_nearest_track(SpaceClip *sc, ListBase *tracksbase, float co[2], float *distance_r)
204 {
205         MovieTrackingTrack *track = NULL, *cur;
206         float mindist = 0.0f;
207         int framenr = ED_space_clip_get_clip_frame_number(sc);
208
209         cur = tracksbase->first;
210         while (cur) {
211                 MovieTrackingMarker *marker = BKE_tracking_marker_get(cur, framenr);
212
213                 if (((cur->flag & TRACK_HIDDEN) == 0) && MARKER_VISIBLE(sc, cur, marker)) {
214                         float dist, d1, d2 = FLT_MAX, d3 = FLT_MAX;
215
216                         /* distance to marker point */
217                         d1 = sqrtf((co[0] - marker->pos[0] - cur->offset[0]) * (co[0] - marker->pos[0] - cur->offset[0]) +
218                                    (co[1] - marker->pos[1] - cur->offset[1]) * (co[1] - marker->pos[1] - cur->offset[1]));
219
220                         /* distance to pattern boundbox */
221                         if (sc->flag & SC_SHOW_MARKER_PATTERN)
222                                 d2 = dist_to_crns(co, marker->pos, marker->pattern_corners);
223
224                         /* distance to search boundbox */
225                         if (sc->flag & SC_SHOW_MARKER_SEARCH && TRACK_VIEW_SELECTED(sc, cur))
226                                 d3 = dist_to_rect(co, marker->pos, marker->search_min, marker->search_max);
227
228                         /* choose minimal distance. useful for cases of overlapped markers. */
229                         dist = min_fff(d1, d2, d3);
230
231                         if (track == NULL || dist < mindist) {
232                                 track = cur;
233                                 mindist = dist;
234                         }
235                 }
236
237                 cur = cur->next;
238         }
239
240         *distance_r = mindist;
241
242         return track;
243 }
244
245 static MovieTrackingPlaneTrack *find_nearest_plane_track(SpaceClip *sc, ListBase *plane_tracks_base,
246                                                          float co[2], float *distance_r)
247 {
248         MovieTrackingPlaneTrack *plane_track = NULL, *current_plane_track;
249         float min_distance = 0.0f;
250         int framenr = ED_space_clip_get_clip_frame_number(sc);
251
252         for (current_plane_track = plane_tracks_base->first;
253              current_plane_track;
254              current_plane_track = current_plane_track->next)
255         {
256                 MovieTrackingPlaneMarker *plane_marker = BKE_tracking_plane_marker_get(current_plane_track, framenr);
257
258                 if ((current_plane_track->flag & TRACK_HIDDEN) == 0) {
259                         float distance = dist_to_crns_abs(co, plane_marker->corners);
260                         if (plane_track == NULL || distance < min_distance) {
261                                 plane_track = current_plane_track;
262                                 min_distance = distance;
263                         }
264                 }
265         }
266
267         *distance_r = min_distance;
268
269         return plane_track;
270 }
271
272 static void delect_all_tracks(ListBase *tracks_base)
273 {
274         MovieTrackingTrack *track;
275         for (track = tracks_base->first;
276              track;
277              track = track->next)
278         {
279                 BKE_tracking_track_flag_clear(track, TRACK_AREA_ALL, SELECT);
280         }
281 }
282
283 static void delect_all_plane_tracks(ListBase *plane_tracks_base)
284 {
285         MovieTrackingPlaneTrack *plane_track;
286         for (plane_track = plane_tracks_base->first;
287              plane_track;
288              plane_track = plane_track->next)
289         {
290                 plane_track->flag &= ~SELECT;
291         }
292 }
293
294 static int mouse_select(bContext *C, float co[2], int extend)
295 {
296         SpaceClip *sc = CTX_wm_space_clip(C);
297         MovieClip *clip = ED_space_clip_get_clip(sc);
298         MovieTracking *tracking = &clip->tracking;
299         ListBase *tracksbase = BKE_tracking_get_active_tracks(tracking);
300         ListBase *plane_tracks_base = BKE_tracking_get_active_plane_tracks(tracking);
301         MovieTrackingTrack *act_track = BKE_tracking_track_get_active(tracking);
302         MovieTrackingTrack *track;
303         MovieTrackingPlaneTrack *plane_track;
304         float distance_to_track, distance_to_plane_track;
305
306         track = find_nearest_track(sc, tracksbase, co, &distance_to_track);
307         plane_track = find_nearest_plane_track(sc, plane_tracks_base, co, &distance_to_plane_track);
308
309         /* Between track and plane we choose closest to the mouse for selection here. */
310         if (track && plane_track) {
311                 if (distance_to_track < distance_to_plane_track) {
312                         plane_track = NULL;
313                 }
314                 else {
315                         track = NULL;
316                 }
317         }
318
319         if (!extend) {
320                 delect_all_plane_tracks(plane_tracks_base);
321         }
322
323         if (track) {
324                 int area = track_mouse_area(C, co, track);
325
326                 if (!extend || !TRACK_VIEW_SELECTED(sc, track))
327                         area = TRACK_AREA_ALL;
328
329                 if (extend && TRACK_AREA_SELECTED(track, area)) {
330                         if (track == act_track) {
331                                 BKE_tracking_track_deselect(track, area);
332                         }
333                         else {
334                                 clip->tracking.act_track = track;
335                                 clip->tracking.act_plane_track = NULL;
336                         }
337                 }
338                 else {
339                         if (area == TRACK_AREA_POINT)
340                                 area = TRACK_AREA_ALL;
341
342                         BKE_tracking_track_select(tracksbase, track, area, extend);
343                         clip->tracking.act_track = track;
344                         clip->tracking.act_plane_track = NULL;
345                 }
346         }
347         else if (plane_track) {
348                 if (!extend) {
349                         delect_all_tracks(tracksbase);
350                 }
351
352                 if (plane_track->flag & SELECT) {
353                         if (extend) {
354                                 plane_track->flag &= ~SELECT;
355                         }
356                 }
357                 else {
358                         plane_track->flag |= SELECT;
359                 }
360
361                 clip->tracking.act_track = NULL;
362                 clip->tracking.act_plane_track = plane_track;
363         }
364
365         if (!extend) {
366                 sc->xlockof = 0.0f;
367                 sc->ylockof = 0.0f;
368         }
369
370         BKE_tracking_dopesheet_tag_update(tracking);
371
372         WM_event_add_notifier(C, NC_GEOM | ND_SELECT, NULL);
373
374         return OPERATOR_FINISHED;
375 }
376
377 static int select_poll(bContext *C)
378 {
379         SpaceClip *sc = CTX_wm_space_clip(C);
380
381         if (sc) {
382                 return sc->clip && sc->view == SC_VIEW_CLIP;
383         }
384
385         return FALSE;
386 }
387
388 static int select_exec(bContext *C, wmOperator *op)
389 {
390         float co[2];
391         int extend;
392
393         RNA_float_get_array(op->ptr, "location", co);
394         extend = RNA_boolean_get(op->ptr, "extend");
395
396         return mouse_select(C, co, extend);
397 }
398
399 static int select_invoke(bContext *C, wmOperator *op, const wmEvent *event)
400 {
401         SpaceClip *sc = CTX_wm_space_clip(C);
402         ARegion *ar = CTX_wm_region(C);
403
404         float co[2];
405         int extend = RNA_boolean_get(op->ptr, "extend");
406
407         if (!extend) {
408                 MovieTrackingTrack *track = tracking_marker_check_slide(C, event, NULL, NULL, NULL);
409
410                 if (track) {
411                         MovieClip *clip = ED_space_clip_get_clip(sc);
412
413                         clip->tracking.act_track = track;
414
415                         WM_event_add_notifier(C, NC_GEOM | ND_SELECT, NULL);
416
417                         return OPERATOR_PASS_THROUGH;
418                 }
419         }
420
421         ED_clip_mouse_pos(sc, ar, event->mval, co);
422         RNA_float_set_array(op->ptr, "location", co);
423
424         return select_exec(C, op);
425 }
426
427 void CLIP_OT_select(wmOperatorType *ot)
428 {
429         /* identifiers */
430         ot->name = "Select";
431         ot->description = "Select tracking markers";
432         ot->idname = "CLIP_OT_select";
433
434         /* api callbacks */
435         ot->exec = select_exec;
436         ot->invoke = select_invoke;
437         ot->poll = select_poll;
438
439         /* flags */
440         ot->flag = OPTYPE_UNDO;
441
442         /* properties */
443         RNA_def_boolean(ot->srna, "extend", 0,
444                         "Extend", "Extend selection rather than clearing the existing selection");
445         RNA_def_float_vector(ot->srna, "location", 2, NULL, -FLT_MAX, FLT_MAX,
446                              "Location", "Mouse location in normalized coordinates, 0.0 to 1.0 is within the image bounds", -100.0f, 100.0f);
447 }
448
449 /********************** border select operator *********************/
450
451 static int border_select_exec(bContext *C, wmOperator *op)
452 {
453         SpaceClip *sc = CTX_wm_space_clip(C);
454         ARegion *ar = CTX_wm_region(C);
455
456         MovieClip *clip = ED_space_clip_get_clip(sc);
457         MovieTracking *tracking = &clip->tracking;
458         MovieTrackingTrack *track;
459         MovieTrackingPlaneTrack *plane_track;
460         ListBase *tracksbase = BKE_tracking_get_active_tracks(tracking);
461         ListBase *plane_tracks_base = BKE_tracking_get_active_plane_tracks(tracking);
462         rcti rect;
463         rctf rectf;
464         int change = FALSE, mode, extend;
465         int framenr = ED_space_clip_get_clip_frame_number(sc);
466
467         /* get rectangle from operator */
468         WM_operator_properties_border_to_rcti(op, &rect);
469
470         ED_clip_point_stable_pos(sc, ar, rect.xmin, rect.ymin, &rectf.xmin, &rectf.ymin);
471         ED_clip_point_stable_pos(sc, ar, rect.xmax, rect.ymax, &rectf.xmax, &rectf.ymax);
472
473         mode = RNA_int_get(op->ptr, "gesture_mode");
474         extend = RNA_boolean_get(op->ptr, "extend");
475
476         /* do actual selection */
477         track = tracksbase->first;
478         while (track) {
479                 if ((track->flag & TRACK_HIDDEN) == 0) {
480                         MovieTrackingMarker *marker = BKE_tracking_marker_get(track, framenr);
481
482                         if (MARKER_VISIBLE(sc, track, marker)) {
483                                 if (BLI_rctf_isect_pt_v(&rectf, marker->pos)) {
484                                         if (mode == GESTURE_MODAL_SELECT)
485                                                 BKE_tracking_track_flag_set(track, TRACK_AREA_ALL, SELECT);
486                                         else
487                                                 BKE_tracking_track_flag_clear(track, TRACK_AREA_ALL, SELECT);
488                                 }
489                                 else if (!extend) {
490                                         BKE_tracking_track_flag_clear(track, TRACK_AREA_ALL, SELECT);
491                                 }
492
493                                 change = TRUE;
494                         }
495                 }
496
497                 track = track->next;
498         }
499
500         for (plane_track = plane_tracks_base->first;
501              plane_track;
502              plane_track = plane_track->next)
503         {
504                 if ((plane_track->flag & PLANE_TRACK_HIDDEN) == 0) {
505                         MovieTrackingPlaneMarker *plane_marker =
506                                 BKE_tracking_plane_marker_get(plane_track, framenr);
507                         int i;
508
509                         for (i = 0; i < 4; i++) {
510                                 if (BLI_rctf_isect_pt_v(&rectf, plane_marker->corners[i])) {
511                                         if (mode == GESTURE_MODAL_SELECT) {
512                                                 plane_track->flag |= SELECT;
513                                         }
514                                         else {
515                                                 plane_track->flag &= ~SELECT;
516                                         }
517                                 }
518                                 else if (!extend) {
519                                         plane_track->flag &= ~SELECT;
520                                 }
521                         }
522
523                         change = TRUE;
524                 }
525         }
526
527         if (change) {
528                 BKE_tracking_dopesheet_tag_update(tracking);
529
530                 WM_event_add_notifier(C, NC_GEOM | ND_SELECT, NULL);
531
532                 return OPERATOR_FINISHED;
533         }
534
535         return OPERATOR_CANCELLED;
536 }
537
538 void CLIP_OT_select_border(wmOperatorType *ot)
539 {
540         /* identifiers */
541         ot->name = "Border Select";
542         ot->description = "Select markers using border selection";
543         ot->idname = "CLIP_OT_select_border";
544
545         /* api callbacks */
546         ot->invoke = WM_border_select_invoke;
547         ot->exec = border_select_exec;
548         ot->modal = WM_border_select_modal;
549         ot->poll = ED_space_clip_tracking_poll;
550
551         /* flags */
552         ot->flag = OPTYPE_UNDO;
553
554         /* properties */
555         WM_operator_properties_gesture_border(ot, TRUE);
556 }
557
558 /********************** lasso select operator *********************/
559
560 static int do_lasso_select_marker(bContext *C, const int mcords[][2], const short moves, short select)
561 {
562         SpaceClip *sc = CTX_wm_space_clip(C);
563         ARegion *ar = CTX_wm_region(C);
564
565         MovieClip *clip = ED_space_clip_get_clip(sc);
566         MovieTracking *tracking = &clip->tracking;
567         MovieTrackingTrack *track;
568         MovieTrackingPlaneTrack *plane_track;
569         ListBase *tracksbase = BKE_tracking_get_active_tracks(tracking);
570         ListBase *plane_tracks_base = BKE_tracking_get_active_plane_tracks(tracking);
571         rcti rect;
572         int change = FALSE;
573         int framenr = ED_space_clip_get_clip_frame_number(sc);
574
575         /* get rectangle from operator */
576         BLI_lasso_boundbox(&rect, mcords, moves);
577
578         /* do actual selection */
579         track = tracksbase->first;
580         while (track) {
581                 if ((track->flag & TRACK_HIDDEN) == 0) {
582                         MovieTrackingMarker *marker = BKE_tracking_marker_get(track, framenr);
583
584                         if (MARKER_VISIBLE(sc, track, marker)) {
585                                 float screen_co[2];
586
587                                 /* marker in screen coords */
588                                 ED_clip_point_stable_pos__reverse(sc, ar, marker->pos, screen_co);
589
590                                 if (BLI_rcti_isect_pt(&rect, screen_co[0], screen_co[1]) &&
591                                     BLI_lasso_is_point_inside(mcords, moves, screen_co[0], screen_co[1], V2D_IS_CLIPPED))
592                                 {
593                                         if (select)
594                                                 BKE_tracking_track_flag_set(track, TRACK_AREA_ALL, SELECT);
595                                         else
596                                                 BKE_tracking_track_flag_clear(track, TRACK_AREA_ALL, SELECT);
597                                 }
598
599                                 change = TRUE;
600                         }
601                 }
602
603                 track = track->next;
604         }
605
606         for (plane_track = plane_tracks_base->first;
607              plane_track;
608              plane_track = plane_track->next)
609         {
610                 if ((plane_track->flag & PLANE_TRACK_HIDDEN) == 0) {
611                         MovieTrackingPlaneMarker *plane_marker =
612                                 BKE_tracking_plane_marker_get(plane_track, framenr);
613                         int i;
614
615                         for (i = 0; i < 4; i++) {
616                                 float screen_co[2];
617
618                                 /* marker in screen coords */
619                                 ED_clip_point_stable_pos__reverse(sc, ar, plane_marker->corners[i], screen_co);
620
621                                 if (BLI_rcti_isect_pt(&rect, screen_co[0], screen_co[1]) &&
622                                     BLI_lasso_is_point_inside(mcords, moves, screen_co[0], screen_co[1], V2D_IS_CLIPPED))
623                                 {
624                                         if (select) {
625                                                 plane_track->flag |= SELECT;
626                                         }
627                                         else {
628                                                 plane_track->flag &= ~SELECT;
629                                         }
630                                 }
631                         }
632
633                         change = TRUE;
634                 }
635         }
636
637         if (change) {
638                 BKE_tracking_dopesheet_tag_update(tracking);
639
640                 WM_event_add_notifier(C, NC_GEOM | ND_SELECT, NULL);
641         }
642
643         return change;
644 }
645
646 static int clip_lasso_select_exec(bContext *C, wmOperator *op)
647 {
648         int mcords_tot;
649         const int (*mcords)[2] = WM_gesture_lasso_path_to_array(C, op, &mcords_tot);
650
651         if (mcords) {
652                 short select;
653
654                 select = !RNA_boolean_get(op->ptr, "deselect");
655                 do_lasso_select_marker(C, mcords, mcords_tot, select);
656
657                 MEM_freeN((void *)mcords);
658
659                 return OPERATOR_FINISHED;
660         }
661         return OPERATOR_PASS_THROUGH;
662 }
663
664 void CLIP_OT_select_lasso(wmOperatorType *ot)
665 {
666         /* identifiers */
667         ot->name = "Lasso Select";
668         ot->description = "Select markers using lasso selection";
669         ot->idname = "CLIP_OT_select_lasso";
670
671         /* api callbacks */
672         ot->invoke = WM_gesture_lasso_invoke;
673         ot->modal = WM_gesture_lasso_modal;
674         ot->exec = clip_lasso_select_exec;
675         ot->poll = ED_space_clip_tracking_poll;
676         ot->cancel = WM_gesture_lasso_cancel;
677
678         /* flags */
679         ot->flag = OPTYPE_UNDO;
680
681         /* properties */
682         RNA_def_collection_runtime(ot->srna, "path", &RNA_OperatorMousePath, "Path", "");
683         RNA_def_boolean(ot->srna, "deselect", 0, "Deselect", "Deselect rather than select items");
684         RNA_def_boolean(ot->srna, "extend", 1, "Extend", "Extend selection instead of deselecting everything first");
685 }
686
687 /********************** circle select operator *********************/
688
689 static int point_inside_ellipse(float point[2], float offset[2], float ellipse[2])
690 {
691         /* normalized ellipse: ell[0] = scaleX, ell[1] = scaleY */
692         float x, y;
693
694         x = (point[0] - offset[0]) * ellipse[0];
695         y = (point[1] - offset[1]) * ellipse[1];
696
697         return x * x + y * y < 1.0f;
698 }
699
700 static int marker_inside_ellipse(MovieTrackingMarker *marker, float offset[2], float ellipse[2])
701 {
702         return point_inside_ellipse(marker->pos, offset, ellipse);
703 }
704
705 static int circle_select_exec(bContext *C, wmOperator *op)
706 {
707         SpaceClip *sc = CTX_wm_space_clip(C);
708         ARegion *ar = CTX_wm_region(C);
709
710         MovieClip *clip = ED_space_clip_get_clip(sc);
711         MovieTracking *tracking = &clip->tracking;
712         MovieTrackingTrack *track;
713         MovieTrackingPlaneTrack *plane_track;
714         ListBase *tracksbase = BKE_tracking_get_active_tracks(tracking);
715         ListBase *plane_tracks_base = BKE_tracking_get_active_plane_tracks(tracking);
716         int x, y, radius, width, height, mode, change = FALSE;
717         float zoomx, zoomy, offset[2], ellipse[2];
718         int framenr = ED_space_clip_get_clip_frame_number(sc);
719
720         /* get operator properties */
721         x = RNA_int_get(op->ptr, "x");
722         y = RNA_int_get(op->ptr, "y");
723         radius = RNA_int_get(op->ptr, "radius");
724
725         mode = RNA_int_get(op->ptr, "gesture_mode");
726
727         /* compute ellipse and position in unified coordinates */
728         ED_space_clip_get_size(sc, &width, &height);
729         ED_space_clip_get_zoom(sc, ar, &zoomx, &zoomy);
730
731         ellipse[0] = width * zoomx / radius;
732         ellipse[1] = height * zoomy / radius;
733
734         ED_clip_point_stable_pos(sc, ar, x, y, &offset[0], &offset[1]);
735
736         /* do selection */
737         track = tracksbase->first;
738         while (track) {
739                 if ((track->flag & TRACK_HIDDEN) == 0) {
740                         MovieTrackingMarker *marker = BKE_tracking_marker_get(track, framenr);
741
742                         if (MARKER_VISIBLE(sc, track, marker) && marker_inside_ellipse(marker, offset, ellipse)) {
743                                 if (mode == GESTURE_MODAL_SELECT)
744                                         BKE_tracking_track_flag_set(track, TRACK_AREA_ALL, SELECT);
745                                 else
746                                         BKE_tracking_track_flag_clear(track, TRACK_AREA_ALL, SELECT);
747
748                                 change = TRUE;
749                         }
750                 }
751
752                 track = track->next;
753         }
754
755         for (plane_track = plane_tracks_base->first;
756              plane_track;
757              plane_track = plane_track->next)
758         {
759                 if ((plane_track->flag & PLANE_TRACK_HIDDEN) == 0) {
760                         MovieTrackingPlaneMarker *plane_marker =
761                                 BKE_tracking_plane_marker_get(plane_track, framenr);
762                         int i;
763
764                         for (i = 0; i < 4; i++) {
765                                 if (point_inside_ellipse(plane_marker->corners[i], offset, ellipse)) {
766                                         if (mode == GESTURE_MODAL_SELECT) {
767                                                 plane_track->flag |= SELECT;
768                                         }
769                                         else {
770                                                 plane_track->flag &= ~SELECT;
771                                         }
772                                 }
773                         }
774
775                         change = TRUE;
776                 }
777         }
778
779         if (change) {
780                 BKE_tracking_dopesheet_tag_update(tracking);
781
782                 WM_event_add_notifier(C, NC_GEOM | ND_SELECT, NULL);
783
784                 return OPERATOR_FINISHED;
785         }
786
787         return OPERATOR_CANCELLED;
788 }
789
790 void CLIP_OT_select_circle(wmOperatorType *ot)
791 {
792         /* identifiers */
793         ot->name = "Circle Select";
794         ot->description = "Select markers using circle selection";
795         ot->idname = "CLIP_OT_select_circle";
796
797         /* api callbacks */
798         ot->invoke = WM_gesture_circle_invoke;
799         ot->modal = WM_gesture_circle_modal;
800         ot->exec = circle_select_exec;
801         ot->poll = ED_space_clip_tracking_poll;
802
803         /* flags */
804         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
805
806         /* properties */
807         RNA_def_int(ot->srna, "x", 0, INT_MIN, INT_MAX, "X", "", INT_MIN, INT_MAX);
808         RNA_def_int(ot->srna, "y", 0, INT_MIN, INT_MAX, "Y", "", INT_MIN, INT_MAX);
809         RNA_def_int(ot->srna, "radius", 0, INT_MIN, INT_MAX, "Radius", "", INT_MIN, INT_MAX);
810         RNA_def_int(ot->srna, "gesture_mode", 0, INT_MIN, INT_MAX, "Gesture Mode", "", INT_MIN, INT_MAX);
811 }
812
813 /********************** select all operator *********************/
814
815 static int select_all_exec(bContext *C, wmOperator *op)
816 {
817         SpaceClip *sc = CTX_wm_space_clip(C);
818         MovieClip *clip = ED_space_clip_get_clip(sc);
819         MovieTracking *tracking = &clip->tracking;
820         MovieTrackingTrack *track = NULL;   /* selected track */
821         MovieTrackingPlaneTrack *plane_track = NULL;   /* selected plane track */
822         MovieTrackingMarker *marker;
823         ListBase *tracksbase = BKE_tracking_get_active_tracks(tracking);
824         ListBase *plane_tracks_base = BKE_tracking_get_active_plane_tracks(tracking);
825         int action = RNA_enum_get(op->ptr, "action");
826         int framenr = ED_space_clip_get_clip_frame_number(sc);
827         int has_selection = FALSE;
828
829         if (action == SEL_TOGGLE) {
830                 action = SEL_SELECT;
831
832                 for (track = tracksbase->first; track; track = track->next) {
833                         if (TRACK_VIEW_SELECTED(sc, track)) {
834                                 marker = BKE_tracking_marker_get(track, framenr);
835
836                                 if (MARKER_VISIBLE(sc, track, marker)) {
837                                         action = SEL_DESELECT;
838                                         break;
839                                 }
840                         }
841                 }
842
843                 for (plane_track = plane_tracks_base->first;
844                      plane_track;
845                      plane_track = plane_track->next)
846                 {
847                         if (plane_track->flag & SELECT) {
848                                 action = SEL_DESELECT;
849                                 break;
850                         }
851                 }
852         }
853
854         for (track = tracksbase->first; track; track = track->next) {
855                 if ((track->flag & TRACK_HIDDEN) == 0) {
856                         marker = BKE_tracking_marker_get(track, framenr);
857
858                         if (MARKER_VISIBLE(sc, track, marker)) {
859                                 switch (action) {
860                                         case SEL_SELECT:
861                                                 track->flag |= SELECT;
862                                                 track->pat_flag |= SELECT;
863                                                 track->search_flag |= SELECT;
864                                                 break;
865                                         case SEL_DESELECT:
866                                                 track->flag &= ~SELECT;
867                                                 track->pat_flag &= ~SELECT;
868                                                 track->search_flag &= ~SELECT;
869                                                 break;
870                                         case SEL_INVERT:
871                                                 track->flag ^= SELECT;
872                                                 track->pat_flag ^= SELECT;
873                                                 track->search_flag ^= SELECT;
874                                                 break;
875                                 }
876                         }
877                 }
878
879                 if (TRACK_VIEW_SELECTED(sc, track))
880                         has_selection = TRUE;
881         }
882
883         for (plane_track = plane_tracks_base->first;
884              plane_track;
885              plane_track = plane_track->next)
886         {
887                 if ((plane_track->flag & PLANE_TRACK_HIDDEN) == 0) {
888                         switch (action) {
889                                 case SEL_SELECT:
890                                         plane_track->flag |= SELECT;
891                                         break;
892                                 case SEL_DESELECT:
893                                         plane_track->flag &= ~SELECT;
894                                         break;
895                                 case SEL_INVERT:
896                                         plane_track->flag ^= SELECT;
897                                         break;
898                         }
899                 }
900
901                 if (plane_track->flag & SELECT) {
902                         has_selection = TRUE;
903                 }
904         }
905
906         if (!has_selection)
907                 sc->flag &= ~SC_LOCK_SELECTION;
908
909         BKE_tracking_dopesheet_tag_update(tracking);
910
911         WM_event_add_notifier(C, NC_GEOM | ND_SELECT, NULL);
912
913         return OPERATOR_FINISHED;
914 }
915
916 void CLIP_OT_select_all(wmOperatorType *ot)
917 {
918         /* identifiers */
919         ot->name = "(De)select All";
920         ot->description = "Change selection of all tracking markers";
921         ot->idname = "CLIP_OT_select_all";
922
923         /* api callbacks */
924         ot->exec = select_all_exec;
925         ot->poll = ED_space_clip_tracking_poll;
926
927         /* flags */
928         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
929
930         WM_operator_properties_select_all(ot);
931 }
932
933 /********************** select grouped operator *********************/
934
935 static int select_groped_exec(bContext *C, wmOperator *op)
936 {
937         SpaceClip *sc = CTX_wm_space_clip(C);
938         MovieClip *clip = ED_space_clip_get_clip(sc);
939         MovieTrackingTrack *track;
940         MovieTrackingMarker *marker;
941         MovieTracking *tracking = &clip->tracking;
942         ListBase *tracksbase = BKE_tracking_get_active_tracks(tracking);
943         int group = RNA_enum_get(op->ptr, "group");
944         int framenr = ED_space_clip_get_clip_frame_number(sc);
945
946         track = tracksbase->first;
947         while (track) {
948                 int ok = FALSE;
949
950                 marker = BKE_tracking_marker_get(track, framenr);
951
952                 if (group == 0) { /* Keyframed */
953                         ok = marker->framenr == framenr && (marker->flag & MARKER_TRACKED) == 0;
954                 }
955                 else if (group == 1) { /* Estimated */
956                         ok = marker->framenr != framenr;
957                 }
958                 else if (group == 2) { /* tracked */
959                         ok = marker->framenr == framenr && (marker->flag & MARKER_TRACKED);
960                 }
961                 else if (group == 3) { /* locked */
962                         ok = track->flag & TRACK_LOCKED;
963                 }
964                 else if (group == 4) { /* disabled */
965                         ok = marker->flag & MARKER_DISABLED;
966                 }
967                 else if (group == 5) { /* color */
968                         MovieTrackingTrack *act_track = BKE_tracking_track_get_active(tracking);
969
970                         if (act_track) {
971                                 ok = (track->flag & TRACK_CUSTOMCOLOR) == (act_track->flag & TRACK_CUSTOMCOLOR);
972
973                                 if (ok && track->flag & TRACK_CUSTOMCOLOR)
974                                         ok = equals_v3v3(track->color, act_track->color);
975                         }
976                 }
977                 else if (group == 6) { /* failed */
978                         ok = (track->flag & TRACK_HAS_BUNDLE) == 0;
979                 }
980
981                 if (ok) {
982                         track->flag |= SELECT;
983                         if (sc->flag & SC_SHOW_MARKER_PATTERN)
984                                 track->pat_flag |= SELECT;
985                         if (sc->flag & SC_SHOW_MARKER_SEARCH)
986                                 track->search_flag |= SELECT;
987                 }
988
989                 track = track->next;
990         }
991
992         BKE_tracking_dopesheet_tag_update(tracking);
993
994         WM_event_add_notifier(C, NC_MOVIECLIP | ND_DISPLAY, clip);
995
996         return OPERATOR_FINISHED;
997 }
998
999 void CLIP_OT_select_grouped(wmOperatorType *ot)
1000 {
1001         static EnumPropertyItem select_group_items[] = {
1002                 {0, "KEYFRAMED", 0, "Keyframed tracks", "Select all keyframed tracks"},
1003                 {1, "ESTIMATED", 0, "Estimated tracks", "Select all estimated tracks"},
1004                 {2, "TRACKED", 0, "Tracked tracks", "Select all tracked tracks"},
1005                 {3, "LOCKED", 0, "Locked tracks", "Select all locked tracks"},
1006                 {4, "DISABLED", 0, "Disabled tracks", "Select all disabled tracks"},
1007                 {5, "COLOR", 0, "Tracks with same color", "Select all tracks with same color as active track"},
1008                 {6, "FAILED", 0, "Failed Tracks", "Select all tracks which failed to be reconstructed"},
1009                 {0, NULL, 0, NULL, NULL}
1010         };
1011
1012         /* identifiers */
1013         ot->name = "Select Grouped";
1014         ot->description = "Select all tracks from specified group";
1015         ot->idname = "CLIP_OT_select_grouped";
1016
1017         /* api callbacks */
1018         ot->exec = select_groped_exec;
1019         ot->poll = ED_space_clip_tracking_poll;
1020
1021         /* flags */
1022         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1023
1024         /* proeprties */
1025         RNA_def_enum(ot->srna, "group", select_group_items, TRACK_CLEAR_REMAINED, "Action", "Clear action to execute");
1026 }