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