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