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