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