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