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