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