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