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