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