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