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