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