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