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