aa91de2b07659761c73ab5cc69b3fe08a1c6c539
[blender.git] / source / blender / editors / space_clip / tracking_ops.c
1 /*
2  * $Id$
3  *
4  * ***** BEGIN GPL LICENSE BLOCK *****
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License
8  * as published by the Free Software Foundation; either version 2
9  * of the License, or (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software Foundation,
18  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19  *
20  * The Original Code is Copyright (C) 2011 Blender Foundation.
21  * All rights reserved.
22  *
23  *
24  * Contributor(s): Blender Foundation,
25  *                 Sergey Sharybin
26  *
27  * ***** END GPL LICENSE BLOCK *****
28  */
29
30 #include "MEM_guardedalloc.h"
31
32 #include "DNA_movieclip_types.h"
33 #include "DNA_object_types.h"   /* SELECT */
34 #include "DNA_scene_types.h"
35 #include "DNA_anim_types.h"
36
37 #include "BLI_utildefines.h"
38 #include "BLI_math.h"
39 #include "BLI_listbase.h"
40 #include "BLI_rect.h"
41 #include "BLI_blenlib.h"
42
43 #include "BKE_main.h"
44 #include "BKE_context.h"
45 #include "BKE_movieclip.h"
46 #include "BKE_tracking.h"
47 #include "BKE_global.h"
48 #include "BKE_animsys.h"
49 #include "BKE_depsgraph.h"
50
51 #include "WM_api.h"
52 #include "WM_types.h"
53
54 #include "ED_screen.h"
55 #include "ED_clip.h"
56 #include "ED_keyframing.h"
57
58 #include "IMB_imbuf_types.h"
59
60 #include "UI_interface.h"
61
62 #include "RNA_access.h"
63 #include "RNA_define.h"
64
65 #include "PIL_time.h"
66
67 #include "UI_view2d.h"
68
69 #include "clip_intern.h"        // own include
70
71 /** \file blender/editors/space_clip/tracking_ops.c
72  *  \ingroup spclip
73  */
74
75 static int space_clip_tracking_poll(bContext *C)
76 {
77         SpaceClip *sc= CTX_wm_space_clip(C);
78
79         if(sc && sc->clip)
80                 return 1;
81
82         return 0;
83 }
84
85 static int space_clip_frame_poll(bContext *C)
86 {
87         SpaceClip *sc= CTX_wm_space_clip(C);
88
89         if(sc) {
90                 MovieClip *clip= ED_space_clip(sc);
91
92                 if(clip)
93                         return BKE_movieclip_has_frame(clip, &sc->user);
94         }
95
96         return 0;
97 }
98
99 /********************** add marker operator *********************/
100
101 static void add_marker(SpaceClip *sc, float x, float y)
102 {
103         MovieClip *clip= ED_space_clip(sc);
104         MovieTrackingTrack *track;
105         MovieTrackingMarker marker;
106         int width, height;
107         float pat[2]= {15.0f, 15.0f}, search[2]= {30.0f, 30.0f}; /* TODO: move to default setting? */
108
109         ED_space_clip_size(sc, &width, &height);
110
111         pat[0] /= (float)width;
112         pat[1] /= (float)height;
113
114         search[0] /= (float)width;
115         search[1] /= (float)height;
116
117         track= MEM_callocN(sizeof(MovieTrackingTrack), "add_marker_exec track");
118
119         marker.pos[0]= x;
120         marker.pos[1]= y;
121         marker.framenr= sc->user.framenr;
122
123         copy_v2_v2(track->pat_max, pat);
124         negate_v2_v2(track->pat_min, pat);
125
126         copy_v2_v2(track->search_max, search);
127         negate_v2_v2(track->search_min, search);
128
129         BKE_tracking_insert_marker(track, &marker);
130
131         BLI_addtail(&clip->tracking.tracks, track);
132
133         BKE_movieclip_select_track(clip, track, TRACK_AREA_ALL, 0);
134         BKE_movieclip_set_selection(clip, MCLIP_SEL_TRACK, track);
135 }
136
137 static int add_marker_exec(bContext *C, wmOperator *op)
138 {
139         SpaceClip *sc= CTX_wm_space_clip(C);
140         MovieClip *clip= ED_space_clip(sc);
141         float pos[2];
142         int width, height;
143
144         ED_space_clip_size(sc, &width, &height);
145         if(!width || !height)
146                 return OPERATOR_CANCELLED;
147
148         RNA_float_get_array(op->ptr, "location", pos);
149
150         add_marker(sc, pos[0], pos[1]);
151
152         WM_event_add_notifier(C, NC_MOVIECLIP|NA_EDITED, clip);
153
154         return OPERATOR_FINISHED;
155 }
156
157 static void mouse_pos(bContext *C, wmEvent *event, float co[2])
158 {
159         ARegion *ar= CTX_wm_region(C);
160         SpaceClip *sc= CTX_wm_space_clip(C);
161         int sx, sy;
162
163         UI_view2d_to_region_no_clip(&ar->v2d, 0.0f, 0.0f, &sx, &sy);
164         co[0]= ((float)event->mval[0]-sx)/sc->zoom;
165         co[1]= ((float)event->mval[1]-sy)/sc->zoom;
166 }
167
168 static int add_marker_invoke(bContext *C, wmOperator *op, wmEvent *event)
169 {
170         SpaceClip *sc= CTX_wm_space_clip(C);
171         int width, height;
172         float co[2];
173
174         ED_space_clip_size(sc, &width, &height);
175         if(!width || !height)
176                 return OPERATOR_CANCELLED;
177
178         mouse_pos(C, event, co);
179         co[0]/= width;
180         co[1]/= height;
181
182         RNA_float_set_array(op->ptr, "location", co);
183
184         return add_marker_exec(C, op);
185 }
186
187 void CLIP_OT_add_marker(wmOperatorType *ot)
188 {
189         /* identifiers */
190         ot->name= "Add Marker";
191         ot->idname= "CLIP_OT_add_marker";
192         ot->description= "Place new marker at specified location";
193
194         /* api callbacks */
195         ot->invoke= add_marker_invoke;
196         ot->exec= add_marker_exec;
197         ot->poll= space_clip_tracking_poll;
198
199         /* flags */
200         ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
201
202         /* properties */
203         RNA_def_float_vector(ot->srna, "location", 2, NULL, -FLT_MIN, FLT_MAX,
204                 "Location", "Location of marker on frame.", -1.f, 1.f);
205 }
206
207 /********************** delete operator *********************/
208
209 static int delete_exec(bContext *C, wmOperator *UNUSED(op))
210 {
211         SpaceClip *sc= CTX_wm_space_clip(C);
212         MovieClip *clip= ED_space_clip(sc);
213         MovieTrackingTrack *track= clip->tracking.tracks.first, *next;
214
215         while(track) {
216                 next= track->next;
217
218                 if(track->flag&SELECT) {
219                         BKE_tracking_free_track(track);
220                         BLI_freelinkN(&clip->tracking.tracks, track);
221                 }
222
223                 track= next;
224         }
225
226         BKE_movieclip_set_selection(clip, MCLIP_SEL_NONE, NULL);
227         WM_event_add_notifier(C, NC_MOVIECLIP|NA_EDITED, clip);
228
229         return OPERATOR_FINISHED;
230 }
231
232 void CLIP_OT_delete(wmOperatorType *ot)
233 {
234         /* identifiers */
235         ot->name= "Delete";
236         ot->idname= "CLIP_OT_delete";
237         ot->description= "Delete selected tracks";
238
239         /* api callbacks */
240         ot->invoke= WM_operator_confirm;
241         ot->exec= delete_exec;
242         ot->poll= space_clip_tracking_poll;
243
244         /* flags */
245         ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
246 }
247
248 /********************** mouse select operator *********************/
249
250 static int mouse_on_size(float co[2], float x1, float y1, float x2, float y2, float epsx, float epsy)
251 {
252         if(x1>x2) SWAP(float, x1, x2);
253         if(y1>y2) SWAP(float, y1, y2);
254
255         return (co[0]>=x1-epsx && co[0]<=x2+epsx) && (co[1]>=y1-epsy && co[1]<=y2+epsy);
256 }
257
258 static int mouse_on_rect(float co[2], float pos[2], float min[2], float max[2], float epsx, float epsy)
259 {
260         return mouse_on_size(co, pos[0]+min[0], pos[1]+min[1], pos[0]+max[0], pos[1]+min[1], epsx, epsy) ||
261                mouse_on_size(co, pos[0]+min[0], pos[1]+min[1], pos[0]+min[0], pos[1]+max[1], epsx, epsy) ||
262                mouse_on_size(co, pos[0]+min[0], pos[1]+max[1], pos[0]+max[0], pos[1]+max[1], epsx, epsy) ||
263                mouse_on_size(co, pos[0]+max[0], pos[1]+min[1], pos[0]+max[0], pos[1]+max[1], epsx, epsy);
264 }
265
266 static int track_mouse_area(SpaceClip *sc, float co[2], MovieTrackingTrack *track)
267 {
268         MovieTrackingMarker *marker= BKE_tracking_get_marker(track, sc->user.framenr);
269         float epsx, epsy;
270         int width, height;
271
272         ED_space_clip_size(sc, &width, &height);
273
274         epsx= MIN4(track->pat_min[0]-track->search_min[0], track->search_max[0]-track->pat_max[0],
275                    fabsf(track->pat_min[0]), fabsf(track->pat_max[0])) / 2;
276         epsy= MIN4(track->pat_min[1]-track->search_min[1], track->search_max[1]-track->pat_max[1],
277                    fabsf(track->pat_min[1]), fabsf(track->pat_max[1])) / 2;
278
279         epsx= MAX2(epsy, 2.0 / width);
280         epsy= MAX2(epsy, 2.0 / height);
281
282         if(fabsf(co[0]-marker->pos[0])< epsx && fabsf(co[1]-marker->pos[1])<=epsy)
283                 return TRACK_AREA_POINT;
284
285         if(sc->flag&SC_SHOW_MARKER_PATTERN)
286                 if(mouse_on_rect(co, marker->pos, track->pat_min, track->pat_max, epsx, epsy))
287                         return TRACK_AREA_PAT;
288
289         if(sc->flag&SC_SHOW_MARKER_SEARCH)
290                 if(mouse_on_rect(co, marker->pos, track->search_min, track->search_max, epsx, epsy))
291                         return TRACK_AREA_SEARCH;
292
293         return TRACK_AREA_NONE;
294 }
295
296 static float dist_to_rect(float co[2], float pos[2], float min[2], float max[2])
297 {
298         float d1, d2, d3, d4;
299         float p[2]= {co[0]-pos[0], co[1]-pos[1]};
300         float v1[2]= {min[0], min[1]}, v2[2]= {max[0], min[1]},
301               v3[2]= {max[0], max[1]}, v4[2]= {min[0], max[1]};
302
303         d1= dist_to_line_segment_v2(p, v1, v2);
304         d2= dist_to_line_segment_v2(p, v2, v3);
305         d3= dist_to_line_segment_v2(p, v3, v4);
306         d4= dist_to_line_segment_v2(p, v4, v1);
307
308         return MIN4(d1, d2, d3, d4);
309 }
310
311 static MovieTrackingTrack *find_nearest_track(SpaceClip *sc, MovieClip *clip, float co[2])
312 {
313         MovieTrackingTrack *track= NULL, *cur;
314         float mindist= 0.0f;
315
316         cur= clip->tracking.tracks.first;
317         while(cur) {
318                 MovieTrackingMarker *marker= BKE_tracking_get_marker(cur, sc->user.framenr);
319                 float dist, d1, d2, d3;
320
321                 if(marker) {
322                         d1= sqrtf((co[0]-marker->pos[0])*(co[0]-marker->pos[0])+
323                                           (co[1]-marker->pos[1])*(co[1]-marker->pos[1])); /* distance to marker point */
324                         d2= dist_to_rect(co, marker->pos, cur->pat_min, cur->pat_max); /* distance to search boundbox */
325                         d3= dist_to_rect(co, marker->pos, cur->search_min, cur->search_max); /* distance to search boundbox */
326
327                         /* choose minimal distance. useful for cases of overlapped markers. */
328                         dist= MIN3(d1, d2, d3);
329
330                         if(track==NULL || dist<mindist) {
331                                 track= cur;
332                                 mindist= dist;
333                         }
334                 }
335
336                 cur= cur->next;
337         }
338
339         return track;
340 }
341
342 static int mouse_select(bContext *C, float co[2], int extend)
343 {
344         SpaceClip *sc= CTX_wm_space_clip(C);
345         MovieClip *clip= ED_space_clip(sc);
346         MovieTrackingTrack *track= NULL;        /* selected marker */
347
348         track= find_nearest_track(sc, clip, co);
349
350         if(track) {
351                 int area= track_mouse_area(sc, co, track);
352
353                 if(area==TRACK_AREA_NONE)
354                         return OPERATOR_FINISHED;
355
356                 if(!TRACK_SELECTED(track))
357                         area= TRACK_AREA_ALL;
358
359                 if(extend && TRACK_AREA_SELECTED(track, area)) {
360                         BKE_movieclip_deselect_track(clip, track, area);
361                 } else {
362                         if(area==TRACK_AREA_POINT) area= TRACK_AREA_ALL;
363
364                         BKE_movieclip_select_track(clip, track, area, extend);
365                         BKE_movieclip_set_selection(clip, MCLIP_SEL_TRACK, track);
366                 }
367         }
368
369         WM_event_add_notifier(C, NC_GEOM|ND_SELECT, NULL);
370
371         return OPERATOR_FINISHED;
372 }
373
374 static int select_exec(bContext *C, wmOperator *op)
375 {
376         float co[2];
377         int extend;
378
379         RNA_float_get_array(op->ptr, "location", co);
380         extend= RNA_boolean_get(op->ptr, "extend");
381
382         return mouse_select(C, co, extend);
383 }
384
385 static int select_invoke(bContext *C, wmOperator *op, wmEvent *event)
386 {
387         ARegion *ar= CTX_wm_region(C);
388         float co[2];
389
390         UI_view2d_region_to_view(&ar->v2d, event->mval[0], event->mval[1], &co[0], &co[1]);
391         RNA_float_set_array(op->ptr, "location", co);
392
393         return select_exec(C, op);
394 }
395
396 void CLIP_OT_select(wmOperatorType *ot)
397 {
398         /* identifiers */
399         ot->name= "Select";
400         ot->description= "Select tracking markers";
401         ot->idname= "CLIP_OT_select";
402
403         /* api callbacks */
404         ot->exec= select_exec;
405         ot->invoke= select_invoke;
406         ot->poll= space_clip_frame_poll;
407
408         /* flags */
409         ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
410
411         /* properties */
412         RNA_def_boolean(ot->srna, "extend", 0,
413                 "Extend", "Extend selection rather than clearing the existing selection.");
414         RNA_def_float_vector(ot->srna, "location", 2, NULL, -FLT_MAX, FLT_MAX,
415                 "Location", "Mouse location in normalized coordinates, 0.0 to 1.0 is within the image bounds.", -100.0f, 100.0f);
416 }
417
418 /********************** border select operator *********************/
419
420 static int border_select_exec(bContext *C, wmOperator *op)
421 {
422         SpaceClip *sc= CTX_wm_space_clip(C);
423         MovieClip *clip= ED_space_clip(sc);
424         MovieTrackingTrack *track;
425         ARegion *ar= CTX_wm_region(C);
426         rcti rect;
427         rctf rectf;
428         int change= 0, mode;
429
430         /* get rectangle from operator */
431         rect.xmin= RNA_int_get(op->ptr, "xmin");
432         rect.ymin= RNA_int_get(op->ptr, "ymin");
433         rect.xmax= RNA_int_get(op->ptr, "xmax");
434         rect.ymax= RNA_int_get(op->ptr, "ymax");
435
436         UI_view2d_region_to_view(&ar->v2d, rect.xmin, rect.ymin, &rectf.xmin, &rectf.ymin);
437         UI_view2d_region_to_view(&ar->v2d, rect.xmax, rect.ymax, &rectf.xmax, &rectf.ymax);
438
439         mode= RNA_int_get(op->ptr, "gesture_mode");
440
441         /* do actual selection */
442         track= clip->tracking.tracks.first;
443         while(track) {
444                 MovieTrackingMarker *marker= BKE_tracking_get_marker(track, sc->user.framenr);
445
446                 if(marker) {
447                         if(BLI_in_rctf(&rectf, marker->pos[0], marker->pos[1])) {
448                                 BKE_tracking_track_flag(track, TRACK_AREA_ALL, SELECT, mode!=GESTURE_MODAL_SELECT);
449
450                                 change= 1;
451                         }
452                 }
453
454                 track= track->next;
455         }
456
457         BKE_movieclip_set_selection(clip, MCLIP_SEL_NONE, NULL);
458
459         if(change) {
460                 WM_event_add_notifier(C, NC_GEOM|ND_SELECT, NULL);
461
462                 return OPERATOR_FINISHED;
463         }
464
465         return OPERATOR_CANCELLED;
466 }
467
468 void CLIP_OT_select_border(wmOperatorType *ot)
469 {
470         /* identifiers */
471         ot->name= "Border Select";
472         ot->description= "Select markers using border selection";
473         ot->idname= "CLIP_OT_select_border";
474
475         /* api callbacks */
476         ot->invoke= WM_border_select_invoke;
477         ot->exec= border_select_exec;
478         ot->modal= WM_border_select_modal;
479         ot->poll= space_clip_frame_poll;
480
481         /* flags */
482         ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
483
484         /* properties */
485         WM_operator_properties_gesture_border(ot, FALSE);
486 }
487
488 /********************** circle select operator *********************/
489
490 static int marker_inside_ellipse(MovieTrackingMarker *marker, float offset[2], float ellipse[2])
491 {
492         /* normalized ellipse: ell[0] = scaleX, ell[1] = scaleY */
493         float x, y;
494
495         x= (marker->pos[0] - offset[0])*ellipse[0];
496         y= (marker->pos[1] - offset[1])*ellipse[1];
497
498         return x*x + y*y < 1.0f;
499 }
500
501 static int circle_select_exec(bContext *C, wmOperator *op)
502 {
503         SpaceClip *sc= CTX_wm_space_clip(C);
504         MovieClip *clip= ED_space_clip(sc);
505         ARegion *ar= CTX_wm_region(C);
506         MovieTrackingTrack *track;
507         int x, y, radius, width, height, mode, change= 0;
508         float zoomx, zoomy, offset[2], ellipse[2];
509
510         /* get operator properties */
511         x= RNA_int_get(op->ptr, "x");
512         y= RNA_int_get(op->ptr, "y");
513         radius= RNA_int_get(op->ptr, "radius");
514
515         mode= RNA_int_get(op->ptr, "gesture_mode");
516
517         /* compute ellipse and position in unified coordinates */
518         ED_space_clip_size(sc, &width, &height);
519         ED_space_clip_zoom(sc, ar, &zoomx, &zoomy);
520
521         ellipse[0]= width*zoomx/radius;
522         ellipse[1]= height*zoomy/radius;
523
524         UI_view2d_region_to_view(&ar->v2d, x, y, &offset[0], &offset[1]);
525
526         /* do selection */
527         track= clip->tracking.tracks.first;
528         while(track) {
529                 MovieTrackingMarker *marker= BKE_tracking_get_marker(track, sc->user.framenr);
530
531                 if(marker) {
532                         if(marker_inside_ellipse(marker, offset, ellipse)) {
533                                 BKE_tracking_track_flag(track, TRACK_AREA_ALL, SELECT, mode!=GESTURE_MODAL_SELECT);
534
535                                 change= 1;
536                         }
537                 }
538
539                 track= track->next;
540         }
541
542         BKE_movieclip_set_selection(clip, MCLIP_SEL_NONE, NULL);
543
544         if(change) {
545                 WM_event_add_notifier(C, NC_GEOM|ND_SELECT, NULL);
546
547                 return OPERATOR_FINISHED;
548         }
549
550         return OPERATOR_CANCELLED;
551 }
552
553 void CLIP_OT_select_circle(wmOperatorType *ot)
554 {
555         /* identifiers */
556         ot->name= "Circle Select";
557         ot->description= "Select markers using circle selection";
558         ot->idname= "CLIP_OT_select_circle";
559
560         /* api callbacks */
561         ot->invoke= WM_gesture_circle_invoke;
562         ot->modal= WM_gesture_circle_modal;
563         ot->exec= circle_select_exec;
564         ot->poll= space_clip_frame_poll;
565
566         /* flags */
567         ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
568
569         /* properties */
570         RNA_def_int(ot->srna, "x", 0, INT_MIN, INT_MAX, "X", "", INT_MIN, INT_MAX);
571         RNA_def_int(ot->srna, "y", 0, INT_MIN, INT_MAX, "Y", "", INT_MIN, INT_MAX);
572         RNA_def_int(ot->srna, "radius", 0, INT_MIN, INT_MAX, "Radius", "", INT_MIN, INT_MAX);
573         RNA_def_int(ot->srna, "gesture_mode", 0, INT_MIN, INT_MAX, "Gesture Mode", "", INT_MIN, INT_MAX);
574 }
575
576 /********************** select all operator *********************/
577
578 static int select_all_exec(bContext *C, wmOperator *op)
579 {
580         SpaceClip *sc= CTX_wm_space_clip(C);
581         MovieClip *clip= ED_space_clip(sc);
582         MovieTrackingTrack *track= NULL;        /* selected track */
583         int action= RNA_enum_get(op->ptr, "action");
584         int sel_type, framenr= sc->user.framenr;
585         void *sel;
586
587         if(action == SEL_TOGGLE){
588                 action= SEL_SELECT;
589                 track= clip->tracking.tracks.first;
590                 while(track) {
591                         if(BKE_tracking_has_marker(track, framenr)) {
592                                 if(TRACK_SELECTED(track)) {
593                                         action= SEL_DESELECT;
594                                         break;
595                                 }
596                         }
597
598                         track= track->next;
599                 }
600         }
601
602         track= clip->tracking.tracks.first;
603         while(track) {
604                 if(BKE_tracking_has_marker(track, framenr)) {
605                         switch (action) {
606                                 case SEL_SELECT:
607                                         if(track->bundle) track->bundle->flag|= SELECT;
608                                         track->flag|= SELECT;
609                                         track->pat_flag|= SELECT;
610                                         track->search_flag|= SELECT;
611                                         break;
612                                 case SEL_DESELECT:
613                                         if(track->bundle) track->bundle->flag&= ~SELECT;
614                                         track->flag&= ~SELECT;
615                                         track->pat_flag&= ~SELECT;
616                                         track->search_flag&= ~SELECT;
617                                         break;
618                                 case SEL_INVERT:
619                                         if(track->bundle) track->bundle->flag^= SELECT;
620                                         track->flag^= SELECT;
621                                         track->pat_flag^= SELECT;
622                                         track->search_flag^= SELECT;
623                                         break;
624                         }
625                 }
626
627                 track= track->next;
628         }
629
630         BKE_movieclip_last_selection(clip, &sel_type, &sel);
631         if(sel_type==MCLIP_SEL_TRACK)
632                 if(!TRACK_SELECTED(((MovieTrackingTrack*)sel)))
633                         BKE_movieclip_set_selection(clip, MCLIP_SEL_NONE, NULL);
634
635         WM_event_add_notifier(C, NC_GEOM|ND_SELECT, NULL);
636
637         return OPERATOR_FINISHED;
638 }
639
640 void CLIP_OT_select_all(wmOperatorType *ot)
641 {
642         /* identifiers */
643         ot->name= "Select or Deselect All";
644         ot->description= "Change selection of all tracking markers";
645         ot->idname= "CLIP_OT_select_all";
646
647         /* api callbacks */
648         ot->exec= select_all_exec;
649         ot->poll= space_clip_frame_poll;
650
651         /* flags */
652         ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
653
654         WM_operator_properties_select_all(ot);
655 }
656
657 /********************** track operator *********************/
658
659 typedef struct TrackMarkersJob {
660         struct MovieTrackingContext *context;   /* tracking context */
661         int sfra, efra, lastfra;        /* Start, end and recently tracked frames */
662         int backwards;                          /* Backwards tracking flag */
663         MovieClip *clip;                        /* Clip which is tracking */
664         float delay;                            /* Delay in milliseconds to allow tracking at fixed FPS */
665
666         struct Main *main;
667         struct Scene *scene;
668         struct bScreen *screen;
669 } TrackMarkersJob;
670
671 static int track_markers_testbreak(void)
672 {
673         return G.afbreek;
674 }
675
676 static void track_markers_initjob(bContext *C, TrackMarkersJob *tmj, int backwards)
677 {
678         SpaceClip *sc= CTX_wm_space_clip(C);
679         MovieClip *clip= ED_space_clip(sc);
680         Scene *scene= CTX_data_scene(C);
681         MovieTrackingSettings *settings= &clip->tracking.settings;
682
683         tmj->sfra= sc->user.framenr;
684         tmj->clip= clip;
685         tmj->backwards= backwards;
686
687         if(backwards) tmj->efra= SFRA;
688         else tmj->efra= EFRA;
689
690         /* limit frames to be tracked by user setting */
691         if(settings->flag&TRACKING_FRAMES_LIMIT) {
692                 if(backwards) tmj->efra= MAX2(tmj->efra, tmj->sfra-settings->frames_limit);
693                 else tmj->efra= MIN2(tmj->efra, tmj->sfra+settings->frames_limit);
694         }
695
696         if(settings->speed!=TRACKING_SPEED_FASTEST) {
697                 tmj->delay= 1.0f/scene->r.frs_sec*1000.0f;
698
699                 if(settings->speed==TRACKING_SPEED_HALF) tmj->delay*= 2;
700                 else if(settings->speed==TRACKING_SPEED_QUARTER) tmj->delay*= 4;
701         }
702
703         tmj->context= BKE_tracking_context_new(clip, &sc->user, backwards);
704
705         clip->tracking_context= tmj->context;
706
707         /* XXX: silly to store this, but this data is needed to update scene and movieclip
708                 frame numbers when tracking is finished. This introduces better feedback for artists.
709                 Maybe there's another way to solve this problem, but can't think better way atm.
710                 Anyway, this way isn't more unstable as animation rendering animation
711                 which uses the same approach (except storing screen). */
712         tmj->scene= scene;
713         tmj->main= CTX_data_main(C);
714         tmj->screen= CTX_wm_screen(C);
715 }
716
717 static void track_markers_startjob(void *tmv, short *UNUSED(stop), short *do_update, float *progress)
718 {
719         TrackMarkersJob *tmj= (TrackMarkersJob *)tmv;
720         int framenr= tmj->sfra;
721
722         while(framenr != tmj->efra) {
723                 if(tmj->delay>0) {
724                         /* tracking should happen with fixed fps. Calculate time
725                            using current timer value before tracking frame and after.
726
727                            Small (and maybe unneeded optimization): do not calculate exec_time
728                            for "Fastest" tracking */
729
730                         double start_time= PIL_check_seconds_timer(), exec_time;
731
732                         if(!BKE_tracking_next(tmj->context))
733                                 break;
734
735                         exec_time= PIL_check_seconds_timer()-start_time;
736                         if(tmj->delay>exec_time)
737                                 PIL_sleep_ms(tmj->delay-exec_time);
738                 } else if(!BKE_tracking_next(tmj->context))
739                                 break;
740
741                 *do_update= 1;
742                 *progress=(float)(framenr-tmj->sfra) / (tmj->efra-tmj->sfra);
743
744                 if(tmj->backwards) framenr--;
745                 else framenr++;
746
747                 tmj->lastfra= framenr;
748
749                 if(track_markers_testbreak())
750                         break;
751         }
752 }
753
754 static void track_markers_updatejob(void *tmv)
755 {
756         TrackMarkersJob *tmj= (TrackMarkersJob *)tmv;
757
758         BKE_tracking_sync(tmj->context);
759 }
760
761 static void track_markers_freejob(void *tmv)
762 {
763         TrackMarkersJob *tmj= (TrackMarkersJob *)tmv;
764
765         tmj->clip->tracking_context= NULL;
766         tmj->scene->r.cfra= tmj->lastfra;
767         ED_update_for_newframe(tmj->main, tmj->scene, tmj->screen, 0);
768
769         BKE_tracking_sync(tmj->context);
770         BKE_tracking_context_free(tmj->context);
771
772         MEM_freeN(tmj);
773
774         WM_main_add_notifier(NC_SCENE|ND_FRAME, tmj->scene);
775 }
776
777 static int track_markers_invoke(bContext *C, wmOperator *op, wmEvent *UNUSED(event))
778 {
779         TrackMarkersJob *tmj;
780         SpaceClip *sc= CTX_wm_space_clip(C);
781         MovieClip *clip= ED_space_clip(sc);
782         wmJob *steve;
783         Scene *scene= CTX_data_scene(C);
784         int backwards= RNA_boolean_get(op->ptr, "backwards");
785
786         if(clip->tracking_context)
787                 return OPERATOR_CANCELLED;
788
789         tmj= MEM_callocN(sizeof(TrackMarkersJob), "TrackMarkersJob data");
790         track_markers_initjob(C, tmj, backwards);
791
792         /* setup job */
793         steve= WM_jobs_get(CTX_wm_manager(C), CTX_wm_window(C), scene, "Track Markers", WM_JOB_EXCL_RENDER|WM_JOB_PRIORITY|WM_JOB_PROGRESS);
794         WM_jobs_customdata(steve, tmj, track_markers_freejob);
795
796         /* if there's delay set in tracking job, tracking should happen
797            with fixed FPS. To deal with editor refresh we have to syncronize
798            tracks from job and tracks in clip. Do this in timer callback
799            to prevent threading conflicts. */
800         if(tmj->delay>0) WM_jobs_timer(steve, tmj->delay/1000.0f, NC_MOVIECLIP|NA_EVALUATED, 0);
801         else WM_jobs_timer(steve, 0.2, NC_MOVIECLIP|NA_EVALUATED, 0);
802
803         WM_jobs_callbacks(steve, track_markers_startjob, NULL, track_markers_updatejob, NULL);
804
805         G.afbreek= 0;
806
807         WM_jobs_start(CTX_wm_manager(C), steve);
808         WM_cursor_wait(0);
809
810         /* add modal handler for ESC */
811         WM_event_add_modal_handler(C, op);
812
813         return OPERATOR_RUNNING_MODAL;
814 }
815
816 static int track_markers_exec(bContext *C, wmOperator *op)
817 {
818         SpaceClip *sc= CTX_wm_space_clip(C);
819         MovieClip *clip= ED_space_clip(sc);
820         Scene *scene= CTX_data_scene(C);
821         struct MovieTrackingContext *context;
822         int framenr= sc->user.framenr;
823         int sfra= framenr, efra;
824         int backwards= RNA_boolean_get(op->ptr, "backwards");
825         MovieTrackingSettings *settings= &clip->tracking.settings;
826
827         if(backwards) efra= SFRA;
828         else efra= EFRA;
829
830         /* limit frames to be tracked by user setting */
831         if(settings->flag&TRACKING_FRAMES_LIMIT) {
832                 if(backwards) efra= MAX2(efra, sfra-settings->frames_limit);
833                 else efra= MIN2(efra, sfra+settings->frames_limit);
834         }
835
836         context= BKE_tracking_context_new(clip, &sc->user, backwards);
837
838         while(framenr != efra) {
839                 if(!BKE_tracking_next(context))
840                         break;
841
842                 if(backwards) framenr--;
843                 else framenr++;
844         }
845
846         BKE_tracking_sync(context);
847         BKE_tracking_context_free(context);
848
849         /* update scene current frame to the lastes tracked frame */
850         scene->r.cfra= framenr;
851
852         WM_event_add_notifier(C, NC_MOVIECLIP|NA_EVALUATED, clip);
853         WM_event_add_notifier(C, NC_SCENE|ND_FRAME, scene);
854
855         return OPERATOR_FINISHED;
856 }
857
858 static int track_marekrs_modal(bContext *C, wmOperator *UNUSED(op), wmEvent *event)
859 {
860         /* no running blender, remove handler and pass through */
861         if(0==WM_jobs_test(CTX_wm_manager(C), CTX_data_scene(C)))
862                 return OPERATOR_FINISHED|OPERATOR_PASS_THROUGH;
863
864         /* running tracking */
865         switch (event->type) {
866                 case ESCKEY:
867                         return OPERATOR_RUNNING_MODAL;
868                         break;
869         }
870
871         return OPERATOR_PASS_THROUGH;
872 }
873
874 void CLIP_OT_track_markers(wmOperatorType *ot)
875 {
876         /* identifiers */
877         ot->name= "Track Markers";
878         ot->description= "Track sleected markers";
879         ot->idname= "CLIP_OT_track_markers";
880
881         /* api callbacks */
882         ot->exec= track_markers_exec;
883         ot->invoke= track_markers_invoke;
884         ot->poll= space_clip_frame_poll;
885         ot->modal= track_marekrs_modal;
886
887         /* flags */
888         ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
889
890         /* properties */
891         RNA_def_boolean(ot->srna, "backwards", 0, "Backwards", "Do backwards tarcking");
892 }
893
894 /********************** clear track operator *********************/
895
896 static int clear_track_path_poll(bContext *C)
897 {
898         SpaceClip *sc= CTX_wm_space_clip(C);
899
900         if(sc) {
901                 MovieClip *clip= ED_space_clip(sc);
902
903                 if(clip && BKE_movieclip_has_frame(clip, &sc->user)) {
904                         int sel_type;
905                         void *sel;
906
907                         BKE_movieclip_last_selection(clip, &sel_type, &sel);
908
909                         return sel_type == MCLIP_SEL_TRACK;
910                 }
911         }
912
913         return 0;
914 }
915
916 static int clear_track_path_exec(bContext *C, wmOperator *UNUSED(op))
917 {
918         SpaceClip *sc= CTX_wm_space_clip(C);
919         MovieClip *clip= ED_space_clip(sc);
920         int sel_type;
921         MovieTrackingTrack *track;
922
923         BKE_movieclip_last_selection(clip, &sel_type, (void**)&track);
924
925         BKE_tracking_clear_path(track, sc->user.framenr);
926
927         WM_event_add_notifier(C, NC_MOVIECLIP|NA_EVALUATED, clip);
928
929         return OPERATOR_FINISHED;
930 }
931
932 void CLIP_OT_clear_track_path(wmOperatorType *ot)
933 {
934         /* identifiers */
935         ot->name= "Clear Track Path";
936         ot->description= "Clear path of active track";
937         ot->idname= "CLIP_OT_clear_track_path";
938
939         /* api callbacks */
940         ot->exec= clear_track_path_exec;
941         ot->poll= clear_track_path_poll;
942
943         /* flags */
944         ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
945 }
946
947 /********************** track to fcurves opertaotr *********************/
948
949 static int track_to_fcurves_poll(bContext *C)
950 {
951         Object *ob= CTX_data_active_object(C);
952         SpaceClip *sc= CTX_wm_space_clip(C);
953
954         if(ob && sc) {
955                 MovieClip *clip= ED_space_clip(sc);
956                 int type;
957                 void *sel;
958
959                 if(clip) {
960                         BKE_movieclip_last_selection(clip, &type, &sel);
961                         if(type==MCLIP_SEL_TRACK)
962                                 return 1;
963                 }
964         }
965
966         return 0;
967 }
968
969 static int track_to_fcurves_exec(bContext *C, wmOperator *op)
970 {
971         SpaceClip *sc= CTX_wm_space_clip(C);
972         MovieClip *clip= ED_space_clip(sc);
973         Object *ob= CTX_data_active_object(C);
974         Scene *scene= CTX_data_scene(C);
975         KeyingSet *ks;
976         int kflag, fra= SFRA, type;
977         MovieTrackingTrack *track;
978         bAction *act= verify_adt_action(&ob->id, 1);
979         MovieClipUser user= {0};
980         int width, height;
981         float scale= RNA_float_get(op->ptr, "scale");
982
983         BKE_movieclip_last_selection(clip, &type, (void**)&track);
984
985         ks= ANIM_builtin_keyingset_get_named(NULL, "Location");
986         kflag= ks->flag;
987         kflag |= ANIM_get_keyframing_flags(scene, 1);
988
989         (void)op;
990
991         BKE_movieclip_acquire_size(clip, &user, &width, &height);
992
993         while(fra<EFRA) {
994                 MovieTrackingMarker *marker= BKE_tracking_get_marker(track, fra);
995
996                 if(marker) {
997                         FCurve *fcu;
998
999                         fcu= verify_fcurve(act, ks->name, "location", 0, 1);
1000                         insert_vert_fcurve(fcu, fra, marker->pos[0]*width*scale, kflag);
1001
1002                         fcu= verify_fcurve(act, ks->name, "location", 1, 1);
1003                         insert_vert_fcurve(fcu, fra, marker->pos[1]*height*scale, kflag);
1004                 }
1005
1006                 fra++;
1007         }
1008
1009         WM_main_add_notifier(NC_ANIMATION|ND_KEYFRAME|NA_EDITED, NULL);
1010
1011         return OPERATOR_FINISHED;
1012 }
1013
1014 void CLIP_OT_track_to_fcurves(wmOperatorType *ot)
1015 {
1016         /* identifiers */
1017         ot->name= "Convert Track To FCurves";
1018         ot->description= "Convert active track to f-curves for active object in the scene";
1019         ot->idname= "CLIP_OT_track_to_fcurves";
1020
1021         /* api callbacks */
1022         ot->exec= track_to_fcurves_exec;
1023         ot->poll= track_to_fcurves_poll;
1024
1025         /* flags */
1026         ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
1027
1028         RNA_def_float(ot->srna, "scale", 1.f, -FLT_MAX, FLT_MAX, "Scale", "Scale factor for generated coordinates", -100.f, 100.f);
1029 }