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