Tracking: Update plane track solution after joining point tracks
[blender.git] / source / blender / editors / space_clip / tracking_ops.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_ops.c
29  *  \ingroup spclip
30  */
31
32 #include "MEM_guardedalloc.h"
33
34 #include "DNA_screen_types.h"
35 #include "DNA_space_types.h"
36
37 #include "BLI_utildefines.h"
38 #include "BLI_ghash.h"
39 #include "BLI_math.h"
40 #include "BLI_blenlib.h"
41
42 #include "BKE_context.h"
43 #include "BKE_movieclip.h"
44 #include "BKE_tracking.h"
45 #include "BKE_depsgraph.h"
46 #include "BKE_report.h"
47 #include "BKE_sound.h"
48
49 #include "WM_api.h"
50 #include "WM_types.h"
51
52 #include "ED_screen.h"
53 #include "ED_clip.h"
54
55 #include "RNA_access.h"
56 #include "RNA_define.h"
57
58 #include "BLT_translation.h"
59
60 #include "clip_intern.h"
61 #include "tracking_ops_intern.h"
62
63 /********************** add marker operator *********************/
64
65 static bool add_marker(const bContext *C, float x, float y)
66 {
67         SpaceClip *sc = CTX_wm_space_clip(C);
68         MovieClip *clip = ED_space_clip_get_clip(sc);
69         MovieTracking *tracking = &clip->tracking;
70         ListBase *tracksbase = BKE_tracking_get_active_tracks(tracking);
71         ListBase *plane_tracks_base = BKE_tracking_get_active_plane_tracks(tracking);
72         MovieTrackingTrack *track;
73         int width, height;
74         int framenr = ED_space_clip_get_clip_frame_number(sc);
75
76         ED_space_clip_get_size(sc, &width, &height);
77
78         if (width == 0 || height == 0) {
79                 return false;
80         }
81
82         track = BKE_tracking_track_add(tracking, tracksbase, x, y, framenr, width, height);
83
84         BKE_tracking_track_select(tracksbase, track, TRACK_AREA_ALL, 0);
85         BKE_tracking_plane_tracks_deselect_all(plane_tracks_base);
86
87         clip->tracking.act_track = track;
88         clip->tracking.act_plane_track = NULL;
89
90         return true;
91 }
92
93 static int add_marker_exec(bContext *C, wmOperator *op)
94 {
95         SpaceClip *sc = CTX_wm_space_clip(C);
96         MovieClip *clip = ED_space_clip_get_clip(sc);
97         float pos[2];
98
99         RNA_float_get_array(op->ptr, "location", pos);
100
101         if (!add_marker(C, pos[0], pos[1])) {
102                 return OPERATOR_CANCELLED;
103         }
104
105         /* Reset offset from locked position, so frame jumping wouldn't be so
106          * confusing.
107          */
108         sc->xlockof = 0;
109         sc->ylockof = 0;
110
111         WM_event_add_notifier(C, NC_MOVIECLIP | NA_EDITED, clip);
112
113         return OPERATOR_FINISHED;
114 }
115
116 static int add_marker_invoke(bContext *C, wmOperator *op, const wmEvent *event)
117 {
118         SpaceClip *sc = CTX_wm_space_clip(C);
119         ARegion *ar = CTX_wm_region(C);
120
121         if (!RNA_struct_property_is_set(op->ptr, "location")) {
122                 /* If location is not set, use mouse positio nas default. */
123                 float co[2];
124                 ED_clip_mouse_pos(sc, ar, event->mval, co);
125                 RNA_float_set_array(op->ptr, "location", co);
126         }
127
128         return add_marker_exec(C, op);
129 }
130
131 void CLIP_OT_add_marker(wmOperatorType *ot)
132 {
133         /* identifiers */
134         ot->name = "Add Marker";
135         ot->idname = "CLIP_OT_add_marker";
136         ot->description = "Place new marker at specified location";
137
138         /* api callbacks */
139         ot->invoke = add_marker_invoke;
140         ot->exec = add_marker_exec;
141         ot->poll = ED_space_clip_tracking_poll;
142
143         /* flags */
144         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
145
146         /* properties */
147         RNA_def_float_vector(ot->srna, "location", 2, NULL, -FLT_MAX, FLT_MAX,
148                              "Location", "Location of marker on frame", -1.0f, 1.0f);
149 }
150
151 /********************** add marker operator *********************/
152
153 static int add_marker_at_click_invoke(bContext *C,
154                                       wmOperator *op,
155                                       const wmEvent *UNUSED(event))
156 {
157         ED_area_headerprint(
158                 CTX_wm_area(C),
159                 IFACE_("Use LMB click to define location where place the marker"));
160
161         /* Add modal handler for ESC. */
162         WM_event_add_modal_handler(C, op);
163
164         return OPERATOR_RUNNING_MODAL;
165 }
166
167 static int add_marker_at_click_modal(bContext *C,
168                                      wmOperator *UNUSED(op),
169                                      const wmEvent *event)
170 {
171         switch (event->type) {
172                 case MOUSEMOVE:
173                         return OPERATOR_RUNNING_MODAL;
174
175                 case LEFTMOUSE:
176                 {
177                         SpaceClip *sc = CTX_wm_space_clip(C);
178                         MovieClip *clip = ED_space_clip_get_clip(sc);
179                         ARegion *ar = CTX_wm_region(C);
180                         float pos[2];
181
182                         ED_area_headerprint(CTX_wm_area(C), NULL);
183
184                         ED_clip_point_stable_pos(sc, ar,
185                                                  event->x - ar->winrct.xmin,
186                                                  event->y - ar->winrct.ymin,
187                                                  &pos[0], &pos[1]);
188
189                         if (!add_marker(C, pos[0], pos[1])) {
190                                 return OPERATOR_CANCELLED;
191                         }
192
193                         WM_event_add_notifier(C, NC_MOVIECLIP | NA_EDITED, clip);
194                         return OPERATOR_FINISHED;
195                 }
196
197                 case ESCKEY:
198                         ED_area_headerprint(CTX_wm_area(C), NULL);
199                         return OPERATOR_CANCELLED;
200         }
201
202         return OPERATOR_PASS_THROUGH;
203 }
204
205 void CLIP_OT_add_marker_at_click(wmOperatorType *ot)
206 {
207         /* identifiers */
208         ot->name = "Add Marker at Click";
209         ot->idname = "CLIP_OT_add_marker_at_click";
210         ot->description = "Place new marker at the desired (clicked) position";
211
212         /* api callbacks */
213         ot->invoke = add_marker_at_click_invoke;
214         ot->poll = ED_space_clip_tracking_poll;
215         ot->modal = add_marker_at_click_modal;
216
217         /* flags */
218         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_BLOCKING;
219 }
220
221 /********************** delete track operator *********************/
222
223 static int delete_track_exec(bContext *C, wmOperator *UNUSED(op))
224 {
225         SpaceClip *sc = CTX_wm_space_clip(C);
226         MovieClip *clip = ED_space_clip_get_clip(sc);
227         MovieTracking *tracking = &clip->tracking;
228         bool changed = false;
229
230         /* Delete selected plane tracks. */
231         ListBase *plane_tracks_base = BKE_tracking_get_active_plane_tracks(tracking);
232         for (MovieTrackingPlaneTrack *plane_track = plane_tracks_base->first,
233                                      *next_plane_track;
234              plane_track != NULL;
235              plane_track = next_plane_track)
236         {
237                 next_plane_track = plane_track->next;
238
239                 if (PLANE_TRACK_VIEW_SELECTED(plane_track)) {
240                         BKE_tracking_plane_track_free(plane_track);
241                         BLI_freelinkN(plane_tracks_base, plane_track);
242                         changed = true;
243                 }
244         }
245
246         /* Remove selected point tracks (they'll also be removed from planes which
247          * uses them).
248          */
249         ListBase *tracksbase = BKE_tracking_get_active_tracks(tracking);
250         for (MovieTrackingTrack *track = tracksbase->first, *next_track;
251              track != NULL;
252              track = next_track)
253         {
254                 next_track = track->next;
255                 if (TRACK_VIEW_SELECTED(sc, track)) {
256                         clip_delete_track(C, clip, track);
257                         changed = true;
258                 }
259         }
260
261         /* Nothing selected now, unlock view so it can be scrolled nice again. */
262         sc->flag &= ~SC_LOCK_SELECTION;
263
264         if (changed) {
265                 WM_event_add_notifier(C, NC_MOVIECLIP | NA_EDITED, clip);
266         }
267
268         return OPERATOR_FINISHED;
269 }
270
271 void CLIP_OT_delete_track(wmOperatorType *ot)
272 {
273         /* identifiers */
274         ot->name = "Delete Track";
275         ot->idname = "CLIP_OT_delete_track";
276         ot->description = "Delete selected tracks";
277
278         /* api callbacks */
279         ot->invoke = WM_operator_confirm;
280         ot->exec = delete_track_exec;
281         ot->poll = ED_space_clip_tracking_poll;
282
283         /* flags */
284         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
285 }
286
287 /********************** delete marker operator *********************/
288
289 static int delete_marker_exec(bContext *C, wmOperator *UNUSED(op))
290 {
291         SpaceClip *sc = CTX_wm_space_clip(C);
292         MovieClip *clip = ED_space_clip_get_clip(sc);
293         MovieTracking *tracking = &clip->tracking;
294         const int framenr = ED_space_clip_get_clip_frame_number(sc);
295         bool has_selection = false;
296         bool changed = false;
297
298         ListBase *tracksbase = BKE_tracking_get_active_tracks(tracking);
299         for (MovieTrackingTrack *track = tracksbase->first, *next_track;
300              track != NULL;
301              track = next_track)
302         {
303                 next_track = track->next;
304                 if (TRACK_VIEW_SELECTED(sc, track)) {
305                         MovieTrackingMarker *marker = BKE_tracking_marker_get_exact(track, framenr);
306                         if (marker != NULL) {
307                                 has_selection |= track->markersnr > 1;
308                                 clip_delete_marker(C, clip, track, marker);
309                                 changed = true;
310                         }
311                 }
312         }
313
314         ListBase *plane_tracks_base = BKE_tracking_get_active_plane_tracks(tracking);
315         for (MovieTrackingPlaneTrack *plane_track = plane_tracks_base->first,
316                                      *plane_track_next;
317              plane_track != NULL;
318              plane_track = plane_track_next)
319         {
320                 plane_track_next = plane_track->next;
321                 if (PLANE_TRACK_VIEW_SELECTED(plane_track)) {
322                         MovieTrackingPlaneMarker *plane_marker =
323                                 BKE_tracking_plane_marker_get_exact(plane_track, framenr);
324                         if (plane_marker != NULL) {
325                                 if (plane_track->markersnr == 1) {
326                                         BKE_tracking_plane_track_free(plane_track);
327                                         BLI_freelinkN(plane_tracks_base, plane_track);
328                                 }
329                                 else {
330                                         BKE_tracking_plane_marker_delete(plane_track, framenr);
331                                 }
332                                 changed = true;
333                         }
334                 }
335         }
336
337         if (!has_selection) {
338                 /* Nothing selected now, unlock view so it can be scrolled nice again. */
339                 sc->flag &= ~SC_LOCK_SELECTION;
340         }
341
342         if (!changed) {
343                 return OPERATOR_CANCELLED;
344         }
345
346         return OPERATOR_FINISHED;
347 }
348
349 void CLIP_OT_delete_marker(wmOperatorType *ot)
350 {
351         /* identifiers */
352         ot->name = "Delete Marker";
353         ot->idname = "CLIP_OT_delete_marker";
354         ot->description = "Delete marker for current frame from selected tracks";
355
356         /* api callbacks */
357         ot->invoke = WM_operator_confirm;
358         ot->exec = delete_marker_exec;
359         ot->poll = ED_space_clip_tracking_poll;
360
361         /* flags */
362         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
363 }
364
365 /********************** slide marker operator *********************/
366
367 enum {
368         SLIDE_ACTION_POS = 0,
369         SLIDE_ACTION_SIZE,
370         SLIDE_ACTION_OFFSET,
371         SLIDE_ACTION_TILT_SIZE,
372 };
373
374 typedef struct {
375         short area, action;
376         MovieTrackingTrack *track;
377         MovieTrackingMarker *marker;
378
379         int mval[2];
380         int width, height;
381         float *min, *max, *pos, *offset, (*corners)[2];
382         float spos[2];
383
384         bool lock, accurate;
385
386         /* Data to restore on cancel. */
387         float old_search_min[2], old_search_max[2], old_pos[2], old_offset[2];
388         float old_corners[4][2];
389         float (*old_markers)[2];
390 } SlideMarkerData;
391
392 static void slide_marker_tilt_slider(const MovieTrackingMarker *marker,
393                                      float r_slider[2])
394 {
395         add_v2_v2v2(r_slider, marker->pattern_corners[1], marker->pattern_corners[2]);
396         add_v2_v2(r_slider, marker->pos);
397 }
398
399 static SlideMarkerData *create_slide_marker_data(SpaceClip *sc,
400                                                  MovieTrackingTrack *track,
401                                                  MovieTrackingMarker *marker,
402                                                  const wmEvent *event,
403                                                  int area,
404                                                  int corner,
405                                                  int action,
406                                                  int width,
407                                                  int height)
408 {
409         SlideMarkerData *data = MEM_callocN(sizeof(SlideMarkerData), "slide marker data");
410         int framenr = ED_space_clip_get_clip_frame_number(sc);
411
412         marker = BKE_tracking_marker_ensure(track, framenr);
413
414         data->area = area;
415         data->action = action;
416         data->track = track;
417         data->marker = marker;
418
419         if (area == TRACK_AREA_POINT) {
420                 data->pos = marker->pos;
421                 data->offset = track->offset;
422         }
423         else if (area == TRACK_AREA_PAT) {
424                 if (action == SLIDE_ACTION_SIZE) {
425                         data->corners = marker->pattern_corners;
426                 }
427                 else if (action == SLIDE_ACTION_OFFSET) {
428                         data->pos = marker->pos;
429                         data->offset = track->offset;
430                         data->old_markers = MEM_callocN(
431                                 sizeof(*data->old_markers) * track->markersnr,
432                                 "slide marekrs");
433                         for (int a = 0; a < track->markersnr; a++) {
434                                 copy_v2_v2(data->old_markers[a], track->markers[a].pos);
435                         }
436                 }
437                 else if (action == SLIDE_ACTION_POS) {
438                         data->corners = marker->pattern_corners;
439                         data->pos = marker->pattern_corners[corner];
440                         copy_v2_v2(data->spos, data->pos);
441                 }
442                 else if (action == SLIDE_ACTION_TILT_SIZE) {
443                         data->corners = marker->pattern_corners;
444                         slide_marker_tilt_slider(marker, data->spos);
445                 }
446         }
447         else if (area == TRACK_AREA_SEARCH) {
448                 data->min = marker->search_min;
449                 data->max = marker->search_max;
450         }
451
452         data->mval[0] = event->mval[0];
453         data->mval[1] = event->mval[1];
454
455         data->width = width;
456         data->height = height;
457
458         if (action == SLIDE_ACTION_SIZE) {
459                 data->lock = true;
460         }
461
462         /* Backup marker's settings. */
463         memcpy(data->old_corners, marker->pattern_corners, sizeof(data->old_corners));
464         copy_v2_v2(data->old_search_min, marker->search_min);
465         copy_v2_v2(data->old_search_max, marker->search_max);
466         copy_v2_v2(data->old_pos, marker->pos);
467         copy_v2_v2(data->old_offset, track->offset);
468
469         return data;
470 }
471
472 static float mouse_to_slide_zone_distance_squared(
473         const float co[2],
474         const float slide_zone[2],
475         int width,
476         int height)
477 {
478         float pixel_co[2] = {co[0] * width, co[1] * height},
479               pixel_slide_zone[2] = {slide_zone[0] * width, slide_zone[1] * height};
480         return SQUARE(pixel_co[0] - pixel_slide_zone[0]) +
481                SQUARE(pixel_co[1] - pixel_slide_zone[1]);
482 }
483
484 static float mouse_to_search_corner_distance_squared(
485         const MovieTrackingMarker *marker,
486         const float co[2],
487         int corner,
488         int width,
489         int height)
490 {
491         float side_zone[2];
492         if (corner == 0) {
493                 side_zone[0] = marker->pos[0] + marker->search_max[0];
494                 side_zone[1] = marker->pos[1] + marker->search_min[1];
495         }
496         else {
497                 side_zone[0] = marker->pos[0] + marker->search_min[0];
498                 side_zone[1] = marker->pos[1] + marker->search_max[1];
499         }
500         return mouse_to_slide_zone_distance_squared(co,
501                                                     side_zone,
502                                                     width,
503                                                     height);
504 }
505
506 static float mouse_to_closest_pattern_corner_distance_squared(
507         const MovieTrackingMarker *marker,
508         const float co[2],
509         int width,
510         int height,
511         int *r_corner)
512 {
513         float min_distance_squared = FLT_MAX;
514         for (int i = 0; i < 4; i++) {
515                 float corner_co[2];
516                 add_v2_v2v2(corner_co, marker->pattern_corners[i], marker->pos);
517                 float distance_squared = mouse_to_slide_zone_distance_squared(co,
518                                                                               corner_co,
519                                                                               width,
520                                                                               height);
521                 if (distance_squared < min_distance_squared) {
522                         min_distance_squared = distance_squared;
523                         *r_corner = i;
524                 }
525         }
526         return min_distance_squared;
527 }
528
529 static float mouse_to_offset_distance_squared(const MovieTrackingTrack *track,
530                                               const MovieTrackingMarker *marker,
531                                               const float co[2],
532                                               int width,
533                                               int height)
534 {
535         float pos[2];
536         add_v2_v2v2(pos, marker->pos, track->offset);
537         return mouse_to_slide_zone_distance_squared(co,
538                                                     pos,
539                                                     width,
540                                                     height);
541 }
542
543 static int mouse_to_tilt_distance_squared(const MovieTrackingMarker *marker,
544                                           const float co[2],
545                                           int width,
546                                           int height)
547 {
548         float slider[2];
549         slide_marker_tilt_slider(marker, slider);
550         return mouse_to_slide_zone_distance_squared(co,
551                                                     slider,
552                                                     width,
553                                                     height);
554 }
555
556 static bool slide_check_corners(float (*corners)[2])
557 {
558         int i, next, prev;
559         float cross = 0.0f;
560         float p[2] = {0.0f, 0.0f};
561
562         if (!isect_point_quad_v2(p, corners[0], corners[1], corners[2], corners[3]))
563                 return false;
564
565         for (i = 0; i < 4; i++) {
566                 float v1[2], v2[2], cur_cross;
567
568                 next = (i + 1) % 4;
569                 prev = (4 + i - 1) % 4;
570
571                 sub_v2_v2v2(v1, corners[i], corners[prev]);
572                 sub_v2_v2v2(v2, corners[next], corners[i]);
573
574                 cur_cross = cross_v2v2(v1, v2);
575
576                 if (fabsf(cur_cross) > FLT_EPSILON) {
577                         if (cross == 0.0f) {
578                                 cross = cur_cross;
579                         }
580                         else if (cross * cur_cross < 0.0f) {
581                                 return false;
582                         }
583                 }
584         }
585
586         return true;
587 }
588
589 MovieTrackingTrack *tracking_marker_check_slide(bContext *C,
590                                                 const wmEvent *event,
591                                                 int *area_r,
592                                                 int *action_r,
593                                                 int *corner_r)
594 {
595         const float distance_clip_squared = 12.0f * 12.0f;
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         MovieTrackingTrack *track;
601         int width, height;
602         float co[2];
603         ListBase *tracksbase = BKE_tracking_get_active_tracks(&clip->tracking);
604         int framenr = ED_space_clip_get_clip_frame_number(sc);
605         float global_min_distance_squared = FLT_MAX;
606
607         /* Sliding zone designator which is the closest to the mouse
608          * across all the tracks.
609          */
610         int min_action = -1, min_area = 0, min_corner = -1;
611         MovieTrackingTrack *min_track = NULL;
612
613         ED_space_clip_get_size(sc, &width, &height);
614
615         if (width == 0 || height == 0)
616                 return NULL;
617
618         ED_clip_mouse_pos(sc, ar, event->mval, co);
619
620         track = tracksbase->first;
621         while (track) {
622                 if (TRACK_VIEW_SELECTED(sc, track) && (track->flag & TRACK_LOCKED) == 0) {
623                         const MovieTrackingMarker *marker = BKE_tracking_marker_get(track,
624                                                                                     framenr);
625                         /* Sliding zone designator which is the closest to the mouse for
626                          * the current tracks.
627                          */
628                         float min_distance_squared = FLT_MAX;
629                         int action = -1, area = 0, corner = -1;
630
631                         if ((marker->flag & MARKER_DISABLED) == 0) {
632                                 float distance_squared;
633
634                                 /* We start checking with whether the mouse is close enough
635                                  * to the pattern offset area.
636                                  */
637                                 distance_squared = mouse_to_offset_distance_squared(track,
638                                                                                     marker,
639                                                                                     co,
640                                                                                     width,
641                                                                                     height);
642                                 area = TRACK_AREA_POINT;
643                                 action = SLIDE_ACTION_POS;
644
645                                 /* NOTE: All checks here are assuming there's no maximum distance
646                                  * limit, so checks are quite simple here.
647                                  * Actual distance clipping happens later once all the sliding
648                                  * zones are checked.
649                                  */
650                                 min_distance_squared = distance_squared;
651
652                                 /* If search area is visible, check how close to it's sliding
653                                  * zones mouse is.
654                                  */
655                                 if (sc->flag & SC_SHOW_MARKER_SEARCH) {
656                                         distance_squared = mouse_to_search_corner_distance_squared(
657                                                 marker,
658                                                 co,
659                                                 1,
660                                                 width,
661                                                 height);
662                                         if (distance_squared < min_distance_squared) {
663                                                 area = TRACK_AREA_SEARCH;
664                                                 action = SLIDE_ACTION_OFFSET;
665                                                 min_distance_squared = distance_squared;
666                                         }
667
668                                         distance_squared = mouse_to_search_corner_distance_squared(
669                                                 marker,
670                                                 co,
671                                                 0,
672                                                 width,
673                                                 height);
674                                         if (distance_squared < min_distance_squared) {
675                                                 area = TRACK_AREA_SEARCH;
676                                                 action = SLIDE_ACTION_SIZE;
677                                                 min_distance_squared = distance_squared;
678                                         }
679                                 }
680
681                                 /* If pattern area is visible, check which corner is closest to
682                                  * the mouse.
683                                  */
684                                 if (sc->flag & SC_SHOW_MARKER_PATTERN) {
685                                         int current_corner;
686                                         distance_squared =
687                                                 mouse_to_closest_pattern_corner_distance_squared(
688                                                         marker,
689                                                         co,
690                                                         width,
691                                                         height,
692                                                         &current_corner);
693                                         if (distance_squared < min_distance_squared) {
694                                                 area = TRACK_AREA_PAT;
695                                                 action = SLIDE_ACTION_POS;
696                                                 corner = current_corner;
697                                                 min_distance_squared = distance_squared;
698                                         }
699
700                                         /* Here we also check whether the mouse is actually closer to
701                                          * the widget which controls scale and tilt.
702                                          */
703                                         distance_squared = mouse_to_tilt_distance_squared(marker,
704                                                                                           co,
705                                                                                           width,
706                                                                                           height);
707                                         if (distance_squared < min_distance_squared) {
708                                                 area = TRACK_AREA_PAT;
709                                                 action = SLIDE_ACTION_TILT_SIZE;
710                                                 min_distance_squared = distance_squared;
711                                         }
712                                 }
713
714                                 if (min_distance_squared < global_min_distance_squared) {
715                                         min_area = area;
716                                         min_action = action;
717                                         min_corner = corner;
718                                         min_track = track;
719                                         global_min_distance_squared = min_distance_squared;
720                                 }
721                         }
722                 }
723
724                 track = track->next;
725         }
726
727         if (global_min_distance_squared < distance_clip_squared / sc->zoom) {
728                 if (area_r) {
729                         *area_r = min_area;
730                 }
731                 if (action_r) {
732                         *action_r = min_action;
733                 }
734                 if (corner_r) {
735                         *corner_r = min_corner;
736                 }
737                 return min_track;
738         }
739         return NULL;
740 }
741
742 static void *slide_marker_customdata(bContext *C, const wmEvent *event)
743 {
744         SpaceClip *sc = CTX_wm_space_clip(C);
745         ARegion *ar = CTX_wm_region(C);
746
747         MovieTrackingTrack *track;
748         int width, height;
749         float co[2];
750         void *customdata = NULL;
751         int framenr = ED_space_clip_get_clip_frame_number(sc);
752         int area, action, corner;
753
754         ED_space_clip_get_size(sc, &width, &height);
755
756         if (width == 0 || height == 0) {
757                 return NULL;
758         }
759
760         ED_clip_mouse_pos(sc, ar, event->mval, co);
761
762         track = tracking_marker_check_slide(C, event, &area, &action, &corner);
763         if (track != NULL) {
764                 MovieTrackingMarker *marker = BKE_tracking_marker_get(track, framenr);
765                 customdata = create_slide_marker_data(sc,
766                                                       track,
767                                                       marker,
768                                                       event,
769                                                       area,
770                                                       corner,
771                                                       action,
772                                                       width,
773                                                       height);
774         }
775
776         return customdata;
777 }
778
779 static int slide_marker_invoke(bContext *C, wmOperator *op, const wmEvent *event)
780 {
781         SlideMarkerData *slidedata = slide_marker_customdata(C, event);
782         if (slidedata != NULL) {
783                 SpaceClip *sc = CTX_wm_space_clip(C);
784                 MovieClip *clip = ED_space_clip_get_clip(sc);
785                 MovieTracking *tracking = &clip->tracking;
786
787                 tracking->act_track = slidedata->track;
788                 tracking->act_plane_track = NULL;
789
790                 op->customdata = slidedata;
791
792                 clip_tracking_hide_cursor(C);
793                 WM_event_add_modal_handler(C, op);
794
795                 WM_event_add_notifier(C, NC_GEOM | ND_SELECT, NULL);
796
797                 return OPERATOR_RUNNING_MODAL;
798         }
799
800         return OPERATOR_PASS_THROUGH;
801 }
802
803 static void cancel_mouse_slide(SlideMarkerData *data)
804 {
805         MovieTrackingTrack *track = data->track;
806         MovieTrackingMarker *marker = data->marker;
807
808         memcpy(marker->pattern_corners,
809                data->old_corners,
810                sizeof(marker->pattern_corners));
811         copy_v2_v2(marker->search_min, data->old_search_min);
812         copy_v2_v2(marker->search_max, data->old_search_max);
813         copy_v2_v2(marker->pos, data->old_pos);
814         copy_v2_v2(track->offset, data->old_offset);
815
816         if (data->old_markers != NULL) {
817                 for (int a = 0; a < data->track->markersnr; a++) {
818                         copy_v2_v2(data->track->markers[a].pos, data->old_markers[a]);
819                 }
820         }
821 }
822
823 static void apply_mouse_slide(bContext *C, SlideMarkerData *data)
824 {
825         if (data->area == TRACK_AREA_POINT) {
826                 SpaceClip *sc = CTX_wm_space_clip(C);
827                 MovieClip *clip = ED_space_clip_get_clip(sc);
828                 MovieTracking *tracking = &clip->tracking;
829                 ListBase *plane_tracks_base = BKE_tracking_get_active_plane_tracks(tracking);
830                 int framenr = ED_space_clip_get_clip_frame_number(sc);
831
832                 for (MovieTrackingPlaneTrack *plane_track = plane_tracks_base->first;
833                      plane_track != NULL;
834                      plane_track = plane_track->next)
835                 {
836                         if ((plane_track->flag & PLANE_TRACK_AUTOKEY) == 0) {
837                                 if (BKE_tracking_plane_track_has_point_track(plane_track,
838                                                                              data->track))
839                                 {
840                                         BKE_tracking_track_plane_from_existing_motion(plane_track,
841                                                                                       framenr);
842                                 }
843                         }
844                 }
845         }
846 }
847
848 static void free_slide_data(SlideMarkerData *data)
849 {
850         if (data->old_markers != NULL) {
851                 MEM_freeN(data->old_markers);
852         }
853         MEM_freeN(data);
854 }
855
856 static int slide_marker_modal(bContext *C, wmOperator *op, const wmEvent *event)
857 {
858         SpaceClip *sc = CTX_wm_space_clip(C);
859         ARegion *ar = CTX_wm_region(C);
860
861         SlideMarkerData *data = (SlideMarkerData *)op->customdata;
862         float dx, dy, mdelta[2];
863
864         switch (event->type) {
865                 case LEFTCTRLKEY:
866                 case RIGHTCTRLKEY:
867                 case LEFTSHIFTKEY:
868                 case RIGHTSHIFTKEY:
869                         if (data->action == SLIDE_ACTION_SIZE) {
870                                 if (ELEM(event->type, LEFTCTRLKEY, RIGHTCTRLKEY)) {
871                                         data->lock = event->val == KM_RELEASE;
872                                 }
873                         }
874
875                         if (ELEM(event->type, LEFTSHIFTKEY, RIGHTSHIFTKEY)) {
876                                 data->accurate = event->val == KM_PRESS;
877                         }
878
879                         /* fall-through */
880                 case MOUSEMOVE:
881                         mdelta[0] = event->mval[0] - data->mval[0];
882                         mdelta[1] = event->mval[1] - data->mval[1];
883
884                         dx = mdelta[0] / data->width / sc->zoom;
885
886                         if (data->lock) {
887                                 dy = -dx / data->height * data->width;
888                         }
889                         else {
890                                 dy = mdelta[1] / data->height / sc->zoom;
891                         }
892
893                         if (data->accurate) {
894                                 dx /= 5.0f;
895                                 dy /= 5.0f;
896                         }
897
898                         if (data->area == TRACK_AREA_POINT) {
899                                 if (data->action == SLIDE_ACTION_OFFSET) {
900                                         data->offset[0] = data->old_offset[0] + dx;
901                                         data->offset[1] = data->old_offset[1] + dy;
902                                 }
903                                 else {
904                                         data->pos[0] = data->old_pos[0] + dx;
905                                         data->pos[1] = data->old_pos[1] + dy;
906                                 }
907
908                                 WM_event_add_notifier(C, NC_OBJECT | ND_TRANSFORM, NULL);
909                                 DAG_id_tag_update(&sc->clip->id, 0);
910                         }
911                         else if (data->area == TRACK_AREA_PAT) {
912                                 if (data->action == SLIDE_ACTION_SIZE) {
913                                         float start[2], end[2];
914                                         float scale;
915
916                                         ED_clip_point_stable_pos(sc,
917                                                                  ar,
918                                                                  data->mval[0],
919                                                                  data->mval[1],
920                                                                  &start[0],
921                                                                  &start[1]);
922
923                                         sub_v2_v2(start, data->old_pos);
924
925                                         if (len_squared_v2(start) != 0.0f) {
926                                                 float mval[2];
927
928                                                 if (data->accurate) {
929                                                         mval[0] = data->mval[0] + (event->mval[0] - data->mval[0]) / 5.0f;
930                                                         mval[1] = data->mval[1] + (event->mval[1] - data->mval[1]) / 5.0f;
931                                                 }
932                                                 else {
933                                                         mval[0] = event->mval[0];
934                                                         mval[1] = event->mval[1];
935                                                 }
936
937                                                 ED_clip_point_stable_pos(sc,
938                                                                          ar,
939                                                                          mval[0],
940                                                                          mval[1],
941                                                                          &end[0],
942                                                                          &end[1]);
943
944                                                 sub_v2_v2(end, data->old_pos);
945                                                 scale = len_v2(end) / len_v2(start);
946
947                                                 if (scale > 0.0f) {
948                                                         for (int a = 0; a < 4; a++) {
949                                                                 mul_v2_v2fl(data->corners[a],
950                                                                             data->old_corners[a],
951                                                                             scale);
952                                                         }
953                                                 }
954                                         }
955
956                                         BKE_tracking_marker_clamp(data->marker, CLAMP_PAT_DIM);
957                                 }
958                                 else if (data->action == SLIDE_ACTION_OFFSET) {
959                                         float d[2] = {dx, dy};
960                                         for (int a = 0; a < data->track->markersnr; a++) {
961                                                 add_v2_v2v2(data->track->markers[a].pos,
962                                                             data->old_markers[a],
963                                                             d);
964                                         }
965                                         sub_v2_v2v2(data->offset, data->old_offset, d);
966                                 }
967                                 else if (data->action == SLIDE_ACTION_POS) {
968                                         float spos[2];
969
970                                         copy_v2_v2(spos, data->pos);
971
972                                         data->pos[0] = data->spos[0] + dx;
973                                         data->pos[1] = data->spos[1] + dy;
974
975                                         if (!slide_check_corners(data->corners)) {
976                                                 copy_v2_v2(data->pos, spos);
977                                         }
978
979                                         /* Currently only patterns are allowed to have such
980                                          * combination of event and data.
981                                          */
982                                         BKE_tracking_marker_clamp(data->marker, CLAMP_PAT_DIM);
983                                 }
984                                 else if (data->action == SLIDE_ACTION_TILT_SIZE) {
985                                         float start[2], end[2];
986                                         float scale = 1.0f, angle = 0.0f;
987                                         float mval[2];
988
989                                         if (data->accurate) {
990                                                 mval[0] = data->mval[0] + (event->mval[0] - data->mval[0]) / 5.0f;
991                                                 mval[1] = data->mval[1] + (event->mval[1] - data->mval[1]) / 5.0f;
992                                         }
993                                         else {
994                                                 mval[0] = event->mval[0];
995                                                 mval[1] = event->mval[1];
996                                         }
997
998                                         sub_v2_v2v2(start, data->spos, data->old_pos);
999
1000                                         ED_clip_point_stable_pos(sc, ar, mval[0], mval[1], &end[0], &end[1]);
1001                                         sub_v2_v2(end, data->old_pos);
1002
1003                                         if (len_squared_v2(start) != 0.0f) {
1004                                                 scale = len_v2(end) / len_v2(start);
1005
1006                                                 if (scale < 0.0f) {
1007                                                         scale = 0.0;
1008                                                 }
1009                                         }
1010
1011                                         angle = -angle_signed_v2v2(start, end);
1012
1013                                         for (int a = 0; a < 4; a++) {
1014                                                 float vec[2];
1015
1016                                                 mul_v2_v2fl(data->corners[a], data->old_corners[a], scale);
1017
1018                                                 copy_v2_v2(vec, data->corners[a]);
1019                                                 vec[0] *= data->width;
1020                                                 vec[1] *= data->height;
1021
1022                                                 data->corners[a][0] = (vec[0] * cosf(angle) - vec[1] * sinf(angle)) / data->width;
1023                                                 data->corners[a][1] = (vec[1] * cosf(angle) + vec[0] * sinf(angle)) / data->height;
1024                                         }
1025
1026                                         BKE_tracking_marker_clamp(data->marker, CLAMP_PAT_DIM);
1027
1028                                 }
1029                         }
1030                         else if (data->area == TRACK_AREA_SEARCH) {
1031                                 if (data->action == SLIDE_ACTION_SIZE) {
1032                                         data->min[0] = data->old_search_min[0] - dx;
1033                                         data->max[0] = data->old_search_max[0] + dx;
1034
1035                                         data->min[1] = data->old_search_min[1] + dy;
1036                                         data->max[1] = data->old_search_max[1] - dy;
1037
1038                                         BKE_tracking_marker_clamp(data->marker, CLAMP_SEARCH_DIM);
1039                                 }
1040                                 else if (data->area == TRACK_AREA_SEARCH) {
1041                                         float d[2] = {dx, dy};
1042                                         add_v2_v2v2(data->min, data->old_search_min, d);
1043                                         add_v2_v2v2(data->max, data->old_search_max, d);
1044                                 }
1045
1046                                 BKE_tracking_marker_clamp(data->marker, CLAMP_SEARCH_POS);
1047                         }
1048
1049                         data->marker->flag &= ~MARKER_TRACKED;
1050
1051                         WM_event_add_notifier(C, NC_MOVIECLIP | NA_EDITED, NULL);
1052
1053                         break;
1054
1055                 case LEFTMOUSE:
1056                         if (event->val == KM_RELEASE) {
1057                                 apply_mouse_slide(C, op->customdata);
1058                                 free_slide_data(op->customdata);
1059
1060                                 clip_tracking_show_cursor(C);
1061
1062                                 return OPERATOR_FINISHED;
1063                         }
1064
1065                         break;
1066
1067                 case ESCKEY:
1068                         cancel_mouse_slide(op->customdata);
1069
1070                         free_slide_data(op->customdata);
1071
1072                         clip_tracking_show_cursor(C);
1073
1074                         WM_event_add_notifier(C, NC_MOVIECLIP | NA_EDITED, NULL);
1075
1076                         return OPERATOR_CANCELLED;
1077         }
1078
1079         return OPERATOR_RUNNING_MODAL;
1080 }
1081
1082 void CLIP_OT_slide_marker(wmOperatorType *ot)
1083 {
1084         /* identifiers */
1085         ot->name = "Slide Marker";
1086         ot->description = "Slide marker areas";
1087         ot->idname = "CLIP_OT_slide_marker";
1088
1089         /* api callbacks */
1090         ot->poll = ED_space_clip_tracking_poll;
1091         ot->invoke = slide_marker_invoke;
1092         ot->modal = slide_marker_modal;
1093
1094         /* flags */
1095         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_GRAB_CURSOR | OPTYPE_BLOCKING;
1096
1097         /* properties */
1098         RNA_def_float_vector(ot->srna, "offset", 2, NULL, -FLT_MAX, FLT_MAX,
1099                              "Offset",
1100                              "Offset in floating point units, 1.0 is the width and height of the image",
1101                              -FLT_MAX, FLT_MAX);
1102 }
1103
1104 /********************** clear track operator *********************/
1105
1106 static int clear_track_path_exec(bContext *C, wmOperator *op)
1107 {
1108         SpaceClip *sc = CTX_wm_space_clip(C);
1109         MovieClip *clip = ED_space_clip_get_clip(sc);
1110         MovieTracking *tracking = &clip->tracking;
1111         ListBase *tracksbase = BKE_tracking_get_active_tracks(tracking);
1112         int action = RNA_enum_get(op->ptr, "action");
1113         const bool clear_active = RNA_boolean_get(op->ptr, "clear_active");
1114         int framenr = ED_space_clip_get_clip_frame_number(sc);
1115
1116         if (clear_active) {
1117                 MovieTrackingTrack *track = BKE_tracking_track_get_active(tracking);
1118                 if (track != NULL) {
1119                         BKE_tracking_track_path_clear(track, framenr, action);
1120                 }
1121         }
1122         else {
1123                 for (MovieTrackingTrack *track = tracksbase->first;
1124                      track != NULL;
1125                      track = track->next)
1126                 {
1127                         if (TRACK_VIEW_SELECTED(sc, track)) {
1128                                 BKE_tracking_track_path_clear(track, framenr, action);
1129                         }
1130                 }
1131         }
1132
1133         BKE_tracking_dopesheet_tag_update(tracking);
1134         WM_event_add_notifier(C, NC_MOVIECLIP | NA_EVALUATED, clip);
1135
1136         return OPERATOR_FINISHED;
1137 }
1138
1139 void CLIP_OT_clear_track_path(wmOperatorType *ot)
1140 {
1141         static EnumPropertyItem clear_path_actions[] = {
1142                 {TRACK_CLEAR_UPTO, "UPTO", 0, "Clear up-to", "Clear path up to current frame"},
1143                 {TRACK_CLEAR_REMAINED, "REMAINED", 0, "Clear remained", "Clear path at remaining frames (after current)"},
1144                 {TRACK_CLEAR_ALL, "ALL", 0, "Clear all", "Clear the whole path"},
1145                 {0, NULL, 0, NULL, NULL}
1146         };
1147
1148         /* identifiers */
1149         ot->name = "Clear Track Path";
1150         ot->description = "Clear tracks after/before current position or clear the whole track";
1151         ot->idname = "CLIP_OT_clear_track_path";
1152
1153         /* api callbacks */
1154         ot->exec = clear_track_path_exec;
1155         ot->poll = ED_space_clip_tracking_poll;
1156
1157         /* flags */
1158         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1159
1160         /* properties */
1161         RNA_def_enum(ot->srna, "action", clear_path_actions, TRACK_CLEAR_REMAINED, "Action", "Clear action to execute");
1162         RNA_def_boolean(ot->srna, "clear_active", 0, "Clear Active", "Clear active track only instead of all selected tracks");
1163 }
1164
1165 /********************** disable markers operator *********************/
1166
1167 enum {
1168         MARKER_OP_DISABLE = 0,
1169         MARKER_OP_ENABLE  = 1,
1170         MARKER_OP_TOGGLE  = 2,
1171 };
1172
1173 static int disable_markers_exec(bContext *C, wmOperator *op)
1174 {
1175         SpaceClip *sc = CTX_wm_space_clip(C);
1176         MovieClip *clip = ED_space_clip_get_clip(sc);
1177         MovieTracking *tracking = &clip->tracking;
1178         ListBase *tracksbase = BKE_tracking_get_active_tracks(tracking);
1179         int action = RNA_enum_get(op->ptr, "action");
1180         int framenr = ED_space_clip_get_clip_frame_number(sc);
1181
1182         for (MovieTrackingTrack *track = tracksbase->first;
1183              track != NULL;
1184              track = track->next)
1185         {
1186                 if (TRACK_VIEW_SELECTED(sc, track) &&
1187                     (track->flag & TRACK_LOCKED) == 0)
1188                 {
1189                         MovieTrackingMarker *marker = BKE_tracking_marker_ensure(track, framenr);
1190                         switch (action) {
1191                                 case MARKER_OP_DISABLE:
1192                                         marker->flag |= MARKER_DISABLED;
1193                                         break;
1194                                 case MARKER_OP_ENABLE:
1195                                         marker->flag &= ~MARKER_DISABLED;
1196                                         break;
1197                                 case MARKER_OP_TOGGLE:
1198                                         marker->flag ^= MARKER_DISABLED;
1199                                         break;
1200                         }
1201                 }
1202         }
1203
1204         DAG_id_tag_update(&clip->id, 0);
1205
1206         WM_event_add_notifier(C, NC_MOVIECLIP | NA_EVALUATED, clip);
1207
1208         return OPERATOR_FINISHED;
1209 }
1210
1211 void CLIP_OT_disable_markers(wmOperatorType *ot)
1212 {
1213         static EnumPropertyItem actions_items[] = {
1214                 {MARKER_OP_DISABLE, "DISABLE", 0, "Disable",
1215                  "Disable selected markers"},
1216                 {MARKER_OP_ENABLE,  "ENABLE", 0, "Enable",
1217                  "Enable selected markers"},
1218                 {MARKER_OP_TOGGLE,  "TOGGLE", 0, "Toggle",
1219                  "Toggle disabled flag for selected markers"},
1220                 {0, NULL, 0, NULL, NULL}
1221         };
1222
1223         /* identifiers */
1224         ot->name = "Disable Markers";
1225         ot->description = "Disable/enable selected markers";
1226         ot->idname = "CLIP_OT_disable_markers";
1227
1228         /* api callbacks */
1229         ot->exec = disable_markers_exec;
1230         ot->poll = ED_space_clip_tracking_poll;
1231
1232         /* flags */
1233         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1234
1235         /* properties */
1236         RNA_def_enum(ot->srna, "action", actions_items, 0, "Action", "Disable action to execute");
1237 }
1238
1239 /********************** set principal center operator *********************/
1240
1241 static int set_center_principal_exec(bContext *C, wmOperator *UNUSED(op))
1242 {
1243         SpaceClip *sc = CTX_wm_space_clip(C);
1244         MovieClip *clip = ED_space_clip_get_clip(sc);
1245         int width, height;
1246
1247         BKE_movieclip_get_size(clip, &sc->user, &width, &height);
1248
1249         if (width == 0 || height == 0) {
1250                 return OPERATOR_CANCELLED;
1251         }
1252
1253         clip->tracking.camera.principal[0] = ((float)width) / 2.0f;
1254         clip->tracking.camera.principal[1] = ((float)height) / 2.0f;
1255
1256         WM_event_add_notifier(C, NC_MOVIECLIP | NA_EDITED, clip);
1257
1258         return OPERATOR_FINISHED;
1259 }
1260
1261 void CLIP_OT_set_center_principal(wmOperatorType *ot)
1262 {
1263         /* identifiers */
1264         ot->name = "Set Principal to Center";
1265         ot->description = "Set optical center to center of footage";
1266         ot->idname = "CLIP_OT_set_center_principal";
1267
1268         /* api callbacks */
1269         ot->exec = set_center_principal_exec;
1270         ot->poll = ED_space_clip_tracking_poll;
1271
1272         /* flags */
1273         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1274 }
1275
1276 /********************** hide tracks operator *********************/
1277
1278 static int hide_tracks_exec(bContext *C, wmOperator *op)
1279 {
1280         SpaceClip *sc = CTX_wm_space_clip(C);
1281         MovieClip *clip = ED_space_clip_get_clip(sc);
1282         MovieTracking *tracking = &clip->tracking;
1283         int unselected;
1284
1285         unselected = RNA_boolean_get(op->ptr, "unselected");
1286
1287         /* Hide point tracks. */
1288         ListBase *tracksbase = BKE_tracking_get_active_tracks(tracking);
1289         MovieTrackingTrack *act_track = BKE_tracking_track_get_active(tracking);
1290         for (MovieTrackingTrack *track = tracksbase->first;
1291              track != NULL;
1292              track = track->next)
1293         {
1294                 if (unselected == 0 && TRACK_VIEW_SELECTED(sc, track)) {
1295                         track->flag |= TRACK_HIDDEN;
1296                 }
1297                 else if (unselected == 1 && !TRACK_VIEW_SELECTED(sc, track)) {
1298                         track->flag |= TRACK_HIDDEN;
1299                 }
1300         }
1301
1302         if (act_track != NULL && act_track->flag & TRACK_HIDDEN) {
1303                 clip->tracking.act_track = NULL;
1304         }
1305
1306         /* Hide place tracks. */
1307         ListBase *plane_tracks_base = BKE_tracking_get_active_plane_tracks(tracking);
1308         MovieTrackingPlaneTrack *act_plane_track =
1309                 BKE_tracking_plane_track_get_active(tracking);
1310         for (MovieTrackingPlaneTrack *plane_track = plane_tracks_base->first;
1311              plane_track != NULL;
1312              plane_track = plane_track->next)
1313         {
1314                 if (unselected == 0 && plane_track->flag & SELECT) {
1315                         plane_track->flag |= PLANE_TRACK_HIDDEN;
1316                 }
1317                 else if (unselected == 1 && (plane_track->flag & SELECT) == 0) {
1318                         plane_track->flag |= PLANE_TRACK_HIDDEN;
1319                 }
1320         }
1321         if (act_plane_track != NULL && act_plane_track->flag & TRACK_HIDDEN) {
1322                 clip->tracking.act_plane_track = NULL;
1323         }
1324
1325         if (unselected == 0) {
1326                 /* No selection on screen now, unlock view so it can be
1327                  * scrolled nice again.
1328                  */
1329                 sc->flag &= ~SC_LOCK_SELECTION;
1330         }
1331
1332         BKE_tracking_dopesheet_tag_update(tracking);
1333         WM_event_add_notifier(C, NC_MOVIECLIP | ND_DISPLAY, NULL);
1334
1335         return OPERATOR_FINISHED;
1336 }
1337
1338 void CLIP_OT_hide_tracks(wmOperatorType *ot)
1339 {
1340         /* identifiers */
1341         ot->name = "Hide Tracks";
1342         ot->description = "Hide selected tracks";
1343         ot->idname = "CLIP_OT_hide_tracks";
1344
1345         /* api callbacks */
1346         ot->exec = hide_tracks_exec;
1347         ot->poll = ED_space_clip_tracking_poll;
1348
1349         /* flags */
1350         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1351
1352         /* properties */
1353         RNA_def_boolean(ot->srna, "unselected", 0, "Unselected", "Hide unselected tracks");
1354 }
1355
1356 /********************** hide tracks clear operator *********************/
1357
1358 static int hide_tracks_clear_exec(bContext *C, wmOperator *UNUSED(op))
1359 {
1360         SpaceClip *sc = CTX_wm_space_clip(C);
1361         MovieClip *clip = ED_space_clip_get_clip(sc);
1362         MovieTracking *tracking = &clip->tracking;
1363
1364         /* Unhide point tracks. */
1365         ListBase *tracksbase = BKE_tracking_get_active_tracks(tracking);
1366         for (MovieTrackingTrack *track = tracksbase->first;
1367              track != NULL;
1368              track = track->next)
1369         {
1370                 track->flag &= ~TRACK_HIDDEN;
1371         }
1372
1373         /* Unhide plane tracks. */
1374         ListBase *plane_tracks_base = BKE_tracking_get_active_plane_tracks(tracking);
1375         for (MovieTrackingPlaneTrack *plane_track = plane_tracks_base->first;
1376              plane_track != NULL;
1377              plane_track = plane_track->next)
1378         {
1379                 plane_track->flag &= ~PLANE_TRACK_HIDDEN;
1380         }
1381
1382         BKE_tracking_dopesheet_tag_update(tracking);
1383
1384         WM_event_add_notifier(C, NC_MOVIECLIP | ND_DISPLAY, NULL);
1385
1386         return OPERATOR_FINISHED;
1387 }
1388
1389 void CLIP_OT_hide_tracks_clear(wmOperatorType *ot)
1390 {
1391         /* identifiers */
1392         ot->name = "Hide Tracks Clear";
1393         ot->description = "Clear hide selected tracks";
1394         ot->idname = "CLIP_OT_hide_tracks_clear";
1395
1396         /* api callbacks */
1397         ot->exec = hide_tracks_clear_exec;
1398         ot->poll = ED_space_clip_tracking_poll;
1399
1400         /* flags */
1401         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1402 }
1403
1404 /********************** frame jump operator *********************/
1405
1406 static int frame_jump_exec(bContext *C, wmOperator *op)
1407 {
1408         Scene *scene = CTX_data_scene(C);
1409         SpaceClip *sc = CTX_wm_space_clip(C);
1410         MovieClip *clip = ED_space_clip_get_clip(sc);
1411         MovieTracking *tracking = &clip->tracking;
1412         int pos = RNA_enum_get(op->ptr, "position");
1413         int delta;
1414
1415         if (pos <= 1) { /* jump to path */
1416                 MovieTrackingTrack *track = BKE_tracking_track_get_active(tracking);
1417                 if (track == NULL) {
1418                         return OPERATOR_CANCELLED;
1419                 }
1420
1421                 delta = pos == 1 ? 1 : -1;
1422                 while (sc->user.framenr + delta >= SFRA &&
1423                        sc->user.framenr + delta <= EFRA)
1424                 {
1425                         int framenr = BKE_movieclip_remap_scene_to_clip_frame(
1426                                 clip,
1427                                 sc->user.framenr + delta);
1428                         MovieTrackingMarker *marker = BKE_tracking_marker_get_exact(track,
1429                                                                                     framenr);
1430
1431                         if (marker == NULL || marker->flag & MARKER_DISABLED) {
1432                                 break;
1433                         }
1434
1435                         sc->user.framenr += delta;
1436                 }
1437         }
1438         else {  /* to to failed frame */
1439                 if (tracking->reconstruction.flag & TRACKING_RECONSTRUCTED) {
1440                         int framenr = ED_space_clip_get_clip_frame_number(sc);
1441                         MovieTrackingObject *object = BKE_tracking_object_get_active(tracking);
1442
1443                         delta = pos == 3 ? 1 : -1;
1444                         framenr += delta;
1445
1446                         while (framenr + delta >= SFRA &&
1447                                framenr + delta <= EFRA)
1448                         {
1449                                 MovieReconstructedCamera *cam =
1450                                         BKE_tracking_camera_get_reconstructed(tracking,
1451                                                                               object,
1452                                                                               framenr);
1453
1454                                 if (cam == NULL) {
1455                                         sc->user.framenr =
1456                                                 BKE_movieclip_remap_clip_to_scene_frame(clip,
1457                                                                                         framenr);
1458                                         break;
1459                                 }
1460
1461                                 framenr += delta;
1462                         }
1463                 }
1464         }
1465
1466         if (CFRA != sc->user.framenr) {
1467                 CFRA = sc->user.framenr;
1468                 BKE_sound_seek_scene(CTX_data_main(C), scene);
1469
1470                 WM_event_add_notifier(C, NC_SCENE | ND_FRAME, scene);
1471         }
1472
1473         WM_event_add_notifier(C, NC_MOVIECLIP | ND_DISPLAY, NULL);
1474
1475         return OPERATOR_FINISHED;
1476 }
1477
1478 void CLIP_OT_frame_jump(wmOperatorType *ot)
1479 {
1480         static EnumPropertyItem position_items[] = {
1481                 {0, "PATHSTART",  0, "Path Start",      "Jump to start of current path"},
1482                 {1, "PATHEND",    0, "Path End",        "Jump to end of current path"},
1483                 {2, "FAILEDPREV", 0, "Previous Failed", "Jump to previous failed frame"},
1484                 {2, "FAILNEXT",   0, "Next Failed",     "Jump to next failed frame"},
1485                 {0, NULL, 0, NULL, NULL}
1486         };
1487
1488         /* identifiers */
1489         ot->name = "Jump to Frame";
1490         ot->description = "Jump to special frame";
1491         ot->idname = "CLIP_OT_frame_jump";
1492
1493         /* api callbacks */
1494         ot->exec = frame_jump_exec;
1495         ot->poll = ED_space_clip_poll;
1496
1497         /* flags */
1498         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1499
1500         /* properties */
1501         RNA_def_enum(ot->srna, "position", position_items, 0, "Position",
1502                      "Position to jump to");
1503 }
1504
1505 /********************** join tracks operator *********************/
1506
1507 static int join_tracks_exec(bContext *C, wmOperator *op)
1508 {
1509         SpaceClip *sc = CTX_wm_space_clip(C);
1510         MovieClip *clip = ED_space_clip_get_clip(sc);
1511         MovieTracking *tracking = &clip->tracking;
1512         ListBase *tracksbase = BKE_tracking_get_active_tracks(tracking);
1513         ListBase *plane_tracks_base = BKE_tracking_get_active_plane_tracks(tracking);
1514
1515         MovieTrackingTrack *act_track = BKE_tracking_track_get_active(tracking);
1516         if (act_track == NULL) {
1517                 BKE_report(op->reports, RPT_ERROR, "No active track to join to");
1518                 return OPERATOR_CANCELLED;
1519         }
1520
1521         GSet *point_tracks = BLI_gset_ptr_new(__func__);
1522
1523         for (MovieTrackingTrack *track = tracksbase->first, *next_track;
1524              track != NULL;
1525              track = next_track)
1526         {
1527                 next_track = track->next;
1528                 if (TRACK_VIEW_SELECTED(sc, track) && track != act_track) {
1529                         BKE_tracking_tracks_join(tracking, act_track, track);
1530
1531                         if (tracking->stabilization.rot_track == track) {
1532                                 tracking->stabilization.rot_track = act_track;
1533                         }
1534
1535                         for (MovieTrackingPlaneTrack *plane_track = plane_tracks_base->first;
1536                              plane_track != NULL;
1537                              plane_track = plane_track->next)
1538                         {
1539                                 if (BKE_tracking_plane_track_has_point_track(plane_track, track)) {
1540                                         BKE_tracking_plane_track_replace_point_track(plane_track,
1541                                                                                      track,
1542                                                                                      act_track);
1543                                         if ((plane_track->flag & PLANE_TRACK_AUTOKEY) == 0) {
1544                                                 BLI_gset_insert(point_tracks, plane_track);
1545                                         }
1546                                 }
1547                         }
1548
1549                         BKE_tracking_track_free(track);
1550                         BLI_freelinkN(tracksbase, track);
1551                 }
1552         }
1553
1554         GSetIterator gs_iter;
1555         int framenr = ED_space_clip_get_clip_frame_number(sc);
1556         GSET_ITER (gs_iter, point_tracks) {
1557                 MovieTrackingPlaneTrack *plane_track = BLI_gsetIterator_getKey(&gs_iter);
1558                 BKE_tracking_track_plane_from_existing_motion( plane_track, framenr);
1559         }
1560
1561         BLI_gset_free(point_tracks, NULL);
1562
1563         WM_event_add_notifier(C, NC_MOVIECLIP | NA_EDITED, clip);
1564
1565         return OPERATOR_FINISHED;
1566 }
1567
1568 void CLIP_OT_join_tracks(wmOperatorType *ot)
1569 {
1570         /* identifiers */
1571         ot->name = "Join Tracks";
1572         ot->description = "Join selected tracks";
1573         ot->idname = "CLIP_OT_join_tracks";
1574
1575         /* api callbacks */
1576         ot->exec = join_tracks_exec;
1577         ot->poll = ED_space_clip_tracking_poll;
1578
1579         /* flags */
1580         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1581 }
1582
1583 /********************** lock tracks operator *********************/
1584
1585 enum {
1586         TRACK_ACTION_LOCK   = 0,
1587         TRACK_ACTION_UNLOCK = 1,
1588         TRACK_ACTION_TOGGLE = 2,
1589 };
1590
1591 static int lock_tracks_exec(bContext *C, wmOperator *op)
1592 {
1593         SpaceClip *sc = CTX_wm_space_clip(C);
1594         MovieClip *clip = ED_space_clip_get_clip(sc);
1595         MovieTracking *tracking = &clip->tracking;
1596         ListBase *tracksbase = BKE_tracking_get_active_tracks(tracking);
1597         int action = RNA_enum_get(op->ptr, "action");
1598
1599         for (MovieTrackingTrack *track = tracksbase->first;
1600              track != NULL;
1601              track = track->next)
1602         {
1603                 if (TRACK_VIEW_SELECTED(sc, track)) {
1604                         switch (action) {
1605                                 case TRACK_ACTION_LOCK:
1606                                         track->flag |= TRACK_LOCKED;
1607                                         break;
1608                                 case TRACK_ACTION_UNLOCK:
1609                                         track->flag &= ~TRACK_LOCKED;
1610                                         break;
1611                                 case TRACK_ACTION_TOGGLE:
1612                                         track->flag ^= TRACK_LOCKED;
1613                                         break;
1614                         }
1615                 }
1616         }
1617
1618         WM_event_add_notifier(C, NC_MOVIECLIP | NA_EVALUATED, clip);
1619
1620         return OPERATOR_FINISHED;
1621 }
1622
1623 void CLIP_OT_lock_tracks(wmOperatorType *ot)
1624 {
1625         static EnumPropertyItem actions_items[] = {
1626                 {TRACK_ACTION_LOCK, "LOCK", 0, "Lock", "Lock selected tracks"},
1627                 {TRACK_ACTION_UNLOCK, "UNLOCK", 0, "Unlock", "Unlock selected tracks"},
1628                 {TRACK_ACTION_TOGGLE, "TOGGLE", 0, "Toggle",
1629                  "Toggle locked flag for selected tracks"},
1630                 {0, NULL, 0, NULL, NULL}
1631         };
1632
1633         /* identifiers */
1634         ot->name = "Lock Tracks";
1635         ot->description = "Lock/unlock selected tracks";
1636         ot->idname = "CLIP_OT_lock_tracks";
1637
1638         /* api callbacks */
1639         ot->exec = lock_tracks_exec;
1640         ot->poll = ED_space_clip_tracking_poll;
1641
1642         /* flags */
1643         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1644
1645         /* properties */
1646         RNA_def_enum(ot->srna, "action", actions_items, 0, "Action",
1647                      "Lock action to execute");
1648 }
1649
1650 /********************** set keyframe operator *********************/
1651
1652 enum {
1653         SOLVER_KEYFRAME_A = 0,
1654         SOLVER_KEYFRAME_B = 1,
1655 };
1656
1657 static int set_solver_keyframe_exec(bContext *C, wmOperator *op)
1658 {
1659         SpaceClip *sc = CTX_wm_space_clip(C);
1660         MovieClip *clip = ED_space_clip_get_clip(sc);
1661         MovieTracking *tracking = &clip->tracking;
1662         MovieTrackingObject *object = BKE_tracking_object_get_active(tracking);
1663         int keyframe = RNA_enum_get(op->ptr, "keyframe");
1664         int framenr = BKE_movieclip_remap_scene_to_clip_frame(clip,
1665                                                               sc->user.framenr);
1666
1667         if (keyframe == SOLVER_KEYFRAME_A) {
1668                 object->keyframe1 = framenr;
1669         }
1670         else {
1671                 object->keyframe2 = framenr;
1672         }
1673
1674         WM_event_add_notifier(C, NC_MOVIECLIP | ND_DISPLAY, clip);
1675
1676         return OPERATOR_FINISHED;
1677 }
1678
1679 void CLIP_OT_set_solver_keyframe(wmOperatorType *ot)
1680 {
1681         static EnumPropertyItem keyframe_items[] = {
1682                 {SOLVER_KEYFRAME_A, "KEYFRAME_A", 0, "Keyframe A", ""},
1683                 {SOLVER_KEYFRAME_B, "KEYFRAME_B", 0, "Keyframe B", ""},
1684                 {0, NULL, 0, NULL, NULL}
1685         };
1686
1687         /* identifiers */
1688         ot->name = "Set Solver Keyframe";
1689         ot->description = "Set keyframe used by solver";
1690         ot->idname = "CLIP_OT_set_solver_keyframe";
1691
1692         /* api callbacks */
1693         ot->exec = set_solver_keyframe_exec;
1694         ot->poll = ED_space_clip_tracking_poll;
1695
1696         /* flags */
1697         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1698
1699         /* properties */
1700         RNA_def_enum(ot->srna, "keyframe", keyframe_items, 0, "Keyframe",
1701                      "Keyframe to set");
1702 }
1703
1704 /********************** track copy color operator *********************/
1705
1706 static int track_copy_color_exec(bContext *C, wmOperator *UNUSED(op))
1707 {
1708         SpaceClip *sc = CTX_wm_space_clip(C);
1709         MovieClip *clip = ED_space_clip_get_clip(sc);
1710         MovieTracking *tracking = &clip->tracking;
1711         ListBase *tracksbase = BKE_tracking_get_active_tracks(tracking);
1712
1713         MovieTrackingTrack  *act_track = BKE_tracking_track_get_active(tracking);
1714         if (act_track == NULL) {
1715                 return OPERATOR_CANCELLED;
1716         }
1717
1718         for (MovieTrackingTrack *track = tracksbase->first;
1719              track != NULL;
1720              track = track->next)
1721         {
1722                 if (TRACK_VIEW_SELECTED(sc, track) && track != act_track) {
1723                         track->flag &= ~TRACK_CUSTOMCOLOR;
1724                         if (act_track->flag & TRACK_CUSTOMCOLOR) {
1725                                 copy_v3_v3(track->color, act_track->color);
1726                                 track->flag |= TRACK_CUSTOMCOLOR;
1727                         }
1728                 }
1729         }
1730
1731         WM_event_add_notifier(C, NC_MOVIECLIP | ND_DISPLAY, clip);
1732
1733         return OPERATOR_FINISHED;
1734 }
1735
1736 void CLIP_OT_track_copy_color(wmOperatorType *ot)
1737 {
1738         /* identifiers */
1739         ot->name = "Copy Color";
1740         ot->description = "Copy color to all selected tracks";
1741         ot->idname = "CLIP_OT_track_copy_color";
1742
1743         /* api callbacks */
1744         ot->exec = track_copy_color_exec;
1745         ot->poll = ED_space_clip_tracking_poll;
1746
1747         /* flags */
1748         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1749 }
1750
1751 /********************** clean tracks operator *********************/
1752
1753 static bool is_track_clean(MovieTrackingTrack *track, int frames, int del)
1754 {
1755         bool ok = true;
1756         int prev = -1, count = 0;
1757         MovieTrackingMarker *markers = track->markers, *new_markers = NULL;
1758         int start_disabled = 0;
1759         int markersnr = track->markersnr;
1760
1761         if (del) {
1762                 new_markers = MEM_callocN(markersnr * sizeof(MovieTrackingMarker),
1763                                           "track cleaned markers");
1764         }
1765
1766         for (int a = 0; a < markersnr; a++) {
1767                 int end = 0;
1768
1769                 if (prev == -1) {
1770                         if ((markers[a].flag & MARKER_DISABLED) == 0) {
1771                                 prev = a;
1772                         }
1773                         else {
1774                                 start_disabled = 1;
1775                         }
1776                 }
1777
1778                 if (prev >= 0) {
1779                         end =  a == markersnr - 1;
1780                         end |= (a < markersnr - 1) && (markers[a].framenr != markers[a + 1].framenr - 1 ||
1781                                                        markers[a].flag & MARKER_DISABLED);
1782                 }
1783
1784                 if (end) {
1785                         int segok = 1, len = 0;
1786
1787                         if (a != prev && markers[a].framenr != markers[a - 1].framenr + 1) {
1788                                 len = a - prev;
1789                         }
1790                         else if (markers[a].flag & MARKER_DISABLED) {
1791                                 len = a - prev;
1792                         }
1793                         else {
1794                                 len = a - prev + 1;
1795                         }
1796
1797                         if (frames) {
1798                                 if (len < frames) {
1799                                         segok = 0;
1800                                         ok = 0;
1801
1802                                         if (!del) {
1803                                                 break;
1804                                         }
1805                                 }
1806                         }
1807
1808                         if (del) {
1809                                 if (segok) {
1810                                         int t = len;
1811
1812                                         if (markers[a].flag & MARKER_DISABLED) {
1813                                                 t++;
1814                                         }
1815
1816                                         /* Place disabled marker in front of current segment. */
1817                                         if (start_disabled) {
1818                                                 memcpy(new_markers + count,
1819                                                        markers + prev,
1820                                                        sizeof(MovieTrackingMarker));
1821                                                 new_markers[count].framenr--;
1822                                                 new_markers[count].flag |= MARKER_DISABLED;
1823
1824                                                 count++;
1825                                                 start_disabled = 0;
1826                                         }
1827
1828                                         memcpy(new_markers + count,
1829                                                markers + prev,
1830                                                t * sizeof(MovieTrackingMarker));
1831                                         count += t;
1832                                 }
1833                                 else if (markers[a].flag & MARKER_DISABLED) {
1834                                         /* Current segment which would be deleted was finished by
1835                                          * disabled marker, so next segment should be started from
1836                                          * disabled marker.
1837                                          */
1838                                         start_disabled = 1;
1839                                 }
1840                         }
1841
1842                         prev = -1;
1843                 }
1844         }
1845
1846         if (del) {
1847                 MEM_freeN(track->markers);
1848
1849                 if (count) {
1850                         track->markers = new_markers;
1851                 }
1852                 else {
1853                         track->markers = NULL;
1854                         MEM_freeN(new_markers);
1855                 }
1856
1857                 track->markersnr = count;
1858         }
1859
1860         return ok;
1861 }
1862
1863 static int clean_tracks_exec(bContext *C, wmOperator *op)
1864 {
1865         SpaceClip *sc = CTX_wm_space_clip(C);
1866         MovieClip *clip = ED_space_clip_get_clip(sc);
1867         MovieTracking *tracking = &clip->tracking;
1868         ListBase *tracksbase = BKE_tracking_get_active_tracks(tracking);
1869         MovieTrackingTrack *act_track = BKE_tracking_track_get_active(tracking);
1870         int frames = RNA_int_get(op->ptr, "frames");
1871         int action = RNA_enum_get(op->ptr, "action");
1872         float error = RNA_float_get(op->ptr, "error");
1873
1874         if (error && action == TRACKING_CLEAN_DELETE_SEGMENT) {
1875                 action = TRACKING_CLEAN_DELETE_TRACK;
1876         }
1877
1878         for (MovieTrackingTrack *track = tracksbase->first, *next_track;
1879              track != NULL;
1880              track = next_track)
1881         {
1882                 next_track = track->next;
1883
1884                 if ((track->flag & TRACK_HIDDEN) == 0 &&
1885                     (track->flag & TRACK_LOCKED) == 0)
1886                 {
1887                         bool ok;
1888
1889                         ok = (is_track_clean(track,
1890                                              frames,
1891                                              action == TRACKING_CLEAN_DELETE_SEGMENT)) &&
1892                              ((error == 0.0f) ||
1893                               (track->flag & TRACK_HAS_BUNDLE) == 0 ||
1894                               (track->error < error));
1895
1896                         if (!ok) {
1897                                 if (action == TRACKING_CLEAN_SELECT) {
1898                                         BKE_tracking_track_flag_set(track, TRACK_AREA_ALL, SELECT);
1899                                 }
1900                                 else if (action == TRACKING_CLEAN_DELETE_TRACK) {
1901                                         if (track == act_track) {
1902                                                 clip->tracking.act_track = NULL;
1903                                         }
1904                                         BKE_tracking_track_free(track);
1905                                         BLI_freelinkN(tracksbase, track);
1906                                         track = NULL;
1907                                 }
1908
1909                                 /* Happens when all tracking segments are not long enough. */
1910                                 if (track && track->markersnr == 0) {
1911                                         if (track == act_track) {
1912                                                 clip->tracking.act_track = NULL;
1913                                         }
1914                                         BKE_tracking_track_free(track);
1915                                         BLI_freelinkN(tracksbase, track);
1916                                 }
1917                         }
1918                 }
1919         }
1920
1921         BKE_tracking_dopesheet_tag_update(tracking);
1922
1923         WM_event_add_notifier(C, NC_MOVIECLIP | ND_SELECT, clip);
1924
1925         return OPERATOR_FINISHED;
1926 }
1927
1928 static int clean_tracks_invoke(bContext *C,
1929                                wmOperator *op,
1930                                const wmEvent *UNUSED(event))
1931 {
1932         SpaceClip *sc = CTX_wm_space_clip(C);
1933         MovieClip *clip = ED_space_clip_get_clip(sc);
1934
1935         if (!RNA_struct_property_is_set(op->ptr, "frames")) {
1936                 RNA_int_set(op->ptr, "frames", clip->tracking.settings.clean_frames);
1937         }
1938
1939         if (!RNA_struct_property_is_set(op->ptr, "error")) {
1940                 RNA_float_set(op->ptr, "error", clip->tracking.settings.clean_error);
1941         }
1942
1943         if (!RNA_struct_property_is_set(op->ptr, "action")) {
1944                 RNA_enum_set(op->ptr, "action", clip->tracking.settings.clean_action);
1945         }
1946
1947         return clean_tracks_exec(C, op);
1948 }
1949
1950 void CLIP_OT_clean_tracks(wmOperatorType *ot)
1951 {
1952         static EnumPropertyItem actions_items[] = {
1953                 {TRACKING_CLEAN_SELECT, "SELECT", 0, "Select",
1954                  "Select unclean tracks"},
1955                 {TRACKING_CLEAN_DELETE_TRACK, "DELETE_TRACK", 0, "Delete Track",
1956                  "Delete unclean tracks"},
1957                 {TRACKING_CLEAN_DELETE_SEGMENT, "DELETE_SEGMENTS", 0, "Delete Segments",
1958                  "Delete unclean segments of tracks"},
1959                 {0, NULL, 0, NULL, NULL}
1960         };
1961
1962         /* identifiers */
1963         ot->name = "Clean Tracks";
1964         ot->description = "Clean tracks with high error values or few frames";
1965         ot->idname = "CLIP_OT_clean_tracks";
1966
1967         /* api callbacks */
1968         ot->exec = clean_tracks_exec;
1969         ot->invoke = clean_tracks_invoke;
1970         ot->poll = ED_space_clip_tracking_poll;
1971
1972         /* flags */
1973         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1974
1975         /* properties */
1976         RNA_def_int(ot->srna, "frames", 0, 0, INT_MAX, "Tracked Frames",
1977                     "Effect on tracks which are tracked less than "
1978                     "specified amount of frames",
1979                     0, INT_MAX);
1980         RNA_def_float(ot->srna, "error", 0.0f, 0.0f, FLT_MAX, "Reprojection Error",
1981                       "Effect on tracks which have got larger re-projection error",
1982                       0.0f, 100.0f);
1983         RNA_def_enum(ot->srna, "action", actions_items, 0, "Action",
1984                      "Cleanup action to execute");
1985 }
1986
1987 /********************** add tracking object *********************/
1988
1989 static int tracking_object_new_exec(bContext *C, wmOperator *UNUSED(op))
1990 {
1991         SpaceClip *sc = CTX_wm_space_clip(C);
1992         MovieClip *clip = ED_space_clip_get_clip(sc);
1993         MovieTracking *tracking = &clip->tracking;
1994
1995         BKE_tracking_object_add(tracking, "Object");
1996
1997         WM_event_add_notifier(C, NC_MOVIECLIP | NA_EDITED, clip);
1998
1999         return OPERATOR_FINISHED;
2000 }
2001
2002 void CLIP_OT_tracking_object_new(wmOperatorType *ot)
2003 {
2004         /* identifiers */
2005         ot->name = "Add Tracking Object";
2006         ot->description = "Add new object for tracking";
2007         ot->idname = "CLIP_OT_tracking_object_new";
2008
2009         /* api callbacks */
2010         ot->exec = tracking_object_new_exec;
2011         ot->poll = ED_space_clip_tracking_poll;
2012
2013         /* flags */
2014         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
2015 }
2016
2017 /********************** remove tracking object *********************/
2018
2019 static int tracking_object_remove_exec(bContext *C, wmOperator *op)
2020 {
2021         SpaceClip *sc = CTX_wm_space_clip(C);
2022         MovieClip *clip = ED_space_clip_get_clip(sc);
2023         MovieTracking *tracking = &clip->tracking;
2024         MovieTrackingObject *object;
2025
2026         object = BKE_tracking_object_get_active(tracking);
2027
2028         if (object->flag & TRACKING_OBJECT_CAMERA) {
2029                 BKE_report(op->reports,
2030                            RPT_WARNING,
2031                            "Object used for camera tracking cannot be deleted");
2032                 return OPERATOR_CANCELLED;
2033         }
2034
2035         BKE_tracking_object_delete(tracking, object);
2036
2037         WM_event_add_notifier(C, NC_MOVIECLIP | NA_EDITED, clip);
2038
2039         return OPERATOR_FINISHED;
2040 }
2041
2042 void CLIP_OT_tracking_object_remove(wmOperatorType *ot)
2043 {
2044         /* identifiers */
2045         ot->name = "Remove Tracking Object";
2046         ot->description = "Remove object for tracking";
2047         ot->idname = "CLIP_OT_tracking_object_remove";
2048
2049         /* api callbacks */
2050         ot->exec = tracking_object_remove_exec;
2051         ot->poll = ED_space_clip_tracking_poll;
2052
2053         /* flags */
2054         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
2055 }
2056
2057 /********************** copy tracks to clipboard operator *********************/
2058
2059 static int copy_tracks_exec(bContext *C, wmOperator *UNUSED(op))
2060 {
2061         SpaceClip *sc = CTX_wm_space_clip(C);
2062         MovieClip *clip = ED_space_clip_get_clip(sc);
2063         MovieTracking *tracking = &clip->tracking;
2064         MovieTrackingObject *object = BKE_tracking_object_get_active(tracking);
2065
2066         clip_tracking_clear_invisible_track_selection(sc, clip);
2067
2068         BKE_tracking_clipboard_copy_tracks(tracking, object);
2069
2070         return OPERATOR_FINISHED;
2071 }
2072
2073 void CLIP_OT_copy_tracks(wmOperatorType *ot)
2074 {
2075         /* identifiers */
2076         ot->name = "Copy Tracks";
2077         ot->description = "Copy selected tracks to clipboard";
2078         ot->idname = "CLIP_OT_copy_tracks";
2079
2080         /* api callbacks */
2081         ot->exec = copy_tracks_exec;
2082         ot->poll = ED_space_clip_tracking_poll;
2083
2084         /* flags */
2085         ot->flag = OPTYPE_REGISTER;
2086 }
2087
2088 /********************* paste tracks from clipboard operator ********************/
2089
2090 static int paste_tracks_poll(bContext *C)
2091 {
2092         if (ED_space_clip_tracking_poll(C)) {
2093                 return BKE_tracking_clipboard_has_tracks();
2094         }
2095
2096         return 0;
2097 }
2098
2099 static int paste_tracks_exec(bContext *C, wmOperator *UNUSED(op))
2100 {
2101         SpaceClip *sc = CTX_wm_space_clip(C);
2102         MovieClip *clip = ED_space_clip_get_clip(sc);
2103         MovieTracking *tracking = &clip->tracking;
2104         MovieTrackingObject *object = BKE_tracking_object_get_active(tracking);
2105         ListBase *tracks_base = BKE_tracking_object_get_tracks(tracking, object);
2106
2107         BKE_tracking_tracks_deselect_all(tracks_base);
2108         BKE_tracking_clipboard_paste_tracks(tracking, object);
2109
2110         WM_event_add_notifier(C, NC_MOVIECLIP | NA_EDITED, clip);
2111
2112         return OPERATOR_FINISHED;
2113 }
2114
2115 void CLIP_OT_paste_tracks(wmOperatorType *ot)
2116 {
2117         /* identifiers */
2118         ot->name = "Paste Tracks";
2119         ot->description = "Paste tracks from clipboard";
2120         ot->idname = "CLIP_OT_paste_tracks";
2121
2122         /* api callbacks */
2123         ot->exec = paste_tracks_exec;
2124         ot->poll = paste_tracks_poll;
2125
2126         /* flags */
2127         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
2128 }
2129
2130 /********************** Insert track keyframe operator *********************/
2131
2132 static void keyframe_set_flag(bContext *C, bool set)
2133 {
2134         SpaceClip *sc = CTX_wm_space_clip(C);
2135         MovieClip *clip = ED_space_clip_get_clip(sc);
2136         MovieTracking *tracking = &clip->tracking;
2137         int framenr = ED_space_clip_get_clip_frame_number(sc);
2138
2139         ListBase *tracks_base = BKE_tracking_get_active_tracks(tracking);
2140         for (MovieTrackingTrack *track = tracks_base->first;
2141              track != NULL;
2142              track = track->next)
2143         {
2144                 if (TRACK_VIEW_SELECTED(sc, track)) {
2145                         if (set) {
2146                                 MovieTrackingMarker *marker =
2147                                         BKE_tracking_marker_ensure(track, framenr);
2148                                 marker->flag &= ~MARKER_TRACKED;
2149                         }
2150                         else {
2151                                 MovieTrackingMarker *marker =
2152                                         BKE_tracking_marker_get_exact(track, framenr);
2153                                 if (marker != NULL) {
2154                                         marker->flag |= MARKER_TRACKED;
2155                                 }
2156                         }
2157                 }
2158         }
2159
2160         ListBase *plane_tracks_base = BKE_tracking_get_active_plane_tracks(tracking);
2161         for (MovieTrackingPlaneTrack *plane_track = plane_tracks_base->first;
2162              plane_track != NULL;
2163              plane_track = plane_track->next)
2164         {
2165                 if (PLANE_TRACK_VIEW_SELECTED(plane_track)) {
2166                         if (set) {
2167                                 MovieTrackingPlaneMarker *plane_marker =
2168                                         BKE_tracking_plane_marker_ensure(plane_track, framenr);
2169                                 if (plane_marker->flag & PLANE_MARKER_TRACKED) {
2170                                         plane_marker->flag &= ~PLANE_MARKER_TRACKED;
2171                                         BKE_tracking_track_plane_from_existing_motion(
2172                                                 plane_track,
2173                                                 plane_marker->framenr);
2174                                 }
2175                         }
2176                         else {
2177                                 MovieTrackingPlaneMarker *plane_marker =
2178                                         BKE_tracking_plane_marker_get_exact(plane_track,
2179                                                                             framenr);
2180                                 if (plane_marker) {
2181                                         if ((plane_marker->flag & PLANE_MARKER_TRACKED) == 0) {
2182                                                 plane_marker->flag |= PLANE_MARKER_TRACKED;
2183                                                 BKE_tracking_retrack_plane_from_existing_motion_at_segment(
2184                                                         plane_track,
2185                                                         plane_marker->framenr);
2186                                         }
2187                                 }
2188                         }
2189                 }
2190         }
2191
2192         WM_event_add_notifier(C, NC_MOVIECLIP | NA_EDITED, clip);
2193 }
2194
2195 static int keyframe_insert_exec(bContext *C, wmOperator *UNUSED(op))
2196 {
2197         keyframe_set_flag(C, true);
2198         return OPERATOR_FINISHED;
2199 }
2200
2201 void CLIP_OT_keyframe_insert(wmOperatorType *ot)
2202 {
2203         /* identifiers */
2204         ot->name = "Insert keyframe";
2205         ot->description = "Insert a keyframe to selected tracks at current frame";
2206         ot->idname = "CLIP_OT_keyframe_insert";
2207
2208         /* api callbacks */
2209         ot->poll = ED_space_clip_tracking_poll;
2210         ot->exec = keyframe_insert_exec;
2211
2212         /* flags */
2213         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
2214 }
2215
2216 /********************** Delete track keyframe operator *********************/
2217
2218 static int keyframe_delete_exec(bContext *C, wmOperator *UNUSED(op))
2219 {
2220         keyframe_set_flag(C, false);
2221         return OPERATOR_FINISHED;
2222 }
2223
2224 void CLIP_OT_keyframe_delete(wmOperatorType *ot)
2225 {
2226         /* identifiers */
2227         ot->name = "Delete keyframe";
2228         ot->description = "Delete a keyframe from selected tracks at current frame";
2229         ot->idname = "CLIP_OT_keyframe_delete";
2230
2231         /* api callbacks */
2232         ot->poll = ED_space_clip_tracking_poll;
2233         ot->exec = keyframe_delete_exec;
2234
2235         /* flags */
2236         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
2237 }