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