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