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