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