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