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