Weighted 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_camera_types.h"
35 #include "DNA_constraint_types.h"
36 #include "DNA_gpencil_types.h"
37 #include "DNA_mask_types.h"
38 #include "DNA_movieclip_types.h"
39 #include "DNA_object_types.h"   /* SELECT */
40 #include "DNA_scene_types.h"
41
42 #include "BLI_utildefines.h"
43 #include "BLI_math.h"
44 #include "BLI_listbase.h"
45 #include "BLI_rect.h"
46 #include "BLI_blenlib.h"
47
48 #include "BKE_main.h"
49 #include "BKE_context.h"
50 #include "BKE_constraint.h"
51 #include "BKE_movieclip.h"
52 #include "BKE_tracking.h"
53 #include "BKE_global.h"
54 #include "BKE_depsgraph.h"
55 #include "BKE_object.h"
56 #include "BKE_report.h"
57 #include "BKE_scene.h"
58 #include "BKE_library.h"
59 #include "BKE_mask.h"
60 #include "BKE_node.h"
61 #include "BKE_sound.h"
62
63 #include "WM_api.h"
64 #include "WM_types.h"
65
66 #include "ED_screen.h"
67 #include "ED_clip.h"
68 #include "ED_keyframing.h"
69
70 #include "IMB_imbuf_types.h"
71 #include "IMB_imbuf.h"
72
73 #include "UI_interface.h"
74
75 #include "RNA_access.h"
76 #include "RNA_define.h"
77
78 #include "BLF_translation.h"
79
80 #include "PIL_time.h"
81
82 #include "UI_view2d.h"
83
84 #include "clip_intern.h"    // own include
85
86 /********************** add marker operator *********************/
87
88 static bool add_marker(const bContext *C, float x, float y)
89 {
90         SpaceClip *sc = CTX_wm_space_clip(C);
91         MovieClip *clip = ED_space_clip_get_clip(sc);
92         MovieTracking *tracking = &clip->tracking;
93         ListBase *tracksbase = BKE_tracking_get_active_tracks(tracking);
94         ListBase *plane_tracks_base = BKE_tracking_get_active_plane_tracks(tracking);
95         MovieTrackingTrack *track;
96         int width, height;
97         int framenr = ED_space_clip_get_clip_frame_number(sc);
98
99         ED_space_clip_get_size(sc, &width, &height);
100
101         if (width == 0 || height == 0) {
102                 return false;
103         }
104
105         track = BKE_tracking_track_add(tracking, tracksbase, x, y, framenr, width, height);
106
107         BKE_tracking_track_select(tracksbase, track, TRACK_AREA_ALL, 0);
108         BKE_tracking_plane_tracks_deselect_all(plane_tracks_base);
109
110         clip->tracking.act_track = track;
111         clip->tracking.act_plane_track = NULL;
112
113         return true;
114 }
115
116 static int add_marker_exec(bContext *C, wmOperator *op)
117 {
118         SpaceClip *sc = CTX_wm_space_clip(C);
119         MovieClip *clip = ED_space_clip_get_clip(sc);
120         float pos[2];
121
122         RNA_float_get_array(op->ptr, "location", pos);
123
124         if (!add_marker(C, pos[0], pos[1])) {
125                 return OPERATOR_CANCELLED;
126         }
127
128         /* reset offset from locked position, so frame jumping wouldn't be so confusing */
129         sc->xlockof = 0;
130         sc->ylockof = 0;
131
132         WM_event_add_notifier(C, NC_MOVIECLIP | NA_EDITED, clip);
133
134         return OPERATOR_FINISHED;
135 }
136
137 static int add_marker_invoke(bContext *C, wmOperator *op, const wmEvent *event)
138 {
139         SpaceClip *sc = CTX_wm_space_clip(C);
140         ARegion *ar = CTX_wm_region(C);
141
142         if (!RNA_struct_property_is_set(op->ptr, "location")) {
143                 /* If location is not set, use mouse positio nas default. */
144                 float co[2];
145                 ED_clip_mouse_pos(sc, ar, event->mval, co);
146                 RNA_float_set_array(op->ptr, "location", co);
147         }
148
149         return add_marker_exec(C, op);
150 }
151
152 void CLIP_OT_add_marker(wmOperatorType *ot)
153 {
154         /* identifiers */
155         ot->name = "Add Marker";
156         ot->idname = "CLIP_OT_add_marker";
157         ot->description = "Place new marker at specified location";
158
159         /* api callbacks */
160         ot->invoke = add_marker_invoke;
161         ot->exec = add_marker_exec;
162         ot->poll = ED_space_clip_tracking_poll;
163
164         /* flags */
165         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
166
167         /* properties */
168         RNA_def_float_vector(ot->srna, "location", 2, NULL, -FLT_MAX, FLT_MAX,
169                              "Location", "Location of marker on frame", -1.0f, 1.0f);
170 }
171
172 /********************** add marker operator *********************/
173
174 static int add_marker_at_click_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
175 {
176         ED_area_headerprint(CTX_wm_area(C), IFACE_("Use LMB click to define location where place the marker"));
177
178         /* add modal handler for ESC */
179         WM_event_add_modal_handler(C, op);
180
181         return OPERATOR_RUNNING_MODAL;
182 }
183
184 static int add_marker_at_click_modal(bContext *C, wmOperator *UNUSED(op), const wmEvent *event)
185 {
186         SpaceClip *sc = CTX_wm_space_clip(C);
187         MovieClip *clip = ED_space_clip_get_clip(sc);
188         ARegion *ar = CTX_wm_region(C);
189         float pos[2];
190
191         switch (event->type) {
192                 case MOUSEMOVE:
193                         return OPERATOR_RUNNING_MODAL;
194                         break;
195
196                 case LEFTMOUSE:
197                         ED_area_headerprint(CTX_wm_area(C), NULL);
198
199                         ED_clip_point_stable_pos(sc, ar,
200                                                  event->x - ar->winrct.xmin,
201                                                  event->y - ar->winrct.ymin,
202                                                  &pos[0], &pos[1]);
203
204                         if (!add_marker(C, pos[0], pos[1]))
205                                 return OPERATOR_CANCELLED;
206
207                         WM_event_add_notifier(C, NC_MOVIECLIP | NA_EDITED, clip);
208                         return OPERATOR_FINISHED;
209                         break;
210
211                 case ESCKEY:
212                         ED_area_headerprint(CTX_wm_area(C), NULL);
213                         return OPERATOR_CANCELLED;
214         }
215
216         return OPERATOR_PASS_THROUGH;
217 }
218
219 void CLIP_OT_add_marker_at_click(wmOperatorType *ot)
220 {
221         /* identifiers */
222         ot->name = "Add Marker at Click";
223         ot->idname = "CLIP_OT_add_marker_at_click";
224         ot->description = "Place new marker at the desired (clicked) position";
225
226         /* api callbacks */
227         ot->invoke = add_marker_at_click_invoke;
228         ot->poll = ED_space_clip_tracking_poll;
229         ot->modal = add_marker_at_click_modal;
230
231         /* flags */
232         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_BLOCKING;
233 }
234
235 /********************** delete track operator *********************/
236
237 static int delete_track_exec(bContext *C, wmOperator *UNUSED(op))
238 {
239         SpaceClip *sc = CTX_wm_space_clip(C);
240         MovieClip *clip = ED_space_clip_get_clip(sc);
241         MovieTracking *tracking = &clip->tracking;
242         ListBase *tracksbase = BKE_tracking_get_active_tracks(tracking);
243         ListBase *plane_tracks_base = BKE_tracking_get_active_plane_tracks(tracking);
244         MovieTrackingTrack *track = tracksbase->first, *next;
245         MovieTrackingPlaneTrack *plane_track, *next_plane_track;
246         bool modified = false;
247
248         /* Delete selected plane tracks. */
249         for (plane_track = plane_tracks_base->first;
250              plane_track;
251              plane_track = next_plane_track)
252         {
253                 next_plane_track = plane_track->next;
254
255                 if (plane_track->flag & SELECT) {
256                         BKE_tracking_plane_track_free(plane_track);
257                         BLI_freelinkN(plane_tracks_base, plane_track);
258                         modified = true;
259                 }
260         }
261
262         /* Remove selected point tracks (they'll also be removed from planes which uses them). */
263         while (track) {
264                 next = track->next;
265
266                 if (TRACK_VIEW_SELECTED(sc, track))
267                         clip_delete_track(C, clip, track);
268
269                 track = next;
270         }
271
272         /* nothing selected now, unlock view so it can be scrolled nice again */
273         sc->flag &= ~SC_LOCK_SELECTION;
274
275         if (modified) {
276                 WM_event_add_notifier(C, NC_MOVIECLIP | NA_EDITED, clip);
277         }
278
279         return OPERATOR_FINISHED;
280 }
281
282 void CLIP_OT_delete_track(wmOperatorType *ot)
283 {
284         /* identifiers */
285         ot->name = "Delete Track";
286         ot->idname = "CLIP_OT_delete_track";
287         ot->description = "Delete selected tracks";
288
289         /* api callbacks */
290         ot->invoke = WM_operator_confirm;
291         ot->exec = delete_track_exec;
292         ot->poll = ED_space_clip_tracking_poll;
293
294         /* flags */
295         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
296 }
297
298 /********************** delete marker operator *********************/
299
300 static int delete_marker_exec(bContext *C, wmOperator *UNUSED(op))
301 {
302         SpaceClip *sc = CTX_wm_space_clip(C);
303         MovieClip *clip = ED_space_clip_get_clip(sc);
304         ListBase *tracksbase = BKE_tracking_get_active_tracks(&clip->tracking);
305         ListBase *plane_tracks_base = BKE_tracking_get_active_plane_tracks(&clip->tracking);
306         MovieTrackingTrack *track = tracksbase->first, *next;
307         MovieTrackingPlaneTrack *plane_track, *plane_track_next;
308         int framenr = ED_space_clip_get_clip_frame_number(sc);
309         int has_selection = 0;
310
311         while (track) {
312                 next = track->next;
313
314                 if (TRACK_VIEW_SELECTED(sc, track)) {
315                         MovieTrackingMarker *marker = BKE_tracking_marker_get_exact(track, framenr);
316
317                         if (marker) {
318                                 has_selection |= track->markersnr > 1;
319
320                                 clip_delete_marker(C, clip, track, marker);
321                         }
322                 }
323
324                 track = next;
325         }
326
327         for (plane_track = plane_tracks_base->first;
328              plane_track;
329              plane_track = plane_track_next)
330         {
331                 plane_track_next = plane_track->next;
332
333                 if (plane_track->flag & SELECT) {
334                         MovieTrackingPlaneMarker *plane_marker = BKE_tracking_plane_marker_get_exact(plane_track, framenr);
335
336                         if (plane_marker) {
337                                 if (plane_track->markersnr == 1) {
338                                         BKE_tracking_plane_track_free(plane_track);
339                                         BLI_freelinkN(plane_tracks_base, plane_track);
340                                 }
341                                 else {
342                                         BKE_tracking_plane_marker_delete(plane_track, framenr);
343                                 }
344                         }
345                 }
346         }
347
348         if (!has_selection) {
349                 /* nothing selected now, unlock view so it can be scrolled nice again */
350                 sc->flag &= ~SC_LOCK_SELECTION;
351         }
352
353         return OPERATOR_FINISHED;
354 }
355
356 void CLIP_OT_delete_marker(wmOperatorType *ot)
357 {
358         /* identifiers */
359         ot->name = "Delete Marker";
360         ot->idname = "CLIP_OT_delete_marker";
361         ot->description = "Delete marker for current frame from selected tracks";
362
363         /* api callbacks */
364         ot->invoke = WM_operator_confirm;
365         ot->exec = delete_marker_exec;
366         ot->poll = ED_space_clip_tracking_poll;
367
368         /* flags */
369         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
370 }
371
372 /********************** slide marker operator *********************/
373
374 #define SLIDE_ACTION_POS       0
375 #define SLIDE_ACTION_SIZE      1
376 #define SLIDE_ACTION_OFFSET    2
377 #define SLIDE_ACTION_TILT_SIZE 3
378
379 typedef struct {
380         short area, action;
381         MovieTrackingTrack *track;
382         MovieTrackingMarker *marker;
383
384         int mval[2];
385         int width, height;
386         float *min, *max, *pos, *offset, (*corners)[2];
387         float spos[2];
388
389         short lock, accurate;
390
391         /* data to restore on cancel */
392         float old_search_min[2], old_search_max[2], old_pos[2], old_offset[2];
393         float old_corners[4][2];
394         float (*old_markers)[2];
395 } SlideMarkerData;
396
397 static void slide_marker_tilt_slider(MovieTrackingMarker *marker, float slider[2])
398 {
399         add_v2_v2v2(slider, marker->pattern_corners[1], marker->pattern_corners[2]);
400         add_v2_v2(slider, marker->pos);
401 }
402
403 static SlideMarkerData *create_slide_marker_data(SpaceClip *sc, MovieTrackingTrack *track,
404                                                  MovieTrackingMarker *marker, const wmEvent *event,
405                                                  int area, int corner, int action, int width, int height)
406 {
407         SlideMarkerData *data = MEM_callocN(sizeof(SlideMarkerData), "slide marker data");
408         int framenr = ED_space_clip_get_clip_frame_number(sc);
409
410         marker = BKE_tracking_marker_ensure(track, framenr);
411
412         data->area = area;
413         data->action = action;
414         data->track = track;
415         data->marker = marker;
416
417         if (area == TRACK_AREA_POINT) {
418                 data->pos = marker->pos;
419                 data->offset = track->offset;
420         }
421         else if (area == TRACK_AREA_PAT) {
422                 if (action == SLIDE_ACTION_SIZE) {
423                         data->corners = marker->pattern_corners;
424                 }
425                 else if (action == SLIDE_ACTION_OFFSET) {
426                         int a;
427
428                         data->pos = marker->pos;
429                         data->offset = track->offset;
430
431                         data->old_markers = MEM_callocN(sizeof(*data->old_markers) * track->markersnr, "slide marekrs");
432                         for (a = 0; a < track->markersnr; a++)
433                                 copy_v2_v2(data->old_markers[a], track->markers[a].pos);
434                 }
435                 else if (action == SLIDE_ACTION_POS) {
436                         data->corners = marker->pattern_corners;
437                         data->pos = marker->pattern_corners[corner];
438                         copy_v2_v2(data->spos, data->pos);
439                 }
440                 else if (action == SLIDE_ACTION_TILT_SIZE) {
441                         data->corners = marker->pattern_corners;
442                         slide_marker_tilt_slider(marker, data->spos);
443                 }
444         }
445         else if (area == TRACK_AREA_SEARCH) {
446                 data->min = marker->search_min;
447                 data->max = marker->search_max;
448         }
449
450         data->mval[0] = event->mval[0];
451         data->mval[1] = event->mval[1];
452
453         data->width = width;
454         data->height = height;
455
456         if (action == SLIDE_ACTION_SIZE)
457                 data->lock = 1;
458
459         /* backup marker's settings */
460         memcpy(data->old_corners, marker->pattern_corners, sizeof(data->old_corners));
461         copy_v2_v2(data->old_search_min, marker->search_min);
462         copy_v2_v2(data->old_search_max, marker->search_max);
463         copy_v2_v2(data->old_pos, marker->pos);
464         copy_v2_v2(data->old_offset, track->offset);
465
466         return data;
467 }
468
469 static int mouse_on_slide_zone(SpaceClip *sc, MovieTrackingMarker *marker,
470                                int area, float co[2], float slide_zone[2],
471                                float padding, int width, int height)
472 {
473         const float size = 12.0f;
474         float min[2], max[2];
475         float dx, dy;
476
477         if (area == TRACK_AREA_SEARCH) {
478                 copy_v2_v2(min, marker->search_min);
479                 copy_v2_v2(max, marker->search_max);
480         }
481         else {
482                 BKE_tracking_marker_pattern_minmax(marker, min, max);
483         }
484
485         min[0] -= padding / width;
486         min[1] -= padding / height;
487         max[0] += padding / width;
488         max[1] += padding / height;
489
490         dx = size / width / sc->zoom;
491         dy = size / height / sc->zoom;
492
493         dx = min_ff(dx, (max[0] - min[0]) / 6.0f);
494         dy = min_ff(dy, (max[1] - min[1]) / 6.0f);
495
496         return IN_RANGE_INCL(co[0], slide_zone[0] - dx, slide_zone[0] + dx) &&
497                IN_RANGE_INCL(co[1], slide_zone[1] - dy, slide_zone[1] + dy);
498 }
499
500 static int mouse_on_corner(SpaceClip *sc, MovieTrackingMarker *marker,
501                            int area, float co[2], int corner, float padding,
502                            int width, int height)
503 {
504         float min[2], max[2], crn[2];
505
506         if (area == TRACK_AREA_SEARCH) {
507                 copy_v2_v2(min, marker->search_min);
508                 copy_v2_v2(max, marker->search_max);
509         }
510         else {
511                 BKE_tracking_marker_pattern_minmax(marker, min, max);
512         }
513
514         min[0] -= padding / width;
515         min[1] -= padding / height;
516         max[0] += padding / width;
517         max[1] += padding / height;
518
519         if (corner == 0) {
520                 crn[0] = marker->pos[0] + max[0];
521                 crn[1] = marker->pos[1] + min[1];
522         }
523         else {
524                 crn[0] = marker->pos[0] + min[0];
525                 crn[1] = marker->pos[1] + max[1];
526         }
527
528         return mouse_on_slide_zone(sc, marker, area, co, crn, padding, width, height);
529 }
530
531 static int get_mouse_pattern_corner(SpaceClip *sc, MovieTrackingMarker *marker, float co[2], int width, int height)
532 {
533         int i, next;
534         float len = FLT_MAX, dx, dy;
535
536         for (i = 0; i < 4; i++) {
537                 float cur_len;
538
539                 next = (i + 1) % 4;
540
541                 cur_len = len_v2v2(marker->pattern_corners[i], marker->pattern_corners[next]);
542
543                 len = min_ff(cur_len, len);
544         }
545
546         dx = 12.0f / width / sc->zoom;
547         dy = 12.0f / height / sc->zoom;
548
549         dx = min_ff(dx, len * 2.0f / 3.0f);
550         dy = min_ff(dy, len * width / height * 2.0f / 3.0f);
551
552         for (i = 0; i < 4; i++) {
553                 float crn[2];
554                 int inside;
555
556                 add_v2_v2v2(crn, marker->pattern_corners[i], marker->pos);
557
558                 inside = IN_RANGE_INCL(co[0], crn[0] - dx, crn[0] + dx) &&
559                          IN_RANGE_INCL(co[1], crn[1] - dy, crn[1] + dy);
560
561                 if (inside)
562                         return i;
563         }
564
565         return -1;
566 }
567
568 static int mouse_on_offset(SpaceClip *sc, MovieTrackingTrack *track, MovieTrackingMarker *marker,
569                            float co[2], int width, int height)
570 {
571         float pos[2], dx, dy;
572         float pat_min[2], pat_max[2];
573
574         BKE_tracking_marker_pattern_minmax(marker, pat_min, pat_max);
575
576         add_v2_v2v2(pos, marker->pos, track->offset);
577
578         dx = 12.0f / width / sc->zoom;
579         dy = 12.0f / height / sc->zoom;
580
581         dx = min_ff(dx, (pat_max[0] - pat_min[0]) / 2.0f);
582         dy = min_ff(dy, (pat_max[1] - pat_min[1]) / 2.0f);
583
584         return co[0] >= pos[0] - dx && co[0] <= pos[0] + dx && co[1] >= pos[1] - dy && co[1] <= pos[1] + dy;
585 }
586
587 static int mouse_on_tilt(SpaceClip *sc, MovieTrackingMarker *marker, float co[2], int width, int height)
588 {
589         float slider[2];
590
591         slide_marker_tilt_slider(marker, slider);
592
593         return mouse_on_slide_zone(sc, marker, TRACK_AREA_PAT, co, slider, 0.0f, width, height);
594 }
595
596 static bool slide_check_corners(float (*corners)[2])
597 {
598         int i, next, prev;
599         float cross = 0.0f;
600         float p[2] = {0.0f, 0.0f};
601
602         if (!isect_point_quad_v2(p, corners[0], corners[1], corners[2], corners[3]))
603                 return false;
604
605         for (i = 0; i < 4; i++) {
606                 float v1[2], v2[2], cur_cross;
607
608                 next = (i + 1) % 4;
609                 prev = (4 + i - 1) % 4;
610
611                 sub_v2_v2v2(v1, corners[i], corners[prev]);
612                 sub_v2_v2v2(v2, corners[next], corners[i]);
613
614                 cur_cross = cross_v2v2(v1, v2);
615
616                 if (fabsf(cur_cross) > FLT_EPSILON) {
617                         if (cross == 0.0f) {
618                                 cross = cur_cross;
619                         }
620                         else if (cross * cur_cross < 0.0f) {
621                                 return false;
622                         }
623                 }
624         }
625
626         return true;
627 }
628
629 static void hide_cursor(bContext *C)
630 {
631         wmWindow *win = CTX_wm_window(C);
632
633         WM_cursor_set(win, CURSOR_NONE);
634 }
635
636 static void show_cursor(bContext *C)
637 {
638         wmWindow *win = CTX_wm_window(C);
639
640         WM_cursor_set(win, CURSOR_STD);
641 }
642
643 MovieTrackingTrack *tracking_marker_check_slide(bContext *C, const wmEvent *event, int *area_r, int *action_r, int *corner_r)
644 {
645         SpaceClip *sc = CTX_wm_space_clip(C);
646         ARegion *ar = CTX_wm_region(C);
647
648         MovieClip *clip = ED_space_clip_get_clip(sc);
649         MovieTrackingTrack *track;
650         int width, height;
651         float co[2];
652         ListBase *tracksbase = BKE_tracking_get_active_tracks(&clip->tracking);
653         int framenr = ED_space_clip_get_clip_frame_number(sc);
654         int action = -1, area = 0, corner = -1;
655
656         ED_space_clip_get_size(sc, &width, &height);
657
658         if (width == 0 || height == 0)
659                 return NULL;
660
661         ED_clip_mouse_pos(sc, ar, event->mval, co);
662
663         track = tracksbase->first;
664         while (track) {
665                 if (TRACK_VIEW_SELECTED(sc, track) && (track->flag & TRACK_LOCKED) == 0) {
666                         MovieTrackingMarker *marker = BKE_tracking_marker_get(track, framenr);
667                         bool ok = false;
668
669                         if ((marker->flag & MARKER_DISABLED) == 0) {
670                                 if (mouse_on_offset(sc, track, marker, co, width, height)) {
671                                         area = TRACK_AREA_POINT;
672                                         action = SLIDE_ACTION_POS;
673                                         ok = true;
674                                 }
675
676                                 if (!ok && (sc->flag & SC_SHOW_MARKER_SEARCH)) {
677                                         if (mouse_on_corner(sc, marker, TRACK_AREA_SEARCH, co, 1, 0.0f, width, height)) {
678                                                 area = TRACK_AREA_SEARCH;
679                                                 action = SLIDE_ACTION_OFFSET;
680                                                 ok = true;
681                                         }
682                                         else if (mouse_on_corner(sc, marker, TRACK_AREA_SEARCH, co, 0, 0.0f, width, height)) {
683                                                 area = TRACK_AREA_SEARCH;
684                                                 action = SLIDE_ACTION_SIZE;
685                                                 ok = true;
686                                         }
687                                 }
688
689                                 if (!ok && (sc->flag & SC_SHOW_MARKER_PATTERN)) {
690                                         int current_corner = get_mouse_pattern_corner(sc, marker, co, width, height);
691
692                                         if (current_corner != -1) {
693                                                 area = TRACK_AREA_PAT;
694                                                 action = SLIDE_ACTION_POS;
695                                                 corner = current_corner;
696                                                 ok = true;
697                                         }
698                                         else {
699 #if 0
700                                                 /* TODO: disable for now, needs better approaches for visualization */
701
702                                                 if (mouse_on_corner(sc, marker, TRACK_AREA_PAT, co, 1, 12.0f, width, height)) {
703                                                         area = TRACK_AREA_PAT;
704                                                         action = SLIDE_ACTION_OFFSET;
705                                                         ok = true;
706                                                 }
707                                                 if (!ok && mouse_on_corner(sc, marker, TRACK_AREA_PAT, co, 0, 12.0f, width, height)) {
708                                                         area = TRACK_AREA_PAT;
709                                                         action = SLIDE_ACTION_SIZE;
710                                                         ok = true;
711                                                 }
712 #endif
713                                                 if (!ok && mouse_on_tilt(sc, marker, co, width, height)) {
714                                                         area = TRACK_AREA_PAT;
715                                                         action = SLIDE_ACTION_TILT_SIZE;
716                                                         ok = true;
717                                                 }
718                                         }
719                                 }
720
721                                 if (ok) {
722                                         if (area_r)
723                                                 *area_r = area;
724
725                                         if (action_r)
726                                                 *action_r = action;
727
728                                         if (corner_r)
729                                                 *corner_r = corner;
730
731                                         return track;
732                                 }
733                         }
734                 }
735
736                 track = track->next;
737         }
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         ED_clip_mouse_pos(sc, ar, event->mval, co);
760
761         track = tracking_marker_check_slide(C, event, &area, &action, &corner);
762         if (track) {
763                 MovieTrackingMarker *marker = BKE_tracking_marker_get(track, framenr);
764
765                 customdata = create_slide_marker_data(sc, track, marker, event, area, corner, action, width, height);
766         }
767
768         return customdata;
769 }
770
771 static int slide_marker_invoke(bContext *C, wmOperator *op, const wmEvent *event)
772 {
773         SlideMarkerData *slidedata = slide_marker_customdata(C, event);
774
775         if (slidedata) {
776                 SpaceClip *sc = CTX_wm_space_clip(C);
777                 MovieClip *clip = ED_space_clip_get_clip(sc);
778                 MovieTracking *tracking = &clip->tracking;
779
780                 tracking->act_track = slidedata->track;
781                 tracking->act_plane_track = NULL;
782
783                 op->customdata = slidedata;
784
785                 hide_cursor(C);
786                 WM_event_add_modal_handler(C, op);
787
788                 WM_event_add_notifier(C, NC_GEOM | ND_SELECT, NULL);
789
790                 return OPERATOR_RUNNING_MODAL;
791         }
792
793         return OPERATOR_PASS_THROUGH;
794 }
795
796 static void cancel_mouse_slide(SlideMarkerData *data)
797 {
798         MovieTrackingTrack *track = data->track;
799         MovieTrackingMarker *marker = data->marker;
800
801         memcpy(marker->pattern_corners, data->old_corners, sizeof(marker->pattern_corners));
802         copy_v2_v2(marker->search_min, data->old_search_min);
803         copy_v2_v2(marker->search_max, data->old_search_max);
804         copy_v2_v2(marker->pos, data->old_pos);
805         copy_v2_v2(track->offset, data->old_offset);
806
807         if (data->old_markers) {
808                 int a;
809
810                 for (a = 0; a < data->track->markersnr; a++)
811                         copy_v2_v2(data->track->markers[a].pos, data->old_markers[a]);
812         }
813 }
814
815 static void apply_mouse_slide(bContext *C, SlideMarkerData *data)
816 {
817         if (data->area == TRACK_AREA_POINT) {
818                 SpaceClip *sc = CTX_wm_space_clip(C);
819                 MovieClip *clip = ED_space_clip_get_clip(sc);
820                 MovieTrackingPlaneTrack *plane_track;
821                 ListBase *plane_tracks_base = BKE_tracking_get_active_plane_tracks(&clip->tracking);
822                 int framenr = ED_space_clip_get_clip_frame_number(sc);
823
824                 for (plane_track = plane_tracks_base->first;
825                      plane_track;
826                      plane_track = plane_track->next)
827                 {
828                         if ((plane_track->flag & PLANE_TRACK_AUTOKEY) == 0) {
829                                 int i;
830                                 for (i = 0; i < plane_track->point_tracksnr; i++) {
831                                         if (plane_track->point_tracks[i] == data->track) {
832                                                 BKE_tracking_track_plane_from_existing_motion(plane_track, framenr);
833                                                 break;
834                                         }
835                                 }
836                         }
837                 }
838         }
839 }
840
841 static void free_slide_data(SlideMarkerData *data)
842 {
843         if (data->old_markers)
844                 MEM_freeN(data->old_markers);
845
846         MEM_freeN(data);
847 }
848
849 static int slide_marker_modal(bContext *C, wmOperator *op, const wmEvent *event)
850 {
851         SpaceClip *sc = CTX_wm_space_clip(C);
852         ARegion *ar = CTX_wm_region(C);
853
854         SlideMarkerData *data = (SlideMarkerData *)op->customdata;
855         float dx, dy, mdelta[2];
856
857         switch (event->type) {
858                 case LEFTCTRLKEY:
859                 case RIGHTCTRLKEY:
860                 case LEFTSHIFTKEY:
861                 case RIGHTSHIFTKEY:
862                         if (data->action == SLIDE_ACTION_SIZE)
863                                 if (ELEM(event->type, LEFTCTRLKEY, RIGHTCTRLKEY))
864                                         data->lock = event->val == KM_RELEASE;
865
866                         if (ELEM(event->type, LEFTSHIFTKEY, RIGHTSHIFTKEY))
867                                 data->accurate = event->val == KM_PRESS;
868
869                         /* fall-through */
870                 case MOUSEMOVE:
871                         mdelta[0] = event->mval[0] - data->mval[0];
872                         mdelta[1] = event->mval[1] - data->mval[1];
873
874                         dx = mdelta[0] / data->width / sc->zoom;
875
876                         if (data->lock)
877                                 dy = -dx / data->height * data->width;
878                         else
879                                 dy = mdelta[1] / data->height / sc->zoom;
880
881                         if (data->accurate) {
882                                 dx /= 5;
883                                 dy /= 5;
884                         }
885
886                         if (data->area == TRACK_AREA_POINT) {
887                                 if (data->action == SLIDE_ACTION_OFFSET) {
888                                         data->offset[0] = data->old_offset[0] + dx;
889                                         data->offset[1] = data->old_offset[1] + dy;
890                                 }
891                                 else {
892                                         data->pos[0] = data->old_pos[0] + dx;
893                                         data->pos[1] = data->old_pos[1] + dy;
894                                 }
895
896                                 WM_event_add_notifier(C, NC_OBJECT | ND_TRANSFORM, NULL);
897                                 DAG_id_tag_update(&sc->clip->id, 0);
898                         }
899                         else if (data->area == TRACK_AREA_PAT) {
900                                 if (data->action == SLIDE_ACTION_SIZE) {
901                                         float start[2], end[2];
902                                         float scale;
903
904                                         ED_clip_point_stable_pos(sc, ar, data->mval[0], data->mval[1], &start[0], &start[1]);
905
906                                         sub_v2_v2(start, data->old_pos);
907
908                                         if (len_v2(start) > 0.0f) {
909                                                 float mval[2];
910
911                                                 if (data->accurate) {
912                                                         mval[0] = data->mval[0] + (event->mval[0] - data->mval[0]) / 5.0f;
913                                                         mval[1] = data->mval[1] + (event->mval[1] - data->mval[1]) / 5.0f;
914                                                 }
915                                                 else {
916                                                         mval[0] = event->mval[0];
917                                                         mval[1] = event->mval[1];
918                                                 }
919
920                                                 ED_clip_point_stable_pos(sc, ar, mval[0], mval[1], &end[0], &end[1]);
921
922                                                 sub_v2_v2(end, data->old_pos);
923
924                                                 scale = len_v2(end) / len_v2(start);
925
926                                                 if (scale > 0.0f) {
927                                                         int a;
928
929                                                         for (a = 0; a < 4; a++) {
930                                                                 mul_v2_v2fl(data->corners[a], data->old_corners[a], scale);
931                                                         }
932                                                 }
933                                         }
934
935                                         BKE_tracking_marker_clamp(data->marker, CLAMP_PAT_DIM);
936                                 }
937                                 else if (data->action == SLIDE_ACTION_OFFSET) {
938                                         float d[2] = {dx, dy};
939                                         int a;
940
941                                         for (a = 0; a < data->track->markersnr; a++)
942                                                 add_v2_v2v2(data->track->markers[a].pos, data->old_markers[a], d);
943
944                                         sub_v2_v2v2(data->offset, data->old_offset, d);
945                                 }
946                                 else if (data->action == SLIDE_ACTION_POS) {
947                                         float spos[2];
948
949                                         copy_v2_v2(spos, data->pos);
950
951                                         data->pos[0] = data->spos[0] + dx;
952                                         data->pos[1] = data->spos[1] + dy;
953
954                                         if (!slide_check_corners(data->corners)) {
955                                                 copy_v2_v2(data->pos, spos);
956                                         }
957
958                                         /* currently only patterns are allowed to have such combination of event and data */
959                                         BKE_tracking_marker_clamp(data->marker, CLAMP_PAT_DIM);
960                                 }
961                                 else if (data->action == SLIDE_ACTION_TILT_SIZE) {
962                                         float start[2], end[2];
963                                         float scale = 1.0f, angle = 0.0f;
964                                         int a;
965                                         float mval[2];
966
967                                         if (data->accurate) {
968                                                 mval[0] = data->mval[0] + (event->mval[0] - data->mval[0]) / 5.0f;
969                                                 mval[1] = data->mval[1] + (event->mval[1] - data->mval[1]) / 5.0f;
970                                         }
971                                         else {
972                                                 mval[0] = event->mval[0];
973                                                 mval[1] = event->mval[1];
974                                         }
975
976                                         sub_v2_v2v2(start, data->spos, data->old_pos);
977
978                                         ED_clip_point_stable_pos(sc, ar, mval[0], mval[1], &end[0], &end[1]);
979                                         sub_v2_v2(end, data->old_pos);
980
981                                         if (len_v2(start) > 0.0f) {
982                                                 scale = len_v2(end) / len_v2(start);
983
984                                                 if (scale < 0.0f) {
985                                                         scale = 0.0;
986                                                 }
987                                         }
988
989                                         angle = -angle_signed_v2v2(start, end);
990
991                                         for (a = 0; a < 4; a++) {
992                                                 float vec[2];
993
994                                                 mul_v2_v2fl(data->corners[a], data->old_corners[a], scale);
995
996                                                 copy_v2_v2(vec, data->corners[a]);
997                                                 vec[0] *= data->width;
998                                                 vec[1] *= data->height;
999
1000                                                 data->corners[a][0] = (vec[0] * cosf(angle) - vec[1] * sinf(angle)) / data->width;
1001                                                 data->corners[a][1] = (vec[1] * cosf(angle) + vec[0] * sinf(angle)) / data->height;
1002                                         }
1003
1004                                         BKE_tracking_marker_clamp(data->marker, CLAMP_PAT_DIM);
1005
1006                                 }
1007                         }
1008                         else if (data->area == TRACK_AREA_SEARCH) {
1009                                 if (data->action == SLIDE_ACTION_SIZE) {
1010                                         data->min[0] = data->old_search_min[0] - dx;
1011                                         data->max[0] = data->old_search_max[0] + dx;
1012
1013                                         data->min[1] = data->old_search_min[1] + dy;
1014                                         data->max[1] = data->old_search_max[1] - dy;
1015
1016                                         BKE_tracking_marker_clamp(data->marker, CLAMP_SEARCH_DIM);
1017                                 }
1018                                 else if (data->area == TRACK_AREA_SEARCH) {
1019                                         float d[2] = {dx, dy};
1020
1021                                         add_v2_v2v2(data->min, data->old_search_min, d);
1022                                         add_v2_v2v2(data->max, data->old_search_max, d);
1023                                 }
1024
1025                                 BKE_tracking_marker_clamp(data->marker, CLAMP_SEARCH_POS);
1026                         }
1027
1028                         data->marker->flag &= ~MARKER_TRACKED;
1029
1030                         WM_event_add_notifier(C, NC_MOVIECLIP | NA_EDITED, NULL);
1031
1032                         break;
1033
1034                 case LEFTMOUSE:
1035                         if (event->val == KM_RELEASE) {
1036                                 apply_mouse_slide(C, op->customdata);
1037                                 free_slide_data(op->customdata);
1038
1039                                 show_cursor(C);
1040
1041                                 return OPERATOR_FINISHED;
1042                         }
1043
1044                         break;
1045
1046                 case ESCKEY:
1047                         cancel_mouse_slide(op->customdata);
1048
1049                         free_slide_data(op->customdata);
1050
1051                         show_cursor(C);
1052
1053                         WM_event_add_notifier(C, NC_MOVIECLIP | NA_EDITED, NULL);
1054
1055                         return OPERATOR_CANCELLED;
1056         }
1057
1058         return OPERATOR_RUNNING_MODAL;
1059 }
1060
1061 void CLIP_OT_slide_marker(wmOperatorType *ot)
1062 {
1063         /* identifiers */
1064         ot->name = "Slide Marker";
1065         ot->description = "Slide marker areas";
1066         ot->idname = "CLIP_OT_slide_marker";
1067
1068         /* api callbacks */
1069         ot->poll = ED_space_clip_tracking_poll;
1070         ot->invoke = slide_marker_invoke;
1071         ot->modal = slide_marker_modal;
1072
1073         /* flags */
1074         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_GRAB_POINTER | OPTYPE_BLOCKING;
1075
1076         /* properties */
1077         RNA_def_float_vector(ot->srna, "offset", 2, NULL, -FLT_MAX, FLT_MAX,
1078                              "Offset", "Offset in floating point units, 1.0 is the width and height of the image", -FLT_MAX, FLT_MAX);
1079 }
1080
1081 /********************** track operator *********************/
1082
1083 typedef struct TrackMarkersJob {
1084         struct MovieTrackingContext *context;   /* tracking context */
1085         int sfra, efra, lastfra;    /* Start, end and recently tracked frames */
1086         int backwards;              /* Backwards tracking flag */
1087         MovieClip *clip;            /* Clip which is tracking */
1088         float delay;                /* Delay in milliseconds to allow tracking at fixed FPS */
1089
1090         struct Main *main;
1091         struct Scene *scene;
1092         struct bScreen *screen;
1093 } TrackMarkersJob;
1094
1095 static int track_markers_testbreak(void)
1096 {
1097         return G.is_break;
1098 }
1099
1100 static int track_count_markers(SpaceClip *sc, MovieClip *clip)
1101 {
1102         int tot = 0;
1103         ListBase *tracksbase = BKE_tracking_get_active_tracks(&clip->tracking);
1104         MovieTrackingTrack *track;
1105         int framenr = ED_space_clip_get_clip_frame_number(sc);
1106
1107         track = tracksbase->first;
1108         while (track) {
1109                 if (TRACK_VIEW_SELECTED(sc, track) && (track->flag & TRACK_LOCKED) == 0) {
1110                         MovieTrackingMarker *marker = BKE_tracking_marker_get(track, framenr);
1111
1112                         if (!marker || (marker->flag & MARKER_DISABLED) == 0)
1113                                 tot++;
1114                 }
1115
1116                 track = track->next;
1117         }
1118
1119         return tot;
1120 }
1121
1122 static void clear_invisible_track_selection(SpaceClip *sc, MovieClip *clip)
1123 {
1124         ListBase *tracksbase = BKE_tracking_get_active_tracks(&clip->tracking);
1125         int hidden = 0;
1126
1127         if ((sc->flag & SC_SHOW_MARKER_PATTERN) == 0)
1128                 hidden |= TRACK_AREA_PAT;
1129
1130         if ((sc->flag & SC_SHOW_MARKER_SEARCH) == 0)
1131                 hidden |= TRACK_AREA_SEARCH;
1132
1133         if (hidden) {
1134                 MovieTrackingTrack *track = tracksbase->first;
1135
1136                 while (track) {
1137                         if ((track->flag & TRACK_HIDDEN) == 0)
1138                                 BKE_tracking_track_flag_clear(track, hidden, SELECT);
1139
1140                         track = track->next;
1141                 }
1142         }
1143 }
1144
1145 static void track_init_markers(SpaceClip *sc, MovieClip *clip, int *frames_limit_r)
1146 {
1147         ListBase *tracksbase = BKE_tracking_get_active_tracks(&clip->tracking);
1148         MovieTrackingTrack *track;
1149         int framenr = ED_space_clip_get_clip_frame_number(sc);
1150         int frames_limit = 0;
1151
1152         clear_invisible_track_selection(sc, clip);
1153
1154         track = tracksbase->first;
1155         while (track) {
1156                 if (TRACK_VIEW_SELECTED(sc, track)) {
1157                         if ((track->flag & TRACK_HIDDEN) == 0 && (track->flag & TRACK_LOCKED) == 0) {
1158                                 BKE_tracking_marker_ensure(track, framenr);
1159
1160                                 if (track->frames_limit) {
1161                                         if (frames_limit == 0)
1162                                                 frames_limit = track->frames_limit;
1163                                         else
1164                                                 frames_limit = min_ii(frames_limit, (int)track->frames_limit);
1165                                 }
1166                         }
1167                 }
1168
1169                 track = track->next;
1170         }
1171
1172         *frames_limit_r = frames_limit;
1173 }
1174
1175 static bool track_markers_check_direction(int backwards, int curfra, int efra)
1176 {
1177         if (backwards) {
1178                 if (curfra < efra)
1179                         return false;
1180         }
1181         else {
1182                 if (curfra > efra)
1183                         return false;
1184         }
1185
1186         return true;
1187 }
1188
1189 static int track_markers_initjob(bContext *C, TrackMarkersJob *tmj, int backwards)
1190 {
1191         SpaceClip *sc = CTX_wm_space_clip(C);
1192         MovieClip *clip = ED_space_clip_get_clip(sc);
1193         Scene *scene = CTX_data_scene(C);
1194         MovieTrackingSettings *settings = &clip->tracking.settings;
1195         int frames_limit;
1196
1197         track_init_markers(sc, clip, &frames_limit);
1198
1199         tmj->sfra = ED_space_clip_get_clip_frame_number(sc);
1200         tmj->clip = clip;
1201         tmj->backwards = backwards;
1202
1203         if (backwards)
1204                 tmj->efra = SFRA;
1205         else
1206                 tmj->efra = EFRA;
1207
1208         /* limit frames to be tracked by user setting */
1209         if (frames_limit) {
1210                 if (backwards)
1211                         tmj->efra = MAX2(tmj->efra, tmj->sfra - frames_limit);
1212                 else
1213                         tmj->efra = MIN2(tmj->efra, tmj->sfra + frames_limit);
1214         }
1215
1216         tmj->efra = BKE_movieclip_remap_scene_to_clip_frame(clip, tmj->efra);
1217
1218         if (settings->speed != TRACKING_SPEED_FASTEST) {
1219                 tmj->delay = 1.0f / scene->r.frs_sec * 1000.0f;
1220
1221                 if (settings->speed == TRACKING_SPEED_HALF)
1222                         tmj->delay *= 2;
1223                 else if (settings->speed == TRACKING_SPEED_QUARTER)
1224                         tmj->delay *= 4;
1225                 else if (settings->speed == TRACKING_SPEED_DOUBLE)
1226                         tmj->delay /= 2;
1227         }
1228
1229         tmj->context = BKE_tracking_context_new(clip, &sc->user, backwards, 1);
1230
1231         clip->tracking_context = tmj->context;
1232
1233         tmj->lastfra = tmj->sfra;
1234
1235         /* XXX: silly to store this, but this data is needed to update scene and movie-clip
1236          *      frame numbers when tracking is finished. This introduces better feedback for artists.
1237          *      Maybe there's another way to solve this problem, but can't think better way atm.
1238          *      Anyway, this way isn't more unstable as animation rendering animation
1239          *      which uses the same approach (except storing screen). */
1240         tmj->scene = scene;
1241         tmj->main = CTX_data_main(C);
1242         tmj->screen = CTX_wm_screen(C);
1243
1244         return track_markers_check_direction(backwards, tmj->sfra, tmj->efra);
1245 }
1246
1247 static void track_markers_startjob(void *tmv, short *stop, short *do_update, float *progress)
1248 {
1249         TrackMarkersJob *tmj = (TrackMarkersJob *)tmv;
1250         int framenr = tmj->sfra;
1251         // double t = PIL_check_seconds_timer();
1252
1253         while (framenr != tmj->efra) {
1254                 if (tmj->delay > 0) {
1255                         /* tracking should happen with fixed fps. Calculate time
1256                          * using current timer value before tracking frame and after.
1257                          *
1258                          * Small (and maybe unneeded optimization): do not calculate exec_time
1259                          * for "Fastest" tracking */
1260
1261                         double start_time = PIL_check_seconds_timer(), exec_time;
1262
1263                         if (!BKE_tracking_context_step(tmj->context))
1264                                 break;
1265
1266                         exec_time = PIL_check_seconds_timer() - start_time;
1267                         if (tmj->delay > (float)exec_time)
1268                                 PIL_sleep_ms(tmj->delay - (float)exec_time);
1269                 }
1270                 else if (!BKE_tracking_context_step(tmj->context))
1271                         break;
1272
1273                 *do_update = TRUE;
1274                 *progress = (float)(framenr - tmj->sfra) / (tmj->efra - tmj->sfra);
1275
1276                 if (tmj->backwards)
1277                         framenr--;
1278                 else
1279                         framenr++;
1280
1281                 tmj->lastfra = framenr;
1282
1283                 if (*stop || track_markers_testbreak())
1284                         break;
1285         }
1286
1287         // printf("Tracking time: %lf\n", PIL_check_seconds_timer()-t);
1288 }
1289
1290 static void track_markers_updatejob(void *tmv)
1291 {
1292         TrackMarkersJob *tmj = (TrackMarkersJob *)tmv;
1293
1294         BKE_tracking_context_sync(tmj->context);
1295 }
1296
1297 static void track_markers_endjob(void *tmv)
1298 {
1299         TrackMarkersJob *tmj = (TrackMarkersJob *)tmv;
1300
1301         tmj->clip->tracking_context = NULL;
1302         tmj->scene->r.cfra = BKE_movieclip_remap_clip_to_scene_frame(tmj->clip, tmj->lastfra);
1303         ED_update_for_newframe(tmj->main, tmj->scene, 0);
1304
1305         BKE_tracking_context_sync(tmj->context);
1306         BKE_tracking_context_finish(tmj->context);
1307
1308         WM_main_add_notifier(NC_SCENE | ND_FRAME, tmj->scene);
1309 }
1310
1311 static void track_markers_freejob(void *tmv)
1312 {
1313         TrackMarkersJob *tmj = (TrackMarkersJob *)tmv;
1314         BKE_tracking_context_free(tmj->context);
1315         MEM_freeN(tmj);
1316 }
1317
1318 static int track_markers_exec(bContext *C, wmOperator *op)
1319 {
1320         SpaceClip *sc = CTX_wm_space_clip(C);
1321         MovieClip *clip = ED_space_clip_get_clip(sc);
1322         Scene *scene = CTX_data_scene(C);
1323         struct MovieTrackingContext *context;
1324         int framenr = ED_space_clip_get_clip_frame_number(sc);
1325         int sfra = framenr, efra;
1326         int backwards = RNA_boolean_get(op->ptr, "backwards");
1327         int sequence = RNA_boolean_get(op->ptr, "sequence");
1328         int frames_limit;
1329
1330         if (track_count_markers(sc, clip) == 0)
1331                 return OPERATOR_CANCELLED;
1332
1333         track_init_markers(sc, clip, &frames_limit);
1334
1335         if (backwards)
1336                 efra = SFRA;
1337         else
1338                 efra = EFRA;
1339
1340         /* limit frames to be tracked by user setting */
1341         if (frames_limit) {
1342                 if (backwards)
1343                         efra = MAX2(efra, sfra - frames_limit);
1344                 else
1345                         efra = MIN2(efra, sfra + frames_limit);
1346         }
1347
1348         efra = BKE_movieclip_remap_scene_to_clip_frame(clip, efra);
1349
1350         if (!track_markers_check_direction(backwards, framenr, efra))
1351                 return OPERATOR_CANCELLED;
1352
1353         /* do not disable tracks due to threshold when tracking frame-by-frame */
1354         context = BKE_tracking_context_new(clip, &sc->user, backwards, sequence);
1355
1356         while (framenr != efra) {
1357                 if (!BKE_tracking_context_step(context))
1358                         break;
1359
1360                 if (backwards) framenr--;
1361                 else framenr++;
1362
1363                 if (!sequence)
1364                         break;
1365         }
1366
1367         BKE_tracking_context_sync(context);
1368         BKE_tracking_context_finish(context);
1369         BKE_tracking_context_free(context);
1370
1371         /* update scene current frame to the lastes tracked frame */
1372         scene->r.cfra = BKE_movieclip_remap_clip_to_scene_frame(clip, framenr);
1373
1374         WM_event_add_notifier(C, NC_MOVIECLIP | NA_EVALUATED, clip);
1375         WM_event_add_notifier(C, NC_SCENE | ND_FRAME, scene);
1376
1377         return OPERATOR_FINISHED;
1378 }
1379
1380 static int track_markers_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
1381 {
1382         TrackMarkersJob *tmj;
1383         ScrArea *sa = CTX_wm_area(C);
1384         SpaceClip *sc = CTX_wm_space_clip(C);
1385         MovieClip *clip = ED_space_clip_get_clip(sc);
1386         wmJob *wm_job;
1387         int backwards = RNA_boolean_get(op->ptr, "backwards");
1388         int sequence = RNA_boolean_get(op->ptr, "sequence");
1389
1390         if (WM_jobs_test(CTX_wm_manager(C), CTX_wm_area(C), WM_JOB_TYPE_ANY)) {
1391                 /* only one tracking is allowed at a time */
1392                 return OPERATOR_CANCELLED;
1393         }
1394
1395         if (clip->tracking_context)
1396                 return OPERATOR_CANCELLED;
1397
1398         if (track_count_markers(sc, clip) == 0)
1399                 return OPERATOR_CANCELLED;
1400
1401         if (!sequence)
1402                 return track_markers_exec(C, op);
1403
1404         tmj = MEM_callocN(sizeof(TrackMarkersJob), "TrackMarkersJob data");
1405         if (!track_markers_initjob(C, tmj, backwards)) {
1406                 track_markers_freejob(tmj);
1407
1408                 return OPERATOR_CANCELLED;
1409         }
1410
1411         /* setup job */
1412         wm_job = WM_jobs_get(CTX_wm_manager(C), CTX_wm_window(C), sa, "Track Markers",
1413                              WM_JOB_PROGRESS, WM_JOB_TYPE_CLIP_TRACK_MARKERS);
1414         WM_jobs_customdata_set(wm_job, tmj, track_markers_freejob);
1415
1416         /* if there's delay set in tracking job, tracking should happen
1417          * with fixed FPS. To deal with editor refresh we have to synchronize
1418          * tracks from job and tracks in clip. Do this in timer callback
1419          * to prevent threading conflicts. */
1420         if (tmj->delay > 0)
1421                 WM_jobs_timer(wm_job, tmj->delay / 1000.0f, NC_MOVIECLIP | NA_EVALUATED, 0);
1422         else
1423                 WM_jobs_timer(wm_job, 0.2, NC_MOVIECLIP | NA_EVALUATED, 0);
1424
1425         WM_jobs_callbacks(wm_job, track_markers_startjob, NULL, track_markers_updatejob, track_markers_endjob);
1426
1427         G.is_break = FALSE;
1428
1429         WM_jobs_start(CTX_wm_manager(C), wm_job);
1430         WM_cursor_wait(0);
1431
1432         /* add modal handler for ESC */
1433         WM_event_add_modal_handler(C, op);
1434
1435         return OPERATOR_RUNNING_MODAL;
1436 }
1437
1438 static int track_markers_modal(bContext *C, wmOperator *UNUSED(op), const wmEvent *event)
1439 {
1440         /* no running tracking, remove handler and pass through */
1441         if (0 == WM_jobs_test(CTX_wm_manager(C), CTX_wm_area(C), WM_JOB_TYPE_ANY))
1442                 return OPERATOR_FINISHED | OPERATOR_PASS_THROUGH;
1443
1444         /* running tracking */
1445         switch (event->type) {
1446                 case ESCKEY:
1447                         return OPERATOR_RUNNING_MODAL;
1448                         break;
1449         }
1450
1451         return OPERATOR_PASS_THROUGH;
1452 }
1453
1454 void CLIP_OT_track_markers(wmOperatorType *ot)
1455 {
1456         /* identifiers */
1457         ot->name = "Track Markers";
1458         ot->description = "Track selected markers";
1459         ot->idname = "CLIP_OT_track_markers";
1460
1461         /* api callbacks */
1462         ot->exec = track_markers_exec;
1463         ot->invoke = track_markers_invoke;
1464         ot->poll = ED_space_clip_tracking_poll;
1465         ot->modal = track_markers_modal;
1466
1467         /* flags */
1468         ot->flag = OPTYPE_UNDO;
1469
1470         /* properties */
1471         RNA_def_boolean(ot->srna, "backwards", 0, "Backwards", "Do backwards tracking");
1472         RNA_def_boolean(ot->srna, "sequence", 0, "Track Sequence", "Track marker during image sequence rather than single image");
1473 }
1474
1475 /********************** refine track position operator *********************/
1476
1477 static int refine_marker_exec(bContext *C, wmOperator *op)
1478 {
1479         SpaceClip *sc = CTX_wm_space_clip(C);
1480         MovieClip *clip = ED_space_clip_get_clip(sc);
1481         MovieTracking *tracking = &clip->tracking;
1482         ListBase *tracksbase = BKE_tracking_get_active_tracks(tracking);
1483         MovieTrackingTrack *track;
1484         bool backwards = RNA_boolean_get(op->ptr, "backwards");
1485         int framenr = ED_space_clip_get_clip_frame_number(sc);
1486
1487         for (track = tracksbase->first; track; track = track->next) {
1488                 if (TRACK_VIEW_SELECTED(sc, track)) {
1489                         MovieTrackingMarker *marker = BKE_tracking_marker_get(track, framenr);
1490
1491                         BKE_tracking_refine_marker(clip, track, marker, backwards);
1492                 }
1493         }
1494
1495         WM_event_add_notifier(C, NC_MOVIECLIP | NA_EVALUATED, clip);
1496
1497         return OPERATOR_FINISHED;
1498 }
1499
1500 void CLIP_OT_refine_markers(wmOperatorType *ot)
1501 {
1502         /* identifiers */
1503         ot->name = "Refine Markers";
1504         ot->description = "Refine selected markers positions "
1505                           "by running the tracker from track's reference to current frame";
1506         ot->idname = "CLIP_OT_refine_markers";
1507
1508         /* api callbacks */
1509         ot->exec = refine_marker_exec;
1510         ot->poll = ED_space_clip_tracking_poll;
1511
1512         /* flags */
1513         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1514
1515         /* properties */
1516         RNA_def_boolean(ot->srna, "backwards", 0, "Backwards", "Do backwards tracking");
1517 }
1518
1519 /********************** solve camera operator *********************/
1520
1521 typedef struct {
1522         Scene *scene;
1523         MovieClip *clip;
1524         MovieClipUser user;
1525
1526         ReportList *reports;
1527
1528         char stats_message[256];
1529
1530         struct MovieReconstructContext *context;
1531 } SolveCameraJob;
1532
1533 static int solve_camera_initjob(bContext *C, SolveCameraJob *scj, wmOperator *op, char *error_msg, int max_error)
1534 {
1535         SpaceClip *sc = CTX_wm_space_clip(C);
1536         MovieClip *clip = ED_space_clip_get_clip(sc);
1537         Scene *scene = CTX_data_scene(C);
1538         MovieTracking *tracking = &clip->tracking;
1539         MovieTrackingObject *object = BKE_tracking_object_get_active(tracking);
1540         int width, height;
1541
1542         if (!BKE_tracking_reconstruction_check(tracking, object, error_msg, max_error))
1543                 return 0;
1544
1545         /* could fail if footage uses images with different sizes */
1546         BKE_movieclip_get_size(clip, &sc->user, &width, &height);
1547
1548         scj->clip = clip;
1549         scj->scene = scene;
1550         scj->reports = op->reports;
1551         scj->user = sc->user;
1552
1553         scj->context = BKE_tracking_reconstruction_context_new(clip, object,
1554                                                                object->keyframe1, object->keyframe2, width, height);
1555
1556         tracking->stats = MEM_callocN(sizeof(MovieTrackingStats), "solve camera stats");
1557
1558         return 1;
1559 }
1560
1561 static void solve_camera_updatejob(void *scv)
1562 {
1563         SolveCameraJob *scj = (SolveCameraJob *)scv;
1564         MovieTracking *tracking = &scj->clip->tracking;
1565
1566         BLI_strncpy(tracking->stats->message, scj->stats_message, sizeof(tracking->stats->message));
1567 }
1568
1569 static void solve_camera_startjob(void *scv, short *stop, short *do_update, float *progress)
1570 {
1571         SolveCameraJob *scj = (SolveCameraJob *)scv;
1572
1573         BKE_tracking_reconstruction_solve(scj->context, stop, do_update, progress,
1574                                           scj->stats_message, sizeof(scj->stats_message));
1575 }
1576
1577 static void solve_camera_freejob(void *scv)
1578 {
1579         SolveCameraJob *scj = (SolveCameraJob *)scv;
1580         MovieTracking *tracking = &scj->clip->tracking;
1581         Scene *scene = scj->scene;
1582         MovieClip *clip = scj->clip;
1583         int solved;
1584
1585         if (!scj->context) {
1586                 /* job weren't fully initialized due to some error */
1587                 MEM_freeN(scj);
1588                 return;
1589         }
1590
1591         solved = BKE_tracking_reconstruction_finish(scj->context, tracking);
1592         if (!solved)
1593                 BKE_report(scj->reports, RPT_WARNING, "Some data failed to reconstruct (see console for details)");
1594         else
1595                 BKE_reportf(scj->reports, RPT_INFO, "Average re-projection error: %.3f", tracking->reconstruction.error);
1596
1597         /* set currently solved clip as active for scene */
1598         if (scene->clip)
1599                 id_us_min(&clip->id);
1600
1601         scene->clip = clip;
1602         id_us_plus(&clip->id);
1603
1604         /* set blender camera focal length so result would look fine there */
1605         if (scene->camera && scene->camera->data && GS(((ID *) scene->camera->data)->name) == ID_CA) {
1606                 Camera *camera = (Camera *)scene->camera->data;
1607                 int width, height;
1608
1609                 BKE_movieclip_get_size(clip, &scj->user, &width, &height);
1610
1611                 BKE_tracking_camera_to_blender(tracking, scene, camera, width, height);
1612
1613                 WM_main_add_notifier(NC_OBJECT, camera);
1614         }
1615
1616         MEM_freeN(tracking->stats);
1617         tracking->stats = NULL;
1618
1619         DAG_id_tag_update(&clip->id, 0);
1620
1621         WM_main_add_notifier(NC_MOVIECLIP | NA_EVALUATED, clip);
1622         WM_main_add_notifier(NC_OBJECT | ND_TRANSFORM, NULL);
1623
1624         /* update active clip displayed in scene buttons */
1625         WM_main_add_notifier(NC_SCENE, scene);
1626
1627         BKE_tracking_reconstruction_context_free(scj->context);
1628         MEM_freeN(scj);
1629 }
1630
1631 static int solve_camera_exec(bContext *C, wmOperator *op)
1632 {
1633         SolveCameraJob *scj;
1634         char error_msg[256] = "\0";
1635
1636         scj = MEM_callocN(sizeof(SolveCameraJob), "SolveCameraJob data");
1637         if (!solve_camera_initjob(C, scj, op, error_msg, sizeof(error_msg))) {
1638                 if (error_msg[0])
1639                         BKE_report(op->reports, RPT_ERROR, error_msg);
1640
1641                 solve_camera_freejob(scj);
1642
1643                 return OPERATOR_CANCELLED;
1644         }
1645
1646         solve_camera_startjob(scj, NULL, NULL, NULL);
1647
1648         solve_camera_freejob(scj);
1649
1650         return OPERATOR_FINISHED;
1651 }
1652
1653 static int solve_camera_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
1654 {
1655         SolveCameraJob *scj;
1656         ScrArea *sa = CTX_wm_area(C);
1657         SpaceClip *sc = CTX_wm_space_clip(C);
1658         MovieClip *clip = ED_space_clip_get_clip(sc);
1659         MovieTracking *tracking = &clip->tracking;
1660         MovieTrackingReconstruction *reconstruction = BKE_tracking_get_active_reconstruction(tracking);
1661         wmJob *wm_job;
1662         char error_msg[256] = "\0";
1663
1664         if (WM_jobs_test(CTX_wm_manager(C), CTX_wm_area(C), WM_JOB_TYPE_ANY)) {
1665                 /* only one solve is allowed at a time */
1666                 return OPERATOR_CANCELLED;
1667         }
1668
1669         scj = MEM_callocN(sizeof(SolveCameraJob), "SolveCameraJob data");
1670         if (!solve_camera_initjob(C, scj, op, error_msg, sizeof(error_msg))) {
1671                 if (error_msg[0])
1672                         BKE_report(op->reports, RPT_ERROR, error_msg);
1673
1674                 solve_camera_freejob(scj);
1675
1676                 return OPERATOR_CANCELLED;
1677         }
1678
1679         BLI_strncpy(tracking->stats->message, "Solving camera | Preparing solve", sizeof(tracking->stats->message));
1680
1681         /* hide reconstruction statistics from previous solve */
1682         reconstruction->flag &= ~TRACKING_RECONSTRUCTED;
1683         WM_event_add_notifier(C, NC_MOVIECLIP | NA_EVALUATED, clip);
1684
1685         /* setup job */
1686         wm_job = WM_jobs_get(CTX_wm_manager(C), CTX_wm_window(C), sa, "Solve Camera",
1687                              WM_JOB_PROGRESS, WM_JOB_TYPE_CLIP_SOLVE_CAMERA);
1688         WM_jobs_customdata_set(wm_job, scj, solve_camera_freejob);
1689         WM_jobs_timer(wm_job, 0.1, NC_MOVIECLIP | NA_EVALUATED, 0);
1690         WM_jobs_callbacks(wm_job, solve_camera_startjob, NULL, solve_camera_updatejob, NULL);
1691
1692         G.is_break = FALSE;
1693
1694         WM_jobs_start(CTX_wm_manager(C), wm_job);
1695         WM_cursor_wait(0);
1696
1697         /* add modal handler for ESC */
1698         WM_event_add_modal_handler(C, op);
1699
1700         return OPERATOR_RUNNING_MODAL;
1701 }
1702
1703 static int solve_camera_modal(bContext *C, wmOperator *UNUSED(op), const wmEvent *event)
1704 {
1705         /* no running solver, remove handler and pass through */
1706         if (0 == WM_jobs_test(CTX_wm_manager(C), CTX_wm_area(C), WM_JOB_TYPE_ANY))
1707                 return OPERATOR_FINISHED | OPERATOR_PASS_THROUGH;
1708
1709         /* running tracking */
1710         switch (event->type) {
1711                 case ESCKEY:
1712                         return OPERATOR_RUNNING_MODAL;
1713                         break;
1714         }
1715
1716         return OPERATOR_PASS_THROUGH;
1717 }
1718
1719 void CLIP_OT_solve_camera(wmOperatorType *ot)
1720 {
1721         /* identifiers */
1722         ot->name = "Solve Camera";
1723         ot->description = "Solve camera motion from tracks";
1724         ot->idname = "CLIP_OT_solve_camera";
1725
1726         /* api callbacks */
1727         ot->exec = solve_camera_exec;
1728         ot->invoke = solve_camera_invoke;
1729         ot->modal = solve_camera_modal;
1730         ot->poll = ED_space_clip_tracking_poll;
1731
1732         /* flags */
1733         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1734 }
1735
1736 /********************** clear solution operator *********************/
1737
1738 static int clear_solution_exec(bContext *C, wmOperator *UNUSED(op))
1739 {
1740         SpaceClip *sc = CTX_wm_space_clip(C);
1741         MovieClip *clip = ED_space_clip_get_clip(sc);
1742         MovieTracking *tracking = &clip->tracking;
1743         ListBase *tracksbase = BKE_tracking_get_active_tracks(&clip->tracking);
1744         MovieTrackingReconstruction *reconstruction = BKE_tracking_get_active_reconstruction(tracking);
1745         MovieTrackingTrack *track = tracksbase->first;
1746
1747         while (track) {
1748                 track->flag &= ~TRACK_HAS_BUNDLE;
1749
1750                 track = track->next;
1751         }
1752
1753         if (reconstruction->cameras)
1754                 MEM_freeN(reconstruction->cameras);
1755
1756         reconstruction->cameras = NULL;
1757         reconstruction->camnr = 0;
1758
1759         reconstruction->flag &= ~TRACKING_RECONSTRUCTED;
1760
1761         DAG_id_tag_update(&clip->id, 0);
1762
1763         WM_event_add_notifier(C, NC_MOVIECLIP | NA_EVALUATED, clip);
1764         WM_event_add_notifier(C, NC_SPACE | ND_SPACE_VIEW3D, NULL);
1765
1766         return OPERATOR_FINISHED;
1767 }
1768
1769 void CLIP_OT_clear_solution(wmOperatorType *ot)
1770 {
1771         /* identifiers */
1772         ot->name = "Clear Solution";
1773         ot->description = "Clear all calculated data";
1774         ot->idname = "CLIP_OT_clear_solution";
1775
1776         /* api callbacks */
1777         ot->exec = clear_solution_exec;
1778         ot->poll = ED_space_clip_tracking_poll;
1779
1780         /* flags */
1781         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1782 }
1783
1784 /********************** clear track operator *********************/
1785
1786 static int clear_track_path_exec(bContext *C, wmOperator *op)
1787 {
1788         SpaceClip *sc = CTX_wm_space_clip(C);
1789         MovieClip *clip = ED_space_clip_get_clip(sc);
1790         MovieTracking *tracking = &clip->tracking;
1791         MovieTrackingTrack *track;
1792         ListBase *tracksbase = BKE_tracking_get_active_tracks(tracking);
1793         int action = RNA_enum_get(op->ptr, "action");
1794         int clear_active = RNA_boolean_get(op->ptr, "clear_active");
1795         int framenr = ED_space_clip_get_clip_frame_number(sc);
1796
1797         if (clear_active) {
1798                 track = BKE_tracking_track_get_active(tracking);
1799                 if (track) {
1800                         BKE_tracking_track_path_clear(track, framenr, action);
1801                 }
1802         }
1803         else {
1804                 track = tracksbase->first;
1805                 while (track) {
1806                         if (TRACK_VIEW_SELECTED(sc, track))
1807                                 BKE_tracking_track_path_clear(track, framenr, action);
1808
1809                         track = track->next;
1810                 }
1811         }
1812
1813         BKE_tracking_dopesheet_tag_update(tracking);
1814         WM_event_add_notifier(C, NC_MOVIECLIP | NA_EVALUATED, clip);
1815
1816         return OPERATOR_FINISHED;
1817 }
1818
1819 void CLIP_OT_clear_track_path(wmOperatorType *ot)
1820 {
1821         static EnumPropertyItem clear_path_actions[] = {
1822                 {TRACK_CLEAR_UPTO, "UPTO", 0, "Clear up-to", "Clear path up to current frame"},
1823                 {TRACK_CLEAR_REMAINED, "REMAINED", 0, "Clear remained", "Clear path at remaining frames (after current)"},
1824                 {TRACK_CLEAR_ALL, "ALL", 0, "Clear all", "Clear the whole path"},
1825                 {0, NULL, 0, NULL, NULL}
1826         };
1827
1828         /* identifiers */
1829         ot->name = "Clear Track Path";
1830         ot->description = "Clear tracks after/before current position or clear the whole track";
1831         ot->idname = "CLIP_OT_clear_track_path";
1832
1833         /* api callbacks */
1834         ot->exec = clear_track_path_exec;
1835         ot->poll = ED_space_clip_tracking_poll;
1836
1837         /* flags */
1838         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1839
1840         /* proeprties */
1841         RNA_def_enum(ot->srna, "action", clear_path_actions, TRACK_CLEAR_REMAINED, "Action", "Clear action to execute");
1842         RNA_def_boolean(ot->srna, "clear_active", 0, "Clear Active", "Clear active track only instead of all selected tracks");
1843 }
1844
1845 /********************** disable markers operator *********************/
1846
1847 static int disable_markers_exec(bContext *C, wmOperator *op)
1848 {
1849         SpaceClip *sc = CTX_wm_space_clip(C);
1850         MovieClip *clip = ED_space_clip_get_clip(sc);
1851         MovieTracking *tracking = &clip->tracking;
1852         ListBase *tracksbase = BKE_tracking_get_active_tracks(tracking);
1853         MovieTrackingTrack *track = tracksbase->first;
1854         int action = RNA_enum_get(op->ptr, "action");
1855         int framenr = ED_space_clip_get_clip_frame_number(sc);
1856
1857         while (track) {
1858                 if (TRACK_VIEW_SELECTED(sc, track) && (track->flag & TRACK_LOCKED) == 0) {
1859                         MovieTrackingMarker *marker = BKE_tracking_marker_ensure(track, framenr);
1860
1861                         if (action == 0)
1862                                 marker->flag |= MARKER_DISABLED;
1863                         else if (action == 1)
1864                                 marker->flag &= ~MARKER_DISABLED;
1865                         else marker->flag ^= MARKER_DISABLED;
1866                 }
1867
1868                 track = track->next;
1869         }
1870
1871         DAG_id_tag_update(&clip->id, 0);
1872
1873         WM_event_add_notifier(C, NC_MOVIECLIP | NA_EVALUATED, clip);
1874
1875         return OPERATOR_FINISHED;
1876 }
1877
1878 void CLIP_OT_disable_markers(wmOperatorType *ot)
1879 {
1880         static EnumPropertyItem actions_items[] = {
1881                 {0, "DISABLE", 0, "Disable", "Disable selected markers"},
1882                 {1, "ENABLE", 0, "Enable", "Enable selected markers"},
1883                 {2, "TOGGLE", 0, "Toggle", "Toggle disabled flag for selected markers"},
1884                 {0, NULL, 0, NULL, NULL}
1885         };
1886
1887         /* identifiers */
1888         ot->name = "Disable Markers";
1889         ot->description = "Disable/enable selected markers";
1890         ot->idname = "CLIP_OT_disable_markers";
1891
1892         /* api callbacks */
1893         ot->exec = disable_markers_exec;
1894         ot->poll = ED_space_clip_tracking_poll;
1895
1896         /* flags */
1897         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1898
1899         /* properties */
1900         RNA_def_enum(ot->srna, "action", actions_items, 0, "Action", "Disable action to execute");
1901 }
1902
1903 /********************** set origin operator *********************/
1904
1905 static Object *get_camera_with_movieclip(Scene *scene, MovieClip *clip)
1906 {
1907         Object *camera = scene->camera;
1908         Base *base;
1909
1910         if (camera && BKE_object_movieclip_get(scene, camera, false) == clip)
1911                 return camera;
1912
1913         base = scene->base.first;
1914         while (base) {
1915                 if (base->object->type == OB_CAMERA) {
1916                         if (BKE_object_movieclip_get(scene, base->object, false) == clip) {
1917                                 camera = base->object;
1918                                 break;
1919                         }
1920                 }
1921
1922                 base = base->next;
1923         }
1924
1925         return camera;
1926 }
1927
1928 static Object *get_orientation_object(bContext *C)
1929 {
1930         Scene *scene = CTX_data_scene(C);
1931         SpaceClip *sc = CTX_wm_space_clip(C);
1932         MovieClip *clip = ED_space_clip_get_clip(sc);
1933         MovieTracking *tracking = &clip->tracking;
1934         MovieTrackingObject *tracking_object = BKE_tracking_object_get_active(tracking);
1935         Object *object = NULL;
1936
1937         if (tracking_object->flag & TRACKING_OBJECT_CAMERA) {
1938                 object = get_camera_with_movieclip(scene, clip);
1939         }
1940         else {
1941                 object = OBACT;
1942         }
1943
1944         if (object && object->parent)
1945                 object = object->parent;
1946
1947         return object;
1948 }
1949
1950 static int set_orientation_poll(bContext *C)
1951 {
1952         SpaceClip *sc = CTX_wm_space_clip(C);
1953
1954         if (sc) {
1955                 Scene *scene = CTX_data_scene(C);
1956                 MovieClip *clip = ED_space_clip_get_clip(sc);
1957
1958                 if (clip) {
1959                         MovieTracking *tracking = &clip->tracking;
1960                         MovieTrackingObject *tracking_object = BKE_tracking_object_get_active(tracking);
1961
1962                         if (tracking_object->flag & TRACKING_OBJECT_CAMERA) {
1963                                 return TRUE;
1964                         }
1965                         else {
1966                                 return OBACT != NULL;
1967                         }
1968                 }
1969         }
1970
1971         return FALSE;
1972 }
1973
1974 static int count_selected_bundles(bContext *C)
1975 {
1976         SpaceClip *sc = CTX_wm_space_clip(C);
1977         MovieClip *clip = ED_space_clip_get_clip(sc);
1978         ListBase *tracksbase = BKE_tracking_get_active_tracks(&clip->tracking);
1979         MovieTrackingTrack *track;
1980         int tot = 0;
1981
1982         track = tracksbase->first;
1983         while (track) {
1984                 if (TRACK_VIEW_SELECTED(sc, track) && (track->flag & TRACK_HAS_BUNDLE))
1985                         tot++;
1986
1987                 track = track->next;
1988         }
1989
1990         return tot;
1991 }
1992
1993 static void object_solver_inverted_matrix(Scene *scene, Object *ob, float invmat[4][4])
1994 {
1995         bConstraint *con;
1996         bool found = false;
1997
1998         for (con = ob->constraints.first; con; con = con->next) {
1999                 bConstraintTypeInfo *cti = BKE_constraint_get_typeinfo(con);
2000
2001                 if (!cti)
2002                         continue;
2003
2004                 if (cti->type == CONSTRAINT_TYPE_OBJECTSOLVER) {
2005                         bObjectSolverConstraint *data = (bObjectSolverConstraint *)con->data;
2006
2007                         if (!found) {
2008                                 Object *cam = data->camera ? data->camera : scene->camera;
2009
2010                                 BKE_object_where_is_calc_mat4(scene, cam, invmat);
2011                         }
2012
2013                         mul_m4_m4m4(invmat, invmat, data->invmat);
2014
2015                         found = true;
2016                 }
2017         }
2018
2019         if (found)
2020                 invert_m4(invmat);
2021         else
2022                 unit_m4(invmat);
2023 }
2024
2025 static Object *object_solver_camera(Scene *scene, Object *ob)
2026 {
2027         bConstraint *con;
2028
2029         for (con = ob->constraints.first; con; con = con->next) {
2030                 bConstraintTypeInfo *cti = BKE_constraint_get_typeinfo(con);
2031
2032                 if (!cti)
2033                         continue;
2034
2035                 if (cti->type == CONSTRAINT_TYPE_OBJECTSOLVER) {
2036                         bObjectSolverConstraint *data = (bObjectSolverConstraint *)con->data;
2037
2038                         return data->camera ? data->camera : scene->camera;
2039                 }
2040         }
2041
2042         return NULL;
2043 }
2044
2045 static int set_origin_exec(bContext *C, wmOperator *op)
2046 {
2047         SpaceClip *sc = CTX_wm_space_clip(C);
2048         MovieClip *clip = ED_space_clip_get_clip(sc);
2049         MovieTracking *tracking = &clip->tracking;
2050         MovieTrackingTrack *track;
2051         MovieTrackingObject *tracking_object;
2052         Scene *scene = CTX_data_scene(C);
2053         Object *object;
2054         Object *camera = get_camera_with_movieclip(scene, clip);
2055         ListBase *tracksbase;
2056         float mat[4][4], vec[3], median[3];
2057         int selected_count = count_selected_bundles(C);
2058
2059         if (selected_count == 0) {
2060                 BKE_report(op->reports, RPT_ERROR,
2061                            "At least one track with bundle should be selected to define origin position");
2062
2063                 return OPERATOR_CANCELLED;
2064         }
2065
2066         object = get_orientation_object(C);
2067         if (!object) {
2068                 BKE_report(op->reports, RPT_ERROR, "No object to apply orientation on");
2069
2070                 return OPERATOR_CANCELLED;
2071         }
2072
2073         tracking_object = BKE_tracking_object_get_active(tracking);
2074
2075         tracksbase = BKE_tracking_object_get_tracks(tracking, tracking_object);
2076
2077         track = tracksbase->first;
2078         zero_v3(median);
2079         while (track) {
2080                 if (TRACK_VIEW_SELECTED(sc, track) && (track->flag & TRACK_HAS_BUNDLE)) {
2081                         add_v3_v3(median, track->bundle_pos);
2082                 }
2083
2084                 track = track->next;
2085         }
2086         mul_v3_fl(median, 1.0f / selected_count);
2087
2088         BKE_tracking_get_camera_object_matrix(scene, camera, mat);
2089
2090         mul_v3_m4v3(vec, mat, median);
2091
2092         if (tracking_object->flag & TRACKING_OBJECT_CAMERA) {
2093                 sub_v3_v3(object->loc, vec);
2094         }
2095         else {
2096                 object_solver_inverted_matrix(scene, object, mat);
2097                 mul_v3_m4v3(vec, mat, vec);
2098                 copy_v3_v3(object->loc, vec);
2099         }
2100
2101         DAG_id_tag_update(&clip->id, 0);
2102         DAG_id_tag_update(&object->id, OB_RECALC_OB);
2103
2104         WM_event_add_notifier(C, NC_MOVIECLIP | NA_EVALUATED, clip);
2105         WM_event_add_notifier(C, NC_OBJECT | ND_TRANSFORM, NULL);
2106
2107         return OPERATOR_FINISHED;
2108 }
2109
2110 void CLIP_OT_set_origin(wmOperatorType *ot)
2111 {
2112         /* identifiers */
2113         ot->name = "Set Origin";
2114         ot->description = "Set active marker as origin by moving camera (or it's parent if present) in 3D space";
2115         ot->idname = "CLIP_OT_set_origin";
2116
2117         /* api callbacks */
2118         ot->exec = set_origin_exec;
2119         ot->poll = set_orientation_poll;
2120
2121         /* flags */
2122         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
2123
2124         /* properties */
2125         RNA_def_boolean(ot->srna, "use_median", 0, "Use Median", "Set origin to median point of selected bundles");
2126 }
2127
2128 /********************** set floor operator *********************/
2129
2130 static void set_axis(Scene *scene,  Object *ob, MovieClip *clip, MovieTrackingObject *tracking_object,
2131                      MovieTrackingTrack *track, char axis)
2132 {
2133         Object *camera = get_camera_with_movieclip(scene, clip);
2134         int is_camera = tracking_object->flag & TRACKING_OBJECT_CAMERA;
2135         bool flip = false;
2136         float mat[4][4], vec[3], obmat[4][4], dvec[3];
2137
2138         BKE_object_to_mat4(ob, obmat);
2139
2140         BKE_tracking_get_camera_object_matrix(scene, camera, mat);
2141         mul_v3_m4v3(vec, mat, track->bundle_pos);
2142         copy_v3_v3(dvec, vec);
2143
2144         if (!is_camera) {
2145                 float imat[4][4];
2146
2147                 object_solver_inverted_matrix(scene, ob, imat);
2148                 mul_v3_m4v3(vec, imat, vec);
2149
2150                 invert_m4_m4(imat, obmat);
2151                 mul_v3_m4v3(dvec, imat, vec);
2152
2153                 sub_v3_v3(vec, obmat[3]);
2154         }
2155
2156         if (len_v2(vec) < 1e-3f)
2157                 return;
2158
2159         unit_m4(mat);
2160
2161         if (axis == 'X') {
2162                 if (fabsf(dvec[1]) < 1e-3f) {
2163                         flip = true;
2164
2165                         mat[0][0] = -1.0f; mat[0][1] = 0.0f; mat[0][2] = 0.0f;
2166                         mat[1][0] = 0.0f; mat[1][1] = -1.0f; mat[1][2] = 0.0f;
2167                         mat[2][0] = 0.0f; mat[2][1] = 0.0f; mat[2][2] = 1.0f;
2168                 }
2169                 else {
2170                         copy_v3_v3(mat[0], vec);
2171
2172                         if (is_camera || fabsf(vec[2]) < 1e-3f) {
2173                                 mat[0][2] = 0.0f;
2174                                 mat[2][0] = 0.0f; mat[2][1] = 0.0f; mat[2][2] = 1.0f;
2175                                 cross_v3_v3v3(mat[1], mat[2], mat[0]);
2176                         }
2177                         else {
2178                                 vec[2] = 0.0f;
2179
2180                                 cross_v3_v3v3(mat[1], mat[0], vec);
2181                                 cross_v3_v3v3(mat[2], mat[0], mat[1]);
2182                         }
2183                 }
2184         }
2185         else {
2186                 if (fabsf(dvec[0]) < 1e-3f) {
2187                         flip = true;
2188
2189                         mat[0][0] = -1.0f; mat[0][1] = 0.0f; mat[0][2] = 0.0f;
2190                         mat[1][0] = 0.0f; mat[1][1] = -1.0f; mat[1][2] = 0.0f;
2191                         mat[2][0] = 0.0f; mat[2][1] = 0.0f; mat[2][2] = 1.0f;
2192                 }
2193                 else {
2194                         copy_v3_v3(mat[1], vec);
2195
2196                         if (is_camera || fabsf(vec[2]) < 1e-3f) {
2197                                 mat[1][2] = 0.0f;
2198                                 mat[2][0] = 0.0f; mat[2][1] = 0.0f; mat[2][2] = 1.0f;
2199                                 cross_v3_v3v3(mat[0], mat[1], mat[2]);
2200                         }
2201                         else {
2202                                 vec[2] = 0.0f;
2203
2204                                 cross_v3_v3v3(mat[0], vec, mat[1]);
2205                                 cross_v3_v3v3(mat[2], mat[0], mat[1]);
2206                         }
2207                 }
2208         }
2209
2210         normalize_v3(mat[0]);
2211         normalize_v3(mat[1]);
2212         normalize_v3(mat[2]);
2213
2214         if (is_camera) {
2215                 invert_m4(mat);
2216
2217                 mul_m4_m4m4(mat, mat, obmat);
2218         }
2219         else {
2220                 if (!flip) {
2221                         float lmat[4][4], ilmat[4][4], rmat[3][3];
2222
2223                         BKE_object_rot_to_mat3(ob, rmat, TRUE);
2224                         invert_m3(rmat);
2225                         mul_m4_m4m3(mat, mat, rmat);
2226
2227                         unit_m4(lmat);
2228                         copy_v3_v3(lmat[3], obmat[3]);
2229                         invert_m4_m4(ilmat, lmat);
2230
2231                         mul_serie_m4(mat, lmat, mat, ilmat, obmat, NULL, NULL, NULL, NULL);
2232                 }
2233                 else {
2234                         mul_m4_m4m4(mat, obmat, mat);
2235                 }
2236         }
2237
2238         BKE_object_apply_mat4(ob, mat, 0, 0);
2239 }
2240
2241 static int set_plane_exec(bContext *C, wmOperator *op)
2242 {
2243         SpaceClip *sc = CTX_wm_space_clip(C);
2244         MovieClip *clip = ED_space_clip_get_clip(sc);
2245         Scene *scene = CTX_data_scene(C);
2246         MovieTracking *tracking = &clip->tracking;
2247         MovieTrackingObject *tracking_object;
2248         MovieTrackingTrack *track, *axis_track = NULL, *act_track;
2249         ListBase *tracksbase;
2250         Object *object;
2251         Object *camera = get_camera_with_movieclip(scene, clip);
2252         int tot = 0;
2253         float vec[3][3], mat[4][4], obmat[4][4], newmat[4][4], orig[3] = {0.0f, 0.0f, 0.0f};
2254         int plane = RNA_enum_get(op->ptr, "plane");
2255         float rot[4][4] = {{0.0f, 0.0f, -1.0f, 0.0f},
2256                            {0.0f, 1.0f, 0.0f, 0.0f},
2257                            {1.0f, 0.0f, 0.0f, 0.0f},
2258                            {0.0f, 0.0f, 0.0f, 1.0f}};  /* 90 degrees Y-axis rotation matrix */
2259
2260         if (count_selected_bundles(C) != 3) {
2261                 BKE_report(op->reports, RPT_ERROR, "Three tracks with bundles are needed to orient the floor");
2262
2263                 return OPERATOR_CANCELLED;
2264         }
2265
2266         tracking_object = BKE_tracking_object_get_active(tracking);
2267         tracksbase = BKE_tracking_object_get_tracks(tracking, tracking_object);
2268         act_track = BKE_tracking_track_get_active(tracking);
2269
2270         object = get_orientation_object(C);
2271         if (!object) {
2272                 BKE_report(op->reports, RPT_ERROR, "No object to apply orientation on");
2273
2274                 return OPERATOR_CANCELLED;
2275         }
2276
2277         BKE_tracking_get_camera_object_matrix(scene, camera, mat);
2278
2279         /* get 3 bundles to use as reference */
2280         track = tracksbase->first;
2281         while (track && tot < 3) {
2282                 if (track->flag & TRACK_HAS_BUNDLE && TRACK_VIEW_SELECTED(sc, track)) {
2283                         mul_v3_m4v3(vec[tot], mat, track->bundle_pos);
2284
2285                         if (tot == 0 || track == act_track)
2286                                 copy_v3_v3(orig, vec[tot]);
2287                         else
2288                                 axis_track = track;
2289
2290                         tot++;
2291                 }
2292
2293                 track = track->next;
2294         }
2295
2296         sub_v3_v3(vec[1], vec[0]);
2297         sub_v3_v3(vec[2], vec[0]);
2298
2299         /* construct ortho-normal basis */
2300         unit_m4(mat);
2301
2302         if (plane == 0) { /* floor */
2303                 cross_v3_v3v3(mat[0], vec[1], vec[2]);
2304                 copy_v3_v3(mat[1], vec[1]);
2305                 cross_v3_v3v3(mat[2], mat[0], mat[1]);
2306         }
2307         else if (plane == 1) { /* wall */
2308                 cross_v3_v3v3(mat[2], vec[1], vec[2]);
2309                 copy_v3_v3(mat[1], vec[1]);
2310                 cross_v3_v3v3(mat[0], mat[1], mat[2]);
2311         }
2312
2313         normalize_v3(mat[0]);
2314         normalize_v3(mat[1]);
2315         normalize_v3(mat[2]);
2316
2317         /* move to origin point */
2318         mat[3][0] = orig[0];
2319         mat[3][1] = orig[1];
2320         mat[3][2] = orig[2];
2321
2322         if (tracking_object->flag & TRACKING_OBJECT_CAMERA) {
2323                 invert_m4(mat);
2324
2325                 BKE_object_to_mat4(object, obmat);
2326                 mul_m4_m4m4(mat, mat, obmat);
2327                 mul_m4_m4m4(newmat, rot, mat);
2328                 BKE_object_apply_mat4(object, newmat, 0, 0);
2329
2330                 /* make camera have positive z-coordinate */
2331                 if (object->loc[2] < 0) {
2332                         invert_m4(rot);
2333                         mul_m4_m4m4(newmat, rot, mat);
2334                         BKE_object_apply_mat4(object, newmat, 0, 0);
2335                 }
2336         }
2337         else {
2338                 BKE_object_apply_mat4(object, mat, 0, 0);
2339         }
2340
2341         BKE_object_where_is_calc(scene, object);
2342         set_axis(scene, object, clip, tracking_object, axis_track, 'X');
2343
2344         DAG_id_tag_update(&clip->id, 0);
2345         DAG_id_tag_update(&object->id, OB_RECALC_OB);
2346
2347         WM_event_add_notifier(C, NC_MOVIECLIP | NA_EVALUATED, clip);
2348         WM_event_add_notifier(C, NC_OBJECT | ND_TRANSFORM, NULL);
2349
2350         return OPERATOR_FINISHED;
2351 }
2352
2353 void CLIP_OT_set_plane(wmOperatorType *ot)
2354 {
2355         static EnumPropertyItem plane_items[] = {
2356                 {0, "FLOOR", 0, "Floor", "Set floor plane"},
2357                 {1, "WALL", 0, "Wall", "Set wall plane"},
2358                 {0, NULL, 0, NULL, NULL}
2359         };
2360
2361         /* identifiers */
2362         ot->name = "Set Plane";
2363         ot->description = "Set plane based on 3 selected bundles by moving camera (or it's parent if present) in 3D space";
2364         ot->idname = "CLIP_OT_set_plane";
2365
2366         /* api callbacks */
2367         ot->exec = set_plane_exec;
2368         ot->poll = set_orientation_poll;
2369
2370         /* flags */
2371         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
2372
2373         /* properties */
2374         RNA_def_enum(ot->srna, "plane", plane_items, 0, "Plane", "Plane to be used for orientation");
2375 }
2376
2377 /********************** set axis operator *********************/
2378
2379 static int set_axis_exec(bContext *C, wmOperator *op)
2380 {
2381         SpaceClip *sc = CTX_wm_space_clip(C);
2382         MovieClip *clip = ED_space_clip_get_clip(sc);
2383         MovieTracking *tracking = &clip->tracking;
2384         MovieTrackingObject *tracking_object = BKE_tracking_object_get_active(tracking);
2385         MovieTrackingTrack *track;
2386         Scene *scene = CTX_data_scene(C);
2387         Object *object;
2388         ListBase *tracksbase;
2389         int axis = RNA_enum_get(op->ptr, "axis");
2390
2391         if (count_selected_bundles(C) != 1) {
2392                 BKE_report(op->reports, RPT_ERROR, "Single track with bundle should be selected to define axis");
2393
2394                 return OPERATOR_CANCELLED;
2395         }
2396
2397         object = get_orientation_object(C);
2398         if (!object) {
2399                 BKE_report(op->reports, RPT_ERROR, "No object to apply orientation on");
2400
2401                 return OPERATOR_CANCELLED;
2402         }
2403
2404         tracksbase = BKE_tracking_object_get_tracks(tracking, tracking_object);
2405
2406         track = tracksbase->first;
2407         while (track) {
2408                 if (TRACK_VIEW_SELECTED(sc, track) && (track->flag & TRACK_HAS_BUNDLE))
2409                         break;
2410
2411                 track = track->next;
2412         }
2413
2414         set_axis(scene, object, clip, tracking_object, track, axis == 0 ? 'X' : 'Y');
2415
2416         DAG_id_tag_update(&clip->id, 0);
2417         DAG_id_tag_update(&object->id, OB_RECALC_OB);
2418
2419         WM_event_add_notifier(C, NC_MOVIECLIP | NA_EVALUATED, clip);
2420         WM_event_add_notifier(C, NC_OBJECT | ND_TRANSFORM, NULL);
2421
2422         return OPERATOR_FINISHED;
2423 }
2424
2425 void CLIP_OT_set_axis(wmOperatorType *ot)
2426 {
2427         static EnumPropertyItem axis_actions[] = {
2428                 {0, "X", 0, "X", "Align bundle align X axis"},
2429                 {1, "Y", 0, "Y", "Align bundle align Y axis"},
2430                 {0, NULL, 0, NULL, NULL}
2431         };
2432
2433         /* identifiers */
2434         ot->name = "Set Axis";
2435         ot->description = "Set direction of scene axis rotating camera (or it's parent if present) and assuming selected track lies on real axis joining it with the origin";
2436         ot->idname = "CLIP_OT_set_axis";
2437
2438         /* api callbacks */
2439         ot->exec = set_axis_exec;
2440         ot->poll = set_orientation_poll;
2441
2442         /* flags */
2443         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
2444
2445         /* properties */
2446         RNA_def_enum(ot->srna, "axis", axis_actions, 0, "Axis", "Axis to use to align bundle along");
2447 }
2448
2449 /********************** set scale operator *********************/
2450
2451 static int do_set_scale(bContext *C, wmOperator *op, bool scale_solution, bool apply_scale)
2452 {
2453         SpaceClip *sc = CTX_wm_space_clip(C);
2454         MovieClip *clip = ED_space_clip_get_clip(sc);
2455         MovieTracking *tracking = &clip->tracking;
2456         MovieTrackingObject *tracking_object = BKE_tracking_object_get_active(tracking);
2457         MovieTrackingTrack *track;
2458         Scene *scene = CTX_data_scene(C);
2459         Object *object = NULL;
2460         Object *camera = get_camera_with_movieclip(scene, clip);
2461         ListBase *tracksbase = BKE_tracking_get_active_tracks(tracking);
2462         int tot = 0;
2463         float vec[2][3], mat[4][4], scale;
2464         float dist = RNA_float_get(op->ptr, "distance");
2465
2466         if (count_selected_bundles(C) != 2) {
2467                 BKE_report(op->reports, RPT_ERROR, "Two tracks with bundles should be selected to set scale");
2468
2469                 return OPERATOR_CANCELLED;
2470         }
2471
2472         if (!scale_solution && !apply_scale) {
2473                 object = get_orientation_object(C);
2474                 if (!object) {
2475                         BKE_report(op->reports, RPT_ERROR, "No object to apply orientation on");
2476
2477                         return OPERATOR_CANCELLED;
2478                 }
2479         }
2480
2481         BKE_tracking_get_camera_object_matrix(scene, camera, mat);
2482
2483         track = tracksbase->first;
2484         while (track) {
2485                 if (TRACK_VIEW_SELECTED(sc, track)) {
2486                         mul_v3_m4v3(vec[tot], mat, track->bundle_pos);
2487                         tot++;
2488                 }
2489
2490                 track = track->next;
2491         }
2492
2493         sub_v3_v3(vec[0], vec[1]);
2494
2495         if (len_v3(vec[0]) > 1e-5f) {
2496                 scale = dist / len_v3(vec[0]);
2497
2498                 if (apply_scale) {
2499                         /* Apply scale on reconstructed scene itself */
2500                         MovieTrackingReconstruction *reconstruction = BKE_tracking_get_active_reconstruction(tracking);
2501                         MovieReconstructedCamera *reconstructed_cameras;
2502                         int i;
2503
2504                         for (track = tracksbase->first; track; track = track->next) {
2505                                 mul_v3_fl(track->bundle_pos, scale);
2506                         }
2507
2508                         reconstructed_cameras = reconstruction->cameras;
2509                         for (i = 0; i < reconstruction->camnr; i++) {
2510                                 mul_v3_fl(reconstructed_cameras[i].mat[3], scale);
2511                         }
2512
2513                         WM_event_add_notifier(C, NC_MOVIECLIP | NA_EVALUATED, clip);
2514                         WM_event_add_notifier(C, NC_OBJECT | ND_TRANSFORM, NULL);
2515                 }
2516                 else {
2517                         if (tracking_object->flag & TRACKING_OBJECT_CAMERA) {
2518                                 mul_v3_fl(object->size, scale);
2519                                 mul_v3_fl(object->loc, scale);
2520                         }
2521                         else if (!scale_solution) {
2522                                 Object *solver_camera = object_solver_camera(scene, object);
2523
2524                                 object->size[0] = object->size[1] = object->size[2] = 1.0f / scale;
2525
2526                                 if (solver_camera) {
2527                                         object->size[0] /= solver_camera->size[0];
2528                                         object->size[1] /= solver_camera->size[1];
2529                                         object->size[2] /= solver_camera->size[2];
2530                                 }
2531                         }
2532                         else {
2533                                 tracking_object->scale = scale;
2534                         }
2535
2536                         DAG_id_tag_update(&clip->id, 0);
2537
2538                         if (object)
2539                                 DAG_id_tag_update(&object->id, OB_RECALC_OB);
2540
2541                         WM_event_add_notifier(C, NC_MOVIECLIP | NA_EVALUATED, clip);
2542                         WM_event_add_notifier(C, NC_OBJECT | ND_TRANSFORM, NULL);
2543                 }
2544         }
2545
2546         return OPERATOR_FINISHED;
2547 }
2548
2549 static int set_scale_exec(bContext *C, wmOperator *op)
2550 {
2551         return do_set_scale(C, op, false, false);
2552 }
2553
2554 static int set_scale_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
2555 {
2556         SpaceClip *sc = CTX_wm_space_clip(C);
2557         MovieClip *clip = ED_space_clip_get_clip(sc);
2558
2559         if (!RNA_struct_property_is_set(op->ptr, "distance"))
2560                 RNA_float_set(op->ptr, "distance", clip->tracking.settings.dist);
2561
2562         return set_scale_exec(C, op);
2563 }
2564
2565 void CLIP_OT_set_scale(wmOperatorType *ot)
2566 {
2567         /* identifiers */
2568         ot->name = "Set Scale";
2569         ot->description = "Set scale of scene by scaling camera (or it's parent if present)";
2570         ot->idname = "CLIP_OT_set_scale";
2571
2572         /* api callbacks */
2573         ot->exec = set_scale_exec;
2574         ot->invoke = set_scale_invoke;
2575         ot->poll = set_orientation_poll;
2576
2577         /* flags */
2578         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
2579
2580         /* properties */
2581         RNA_def_float(ot->srna, "distance", 0.0f, -FLT_MAX, FLT_MAX,
2582                       "Distance", "Distance between selected tracks", -100.0f, 100.0f);
2583 }
2584
2585 /********************** set solution scale operator *********************/
2586
2587 static int set_solution_scale_poll(bContext *C)
2588 {
2589         SpaceClip *sc = CTX_wm_space_clip(C);
2590
2591         if (sc) {
2592                 MovieClip *clip = ED_space_clip_get_clip(sc);
2593
2594                 if (clip) {
2595                         MovieTracking *tracking = &clip->tracking;
2596                         MovieTrackingObject *tracking_object = BKE_tracking_object_get_active(tracking);
2597
2598                         return (tracking_object->flag & TRACKING_OBJECT_CAMERA) == 0;
2599                 }
2600         }
2601
2602         return FALSE;
2603 }
2604
2605 static int set_solution_scale_exec(bContext *C, wmOperator *op)
2606 {
2607         return do_set_scale(C, op, true, false);
2608 }
2609
2610 static int set_solution_scale_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
2611 {
2612         SpaceClip *sc = CTX_wm_space_clip(C);
2613         MovieClip *clip = ED_space_clip_get_clip(sc);
2614
2615         if (!RNA_struct_property_is_set(op->ptr, "distance"))
2616                 RNA_float_set(op->ptr, "distance", clip->tracking.settings.object_distance);
2617
2618         return set_solution_scale_exec(C, op);
2619 }
2620
2621 void CLIP_OT_set_solution_scale(wmOperatorType *ot)
2622 {
2623         /* identifiers */
2624         ot->name = "Set Solution Scale";
2625         ot->description = "Set object solution scale using distance between two selected tracks";
2626         ot->idname = "CLIP_OT_set_solution_scale";
2627
2628         /* api callbacks */
2629         ot->exec = set_solution_scale_exec;
2630         ot->invoke = set_solution_scale_invoke;
2631         ot->poll = set_solution_scale_poll;
2632
2633         /* flags */
2634         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
2635
2636         /* properties */
2637         RNA_def_float(ot->srna, "distance", 0.0f, -FLT_MAX, FLT_MAX,
2638                       "Distance", "Distance between selected tracks", -100.0f, 100.0f);
2639 }
2640
2641 /********************** apply solution scale operator *********************/
2642
2643 static int apply_solution_scale_poll(bContext *C)
2644 {
2645         SpaceClip *sc = CTX_wm_space_clip(C);
2646
2647         if (sc) {
2648                 MovieClip *clip = ED_space_clip_get_clip(sc);
2649
2650                 if (clip) {
2651                         MovieTracking *tracking = &clip->tracking;
2652                         MovieTrackingObject *tracking_object = BKE_tracking_object_get_active(tracking);
2653
2654                         return tracking_object->flag & TRACKING_OBJECT_CAMERA;
2655                 }
2656         }
2657
2658         return FALSE;
2659 }
2660
2661 static int apply_solution_scale_exec(bContext *C, wmOperator *op)
2662 {
2663         return do_set_scale(C, op, false, true);
2664 }
2665
2666 static int apply_solution_scale_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
2667 {
2668         SpaceClip *sc = CTX_wm_space_clip(C);
2669         MovieClip *clip = ED_space_clip_get_clip(sc);
2670
2671         if (!RNA_struct_property_is_set(op->ptr, "distance"))
2672                 RNA_float_set(op->ptr, "distance", clip->tracking.settings.dist);
2673
2674         return apply_solution_scale_exec(C, op);
2675 }
2676
2677 void CLIP_OT_apply_solution_scale(wmOperatorType *ot)
2678 {
2679         /* identifiers */
2680         ot->name = "Apply Solution Scale";
2681         ot->description = "Apply scale on solution itself to make distance between selected tracks equals to desired";
2682         ot->idname = "CLIP_OT_apply_solution_scale";
2683
2684         /* api callbacks */
2685         ot->exec = apply_solution_scale_exec;
2686         ot->invoke = apply_solution_scale_invoke;
2687         ot->poll = apply_solution_scale_poll;
2688
2689         /* flags */
2690         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
2691
2692         /* properties */
2693         RNA_def_float(ot->srna, "distance", 0.0f, -FLT_MAX, FLT_MAX,
2694                       "Distance", "Distance between selected tracks", -100.0f, 100.0f);
2695 }
2696
2697 /********************** set principal center operator *********************/
2698
2699 static int set_center_principal_exec(bContext *C, wmOperator *UNUSED(op))
2700 {
2701         SpaceClip *sc = CTX_wm_space_clip(C);
2702         MovieClip *clip = ED_space_clip_get_clip(sc);
2703         int width, height;
2704
2705         BKE_movieclip_get_size(clip, &sc->user, &width, &height);
2706
2707         if (width == 0 || height == 0)
2708                 return OPERATOR_CANCELLED;
2709
2710         clip->tracking.camera.principal[0] = ((float)width) / 2.0f;
2711         clip->tracking.camera.principal[1] = ((float)height) / 2.0f;
2712
2713         WM_event_add_notifier(C, NC_MOVIECLIP | NA_EDITED, clip);
2714
2715         return OPERATOR_FINISHED;
2716 }
2717
2718 void CLIP_OT_set_center_principal(wmOperatorType *ot)
2719 {
2720         /* identifiers */
2721         ot->name = "Set Principal to Center";
2722         ot->description = "Set optical center to center of footage";
2723         ot->idname = "CLIP_OT_set_center_principal";
2724
2725         /* api callbacks */
2726         ot->exec = set_center_principal_exec;
2727         ot->poll = ED_space_clip_tracking_poll;
2728
2729         /* flags */
2730         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
2731 }
2732
2733 /********************** hide tracks operator *********************/
2734
2735 static int hide_tracks_exec(bContext *C, wmOperator *op)
2736 {
2737         SpaceClip *sc = CTX_wm_space_clip(C);
2738         MovieClip *clip = ED_space_clip_get_clip(sc);
2739         MovieTrackingTrack *track;
2740         MovieTracking *tracking = &clip->tracking;
2741         ListBase *tracksbase = BKE_tracking_get_active_tracks(tracking);
2742         MovieTrackingTrack *act_track = BKE_tracking_track_get_active(tracking);
2743         int unselected;
2744
2745         unselected = RNA_boolean_get(op->ptr, "unselected");
2746
2747         track = tracksbase->first;
2748         while (track) {
2749                 if (unselected == 0 && TRACK_VIEW_SELECTED(sc, track)) {
2750                         track->flag |= TRACK_HIDDEN;
2751                 }
2752                 else if (unselected == 1 && !TRACK_VIEW_SELECTED(sc, track)) {
2753                         track->flag |= TRACK_HIDDEN;
2754                 }
2755
2756                 track = track->next;
2757         }
2758
2759         if (act_track && act_track->flag & TRACK_HIDDEN)
2760                 clip->tracking.act_track = NULL;
2761
2762         if (unselected == 0) {
2763                 /* no selection on screen now, unlock view so it can be scrolled nice again */
2764                 sc->flag &= ~SC_LOCK_SELECTION;
2765         }
2766
2767         BKE_tracking_dopesheet_tag_update(tracking);
2768
2769         WM_event_add_notifier(C, NC_MOVIECLIP | ND_DISPLAY, NULL);
2770
2771         return OPERATOR_FINISHED;
2772 }
2773
2774 void CLIP_OT_hide_tracks(wmOperatorType *ot)
2775 {
2776         /* identifiers */
2777         ot->name = "Hide Tracks";
2778         ot->description = "Hide selected tracks";
2779         ot->idname = "CLIP_OT_hide_tracks";
2780
2781         /* api callbacks */
2782         ot->exec = hide_tracks_exec;
2783         ot->poll = ED_space_clip_tracking_poll;
2784
2785         /* flags */
2786         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
2787
2788         /* properties */
2789         RNA_def_boolean(ot->srna, "unselected", 0, "Unselected", "Hide unselected tracks");
2790 }
2791
2792 /********************** hide tracks clear operator *********************/
2793
2794 static int hide_tracks_clear_exec(bContext *C, wmOperator *UNUSED(op))
2795 {
2796         SpaceClip *sc = CTX_wm_space_clip(C);
2797         MovieClip *clip = ED_space_clip_get_clip(sc);
2798         MovieTracking *tracking = &clip->tracking;
2799         ListBase *tracksbase = BKE_tracking_get_active_tracks(tracking);
2800         MovieTrackingTrack *track;
2801
2802         track = tracksbase->first;
2803         while (track) {
2804                 track->flag &= ~TRACK_HIDDEN;
2805
2806                 track = track->next;
2807         }
2808
2809         BKE_tracking_dopesheet_tag_update(tracking);
2810
2811         WM_event_add_notifier(C, NC_MOVIECLIP | ND_DISPLAY, NULL);
2812
2813         return OPERATOR_FINISHED;
2814 }
2815
2816 void CLIP_OT_hide_tracks_clear(wmOperatorType *ot)
2817 {
2818         /* identifiers */
2819         ot->name = "Hide Tracks Clear";
2820         ot->description = "Clear hide selected tracks";
2821         ot->idname = "CLIP_OT_hide_tracks_clear";
2822
2823         /* api callbacks */
2824         ot->exec = hide_tracks_clear_exec;
2825         ot->poll = ED_space_clip_tracking_poll;
2826
2827         /* flags */
2828         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
2829 }
2830
2831 /********************** detect features operator *********************/
2832
2833 static bGPDlayer *detect_get_layer(MovieClip *clip)
2834 {
2835         bGPDlayer *layer;
2836
2837         if (!clip->gpd)
2838                 return NULL;
2839
2840         layer = clip->gpd->layers.first;
2841         while (layer) {
2842                 if (layer->flag & GP_LAYER_ACTIVE)
2843                         return layer;
2844
2845                 layer = layer->next;
2846         }
2847
2848         return NULL;
2849 }
2850
2851 static int detect_features_exec(bContext *C, wmOperator *op)
2852 {
2853         SpaceClip *sc = CTX_wm_space_clip(C);
2854         MovieClip *clip = ED_space_clip_get_clip(sc);
2855         int clip_flag = clip->flag & MCLIP_TIMECODE_FLAGS;
2856         ImBuf *ibuf = BKE_movieclip_get_ibuf_flag(clip, &sc->user, clip_flag, MOVIECLIP_CACHE_SKIP);
2857         MovieTracking *tracking = &clip->tracking;
2858         ListBase *tracksbase = BKE_tracking_get_active_tracks(tracking);
2859         MovieTrackingTrack *track = tracksbase->first;
2860         int placement = RNA_enum_get(op->ptr, "placement");
2861         int margin = RNA_int_get(op->ptr, "margin");
2862         int min_trackability = RNA_int_get(op->ptr, "min_trackability");
2863         int min_distance = RNA_int_get(op->ptr, "min_distance");
2864         int place_outside_layer = 0;
2865         int framenr = ED_space_clip_get_clip_frame_number(sc);
2866         bGPDlayer *layer = NULL;
2867
2868         if (!ibuf) {
2869                 BKE_report(op->reports, RPT_ERROR, "Feature detection requires valid clip frame");
2870                 return OPERATOR_CANCELLED;
2871         }
2872
2873         if (placement != 0) {
2874                 layer = detect_get_layer(clip);
2875                 place_outside_layer = placement == 2;
2876         }
2877
2878         /* deselect existing tracks */
2879         while (track) {
2880                 track->flag &= ~SELECT;
2881                 track->pat_flag &= ~SELECT;
2882                 track->search_flag &= ~SELECT;
2883
2884                 track = track->next;
2885         }
2886
2887         BKE_tracking_detect_fast(tracking, tracksbase, ibuf, framenr, margin,
2888                                  min_trackability, min_distance, layer, place_outside_layer);
2889
2890         IMB_freeImBuf(ibuf);
2891
2892         BKE_tracking_dopesheet_tag_update(tracking);
2893         WM_event_add_notifier(C, NC_MOVIECLIP | NA_EDITED, NULL);
2894
2895         return OPERATOR_FINISHED;
2896 }
2897
2898 void CLIP_OT_detect_features(wmOperatorType *ot)
2899 {
2900         static EnumPropertyItem placement_items[] = {
2901                 {0, "FRAME",            0, "Whole Frame",           "Place markers across the whole frame"},
2902                 {1, "INSIDE_GPENCIL",   0, "Inside grease pencil",  "Place markers only inside areas outlined with grease pencil"},
2903                 {2, "OUTSIDE_GPENCIL",  0, "Outside grease pencil", "Place markers only outside areas outlined with grease pencil"},
2904                 {0, NULL, 0, NULL, NULL}
2905         };
2906
2907         /* identifiers */
2908         ot->name = "Detect Features";
2909         ot->description = "Automatically detect features and place markers to track";
2910         ot->idname = "CLIP_OT_detect_features";
2911
2912         /* api callbacks */
2913         ot->exec = detect_features_exec;
2914         ot->poll = ED_space_clip_tracking_poll;
2915
2916         /* flags */
2917         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
2918
2919         /* properties */
2920         RNA_def_enum(ot->srna, "placement", placement_items, 0, "Placement", "Placement for detected features");
2921         RNA_def_int(ot->srna, "margin", 16, 0, INT_MAX, "Margin", "Only corners further than margin pixels from the image edges are considered", 0, 300);
2922         RNA_def_int(ot->srna, "min_trackability", 16, 0, INT_MAX, "Trackability", "Minimum trackability score to add a corner", 0, 300);
2923         RNA_def_int(ot->srna, "min_distance", 120, 0, INT_MAX, "Distance", "Minimal distance accepted between two corners", 0, 300);
2924 }
2925
2926 /********************** frame jump operator *********************/
2927
2928 static int frame_jump_exec(bContext *C, wmOperator *op)
2929 {
2930         Scene *scene = CTX_data_scene(C);
2931         SpaceClip *sc = CTX_wm_space_clip(C);
2932         MovieClip *clip = ED_space_clip_get_clip(sc);
2933         MovieTrackingTrack *track;
2934         int pos = RNA_enum_get(op->ptr, "position");
2935         int delta;
2936
2937         if (pos <= 1) { /* jump to path */
2938                 track = BKE_tracking_track_get_active(&clip->tracking);
2939
2940                 if (!track)
2941                         return OPERATOR_CANCELLED;
2942
2943                 delta = pos == 1 ? 1 : -1;
2944
2945                 while (sc->user.framenr + delta >= SFRA && sc->user.framenr + delta <= EFRA) {
2946                         int framenr = BKE_movieclip_remap_scene_to_clip_frame(clip, sc->user.framenr + delta);
2947                         MovieTrackingMarker *marker = BKE_tracking_marker_get_exact(track, framenr);
2948
2949                         if (!marker || marker->flag & MARKER_DISABLED)
2950                                 break;
2951
2952                         sc->user.framenr += delta;
2953                 }
2954         }
2955         else {  /* to to failed frame */
2956                 if (clip->tracking.reconstruction.flag & TRACKING_RECONSTRUCTED) {
2957                         int a = ED_space_clip_get_clip_frame_number(sc);
2958                         MovieTracking *tracking = &clip->tracking;
2959                         MovieTrackingObject *object = BKE_tracking_object_get_active(tracking);
2960
2961                         delta = pos == 3 ? 1 : -1;
2962
2963                         a += delta;
2964
2965                         while (a + delta >= SFRA && a + delta <= EFRA) {
2966                                 MovieReconstructedCamera *cam;
2967
2968                                 cam = BKE_tracking_camera_get_reconstructed(tracking, object, a);
2969
2970                                 if (!cam) {
2971                                         sc->user.framenr = BKE_movieclip_remap_clip_to_scene_frame(clip, a);
2972
2973                                         break;
2974                                 }
2975
2976                                 a += delta;
2977                         }
2978                 }
2979         }
2980
2981         if (CFRA != sc->user.framenr) {
2982                 CFRA = sc->user.framenr;
2983                 sound_seek_scene(CTX_data_main(C), CTX_data_scene(C));
2984
2985                 WM_event_add_notifier(C, NC_SCENE | ND_FRAME, scene);
2986         }
2987
2988         WM_event_add_notifier(C, NC_MOVIECLIP | ND_DISPLAY, NULL);
2989
2990         return OPERATOR_FINISHED;
2991 }
2992
2993 void CLIP_OT_frame_jump(wmOperatorType *ot)
2994 {
2995         static EnumPropertyItem position_items[] = {
2996                 {0, "PATHSTART",    0, "Path Start",        "Jump to start of current path"},
2997                 {1, "PATHEND",      0, "Path End",          "Jump to end of current path"},
2998                 {2, "FAILEDPREV",   0, "Previous Failed",   "Jump to previous failed frame"},
2999                 {2, "FAILNEXT",     0, "Next Failed",       "Jump to next failed frame"},
3000                 {0, NULL, 0, NULL, NULL}
3001         };
3002
3003         /* identifiers */
3004         ot->name = "Jump to Frame";
3005         ot->description = "Jump to special frame";
3006         ot->idname = "CLIP_OT_frame_jump";
3007
3008         /* api callbacks */
3009         ot->exec = frame_jump_exec;
3010         ot->poll = ED_space_clip_poll;
3011
3012         /* flags */
3013         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
3014
3015         /* properties */
3016         RNA_def_enum(ot->srna, "position", position_items, 0, "Position", "Position to jump to");
3017 }
3018
3019 /********************** join tracks operator *********************/
3020
3021 static int join_tracks_exec(bContext *C, wmOperator *op)
3022 {
3023         SpaceClip *sc = CTX_wm_space_clip(C);
3024         MovieClip *clip = ED_space_clip_get_clip(sc);
3025         MovieTracking *tracking = &clip->tracking;
3026         ListBase *tracksbase = BKE_tracking_get_active_tracks(tracking);
3027         MovieTrackingTrack *act_track, *track, *next;
3028
3029         act_track = BKE_tracking_track_get_active(tracking);
3030
3031         if (!act_track) {
3032                 BKE_report(op->reports, RPT_ERROR, "No active track to join to");
3033                 return OPERATOR_CANCELLED;
3034         }
3035
3036         track = tracksbase->first;
3037         while (track) {
3038                 next = track->next;
3039
3040                 if (TRACK_VIEW_SELECTED(sc, track) && track != act_track) {
3041                         BKE_tracking_tracks_join(tracking, act_track, track);
3042
3043                         if (tracking->stabilization.rot_track == track)
3044                                 tracking->stabilization.rot_track = act_track;
3045
3046                         BKE_tracking_track_free(track);
3047                         BLI_freelinkN(tracksbase, track);
3048                 }
3049
3050                 track = next;
3051         }
3052
3053         WM_event_add_notifier(C, NC_MOVIECLIP | NA_EDITED, clip);
3054
3055         return OPERATOR_FINISHED;
3056 }
3057
3058 void CLIP_OT_join_tracks(wmOperatorType *ot)
3059 {
3060         /* identifiers */
3061         ot->name = "Join Tracks";
3062         ot->description = "Join selected tracks";
3063         ot->idname = "CLIP_OT_join_tracks";
3064
3065         /* api callbacks */
3066         ot->exec = join_tracks_exec;
3067         ot->poll = ED_space_clip_tracking_poll;
3068
3069         /* flags */
3070         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
3071 }
3072
3073 /********************** lock tracks operator *********************/
3074
3075 static int lock_tracks_exec(bContext *C, wmOperator *op)
3076 {
3077         SpaceClip *sc = CTX_wm_space_clip(C);
3078         MovieClip *clip = ED_space_clip_get_clip(sc);
3079         MovieTracking *tracking = &clip->tracking;
3080         ListBase *tracksbase = BKE_tracking_get_active_tracks(tracking);
3081         MovieTrackingTrack *track = tracksbase->first;
3082         int action = RNA_enum_get(op->ptr, "action");
3083
3084         while (track) {
3085                 if (TRACK_VIEW_SELECTED(sc, track)) {
3086                         if (action == 0)
3087                                 track->flag |= TRACK_LOCKED;
3088                         else if (action == 1)
3089                                 track->flag &= ~TRACK_LOCKED;
3090                         else track->flag ^= TRACK_LOCKED;
3091                 }
3092
3093                 track = track->next;
3094         }
3095
3096         WM_event_add_notifier(C, NC_MOVIECLIP | NA_EVALUATED, clip);
3097
3098         return OPERATOR_FINISHED;
3099 }
3100
3101 void CLIP_OT_lock_tracks(wmOperatorType *ot)
3102 {
3103         static EnumPropertyItem actions_items[] = {
3104                 {0, "LOCK", 0, "Lock", "Lock selected tracks"},
3105                 {1, "UNLOCK", 0, "Unlock", "Unlock selected tracks"},
3106                 {2, "TOGGLE", 0, "Toggle", "Toggle locked flag for selected tracks"},
3107                 {0, NULL, 0, NULL, NULL}
3108         };
3109
3110         /* identifiers */
3111         ot->name = "Lock Tracks";
3112         ot->description = "Lock/unlock selected tracks";
3113         ot->idname = "CLIP_OT_lock_tracks";
3114
3115         /* api callbacks */
3116         ot->exec = lock_tracks_exec;
3117         ot->poll = ED_space_clip_tracking_poll;
3118
3119         /* flags */
3120         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
3121
3122         /* properties */
3123         RNA_def_enum(ot->srna, "action", actions_items, 0, "Action", "Lock action to execute");
3124 }
3125
3126 /********************** set keyframe operator *********************/
3127
3128 static int set_solver_keyframe_exec(bContext *C, wmOperator *op)
3129 {
3130         SpaceClip *sc = CTX_wm_space_clip(C);
3131         MovieClip *clip = ED_space_clip_get_clip(sc);
3132         MovieTracking *tracking = &clip->tracking;
3133         MovieTrackingObject *object = BKE_tracking_object_get_active(tracking);
3134         int keyframe = RNA_enum_get(op->ptr, "keyframe");
3135         int framenr = BKE_movieclip_remap_scene_to_clip_frame(clip, sc->user.framenr);
3136
3137         if (keyframe == 0)
3138                 object->keyframe1 = framenr;
3139         else
3140                 object->keyframe2 = framenr;
3141
3142         WM_event_add_notifier(C, NC_MOVIECLIP | ND_DISPLAY, clip);
3143
3144         return OPERATOR_FINISHED;
3145 }
3146
3147 void CLIP_OT_set_solver_keyframe(wmOperatorType *ot)
3148 {
3149         static EnumPropertyItem keyframe_items[] = {
3150                 {0, "KEYFRAME_A", 0, "Keyframe A", ""},
3151                 {1, "KEYFRAME_B", 0, "Keyframe B", ""},
3152                 {0, NULL, 0, NULL, NULL}
3153         };
3154
3155         /* identifiers */
3156         ot->name = "Set Solver Keyframe";
3157         ot->description = "Set keyframe used by solver";
3158         ot->idname = "CLIP_OT_set_solver_keyframe";
3159
3160         /* api callbacks */
3161         ot->exec = set_solver_keyframe_exec;
3162         ot->poll = ED_space_clip_tracking_poll;
3163
3164         /* flags */
3165         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
3166
3167         /* properties */
3168         RNA_def_enum(ot->srna, "keyframe", keyframe_items, 0, "Keyframe", "Keyframe to set");
3169 }
3170
3171 /********************** track copy color operator *********************/
3172
3173 static int track_copy_color_exec(bContext *C, wmOperator *UNUSED(op))
3174 {
3175         SpaceClip *sc = CTX_wm_space_clip(C);
3176         MovieClip *clip = ED_space_clip_get_clip(sc);
3177         MovieTracking *tracking = &clip->tracking;
3178         ListBase *tracksbase = BKE_tracking_get_active_tracks(tracking);
3179         MovieTrackingTrack *track, *act_track = BKE_tracking_track_get_active(tracking);
3180
3181         if (!act_track)
3182                 return OPERATOR_CANCELLED;
3183
3184         track = tracksbase->first;
3185         while (track) {
3186                 if (TRACK_VIEW_SELECTED(sc, track) && track != act_track) {
3187                         track->flag &= ~TRACK_CUSTOMCOLOR;
3188
3189                         if (act_track->flag & TRACK_CUSTOMCOLOR) {
3190                                 copy_v3_v3(track->color, act_track->color);
3191                                 track->flag |= TRACK_CUSTOMCOLOR;
3192                         }
3193                 }
3194
3195                 track = track->next;
3196         }
3197
3198         WM_event_add_notifier(C, NC_MOVIECLIP | ND_DISPLAY, clip);
3199
3200         return OPERATOR_FINISHED;
3201 }
3202
3203 void CLIP_OT_track_copy_color(wmOperatorType *ot)
3204 {
3205         /* identifiers */
3206         ot->name = "Copy Color";
3207         ot->description = "Copy color to all selected tracks";
3208         ot->idname = "CLIP_OT_track_copy_color";
3209
3210         /* api callbacks */
3211         ot->exec = track_copy_color_exec;
3212         ot->poll = ED_space_clip_tracking_poll;
3213
3214         /* flags */
3215         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
3216 }
3217
3218 /********************** add 2d stabilization tracks operator *********************/
3219
3220 static int stabilize_2d_poll(bContext *C)
3221 {
3222         if (ED_space_clip_tracking_poll(C)) {
3223                 SpaceClip *sc = CTX_wm_space_clip(C);
3224                 MovieClip *clip = ED_space_clip_get_clip(sc);
3225                 MovieTrackingObject *tracking_object = BKE_tracking_object_get_active(&clip->tracking);
3226
3227                 return tracking_object->flag & TRACKING_OBJECT_CAMERA;
3228         }
3229
3230         return FALSE;
3231 }
3232
3233 static int stabilize_2d_add_exec(bContext *C, wmOperator *UNUSED(op))
3234 {
3235         SpaceClip *sc = CTX_wm_space_clip(C);
3236         MovieClip *clip = ED_space_clip_get_clip(sc);
3237         MovieTracking *tracking = &clip->tracking;
3238         ListBase *tracksbase = BKE_tracking_get_active_tracks(tracking);
3239         MovieTrackingTrack *track;
3240         MovieTrackingStabilization *stab = &tracking->stabilization;
3241         int update = 0;
3242
3243         track = tracksbase->first;
3244         while (track) {
3245                 if (TRACK_VIEW_SELECTED(sc, track) && (track->flag & TRACK_USE_2D_STAB) == 0) {
3246                         track->flag |= TRACK_USE_2D_STAB;
3247                         stab->tot_track++;
3248
3249                         update = 1;
3250                 }
3251
3252                 track = track->next;
3253         }
3254
3255         if (update) {
3256                 stab->ok = 0;
3257
3258                 DAG_id_tag_update(&clip->id, 0);