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