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