Assorted camera tracker improvements
[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(epsy, 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)
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;
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
885         /* do actual selection */
886         track= clip->tracking.tracks.first;
887         while(track) {
888                 if((track->flag&TRACK_HIDDEN)==0) {
889                         MovieTrackingMarker *marker= BKE_tracking_get_marker(track, sc->user.framenr);
890
891                         if(MARKER_VISIBLE(sc, marker) && BLI_in_rctf(&rectf, marker->pos[0], marker->pos[1])) {
892                                 BKE_tracking_track_flag(track, TRACK_AREA_ALL, SELECT, mode!=GESTURE_MODAL_SELECT);
893
894                                 change= 1;
895                         }
896                 }
897
898                 track= track->next;
899         }
900
901         if(change) {
902                 WM_event_add_notifier(C, NC_GEOM|ND_SELECT, NULL);
903
904                 return OPERATOR_FINISHED;
905         }
906
907         return OPERATOR_CANCELLED;
908 }
909
910 void CLIP_OT_select_border(wmOperatorType *ot)
911 {
912         /* identifiers */
913         ot->name= "Border Select";
914         ot->description= "Select markers using border selection";
915         ot->idname= "CLIP_OT_select_border";
916
917         /* api callbacks */
918         ot->invoke= WM_border_select_invoke;
919         ot->exec= border_select_exec;
920         ot->modal= WM_border_select_modal;
921         ot->poll= ED_space_clip_poll;
922
923         /* flags */
924         ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
925
926         /* properties */
927         WM_operator_properties_gesture_border(ot, FALSE);
928 }
929
930 /********************** circle select operator *********************/
931
932 static int marker_inside_ellipse(MovieTrackingMarker *marker, float offset[2], float ellipse[2])
933 {
934         /* normalized ellipse: ell[0] = scaleX, ell[1] = scaleY */
935         float x, y;
936
937         x= (marker->pos[0] - offset[0])*ellipse[0];
938         y= (marker->pos[1] - offset[1])*ellipse[1];
939
940         return x*x + y*y < 1.0f;
941 }
942
943 static int circle_select_exec(bContext *C, wmOperator *op)
944 {
945         SpaceClip *sc= CTX_wm_space_clip(C);
946         MovieClip *clip= ED_space_clip(sc);
947         ARegion *ar= CTX_wm_region(C);
948         MovieTrackingTrack *track;
949         int x, y, radius, width, height, mode, change= 0;
950         float zoomx, zoomy, offset[2], ellipse[2];
951
952         /* get operator properties */
953         x= RNA_int_get(op->ptr, "x");
954         y= RNA_int_get(op->ptr, "y");
955         radius= RNA_int_get(op->ptr, "radius");
956
957         mode= RNA_int_get(op->ptr, "gesture_mode");
958
959         /* compute ellipse and position in unified coordinates */
960         ED_space_clip_size(sc, &width, &height);
961         ED_space_clip_zoom(sc, ar, &zoomx, &zoomy);
962
963         ellipse[0]= width*zoomx/radius;
964         ellipse[1]= height*zoomy/radius;
965
966         ED_clip_point_stable_pos(C, x, y, &offset[0], &offset[1]);
967
968         /* do selection */
969         track= clip->tracking.tracks.first;
970         while(track) {
971                 if((track->flag&TRACK_HIDDEN)==0) {
972                         MovieTrackingMarker *marker= BKE_tracking_get_marker(track, sc->user.framenr);
973
974                         if(MARKER_VISIBLE(sc, marker) && marker_inside_ellipse(marker, offset, ellipse)) {
975                                 BKE_tracking_track_flag(track, TRACK_AREA_ALL, SELECT, mode!=GESTURE_MODAL_SELECT);
976
977                                 change= 1;
978                         }
979                 }
980
981                 track= track->next;
982         }
983
984         if(change) {
985                 WM_event_add_notifier(C, NC_GEOM|ND_SELECT, NULL);
986
987                 return OPERATOR_FINISHED;
988         }
989
990         return OPERATOR_CANCELLED;
991 }
992
993 void CLIP_OT_select_circle(wmOperatorType *ot)
994 {
995         /* identifiers */
996         ot->name= "Circle Select";
997         ot->description= "Select markers using circle selection";
998         ot->idname= "CLIP_OT_select_circle";
999
1000         /* api callbacks */
1001         ot->invoke= WM_gesture_circle_invoke;
1002         ot->modal= WM_gesture_circle_modal;
1003         ot->exec= circle_select_exec;
1004         ot->poll= ED_space_clip_poll;
1005
1006         /* flags */
1007         ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
1008
1009         /* properties */
1010         RNA_def_int(ot->srna, "x", 0, INT_MIN, INT_MAX, "X", "", INT_MIN, INT_MAX);
1011         RNA_def_int(ot->srna, "y", 0, INT_MIN, INT_MAX, "Y", "", INT_MIN, INT_MAX);
1012         RNA_def_int(ot->srna, "radius", 0, INT_MIN, INT_MAX, "Radius", "", INT_MIN, INT_MAX);
1013         RNA_def_int(ot->srna, "gesture_mode", 0, INT_MIN, INT_MAX, "Gesture Mode", "", INT_MIN, INT_MAX);
1014 }
1015
1016 /********************** select all operator *********************/
1017
1018 static int select_all_exec(bContext *C, wmOperator *op)
1019 {
1020         SpaceClip *sc= CTX_wm_space_clip(C);
1021         MovieClip *clip= ED_space_clip(sc);
1022         MovieTrackingTrack *track= NULL;        /* selected track */
1023         int action= RNA_enum_get(op->ptr, "action");
1024         int framenr= sc->user.framenr;
1025         int has_selection= 0;
1026
1027         if(action == SEL_TOGGLE){
1028                 action= SEL_SELECT;
1029                 track= clip->tracking.tracks.first;
1030                 while(track) {
1031                         if(TRACK_VIEW_SELECTED(sc, track)) {
1032                                 action= SEL_DESELECT;
1033                                 break;
1034                         }
1035
1036                         track= track->next;
1037                 }
1038         }
1039
1040         track= clip->tracking.tracks.first;
1041         while(track) {
1042                 if((track->flag&TRACK_HIDDEN)==0) {
1043                         MovieTrackingMarker *marker= BKE_tracking_get_marker(track, framenr);
1044
1045                         if(marker && MARKER_VISIBLE(sc, marker)) {
1046                                 switch (action) {
1047                                         case SEL_SELECT:
1048                                                 track->flag|= SELECT;
1049                                                 track->pat_flag|= SELECT;
1050                                                 track->search_flag|= SELECT;
1051                                                 break;
1052                                         case SEL_DESELECT:
1053                                                 track->flag&= ~SELECT;
1054                                                 track->pat_flag&= ~SELECT;
1055                                                 track->search_flag&= ~SELECT;
1056                                                 break;
1057                                         case SEL_INVERT:
1058                                                 track->flag^= SELECT;
1059                                                 track->pat_flag^= SELECT;
1060                                                 track->search_flag^= SELECT;
1061                                                 break;
1062                                 }
1063                         }
1064                 }
1065
1066                 if(TRACK_VIEW_SELECTED(sc, track))
1067                         has_selection= 1;
1068
1069                 track= track->next;
1070         }
1071
1072         if(!has_selection)
1073                 sc->flag&= ~SC_LOCK_SELECTION;
1074
1075         WM_event_add_notifier(C, NC_GEOM|ND_SELECT, NULL);
1076
1077         return OPERATOR_FINISHED;
1078 }
1079
1080 void CLIP_OT_select_all(wmOperatorType *ot)
1081 {
1082         /* identifiers */
1083         ot->name= "Select or Deselect All";
1084         ot->description= "Change selection of all tracking markers";
1085         ot->idname= "CLIP_OT_select_all";
1086
1087         /* api callbacks */
1088         ot->exec= select_all_exec;
1089         ot->poll= ED_space_clip_poll;
1090
1091         /* flags */
1092         ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
1093
1094         WM_operator_properties_select_all(ot);
1095 }
1096
1097 /********************** select grouped operator *********************/
1098
1099 static int select_groped_exec(bContext *C, wmOperator *op)
1100 {
1101         SpaceClip *sc= CTX_wm_space_clip(C);
1102         MovieClip *clip= ED_space_clip(sc);
1103         MovieTrackingTrack *track;
1104         MovieTrackingMarker *marker;
1105         int group= RNA_enum_get(op->ptr, "group");
1106
1107         track= clip->tracking.tracks.first;
1108         while(track) {
1109                 int ok= 0;
1110
1111                 marker= BKE_tracking_get_marker(track, sc->user.framenr);
1112
1113                 if(group==0) { /* Keyframed */
1114                         ok= marker->framenr==sc->user.framenr && (marker->flag&MARKER_TRACKED)==0;
1115                 }
1116                 else if(group==1) { /* Estimated */
1117                         ok= marker->framenr!=sc->user.framenr;
1118                 }
1119                 else if(group==2) { /* tracked */
1120                         ok= marker->framenr==sc->user.framenr && (marker->flag&MARKER_TRACKED);
1121                 }
1122                 else if(group==3) { /* locked */
1123                         ok= track->flag&TRACK_LOCKED;
1124                 }
1125                 else if(group==4) { /* disabled */
1126                         ok= marker->flag&MARKER_DISABLED;
1127                 }
1128                 else if(group==5) { /* color */
1129                         if(clip->tracking.act_track) {
1130                                 ok= (track->flag&TRACK_CUSTOMCOLOR) == (clip->tracking.act_track->flag&TRACK_CUSTOMCOLOR);
1131
1132                                 if(ok && track->flag&TRACK_CUSTOMCOLOR)
1133                                         ok= equals_v3v3(track->color, clip->tracking.act_track->color);
1134                         }
1135                 }
1136                 else if(group==6) { /* failed */
1137                         ok= (track->flag&TRACK_HAS_BUNDLE) == 0;
1138                 }
1139
1140                 if(ok) {
1141                         track->flag|= SELECT;
1142                         if(sc->flag&SC_SHOW_MARKER_PATTERN) track->pat_flag|= SELECT;;
1143                         if(sc->flag&SC_SHOW_MARKER_SEARCH) track->search_flag|= SELECT;;
1144                 }
1145
1146                 track= track->next;
1147         }
1148
1149         WM_event_add_notifier(C, NC_MOVIECLIP|ND_DISPLAY, clip);
1150
1151         return OPERATOR_FINISHED;
1152 }
1153
1154 void CLIP_OT_select_grouped(wmOperatorType *ot)
1155 {
1156         static EnumPropertyItem select_group_items[] = {
1157                         {0, "KEYFRAMED", 0, "Keyframed tracks", "Select all keyframed tracks"},
1158                         {1, "ESTIMATED", 0, "Estimated tracks", "Select all estimated tracks"},
1159                         {2, "TRACKED", 0, "Tracked tracks", "Select all tracked tracks"},
1160                         {3, "LOCKED", 0, "Locked tracks", "Select all locked tracks"},
1161                         {4, "DISABLED", 0, "Disabled tracks", "Select all disabled tracks"},
1162                         {5, "COLOR", 0, "Tracks with same color", "Select all tracks with same color as actiev track"},
1163                         {6, "FAILED", 0, "Failed Tracks", "Select all tracks which failed to be reconstructed"},
1164                         {0, NULL, 0, NULL, NULL}
1165         };
1166
1167         /* identifiers */
1168         ot->name= "Select Grouped";
1169         ot->description= "Joint Selected Tracks";
1170         ot->idname= "CLIP_OT_select_grouped";
1171
1172         /* api callbacks */
1173         ot->exec= select_groped_exec;
1174         ot->poll= space_clip_frame_poll;
1175
1176         /* flags */
1177         ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
1178
1179         /* proeprties */
1180         RNA_def_enum(ot->srna, "group", select_group_items, TRACK_CLEAR_REMAINED, "Action", "Clear action to execute");
1181 }
1182
1183 /********************** track operator *********************/
1184
1185 typedef struct TrackMarkersJob {
1186         struct MovieTrackingContext *context;   /* tracking context */
1187         int sfra, efra, lastfra;        /* Start, end and recently tracked frames */
1188         int backwards;                          /* Backwards tracking flag */
1189         MovieClip *clip;                        /* Clip which is tracking */
1190         float delay;                            /* Delay in milliseconds to allow tracking at fixed FPS */
1191
1192         struct Main *main;
1193         struct Scene *scene;
1194         struct bScreen *screen;
1195 } TrackMarkersJob;
1196
1197 static int track_markers_testbreak(void)
1198 {
1199         return G.afbreek;
1200 }
1201
1202 static int track_count_markers(SpaceClip *sc, MovieClip *clip)
1203 {
1204         int tot= 0;
1205         MovieTrackingTrack *track;
1206
1207         track= clip->tracking.tracks.first;
1208         while(track) {
1209                 if(TRACK_VIEW_SELECTED(sc, track) && (track->flag&TRACK_LOCKED)==0)
1210                         tot++;
1211
1212                 track= track->next;
1213         }
1214
1215         return tot;
1216 }
1217
1218 static void track_init_markers(SpaceClip *sc, MovieClip *clip)
1219 {
1220         MovieTrackingTrack *track;
1221         int framenr= sc->user.framenr, hidden= 0;
1222
1223         if((sc->flag&SC_SHOW_MARKER_PATTERN)==0) hidden|= TRACK_AREA_PAT;
1224         if((sc->flag&SC_SHOW_MARKER_SEARCH)==0) hidden|= TRACK_AREA_SEARCH;
1225
1226         track= clip->tracking.tracks.first;
1227         while(track) {
1228                 if(hidden)
1229                         BKE_tracking_track_flag(track, hidden, SELECT, 1);
1230
1231                 if(TRACK_SELECTED(track)) {
1232                         if((track->flag&TRACK_HIDDEN)==0 && (track->flag&TRACK_LOCKED)==0)
1233                                 BKE_tracking_ensure_marker(track, framenr);
1234                 }
1235
1236                 track= track->next;
1237         }
1238 }
1239
1240 static int track_markers_check_direction(int backwards, int curfra, int efra)
1241 {
1242         if(backwards) {
1243                 if(curfra<efra) return 0;
1244         }
1245         else {
1246                 if(curfra>efra) return 0;
1247         }
1248
1249         return 1;
1250 }
1251
1252 static int track_markers_initjob(bContext *C, TrackMarkersJob *tmj, int backwards)
1253 {
1254         SpaceClip *sc= CTX_wm_space_clip(C);
1255         MovieClip *clip= ED_space_clip(sc);
1256         Scene *scene= CTX_data_scene(C);
1257         MovieTrackingSettings *settings= &clip->tracking.settings;
1258
1259         tmj->sfra= sc->user.framenr;
1260         tmj->clip= clip;
1261         tmj->backwards= backwards;
1262
1263         if(backwards) tmj->efra= SFRA;
1264         else tmj->efra= EFRA;
1265
1266         /* limit frames to be tracked by user setting */
1267         if(settings->frames_limit) {
1268                 if(backwards) tmj->efra= MAX2(tmj->efra, tmj->sfra-settings->frames_limit);
1269                 else tmj->efra= MIN2(tmj->efra, tmj->sfra+settings->frames_limit);
1270         }
1271
1272         if(settings->speed!=TRACKING_SPEED_FASTEST) {
1273                 tmj->delay= 1.0f/scene->r.frs_sec*1000.0f;
1274
1275                 if(settings->speed==TRACKING_SPEED_HALF) tmj->delay*= 2;
1276                 else if(settings->speed==TRACKING_SPEED_QUARTER) tmj->delay*= 4;
1277                 else if(settings->speed==TRACKING_SPEED_DOUBLE) tmj->delay/= 2;
1278         }
1279
1280         track_init_markers(sc, clip);
1281
1282         tmj->context= BKE_tracking_context_new(clip, &sc->user, backwards, 1);
1283
1284         clip->tracking_context= tmj->context;
1285
1286         tmj->lastfra= tmj->sfra;
1287
1288         /* XXX: silly to store this, but this data is needed to update scene and movieclip
1289                 frame numbers when tracking is finished. This introduces better feedback for artists.
1290                 Maybe there's another way to solve this problem, but can't think better way atm.
1291                 Anyway, this way isn't more unstable as animation rendering animation
1292                 which uses the same approach (except storing screen). */
1293         tmj->scene= scene;
1294         tmj->main= CTX_data_main(C);
1295         tmj->screen= CTX_wm_screen(C);
1296
1297         return track_markers_check_direction(backwards, tmj->sfra, tmj->efra);
1298 }
1299
1300 static void track_markers_startjob(void *tmv, short *stop, short *do_update, float *progress)
1301 {
1302         TrackMarkersJob *tmj= (TrackMarkersJob *)tmv;
1303         int framenr= tmj->sfra;
1304         //double t= PIL_check_seconds_timer();
1305
1306         while(framenr != tmj->efra) {
1307                 if(tmj->delay>0) {
1308                         /* tracking should happen with fixed fps. Calculate time
1309                            using current timer value before tracking frame and after.
1310
1311                            Small (and maybe unneeded optimization): do not calculate exec_time
1312                            for "Fastest" tracking */
1313
1314                         double start_time= PIL_check_seconds_timer(), exec_time;
1315
1316                         if(!BKE_tracking_next(tmj->context))
1317                                 break;
1318
1319                         exec_time= PIL_check_seconds_timer()-start_time;
1320                         if(tmj->delay>exec_time)
1321                                 PIL_sleep_ms(tmj->delay-exec_time);
1322                 } else if(!BKE_tracking_next(tmj->context))
1323                                 break;
1324
1325                 *do_update= 1;
1326                 *progress=(float)(framenr-tmj->sfra) / (tmj->efra-tmj->sfra);
1327
1328                 if(tmj->backwards) framenr--;
1329                 else framenr++;
1330
1331                 tmj->lastfra= framenr;
1332
1333                 if(*stop || track_markers_testbreak())
1334                         break;
1335         }
1336
1337         //printf("Tracking time: %lf\n", PIL_check_seconds_timer()-t);
1338 }
1339
1340 static void track_markers_updatejob(void *tmv)
1341 {
1342         TrackMarkersJob *tmj= (TrackMarkersJob *)tmv;
1343
1344         BKE_tracking_sync(tmj->context);
1345 }
1346
1347 static void track_markers_freejob(void *tmv)
1348 {
1349         TrackMarkersJob *tmj= (TrackMarkersJob *)tmv;
1350
1351         tmj->clip->tracking_context= NULL;
1352         tmj->scene->r.cfra= tmj->lastfra;
1353         ED_update_for_newframe(tmj->main, tmj->scene, tmj->screen, 0);
1354
1355         BKE_tracking_sync(tmj->context);
1356         BKE_tracking_context_free(tmj->context);
1357
1358         MEM_freeN(tmj);
1359
1360         WM_main_add_notifier(NC_SCENE|ND_FRAME, tmj->scene);
1361 }
1362
1363 static int track_markers_exec(bContext *C, wmOperator *op)
1364 {
1365         SpaceClip *sc= CTX_wm_space_clip(C);
1366         MovieClip *clip= ED_space_clip(sc);
1367         Scene *scene= CTX_data_scene(C);
1368         struct MovieTrackingContext *context;
1369         int framenr= sc->user.framenr;
1370         int sfra= framenr, efra;
1371         int backwards= RNA_boolean_get(op->ptr, "backwards");
1372         int sequence= RNA_boolean_get(op->ptr, "sequence");
1373         MovieTrackingSettings *settings= &clip->tracking.settings;
1374
1375         if(track_count_markers(sc, clip)==0)
1376                 return OPERATOR_CANCELLED;
1377
1378         if(backwards) efra= SFRA;
1379         else efra= EFRA;
1380
1381         /* limit frames to be tracked by user setting */
1382         if(settings->frames_limit) {
1383                 if(backwards) efra= MAX2(efra, sfra-settings->frames_limit);
1384                 else efra= MIN2(efra, sfra+settings->frames_limit);
1385         }
1386
1387         if(!track_markers_check_direction(backwards, framenr, efra))
1388                 return OPERATOR_CANCELLED;
1389
1390         track_init_markers(sc, clip);
1391
1392         /* do not disable tracks due to threshold when tracking frame-by-frame */
1393         context= BKE_tracking_context_new(clip, &sc->user, backwards, sequence);
1394
1395         while(framenr != efra) {
1396                 if(!BKE_tracking_next(context))
1397                         break;
1398
1399                 if(backwards) framenr--;
1400                 else framenr++;
1401
1402                 if(!sequence)
1403                         break;
1404         }
1405
1406         BKE_tracking_sync(context);
1407         BKE_tracking_context_free(context);
1408
1409         /* update scene current frame to the lastes tracked frame */
1410         scene->r.cfra= framenr;
1411
1412         WM_event_add_notifier(C, NC_MOVIECLIP|NA_EVALUATED, clip);
1413         WM_event_add_notifier(C, NC_SCENE|ND_FRAME, scene);
1414
1415         return OPERATOR_FINISHED;
1416 }
1417
1418 static int track_markers_invoke(bContext *C, wmOperator *op, wmEvent *UNUSED(event))
1419 {
1420         TrackMarkersJob *tmj;
1421         ScrArea *sa= CTX_wm_area(C);
1422         SpaceClip *sc= CTX_wm_space_clip(C);
1423         MovieClip *clip= ED_space_clip(sc);
1424         wmJob *steve;
1425         int backwards= RNA_boolean_get(op->ptr, "backwards");
1426         int sequence= RNA_boolean_get(op->ptr, "sequence");
1427
1428         if(clip->tracking_context)
1429                 return OPERATOR_CANCELLED;
1430
1431         if(track_count_markers(sc, clip)==0)
1432                 return OPERATOR_CANCELLED;
1433
1434         if(!sequence)
1435                 return track_markers_exec(C, op);
1436
1437         tmj= MEM_callocN(sizeof(TrackMarkersJob), "TrackMarkersJob data");
1438         if(!track_markers_initjob(C, tmj, backwards)) {
1439                 track_markers_freejob(tmj);
1440
1441                 return OPERATOR_CANCELLED;
1442         }
1443
1444         /* setup job */
1445         steve= WM_jobs_get(CTX_wm_manager(C), CTX_wm_window(C), sa, "Track Markers", WM_JOB_PROGRESS);
1446         WM_jobs_customdata(steve, tmj, track_markers_freejob);
1447
1448         /* if there's delay set in tracking job, tracking should happen
1449            with fixed FPS. To deal with editor refresh we have to syncronize
1450            tracks from job and tracks in clip. Do this in timer callback
1451            to prevent threading conflicts. */
1452         if(tmj->delay>0) WM_jobs_timer(steve, tmj->delay/1000.0f, NC_MOVIECLIP|NA_EVALUATED, 0);
1453         else WM_jobs_timer(steve, 0.2, NC_MOVIECLIP|NA_EVALUATED, 0);
1454
1455         WM_jobs_callbacks(steve, track_markers_startjob, NULL, track_markers_updatejob, NULL);
1456
1457         G.afbreek= 0;
1458
1459         WM_jobs_start(CTX_wm_manager(C), steve);
1460         WM_cursor_wait(0);
1461
1462         /* add modal handler for ESC */
1463         WM_event_add_modal_handler(C, op);
1464
1465         return OPERATOR_RUNNING_MODAL;
1466 }
1467
1468 static int track_markers_modal(bContext *C, wmOperator *UNUSED(op), wmEvent *event)
1469 {
1470         /* no running blender, remove handler and pass through */
1471         if(0==WM_jobs_test(CTX_wm_manager(C), CTX_wm_area(C)))
1472                 return OPERATOR_FINISHED|OPERATOR_PASS_THROUGH;
1473
1474         /* running tracking */
1475         switch (event->type) {
1476                 case ESCKEY:
1477                         return OPERATOR_RUNNING_MODAL;
1478                         break;
1479         }
1480
1481         return OPERATOR_PASS_THROUGH;
1482 }
1483
1484 void CLIP_OT_track_markers(wmOperatorType *ot)
1485 {
1486         /* identifiers */
1487         ot->name= "Track Markers";
1488         ot->description= "Track selected markers";
1489         ot->idname= "CLIP_OT_track_markers";
1490
1491         /* api callbacks */
1492         ot->exec= track_markers_exec;
1493         ot->invoke= track_markers_invoke;
1494         ot->poll= space_clip_frame_poll;
1495         ot->modal= track_markers_modal;
1496
1497         /* flags */
1498         ot->flag= OPTYPE_UNDO;
1499
1500         /* properties */
1501         RNA_def_boolean(ot->srna, "backwards", 0, "Backwards", "Do backwards tracking");
1502         RNA_def_boolean(ot->srna, "sequence", 0, "Track Sequence", "Track marker during image sequence rather than single image");
1503 }
1504
1505 /********************** solve camera operator *********************/
1506
1507 static int check_solve_track_count(MovieTracking *tracking)
1508 {
1509         int tot= 0;
1510         int frame1= tracking->settings.keyframe1, frame2= tracking->settings.keyframe2;
1511         MovieTrackingTrack *track;
1512
1513         track= tracking->tracks.first;
1514         while(track) {
1515                 if(BKE_tracking_has_marker(track, frame1))
1516                         if(BKE_tracking_has_marker(track, frame2))
1517                                 tot++;
1518
1519                 track= track->next;
1520         }
1521
1522         return tot>=8;
1523 }
1524
1525 static int solve_camera_exec(bContext *C, wmOperator *op)
1526 {
1527         SpaceClip *sc= CTX_wm_space_clip(C);
1528         MovieClip *clip= ED_space_clip(sc);
1529         Scene *scene= CTX_data_scene(C);
1530         MovieTracking *tracking= &clip->tracking;
1531         int width, height;
1532         float error;
1533
1534         if(!check_solve_track_count(tracking)) {
1535                 BKE_report(op->reports, RPT_ERROR, "At least 8 tracks on both of keyframes are needed for reconstruction");
1536                 return OPERATOR_CANCELLED;
1537         }
1538         /* XXX sergey, please fix this. it's not obvious to me that it is not
1539          * an encapsulation violation to call the libmv c api from here. */
1540         /*
1541         if(!libmv_refineParametersAreValid(tracking->settings.refine_camera_intrinsics)) {
1542                 BKE_report(op->reports, RPT_ERROR, "Invalid combination for intrinsic refinement");
1543                 return OPERATOR_CANCELLED;
1544         }*/
1545
1546         /* could fail if footage uses images with different sizes */
1547         BKE_movieclip_get_size(clip, NULL, &width, &height);
1548
1549         error= BKE_tracking_solve_reconstruction(tracking, width, height);
1550
1551         if(error<0)
1552                 BKE_report(op->reports, RPT_WARNING, "Some data failed to reconstruct, see console for details");
1553         else
1554                 BKE_reportf(op->reports, RPT_INFO, "Average reprojection error %.3f", error);
1555
1556         scene->clip= clip;
1557         id_us_plus(&clip->id);
1558
1559         if(!scene->camera)
1560                 scene->camera= scene_find_camera(scene);
1561
1562         if(scene->camera) {
1563                 /* set blender camera focal length so result would look fine there */
1564                 Camera *camera= (Camera*)scene->camera->data;
1565
1566                 BKE_tracking_camera_to_blender(tracking, scene, camera, width, height);
1567
1568                 WM_event_add_notifier(C, NC_OBJECT, camera);
1569         }
1570
1571         DAG_id_tag_update(&clip->id, 0);
1572
1573         WM_event_add_notifier(C, NC_MOVIECLIP|NA_EVALUATED, clip);
1574         WM_event_add_notifier(C, NC_OBJECT|ND_TRANSFORM, NULL);
1575
1576         /* update active clip displayed in scene buttons */
1577         WM_event_add_notifier(C, NC_SCENE, scene);
1578
1579         return OPERATOR_FINISHED;
1580 }
1581
1582 void CLIP_OT_solve_camera(wmOperatorType *ot)
1583 {
1584         /* identifiers */
1585         ot->name= "Solve Camera";
1586         ot->description= "Solve camera motion from tracks";
1587         ot->idname= "CLIP_OT_solve_camera";
1588
1589         /* api callbacks */
1590         ot->exec= solve_camera_exec;
1591         ot->poll= ED_space_clip_poll;
1592
1593         /* flags */
1594         ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
1595 }
1596
1597 /********************** clear solution operator *********************/
1598
1599 static int clear_solution_exec(bContext *C, wmOperator *UNUSED(op))
1600 {
1601         SpaceClip *sc= CTX_wm_space_clip(C);
1602         MovieClip *clip= ED_space_clip(sc);
1603         MovieTracking *tracking= &clip->tracking;
1604         MovieTrackingTrack *track= tracking->tracks.first;
1605
1606         while(track) {
1607                 track->flag&= ~TRACK_HAS_BUNDLE;
1608
1609                 track= track->next;
1610         }
1611
1612         if(tracking->reconstruction.cameras)
1613                 MEM_freeN(tracking->reconstruction.cameras);
1614
1615         tracking->reconstruction.cameras= NULL;
1616         tracking->reconstruction.camnr= 0;
1617
1618         tracking->reconstruction.flag&= ~TRACKING_RECONSTRUCTED;
1619
1620         DAG_id_tag_update(&clip->id, 0);
1621
1622         WM_event_add_notifier(C, NC_MOVIECLIP|NA_EVALUATED, clip);
1623         WM_event_add_notifier(C, NC_SPACE|ND_SPACE_VIEW3D, NULL);
1624
1625         return OPERATOR_FINISHED;
1626 }
1627
1628 void CLIP_OT_clear_solution(wmOperatorType *ot)
1629 {
1630         /* identifiers */
1631         ot->name= "Clear Solution";
1632         ot->description= "Clear all calculated data";
1633         ot->idname= "CLIP_OT_clear_solution";
1634
1635         /* api callbacks */
1636         ot->exec= clear_solution_exec;
1637         ot->poll= ED_space_clip_poll;
1638
1639         /* flags */
1640         ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
1641 }
1642
1643 /********************** clear track operator *********************/
1644
1645 static int clear_track_path_exec(bContext *C, wmOperator *op)
1646 {
1647         SpaceClip *sc= CTX_wm_space_clip(C);
1648         MovieClip *clip= ED_space_clip(sc);
1649         MovieTrackingTrack *track;
1650         int action= RNA_enum_get(op->ptr, "action");
1651
1652         track= clip->tracking.tracks.first;
1653         while(track) {
1654                 if(TRACK_VIEW_SELECTED(sc, track))
1655                         BKE_tracking_clear_path(track, sc->user.framenr, action);
1656
1657                 track= track->next;
1658         }
1659
1660         WM_event_add_notifier(C, NC_MOVIECLIP|NA_EVALUATED, clip);
1661
1662         return OPERATOR_FINISHED;
1663 }
1664
1665 void CLIP_OT_clear_track_path(wmOperatorType *ot)
1666 {
1667         static EnumPropertyItem clear_path_actions[] = {
1668                         {TRACK_CLEAR_UPTO, "UPTO", 0, "Clear up-to", "Clear path up to current frame"},
1669                         {TRACK_CLEAR_REMAINED, "REMAINED", 0, "Clear remained", "Clear path at remained frames (after current)"},
1670                         {TRACK_CLEAR_ALL, "ALL", 0, "Clear all", "Clear the whole path"},
1671                         {0, NULL, 0, NULL, NULL}
1672         };
1673
1674         /* identifiers */
1675         ot->name= "Clear Track Path";
1676         ot->description= "Clear path of selected tracks";
1677         ot->idname= "CLIP_OT_clear_track_path";
1678
1679         /* api callbacks */
1680         ot->exec= clear_track_path_exec;
1681         ot->poll= ED_space_clip_poll;
1682
1683         /* flags */
1684         ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
1685
1686         /* proeprties */
1687         RNA_def_enum(ot->srna, "action", clear_path_actions, TRACK_CLEAR_REMAINED, "Action", "Clear action to execute");
1688 }
1689
1690 /********************** disable markers operator *********************/
1691
1692 static int disable_markers_exec(bContext *C, wmOperator *op)
1693 {
1694         SpaceClip *sc= CTX_wm_space_clip(C);
1695         MovieClip *clip= ED_space_clip(sc);
1696         MovieTracking *tracking= &clip->tracking;
1697         MovieTrackingTrack *track= tracking->tracks.first;
1698         int action= RNA_enum_get(op->ptr, "action");
1699
1700         while(track) {
1701                 if(TRACK_VIEW_SELECTED(sc, track) && (track->flag&TRACK_LOCKED)==0) {
1702                         MovieTrackingMarker *marker= BKE_tracking_ensure_marker(track, sc->user.framenr);
1703
1704                         if(action==0) marker->flag|= MARKER_DISABLED;
1705                         else if(action==1) marker->flag&= ~MARKER_DISABLED;
1706                         else marker->flag^= MARKER_DISABLED;
1707                 }
1708
1709                 track= track->next;
1710         }
1711
1712         DAG_id_tag_update(&clip->id, 0);
1713
1714         WM_event_add_notifier(C, NC_MOVIECLIP|NA_EVALUATED, clip);
1715
1716         return OPERATOR_FINISHED;
1717 }
1718
1719 void CLIP_OT_disable_markers(wmOperatorType *ot)
1720 {
1721         static EnumPropertyItem actions_items[] = {
1722                         {0, "DISABLE", 0, "Disable", "Disable selected markers"},
1723                         {1, "ENABLE", 0, "Enable", "Enable selected markers"},
1724                         {2, "TOGGLE", 0, "Toggle", "Toggle disabled flag for selected markers"},
1725                         {0, NULL, 0, NULL, NULL}
1726         };
1727
1728         /* identifiers */
1729         ot->name= "Disable Markers";
1730         ot->description= "Disable/enable selected markers";
1731         ot->idname= "CLIP_OT_disable_markers";
1732
1733         /* api callbacks */
1734         ot->exec= disable_markers_exec;
1735         ot->poll= ED_space_clip_poll;
1736
1737         /* flags */
1738         ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
1739
1740         /* properties */
1741         RNA_def_enum(ot->srna, "action", actions_items, 0, "Action", "Disable action to execute");
1742 }
1743
1744 /********************** set origin operator *********************/
1745
1746 static int count_selected_bundles(bContext *C)
1747 {
1748         SpaceClip *sc= CTX_wm_space_clip(C);
1749         MovieClip *clip= ED_space_clip(sc);
1750         MovieTrackingTrack *track;
1751         int tot= 0;
1752
1753         track= clip->tracking.tracks.first;
1754         while(track) {
1755                 if(TRACK_VIEW_SELECTED(sc, track) && (track->flag&TRACK_HAS_BUNDLE))
1756                         tot++;
1757
1758                 track= track->next;
1759         }
1760
1761         return tot;
1762 }
1763
1764 static int set_origin_exec(bContext *C, wmOperator *op)
1765 {
1766         SpaceClip *sc= CTX_wm_space_clip(C);
1767         MovieClip *clip= ED_space_clip(sc);
1768         MovieTrackingTrack *track;
1769         Scene *scene= CTX_data_scene(C);
1770         Object *parent= scene->camera;
1771         float mat[4][4], vec[3];
1772
1773         if(count_selected_bundles(C)!=1) {
1774                 BKE_report(op->reports, RPT_ERROR, "Track with bundle should be selected to define origin position");
1775                 return OPERATOR_CANCELLED;
1776         }
1777
1778         if(scene->camera->parent)
1779                 parent= scene->camera->parent;
1780
1781         track= clip->tracking.tracks.first;
1782         while(track) {
1783                 if(TRACK_VIEW_SELECTED(sc, track))
1784                         break;
1785
1786                 track= track->next;
1787         }
1788
1789         BKE_get_tracking_mat(scene, NULL, mat);
1790         mul_v3_m4v3(vec, mat, track->bundle_pos);
1791
1792         sub_v3_v3(parent->loc, vec);
1793
1794         DAG_id_tag_update(&clip->id, 0);
1795         DAG_id_tag_update(&parent->id, OB_RECALC_OB);
1796
1797         WM_event_add_notifier(C, NC_MOVIECLIP|NA_EVALUATED, clip);
1798         WM_event_add_notifier(C, NC_OBJECT|ND_TRANSFORM, NULL);
1799
1800         return OPERATOR_FINISHED;
1801 }
1802
1803 void CLIP_OT_set_origin(wmOperatorType *ot)
1804 {
1805         /* identifiers */
1806         ot->name= "Set Origin";
1807         ot->description= "Set active marker as origin by moving camera (or it's parent if present) in 3d space";
1808         ot->idname= "CLIP_OT_set_origin";
1809
1810         /* api callbacks */
1811         ot->exec= set_origin_exec;
1812         ot->poll= space_clip_frame_camera_poll;
1813
1814         /* flags */
1815         ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
1816 }
1817
1818 /********************** set floor operator *********************/
1819
1820 static void set_axis(Scene *scene,  Object *ob, MovieTrackingTrack *track, char axis)
1821 {
1822         float mat[4][4], vec[3], obmat[4][4];
1823
1824         BKE_get_tracking_mat(scene, NULL, mat);
1825         mul_v3_m4v3(vec, mat, track->bundle_pos);
1826
1827         if(len_v2(vec)<1e-3)
1828                 return;
1829
1830         unit_m4(mat);
1831
1832         if(axis=='X') {
1833                 if(fabsf(vec[1])<1e-3) {
1834                         mat[0][0]= -1.0f; mat[0][1]= 0.0f; mat[0][2]= 0.0f;
1835                         mat[1][0]= 0.0f; mat[1][1]= -1.0f; mat[1][2]= 0.0f;
1836                         mat[2][0]= 0.0f; mat[2][1]= 0.0f; mat[2][2]= 1.0f;
1837                 } else {
1838                         copy_v3_v3(mat[0], vec);
1839                         mat[0][2]= 0.0f;
1840                         mat[2][0]= 0.0f; mat[2][1]= 0.0f; mat[2][2]= 1.0f;
1841                         cross_v3_v3v3(mat[1], mat[2], mat[0]);
1842                 }
1843         } else {
1844                 if(fabsf(vec[0])<1e-3) {
1845                         mat[0][0]= -1.0f; mat[0][1]= 0.0f; mat[0][2]= 0.0f;
1846                         mat[1][0]= 0.0f; mat[1][1]= -1.0f; mat[1][2]= 0.0f;
1847                         mat[2][0]= 0.0f; mat[2][1]= 0.0f; mat[2][2]= 1.0f;
1848                 } else {
1849                         copy_v3_v3(mat[1], vec);
1850                         mat[1][2]= 0.0f;
1851                         mat[2][0]= 0.0f; mat[2][1]= 0.0f; mat[2][2]= 1.0f;
1852                         cross_v3_v3v3(mat[0], mat[1], mat[2]);
1853                 }
1854         }
1855
1856         normalize_v3(mat[0]);
1857         normalize_v3(mat[1]);
1858         normalize_v3(mat[2]);
1859
1860         invert_m4(mat);
1861
1862         object_to_mat4(ob, obmat);
1863         mul_m4_m4m4(mat, obmat, mat);
1864         object_apply_mat4(ob, mat, 0, 0);
1865 }
1866
1867 static int set_floor_exec(bContext *C, wmOperator *op)
1868 {
1869         SpaceClip *sc= CTX_wm_space_clip(C);
1870         MovieClip *clip= ED_space_clip(sc);
1871         Scene *scene= CTX_data_scene(C);
1872         MovieTrackingTrack *track, *axis_track= NULL;
1873         Object *camera= scene->camera;
1874         Object *parent= camera;
1875         int tot= 0;
1876         float vec[3][3], mat[4][4], obmat[4][4], newmat[4][4], orig[3]= {0.0f, 0.0f, 0.0f};
1877         float rot[4][4]={{0.0f, 0.0f, -1.0f, 0.0f},
1878                          {0.0f, 1.0f, 0.0f, 0.0f},
1879                          {1.0f, 0.0f, 0.0f, 0.0f},
1880                          {0.0f, 0.0f, 0.0f, 1.0f}};     /* 90 degrees Y-axis rotation matrix */
1881
1882         if(count_selected_bundles(C)!=3) {
1883                 BKE_report(op->reports, RPT_ERROR, "Three tracks with bundles are needed to orient the floor");
1884                 return OPERATOR_CANCELLED;
1885         }
1886
1887         if(scene->camera->parent)
1888                 parent= scene->camera->parent;
1889
1890         BKE_get_tracking_mat(scene, NULL, mat);
1891
1892         /* get 3 bundles to use as reference */
1893         track= clip->tracking.tracks.first;
1894         while(track && tot<3) {
1895                 if(track->flag&TRACK_HAS_BUNDLE && TRACK_VIEW_SELECTED(sc, track)) {
1896                         mul_v3_m4v3(vec[tot], mat, track->bundle_pos);
1897
1898                         if(tot==0 || track==clip->tracking.act_track)
1899                                 copy_v3_v3(orig, vec[tot]);
1900                         else
1901                                 axis_track= track;
1902
1903                         tot++;
1904                 }
1905
1906                 track= track->next;
1907         }
1908
1909         sub_v3_v3(vec[1], vec[0]);
1910         sub_v3_v3(vec[2], vec[0]);
1911
1912         /* construct ortho-normal basis */
1913         unit_m4(mat);
1914
1915         cross_v3_v3v3(mat[0], vec[1], vec[2]);
1916         copy_v3_v3(mat[1], vec[1]);
1917         cross_v3_v3v3(mat[2], mat[0], mat[1]);
1918
1919         normalize_v3(mat[0]);
1920         normalize_v3(mat[1]);
1921         normalize_v3(mat[2]);
1922
1923         /* move to origin point */
1924         mat[3][0]= orig[0];
1925         mat[3][1]= orig[1];
1926         mat[3][2]= orig[2];
1927
1928         invert_m4(mat);
1929
1930         object_to_mat4(parent, obmat);
1931         mul_m4_m4m4(mat, obmat, mat);
1932         mul_m4_m4m4(newmat, mat, rot);
1933         object_apply_mat4(parent, newmat, 0, 0);
1934
1935         /* make camera have positive z-coordinate */
1936         mul_v3_m4v3(vec[0], mat, camera->loc);
1937         if(camera->loc[2]<0) {
1938                 invert_m4(rot);
1939                 mul_m4_m4m4(newmat, mat, rot);
1940                 object_apply_mat4(camera, newmat, 0, 0);
1941         }
1942
1943         where_is_object(scene, parent);
1944         set_axis(scene, parent, axis_track, 'X');
1945
1946         DAG_id_tag_update(&clip->id, 0);
1947         DAG_id_tag_update(&parent->id, OB_RECALC_OB);
1948
1949         WM_event_add_notifier(C, NC_MOVIECLIP|NA_EVALUATED, clip);
1950         WM_event_add_notifier(C, NC_OBJECT|ND_TRANSFORM, NULL);
1951
1952         return OPERATOR_FINISHED;
1953 }
1954
1955 void CLIP_OT_set_floor(wmOperatorType *ot)
1956 {
1957         /* identifiers */
1958         ot->name= "Set Floor";
1959         ot->description= "Set floor based on 3 selected bundles by moving camera (or it's parent if present) in 3d space";
1960         ot->idname= "CLIP_OT_set_floor";
1961
1962         /* api callbacks */
1963         ot->exec= set_floor_exec;
1964         ot->poll= space_clip_camera_poll;
1965
1966         /* flags */
1967         ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
1968 }
1969
1970 /********************** set axis operator *********************/
1971
1972 static int set_axis_exec(bContext *C, wmOperator *op)
1973 {
1974         SpaceClip *sc= CTX_wm_space_clip(C);
1975         MovieClip *clip= ED_space_clip(sc);
1976         MovieTrackingTrack *track;
1977         Scene *scene= CTX_data_scene(C);
1978         Object *parent= scene->camera;
1979         int axis= RNA_enum_get(op->ptr, "axis");
1980
1981         if(count_selected_bundles(C)!=1) {
1982                 BKE_report(op->reports, RPT_ERROR, "Single track with bundle should be selected to define axis");
1983
1984                 return OPERATOR_CANCELLED;
1985         }
1986
1987         if(scene->camera->parent)
1988                 parent= scene->camera->parent;
1989
1990         track= clip->tracking.tracks.first;
1991         while(track) {
1992                 if(TRACK_VIEW_SELECTED(sc, track))
1993                         break;
1994
1995                 track= track->next;
1996         }
1997
1998         set_axis(scene, parent, track, axis==0?'X':'Y');
1999
2000         DAG_id_tag_update(&clip->id, 0);
2001         DAG_id_tag_update(&parent->id, OB_RECALC_OB);
2002
2003         WM_event_add_notifier(C, NC_MOVIECLIP|NA_EVALUATED, clip);
2004         WM_event_add_notifier(C, NC_OBJECT|ND_TRANSFORM, NULL);
2005
2006         return OPERATOR_FINISHED;
2007 }
2008
2009 void CLIP_OT_set_axis(wmOperatorType *ot)
2010 {
2011         static EnumPropertyItem axis_actions[] = {
2012                         {0, "X", 0, "X", "Align bundle align X axis"},
2013                         {1, "Y", 0, "Y", "Align bundle align Y axis"},
2014                         {0, NULL, 0, NULL, NULL}
2015         };
2016
2017         /* identifiers */
2018         ot->name= "Set Axis";
2019         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";
2020         ot->idname= "CLIP_OT_set_axis";
2021
2022         /* api callbacks */
2023         ot->exec= set_axis_exec;
2024         ot->poll= space_clip_frame_camera_poll;
2025
2026         /* flags */
2027         ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
2028
2029         /* properties */
2030         RNA_def_enum(ot->srna, "axis", axis_actions, 0, "Axis", "Axis to use to align bundle along");
2031 }
2032
2033 /********************** set scale operator *********************/
2034
2035 static int set_scale_exec(bContext *C, wmOperator *op)
2036 {
2037         SpaceClip *sc= CTX_wm_space_clip(C);
2038         MovieClip *clip= ED_space_clip(sc);
2039         MovieTrackingTrack *track;
2040         Scene *scene= CTX_data_scene(C);
2041         Object *parent= scene->camera;
2042         int tot= 0;
2043         float vec[2][3], mat[4][4], scale;
2044         float dist= RNA_float_get(op->ptr, "distance");
2045
2046         if(count_selected_bundles(C)!=2) {
2047                 BKE_report(op->reports, RPT_ERROR, "Two tracks with bundles should be selected to scale scene");
2048
2049                 return OPERATOR_CANCELLED;
2050         }
2051
2052         if(scene->camera->parent)
2053                 parent= scene->camera->parent;
2054
2055         BKE_get_tracking_mat(scene, NULL, mat);
2056
2057         track= clip->tracking.tracks.first;
2058         while(track) {
2059                 if(TRACK_VIEW_SELECTED(sc, track)) {
2060                         mul_v3_m4v3(vec[tot], mat, track->bundle_pos);
2061                         tot++;
2062                 }
2063
2064                 track= track->next;
2065         }
2066
2067         sub_v3_v3(vec[0], vec[1]);
2068
2069         if(len_v3(vec[0])>1e-5) {
2070                 scale= dist / len_v3(vec[0]);
2071
2072                 mul_v3_fl(parent->size, scale);
2073                 mul_v3_fl(parent->loc, scale);
2074
2075                 DAG_id_tag_update(&clip->id, 0);
2076                 DAG_id_tag_update(&parent->id, OB_RECALC_OB);
2077
2078                 WM_event_add_notifier(C, NC_MOVIECLIP|NA_EVALUATED, clip);
2079                 WM_event_add_notifier(C, NC_OBJECT|ND_TRANSFORM, NULL);
2080         }
2081
2082         return OPERATOR_FINISHED;
2083 }
2084
2085 static int set_scale_invoke(bContext *C, wmOperator *op, wmEvent *UNUSED(event))
2086 {
2087         SpaceClip *sc= CTX_wm_space_clip(C);
2088         MovieClip *clip= ED_space_clip(sc);
2089         float dist= RNA_float_get(op->ptr, "distance");
2090
2091         if(dist==0.0f)
2092                 RNA_float_set(op->ptr, "distance", clip->tracking.settings.dist);
2093
2094         return set_scale_exec(C, op);
2095 }
2096
2097 void CLIP_OT_set_scale(wmOperatorType *ot)
2098 {
2099         /* identifiers */
2100         ot->name= "Set Scale";
2101         ot->description= "Set scale of scene by scaling camera (or it's parent if present)";
2102         ot->idname= "CLIP_OT_set_scale";
2103
2104         /* api callbacks */
2105         ot->exec= set_scale_exec;
2106         ot->invoke= set_scale_invoke;
2107         ot->poll= space_clip_frame_camera_poll;
2108
2109         /* flags */
2110         ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
2111
2112         /* properties */
2113         RNA_def_float(ot->srna, "distance", 0.0f, -FLT_MAX, FLT_MAX,
2114                 "Distance", "Distance between selected tracks", -100.0f, 100.0f);
2115 }
2116
2117 /********************** set principal center operator *********************/
2118
2119 static int set_center_principal_exec(bContext *C, wmOperator *UNUSED(op))
2120 {
2121         SpaceClip *sc= CTX_wm_space_clip(C);
2122         MovieClip *clip= ED_space_clip(sc);
2123         int width, height;
2124
2125         BKE_movieclip_get_size(clip, &sc->user, &width, &height);
2126
2127         if(width==0 || height==0)
2128                 return OPERATOR_CANCELLED;
2129
2130         clip->tracking.camera.principal[0]= ((float)width)/2.0f;
2131         clip->tracking.camera.principal[1]= ((float)height)/2.0f;
2132
2133         WM_event_add_notifier(C, NC_MOVIECLIP|NA_EDITED, clip);
2134
2135         return OPERATOR_FINISHED;
2136 }
2137
2138 void CLIP_OT_set_center_principal(wmOperatorType *ot)
2139 {
2140         /* identifiers */
2141         ot->name= "Set Principal to Center";
2142         ot->description= "Set principal point to center of footage";
2143         ot->idname= "CLIP_OT_set_center_principal";
2144
2145         /* api callbacks */
2146         ot->exec= set_center_principal_exec;
2147         ot->poll= ED_space_clip_poll;
2148
2149         /* flags */
2150         ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
2151 }
2152
2153 /********************** hide tracks operator *********************/
2154
2155 static int hide_tracks_exec(bContext *C, wmOperator *op)
2156 {
2157         SpaceClip *sc= CTX_wm_space_clip(C);
2158         MovieClip *clip= ED_space_clip(sc);
2159         MovieTrackingTrack *track;
2160         int unselected;
2161
2162         unselected= RNA_boolean_get(op->ptr, "unselected");
2163
2164         track= clip->tracking.tracks.first;
2165         while(track) {
2166                 if(unselected==0 && TRACK_VIEW_SELECTED(sc, track)) {
2167                         track->flag|= TRACK_HIDDEN;
2168                 } else if(unselected==1 && !TRACK_VIEW_SELECTED(sc, track)) {
2169                         track->flag|= TRACK_HIDDEN;
2170                 }
2171
2172                 track= track->next;
2173         }
2174
2175         if(clip->tracking.act_track && clip->tracking.act_track->flag&TRACK_HIDDEN)
2176                 clip->tracking.act_track= NULL;
2177
2178         if(unselected==0) {
2179                 /* no selection on screen now, unlock view so it can be scrolled nice again */
2180                 sc->flag&= ~SC_LOCK_SELECTION;
2181         }
2182
2183         WM_event_add_notifier(C, NC_MOVIECLIP|ND_DISPLAY, NULL);
2184
2185         return OPERATOR_FINISHED;
2186 }
2187
2188 void CLIP_OT_hide_tracks(wmOperatorType *ot)
2189 {
2190         /* identifiers */
2191         ot->name= "Hide Tracks";
2192         ot->description= "Hide selected tracks";
2193         ot->idname= "CLIP_OT_hide_tracks";
2194
2195         /* api callbacks */
2196         ot->exec= hide_tracks_exec;
2197         ot->poll= ED_space_clip_poll;
2198
2199         /* flags */
2200         ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
2201
2202         /* properties */
2203         RNA_def_boolean(ot->srna, "unselected", 0, "Unselected", "Hide unselected tracks");
2204 }
2205
2206 /********************** hide tracks clear operator *********************/
2207
2208 static int hide_tracks_clear_exec(bContext *C, wmOperator *UNUSED(op))
2209 {
2210         SpaceClip *sc= CTX_wm_space_clip(C);
2211         MovieClip *clip= ED_space_clip(sc);
2212         MovieTrackingTrack *track;
2213
2214         track= clip->tracking.tracks.first;
2215         while(track) {
2216                 track->flag&= ~TRACK_HIDDEN;
2217
2218                 track= track->next;
2219         }
2220
2221         WM_event_add_notifier(C, NC_MOVIECLIP|ND_DISPLAY, NULL);
2222
2223         return OPERATOR_FINISHED;
2224 }
2225
2226 void CLIP_OT_hide_tracks_clear(wmOperatorType *ot)
2227 {
2228         /* identifiers */
2229         ot->name= "Hide Tracks Clear";
2230         ot->description= "Clear hide selected tracks";
2231         ot->idname= "CLIP_OT_hide_tracks_clear";
2232
2233         /* api callbacks */
2234         ot->exec= hide_tracks_clear_exec;
2235         ot->poll= ED_space_clip_poll;
2236
2237         /* flags */
2238         ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
2239 }
2240
2241 /********************** detect features operator *********************/
2242
2243 static bGPDlayer *detect_get_layer(MovieClip *clip)
2244 {
2245         bGPDlayer *layer;
2246
2247         if(!clip->gpd)
2248                 return NULL;
2249
2250         layer= clip->gpd->layers.first;
2251         while(layer) {
2252                 if(layer->flag&GP_LAYER_ACTIVE)
2253                         return layer;
2254
2255                 layer= layer->next;
2256         }
2257
2258         return NULL;
2259 }
2260
2261 static int detect_features_exec(bContext *C, wmOperator *op)
2262 {
2263         SpaceClip *sc= CTX_wm_space_clip(C);
2264         MovieClip *clip= ED_space_clip(sc);
2265         ImBuf *ibuf= BKE_movieclip_get_ibuf_flag(clip, &sc->user, 0);
2266         MovieTrackingTrack *track= clip->tracking.tracks.first;
2267         int placement= RNA_enum_get(op->ptr, "placement");
2268         int margin= RNA_int_get(op->ptr, "margin");
2269         int min_trackness= RNA_int_get(op->ptr, "min_trackness");
2270         int min_distance= RNA_int_get(op->ptr, "min_distance");
2271         int place_outside_layer= 0;
2272         bGPDlayer *layer= NULL;
2273
2274         if(placement!=0) {
2275                 layer= detect_get_layer(clip);
2276                 place_outside_layer= placement==2;
2277         }
2278
2279         /* deselect existing tracks */
2280         while(track) {
2281                 track->flag&= ~SELECT;
2282                 track->pat_flag&= ~SELECT;
2283                 track->search_flag&= ~SELECT;
2284
2285                 track= track->next;
2286         }
2287
2288         BKE_tracking_detect_fast(&clip->tracking, ibuf, sc->user.framenr, margin, min_trackness, min_distance, layer, place_outside_layer);
2289
2290         IMB_freeImBuf(ibuf);
2291
2292         WM_event_add_notifier(C, NC_MOVIECLIP|NA_EDITED, NULL);
2293
2294         return OPERATOR_FINISHED;
2295 }
2296
2297 void CLIP_OT_detect_features(wmOperatorType *ot)
2298 {
2299         static EnumPropertyItem placement_items[] = {
2300                         {0, "FRAME",                    0, "Whole Frame",                       "Place markers across the whole frame"},
2301                         {1, "INSIDE_GPENCIL",   0, "Inside grease pencil",      "Place markers only inside areas oulined with grease pencil"},
2302                         {2, "OUTSIDE_GPENCIL",  0, "Outside grease pencil",     "Place markers only outside areas oulined with grease pencil"},
2303                         {0, NULL, 0, NULL, NULL}
2304         };
2305
2306         /* identifiers */
2307         ot->name= "Detect Features";
2308         ot->description= "Automatically detect features to track";
2309         ot->idname= "CLIP_OT_detect_features";
2310
2311         /* api callbacks */
2312         ot->exec= detect_features_exec;
2313         ot->poll= space_clip_frame_poll;
2314
2315         /* flags */
2316         ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
2317
2318         /* properties */
2319         RNA_def_enum(ot->srna, "placement", placement_items, 0, "Placement", "Placement for detected features");
2320         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);
2321         RNA_def_int(ot->srna, "min_trackness", 16, 0, INT_MAX, "Trackness", "Minimum score to add a corner", 0, 300);
2322         RNA_def_int(ot->srna, "min_distance", 120, 0, INT_MAX, "Distance", "Minimal distance accepted between two corners", 0, 300);
2323 }
2324
2325 /********************** frame jump operator *********************/
2326
2327 static int frame_jump_exec(bContext *C, wmOperator *op)
2328 {
2329         Scene *scene= CTX_data_scene(C);
2330         SpaceClip *sc= CTX_wm_space_clip(C);
2331         MovieClip *clip= ED_space_clip(sc);
2332         MovieTrackingTrack *track;
2333         int pos= RNA_enum_get(op->ptr, "position");
2334         int delta;
2335
2336         if(pos<=1) {    /* jump to path */
2337                 track= clip->tracking.act_track;
2338
2339                 if(!track)
2340                         return OPERATOR_CANCELLED;
2341
2342                 delta= pos == 1 ? 1 : -1;
2343
2344                 while(sc->user.framenr+delta >= SFRA && sc->user.framenr+delta <= EFRA) {
2345                         MovieTrackingMarker *marker= BKE_tracking_exact_marker(track, sc->user.framenr+delta);
2346
2347                         if(!marker || marker->flag&MARKER_DISABLED)
2348                                 break;
2349
2350                         sc->user.framenr+= delta;
2351                 }
2352         }
2353         else {  /* to to failed frame */
2354                 if(clip->tracking.reconstruction.flag&TRACKING_RECONSTRUCTED) {
2355                         int a= sc->user.framenr;
2356                         MovieTracking *tracking= &clip->tracking;
2357
2358                         delta= pos == 3 ? 1 : -1;
2359
2360                         a+= delta;
2361
2362                         while(a+delta >= SFRA && a+delta <= EFRA) {
2363                                 MovieReconstructedCamera *cam= BKE_tracking_get_reconstructed_camera(tracking, a);
2364
2365                                 if(!cam) {
2366                                         sc->user.framenr= a;
2367
2368                                         break;
2369                                 }
2370
2371                                 a+= delta;
2372                         }
2373                 }
2374         }
2375
2376         if(CFRA!=sc->user.framenr) {
2377                 CFRA= sc->user.framenr;
2378                 sound_seek_scene(CTX_data_main(C), CTX_data_scene(C));
2379
2380                 WM_event_add_notifier(C, NC_SCENE|ND_FRAME, scene);
2381         }
2382
2383         WM_event_add_notifier(C, NC_MOVIECLIP|ND_DISPLAY, NULL);
2384
2385         return OPERATOR_FINISHED;
2386 }
2387
2388 void CLIP_OT_frame_jump(wmOperatorType *ot)
2389 {
2390         static EnumPropertyItem position_items[] = {
2391                         {0, "PATHSTART",        0, "Path Start",                "Jump to start of current path"},
2392                         {1, "PATHEND",          0, "Path End",                  "Jump to end of current path"},
2393                         {2, "FAILEDPREV",       0, "Previons Failed",   "Jump to previous failed frame"},
2394                         {2, "FAILNEXT",         0, "Next Failed",               "Jump to next failed frame"},
2395                         {0, NULL, 0, NULL, NULL}
2396         };
2397
2398         /* identifiers */
2399         ot->name= "Jump to Frame";
2400         ot->description= "Jump to special frame";
2401         ot->idname= "CLIP_OT_frame_jump";
2402
2403         /* api callbacks */
2404         ot->exec= frame_jump_exec;
2405         ot->poll= space_clip_frame_poll;
2406
2407         /* flags */
2408         ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
2409
2410         /* properties */
2411         RNA_def_enum(ot->srna, "position", position_items, 0, "Position", "Position to jumo to");
2412 }
2413
2414 /********************** join tracks operator *********************/
2415
2416 static int join_tracks_exec(bContext *C, wmOperator *op)
2417 {
2418         SpaceClip *sc= CTX_wm_space_clip(C);
2419         MovieClip *clip= ED_space_clip(sc);
2420         MovieTrackingTrack *act_track, *track, *next;
2421
2422         act_track= clip->tracking.act_track;
2423
2424         if(!act_track) {
2425                 BKE_report(op->reports, RPT_ERROR, "No active track to join to");
2426                 return OPERATOR_CANCELLED;
2427         }
2428
2429         track= clip->tracking.tracks.first;
2430         while(track) {
2431                 if(TRACK_VIEW_SELECTED(sc, track) && track!=act_track) {
2432                         if(!BKE_tracking_test_join_tracks(act_track, track)) {
2433                                 BKE_report(op->reports, RPT_ERROR, "Some selected tracks have got keyframed markers to the same frame");
2434                                 return OPERATOR_CANCELLED;
2435                         }
2436                 }
2437
2438                 track= track->next;
2439         }
2440
2441         track= clip->tracking.tracks.first;
2442         while(track) {
2443                 next= track->next;
2444
2445                 if(TRACK_VIEW_SELECTED(sc, track) && track!=act_track) {
2446                         BKE_tracking_join_tracks(act_track, track);
2447
2448                         BKE_tracking_free_track(track);
2449                         BLI_freelinkN(&clip->tracking.tracks, track);
2450                 }
2451
2452                 track= next;
2453         }
2454
2455         WM_event_add_notifier(C, NC_MOVIECLIP|NA_EDITED, clip);
2456
2457         return OPERATOR_FINISHED;
2458 }
2459
2460 void CLIP_OT_join_tracks(wmOperatorType *ot)
2461 {
2462         /* identifiers */
2463         ot->name= "Join Tracks";
2464         ot->description= "Joint Selected Tracks";
2465         ot->idname= "CLIP_OT_join_tracks";
2466
2467         /* api callbacks */
2468         ot->exec= join_tracks_exec;
2469         ot->poll= space_clip_frame_poll;
2470
2471         /* flags */
2472         ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
2473 }
2474
2475 /********************** lock tracks operator *********************/
2476
2477 static int lock_tracks_exec(bContext *C, wmOperator *op)
2478 {
2479         SpaceClip *sc= CTX_wm_space_clip(C);
2480         MovieClip *clip= ED_space_clip(sc);
2481         MovieTracking *tracking= &clip->tracking;
2482         MovieTrackingTrack *track= tracking->tracks.first;
2483         int action= RNA_enum_get(op->ptr, "action");
2484
2485         while(track) {
2486                 if(TRACK_VIEW_SELECTED(sc, track)) {
2487                         if(action==0) track->flag|= TRACK_LOCKED;
2488                         else if(action==1) track->flag&= ~TRACK_LOCKED;
2489                         else track->flag^= TRACK_LOCKED;
2490                 }
2491
2492                 track= track->next;
2493         }
2494
2495         WM_event_add_notifier(C, NC_MOVIECLIP|NA_EVALUATED, clip);
2496
2497         return OPERATOR_FINISHED;
2498 }
2499
2500 void CLIP_OT_lock_tracks(wmOperatorType *ot)
2501 {
2502         static EnumPropertyItem actions_items[] = {
2503                         {0, "LOCK", 0, "Lock", "Lock selected tracks"},
2504                         {1, "UNLOCK", 0, "Unlock", "Unlock selected tracks"},
2505                         {2, "TOGGLE", 0, "Toggle", "Toggle locked flag for selected tracks"},
2506                         {0, NULL, 0, NULL, NULL}
2507         };
2508
2509         /* identifiers */
2510         ot->name= "Lock Tracks";
2511         ot->description= "Lock/unlock selected tracks";
2512         ot->idname= "CLIP_OT_lock_tracks";
2513
2514         /* api callbacks */
2515         ot->exec= lock_tracks_exec;
2516         ot->poll= ED_space_clip_poll;
2517
2518         /* flags */
2519         ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
2520
2521         /* properties */
2522         RNA_def_enum(ot->srna, "action", actions_items, 0, "Action", "Lock action to execute");
2523 }
2524
2525 /********************** track copy color operator *********************/
2526
2527 static int track_copy_color_exec(bContext *C, wmOperator *UNUSED(op))
2528 {
2529         SpaceClip *sc= CTX_wm_space_clip(C);
2530         MovieClip *clip= ED_space_clip(sc);
2531         MovieTrackingTrack *track, *act_track= clip->tracking.act_track;
2532
2533         if(!act_track)
2534                 return OPERATOR_CANCELLED;
2535
2536         track= clip->tracking.tracks.first;
2537         while(track) {
2538                 if(TRACK_VIEW_SELECTED(sc, track) && track!=act_track) {
2539                         track->flag&= ~TRACK_CUSTOMCOLOR;
2540
2541                         if(act_track->flag&TRACK_CUSTOMCOLOR) {
2542                                 copy_v3_v3(track->color, act_track->color);
2543                                 track->flag|= TRACK_CUSTOMCOLOR;
2544                         }
2545                 }
2546
2547                 track= track->next;
2548         }
2549
2550         WM_event_add_notifier(C, NC_MOVIECLIP|ND_DISPLAY, clip);
2551
2552         return OPERATOR_FINISHED;
2553 }
2554
2555 void CLIP_OT_track_copy_color(wmOperatorType *ot)
2556 {
2557         /* identifiers */
2558         ot->name= "Copy Color";
2559         ot->description= "Copy color to all selected tracks";
2560         ot->idname= "CLIP_OT_track_copy_color";
2561
2562         /* api callbacks */
2563         ot->exec= track_copy_color_exec;
2564         ot->poll= ED_space_clip_poll;
2565
2566         /* flags */
2567         ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
2568 }
2569
2570 /********************** add 2d stabilization tracks operator *********************/
2571
2572 static int stabilize_2d_add_exec(bContext *C, wmOperator *UNUSED(op))
2573 {
2574         SpaceClip *sc= CTX_wm_space_clip(C);
2575         MovieClip *clip= ED_space_clip(sc);
2576         MovieTracking *tracking= &clip->tracking;
2577         MovieTrackingTrack *track;
2578         MovieTrackingStabilization *stab= &tracking->stabilization;
2579         int update= 0;
2580
2581         track= tracking->tracks.first;
2582         while(track) {
2583                 if(TRACK_VIEW_SELECTED(sc, track) && (track->flag&TRACK_USE_2D_STAB)==0) {
2584                         track->flag|= TRACK_USE_2D_STAB;
2585                         stab->tot_track++;
2586
2587                         update= 1;
2588                 }
2589
2590                 track= track->next;
2591         }
2592
2593         if(update) {
2594                 stab->ok= 0;
2595
2596                 DAG_id_tag_update(&clip->id, 0);
2597                 WM_event_add_notifier(C, NC_MOVIECLIP|ND_DISPLAY, clip);
2598         }
2599
2600         return OPERATOR_FINISHED;
2601 }
2602
2603 void CLIP_OT_stabilize_2d_add(wmOperatorType *ot)
2604 {
2605         /* identifiers */
2606         ot->name= "Add Stabilization Tracks";
2607         ot->description= "Add selected tracks to 2D stabilization tool";
2608         ot->idname= "CLIP_OT_stabilize_2d_add";
2609
2610         /* api callbacks */
2611         ot->exec= stabilize_2d_add_exec;
2612         ot->poll= ED_space_clip_poll;
2613
2614         /* flags */
2615         ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
2616 }
2617
2618 /********************** remove 2d stabilization tracks operator *********************/
2619
2620 static int stabilize_2d_remove_exec(bContext *C, wmOperator *UNUSED(op))
2621 {
2622         SpaceClip *sc= CTX_wm_space_clip(C);
2623         MovieClip *clip= ED_space_clip(sc);
2624         MovieTracking *tracking= &clip->tracking;
2625         MovieTrackingStabilization *stab= &tracking->stabilization;
2626         MovieTrackingTrack *track;
2627         int a= 0, update= 0;
2628
2629         track= tracking->tracks.first;
2630         while(track) {
2631                 if(track->flag&TRACK_USE_2D_STAB) {
2632                         if(a==stab->act_track) {
2633                                 track->flag&= ~TRACK_USE_2D_STAB;
2634
2635                                 stab->act_track--;
2636                                 stab->tot_track--;
2637
2638                                 if(stab->act_track<0)
2639                                         stab->act_track= 0;
2640
2641                                 update= 1;
2642
2643                                 break;
2644                         }
2645
2646                         a++;
2647                 }
2648
2649                 track= track->next;
2650         }
2651
2652         if(update) {
2653                 stab->ok= 0;
2654
2655                 DAG_id_tag_update(&clip->id, 0);
2656                 WM_event_add_notifier(C, NC_MOVIECLIP|ND_DISPLAY, clip);
2657         }
2658
2659         return OPERATOR_FINISHED;
2660 }
2661
2662 void CLIP_OT_stabilize_2d_remove(wmOperatorType *ot)
2663 {
2664         /* identifiers */
2665         ot->name= "Remove Stabilization Track";
2666         ot->description= "Remove selected track from stabilization";
2667         ot->idname= "CLIP_OT_stabilize_2d_remove";
2668
2669         /* api callbacks */
2670         ot->exec= stabilize_2d_remove_exec;
2671         ot->poll= ED_space_clip_poll;
2672
2673         /* flags */
2674         ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
2675 }
2676
2677 /********************** select 2d stabilization tracks operator *********************/
2678
2679 static int stabilize_2d_select_exec(bContext *C, wmOperator *UNUSED(op))
2680 {
2681         SpaceClip *sc= CTX_wm_space_clip(C);
2682         MovieClip *clip= ED_space_clip(sc);
2683         MovieTracking *tracking= &clip->tracking;
2684         MovieTrackingTrack *track;
2685         int update= 0;
2686
2687         track= tracking->tracks.first;
2688         while(track) {
2689                 if(track->flag&TRACK_USE_2D_STAB) {
2690                         BKE_tracking_track_flag(track, TRACK_AREA_ALL, SELECT, 0);
2691
2692                         update= 1;
2693                 }
2694
2695                 track= track->next;
2696         }
2697
2698         if(update)
2699                 WM_event_add_notifier(C, NC_MOVIECLIP|ND_SELECT, clip);
2700
2701         return OPERATOR_FINISHED;
2702 }
2703
2704 void CLIP_OT_stabilize_2d_select(wmOperatorType *ot)
2705 {
2706         /* identifiers */
2707         ot->name= "Select Stabilization Tracks";
2708         ot->description= "Select track which are used for stabilization";
2709         ot->idname= "CLIP_OT_stabilize_2d_select";
2710
2711         /* api callbacks */
2712         ot->exec= stabilize_2d_select_exec;
2713         ot->poll= ED_space_clip_poll;
2714
2715         /* flags */
2716         ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
2717 }
2718
2719 /********************** set 2d stabilization rotation track operator *********************/
2720
2721 static int stabilize_2d_set_rotation_exec(bContext *C, wmOperator *UNUSED(op))
2722 {
2723         SpaceClip *sc= CTX_wm_space_clip(C);
2724         MovieClip *clip= ED_space_clip(sc);
2725         MovieTracking *tracking= &clip->tracking;
2726
2727         if(tracking->act_track) {
2728                 MovieTrackingStabilization *stab= &tracking->stabilization;
2729
2730                 stab->rot_track= tracking->act_track;
2731                 stab->ok= 0;
2732
2733                 DAG_id_tag_update(&clip->id, 0);
2734                 WM_event_add_notifier(C, NC_MOVIECLIP|ND_DISPLAY, clip);
2735         }
2736
2737         return OPERATOR_FINISHED;
2738 }
2739
2740 void CLIP_OT_stabilize_2d_set_rotation(wmOperatorType *ot)
2741 {
2742         /* identifiers */
2743         ot->name= "Set Rotation Track";
2744         ot->description= "Use active track to compensate rotaiton when doing 2D stabilization";
2745         ot->idname= "CLIP_OT_stabilize_2d_set_rotation";
2746
2747         /* api callbacks */
2748         ot->exec= stabilize_2d_set_rotation_exec;
2749         ot->poll= ED_space_clip_poll;
2750
2751         /* flags */
2752         ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
2753 }
2754
2755 /********************** clean tracks operator *********************/
2756
2757 static int is_track_clean(MovieTrackingTrack *track, int frames, int del)
2758 {
2759         int ok= 1, a, prev= -1, count= 0;
2760         MovieTrackingMarker *markers= track->markers, *new_markers= NULL;
2761         int start_disabled= 0;
2762         int markersnr= track->markersnr;
2763
2764         if(del)
2765                 new_markers= MEM_callocN(markersnr*sizeof(MovieTrackingMarker), "track cleaned markers");
2766
2767         for(a= 0; a<markersnr; a++) {
2768                 int end= 0;
2769
2770                 if(prev==-1) {
2771                         if((markers[a].flag&MARKER_DISABLED)==0)
2772                                 prev= a;
2773                         else
2774                                 start_disabled= 1;
2775                 }
2776
2777                 if(prev >= 0) {
2778                         end=  a == markersnr-1;
2779                         end|= (a < markersnr-1) && (markers[a].framenr != markers[a+1].framenr-1 ||
2780                                                     markers[a].flag&MARKER_DISABLED);
2781                 }
2782
2783                 if(end) {
2784                         int segok= 1, len= 0;
2785
2786                         if(a != prev && markers[a].framenr != markers[a-1].framenr+1)
2787                                 len= a-prev;
2788                         else if(markers[a].flag&MARKER_DISABLED)
2789                                 len= a-prev;
2790                         else len= a-prev+1;
2791
2792                         if(frames) {
2793                                 if(len < frames) {
2794                                         segok= 0;
2795                                         ok= 0;
2796
2797                                         if(!del)
2798                                                 break;
2799                                 }
2800                         }
2801
2802                         if(del) {
2803                                 if(segok) {
2804                                         int t= len;
2805
2806                                         if(markers[a].flag&MARKER_DISABLED)
2807                                                 t++;
2808
2809                                         /* place disabled marker in front of current segment */
2810                                         if(start_disabled) {
2811                                                 memcpy(new_markers+count, markers+prev, sizeof(MovieTrackingMarker));
2812                                                 new_markers[count].framenr--;
2813                                                 new_markers[count].flag|= MARKER_DISABLED;
2814
2815                                                 count++;
2816                                                 start_disabled= 0;
2817                                         }
2818
2819                                         memcpy(new_markers+count, markers+prev, t*sizeof(MovieTrackingMarker));
2820                                         count+= t;
2821                                 }
2822                                 else if(markers[a].flag&MARKER_DISABLED) {
2823                                         /* current segment which would be deleted was finished by disabled marker,
2824                                            so next segment should be started from disabled marker */
2825                                         start_disabled= 1;
2826                                 }
2827                         }
2828
2829                         prev= -1;
2830                 }
2831         }
2832
2833         if(del) {
2834                 MEM_freeN(track->markers);
2835
2836                 if(count) {
2837                         track->markers= new_markers;
2838                 }
2839                 else {
2840                         track->markers= NULL;
2841                         MEM_freeN(new_markers);
2842                 }
2843
2844                 track->markersnr= count;
2845         }
2846
2847         return ok;
2848 }
2849
2850 static int clean_tracks_exec(bContext *C, wmOperator *op)
2851 {
2852         SpaceClip *sc= CTX_wm_space_clip(C);
2853         MovieClip *clip= ED_space_clip(sc);
2854         MovieTracking *tracking= &clip->tracking;
2855         MovieTrackingTrack *track, *next, *act_track= clip->tracking.act_track;
2856         int frames= RNA_int_get(op->ptr, "frames");
2857         int action= RNA_enum_get(op->ptr, "action");
2858         float error= RNA_float_get(op->ptr, "error");
2859
2860         if(error && action==TRACKING_CLEAN_DELETE_SEGMENT)
2861                 action= TRACKING_CLEAN_DELETE_TRACK;
2862
2863         track= tracking->tracks.first;
2864         while(track) {
2865                 next= track->next;
2866
2867                 if((track->flag&TRACK_HIDDEN)==0 && (track->flag&TRACK_LOCKED)==0) {
2868                         int ok= 1;
2869
2870                         ok= (is_track_clean(track, frames, action==TRACKING_CLEAN_DELETE_SEGMENT)) &&
2871                             (error == 0.0f || (track->flag&TRACK_HAS_BUNDLE)==0  || track->error < error);
2872
2873                         if(!ok) {
2874                                 if(action==TRACKING_CLEAN_SELECT) {
2875                                         BKE_tracking_track_flag(track, TRACK_AREA_ALL, SELECT, 0);
2876                                 }
2877                                 else if(action==TRACKING_CLEAN_DELETE_TRACK) {
2878                                         if(track==act_track)
2879                                                 clip->tracking.act_track= NULL;
2880
2881                                         BKE_tracking_free_track(track);
2882                                         BLI_freelinkN(&clip->tracking.tracks, track);
2883                                 }
2884
2885                                 /* happens when all tracking segments are not long enough */
2886                                 if(track->markersnr==0) {
2887                                         if(track==act_track)
2888                                                 clip->tracking.act_track= NULL;
2889
2890                                         BKE_tracking_free_track(track);
2891                                         BLI_freelinkN(&clip->tracking.tracks, track);
2892                                 }
2893                         }
2894                 }
2895
2896                 track= next;
2897         }
2898
2899         WM_event_add_notifier(C, NC_MOVIECLIP|ND_SELECT, clip);
2900
2901         return OPERATOR_FINISHED;
2902 }
2903
2904 static int clean_tracks_invoke(bContext *C, wmOperator *op, wmEvent *UNUSED(event))
2905 {
2906         SpaceClip *sc= CTX_wm_space_clip(C);
2907         MovieClip *clip= ED_space_clip(sc);
2908         int frames= RNA_int_get(op->ptr, "frames");
2909         float error= RNA_float_get(op->ptr, "error");
2910         int action= RNA_enum_get(op->ptr, "action");
2911
2912         if(frames==0 && error==0 && action==0) {
2913                 RNA_int_set(op->ptr, "frames", clip->tracking.settings.clean_frames);
2914                 RNA_float_set(op->ptr, "error", clip->tracking.settings.clean_error);
2915                 RNA_enum_set(op->ptr, "action", clip->tracking.settings.clean_action);
2916         }
2917
2918         return clean_tracks_exec(C, op);
2919 }
2920
2921 void CLIP_OT_clean_tracks(wmOperatorType *ot)
2922 {
2923         static EnumPropertyItem actions_items[] = {
2924                         {TRACKING_CLEAN_SELECT, "SELECT", 0, "Select", "Select unclean tracks"},
2925                         {TRACKING_CLEAN_DELETE_TRACK, "DELETE_TRACK", 0, "Delete Track", "Delete unclean tracks"},
2926                         {TRACKING_CLEAN_DELETE_SEGMENT, "DELETE_SEGMENTS", 0, "Delete Segments", "Delete unclean segments of tracks"},
2927                         {0, NULL, 0, NULL, NULL}
2928         };
2929
2930         /* identifiers */
2931         ot->name= "Clean Tracks";
2932         ot->description= "Clean tracks";
2933         ot->idname= "CLIP_OT_clean_tracks";
2934
2935         /* api callbacks */
2936         ot->exec= clean_tracks_exec;
2937         ot->invoke= clean_tracks_invoke;
2938         ot->poll= ED_space_clip_poll;
2939
2940         /* flags */
2941         ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
2942
2943         /* properties */
2944         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);
2945         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);
2946         RNA_def_enum(ot->srna, "action", actions_items, 0, "Action", "Cleanup action to execute");
2947 }