Fixed curves and dopeseet views of motion tracking data not taking clip's
[blender.git] / source / blender / editors / space_clip / clip_graph_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/clip_graph_ops.c
29  *  \ingroup spclip
30  */
31
32 #include "DNA_object_types.h"   /* SELECT */
33 #include "DNA_scene_types.h"
34
35 #include "MEM_guardedalloc.h"
36
37 #include "BLI_utildefines.h"
38 #include "BLI_math.h"
39 #include "BLI_listbase.h"
40 #include "BLI_rect.h"
41
42 #include "BKE_context.h"
43 #include "BKE_movieclip.h"
44 #include "BKE_tracking.h"
45 #include "BKE_depsgraph.h"
46
47 #include "WM_api.h"
48 #include "WM_types.h"
49
50 #include "ED_screen.h"
51 #include "ED_clip.h"
52
53 #include "UI_interface.h"
54
55 #include "RNA_access.h"
56 #include "RNA_define.h"
57
58 #include "UI_view2d.h"
59
60 #include "clip_intern.h"        // own include
61
62 /******************** common graph-editing utilities ********************/
63
64 static int ED_space_clip_graph_poll(bContext *C)
65 {
66         if (ED_space_clip_tracking_poll(C)) {
67                 SpaceClip *sc = CTX_wm_space_clip(C);
68
69                 return sc->view == SC_VIEW_GRAPH;
70         }
71
72         return FALSE;
73 }
74
75 typedef struct {
76         int action;
77 } SelectUserData;
78
79 static void toggle_selection_cb(void *userdata, MovieTrackingMarker *marker)
80 {
81         SelectUserData *data = (SelectUserData *)userdata;
82
83         switch (data->action) {
84                 case SEL_SELECT:
85                         marker->flag |= MARKER_GRAPH_SEL;
86                         break;
87                 case SEL_DESELECT:
88                         marker->flag &= ~MARKER_GRAPH_SEL;
89                         break;
90                 case SEL_INVERT:
91                         marker->flag ^= MARKER_GRAPH_SEL;
92                         break;
93         }
94 }
95
96 /******************** mouse select operator ********************/
97
98 typedef struct {
99         int coord,              /* coordinate index of found entuty (0 = X-axis, 1 = Y-axis) */
100             has_prev;           /* if there's valid coordinate of previous point of curve segment */
101
102         float min_dist,         /* minimal distance between mouse and currently found entuty */
103               mouse_co[2],      /* mouse coordinate */
104               prev_co[2],       /* coordinate of previeous point of segment */
105               min_co[2];        /* coordinate of entity with minimal distance */
106
107         MovieTrackingTrack *track;      /* nearest found track */
108         MovieTrackingMarker *marker;    /* nearest found marker */
109 } MouseSelectUserData;
110
111 static void find_nearest_tracking_segment_cb(void *userdata, MovieTrackingTrack *track,
112                                              MovieTrackingMarker *UNUSED(marker),
113                                              int coord, int scene_framenr, float val)
114 {
115         MouseSelectUserData *data = userdata;
116         float co[2] = {scene_framenr, val};
117
118         if (data->has_prev) {
119                 float d = dist_to_line_segment_v2(data->mouse_co, data->prev_co, co);
120
121                 if (data->track == NULL || d < data->min_dist) {
122                         data->track = track;
123                         data->min_dist = d;
124                         data->coord = coord;
125                         copy_v2_v2(data->min_co, co);
126                 }
127         }
128
129         data->has_prev = TRUE;
130         copy_v2_v2(data->prev_co, co);
131 }
132
133 void find_nearest_tracking_segment_end_cb(void *userdata)
134 {
135         MouseSelectUserData *data = userdata;
136
137         data->has_prev = FALSE;
138 }
139
140 static void find_nearest_tracking_knot_cb(void *userdata, MovieTrackingTrack *track,
141                                           MovieTrackingMarker *marker, int coord, int scene_framenr, float val)
142 {
143         MouseSelectUserData *data = userdata;
144         float dx = scene_framenr - data->mouse_co[0], dy = val - data->mouse_co[1];
145         float d = dx * dx + dy * dy;
146
147         if (data->marker == NULL || d < data->min_dist) {
148                 float co[2] = {scene_framenr, val};
149
150                 data->track = track;
151                 data->marker = marker;
152                 data->min_dist = d;
153                 data->coord = coord;
154                 copy_v2_v2(data->min_co, co);
155         }
156
157 }
158
159 static void mouse_select_init_data(MouseSelectUserData *userdata, float *co)
160 {
161         memset(userdata, 0, sizeof(MouseSelectUserData));
162         userdata->min_dist = FLT_MAX;
163         copy_v2_v2(userdata->mouse_co, co);
164 }
165
166 static int mouse_select_knot(bContext *C, float co[2], int extend)
167 {
168         SpaceClip *sc = CTX_wm_space_clip(C);
169         MovieClip *clip = ED_space_clip(sc);
170         ARegion *ar = CTX_wm_region(C);
171         View2D *v2d = &ar->v2d;
172         MovieTracking *tracking = &clip->tracking;
173         MovieTrackingTrack *act_track = BKE_tracking_active_track(tracking);
174         static const int delta = 6;
175
176         if (act_track) {
177                 MouseSelectUserData userdata;
178
179                 mouse_select_init_data(&userdata, co);
180                 clip_graph_tracking_values_iterate_track(sc, act_track, &userdata,
181                                                          find_nearest_tracking_knot_cb, NULL, NULL);
182
183                 if (userdata.marker) {
184                         int x1, y1, x2, y2;
185
186                         UI_view2d_view_to_region(v2d, co[0], co[1], &x1, &y1);
187                         UI_view2d_view_to_region(v2d, userdata.min_co[0], userdata.min_co[1], &x2, &y2);
188
189                         if (abs(x2 - x1) <= delta && abs(y2 - y1) <= delta) {
190                                 if (!extend) {
191                                         SelectUserData selectdata = {SEL_DESELECT};
192
193                                         clip_graph_tracking_iterate(sc, &selectdata, toggle_selection_cb);
194                                 }
195
196                                 if (userdata.coord == 0)
197                                         userdata.marker->flag |= MARKER_GRAPH_SEL_X;
198                                 else
199                                         userdata.marker->flag |= MARKER_GRAPH_SEL_Y;
200
201                                 return TRUE;
202                         }
203                 }
204         }
205
206         return FALSE;
207 }
208
209 static int mouse_select_curve(bContext *C, float co[2], int extend)
210 {
211         SpaceClip *sc = CTX_wm_space_clip(C);
212         MovieClip *clip = ED_space_clip(sc);
213         MovieTracking *tracking = &clip->tracking;
214         MovieTrackingTrack *act_track = BKE_tracking_active_track(tracking);
215         MouseSelectUserData userdata;
216
217         mouse_select_init_data(&userdata, co);
218         clip_graph_tracking_values_iterate(sc, &userdata, find_nearest_tracking_segment_cb,
219                                            NULL, find_nearest_tracking_segment_end_cb);
220
221         if (userdata.track) {
222                 if (extend) {
223                         if (act_track == userdata.track) {
224                                 /* currently only single curve can be selected (selected curve represents active track) */
225                                 act_track = NULL;
226                         }
227                 }
228                 else if (act_track != userdata.track) {
229                         SelectUserData selectdata = {SEL_DESELECT};
230
231                         tracking->act_track = userdata.track;
232
233                         /* deselect all knots on newly selected curve */
234                         clip_graph_tracking_iterate(sc, &selectdata, toggle_selection_cb);
235                 }
236
237                 return TRUE;
238         }
239
240         return FALSE;
241 }
242
243 static int mouse_select(bContext *C, float co[2], int extend)
244 {
245         int sel = FALSE;
246
247         /* first try to select knot on selected curves */
248         sel = mouse_select_knot(C, co, extend);
249
250         if (!sel) {
251                 /* if there's no close enough knot to mouse osition, select nearest curve */
252                 sel = mouse_select_curve(C, co, extend);
253         }
254
255         if (sel)
256                 WM_event_add_notifier(C, NC_GEOM | ND_SELECT, NULL);
257
258         return OPERATOR_FINISHED;
259 }
260
261 static int select_exec(bContext *C, wmOperator *op)
262 {
263         float co[2];
264         int  extend = RNA_boolean_get(op->ptr, "extend");
265
266         RNA_float_get_array(op->ptr, "location", co);
267
268         return mouse_select(C, co, extend);
269 }
270
271 static int select_invoke(bContext *C, wmOperator *op, wmEvent *event)
272 {
273         ARegion *ar = CTX_wm_region(C);
274         float co[2];
275
276         UI_view2d_region_to_view(&ar->v2d, event->mval[0], event->mval[1], &co[0], &co[1]);
277         RNA_float_set_array(op->ptr, "location", co);
278
279         return select_exec(C, op);
280 }
281
282 void CLIP_OT_graph_select(wmOperatorType *ot)
283 {
284         /* identifiers */
285         ot->name = "Select";
286         ot->description = "Select graph curves";
287         ot->idname = "CLIP_OT_graph_select";
288
289         /* api callbacks */
290         ot->exec = select_exec;
291         ot->invoke = select_invoke;
292         ot->poll = ED_space_clip_graph_poll;
293
294         /* flags */
295         ot->flag = OPTYPE_UNDO;
296
297         /* properties */
298         RNA_def_float_vector(ot->srna, "location", 2, NULL, -FLT_MAX, FLT_MAX,
299                 "Location", "Mouse location to select nearest entity", -100.0f, 100.0f);
300         RNA_def_boolean(ot->srna, "extend", 0,
301                 "Extend", "Extend selection rather than clearing the existing selection");
302 }
303
304 /********************** border select operator *********************/
305
306 typedef struct BorderSelectuserData {
307         rctf rect;
308         int change, mode, extend;
309 } BorderSelectuserData;
310
311 static void border_select_cb(void *userdata, MovieTrackingTrack *UNUSED(track),
312                              MovieTrackingMarker *marker, int coord, int scene_framenr, float val)
313 {
314         BorderSelectuserData *data = (BorderSelectuserData *) userdata;
315
316         if (BLI_in_rctf(&data->rect, scene_framenr, val)) {
317                 int flag = 0;
318
319                 if (coord == 0)
320                         flag = MARKER_GRAPH_SEL_X;
321                 else
322                         flag = MARKER_GRAPH_SEL_Y;
323
324                 if (data->mode == GESTURE_MODAL_SELECT)
325                         marker->flag |= flag;
326                 else
327                         marker->flag &= ~flag;
328
329                 data->change = TRUE;
330         }
331         else if (!data->extend) {
332                 marker->flag &= ~MARKER_GRAPH_SEL;
333         }
334 }
335
336 static int border_select_graph_exec(bContext *C, wmOperator *op)
337 {
338         SpaceClip *sc = CTX_wm_space_clip(C);
339         ARegion *ar = CTX_wm_region(C);
340         MovieClip *clip = ED_space_clip(sc);
341         MovieTracking *tracking = &clip->tracking;
342         MovieTrackingTrack *act_track = BKE_tracking_active_track(tracking);
343         BorderSelectuserData userdata;
344         rcti rect;
345
346         /* get rectangle from operator */
347         rect.xmin = RNA_int_get(op->ptr, "xmin");
348         rect.ymin = RNA_int_get(op->ptr, "ymin");
349         rect.xmax = RNA_int_get(op->ptr, "xmax");
350         rect.ymax = RNA_int_get(op->ptr, "ymax");
351
352         UI_view2d_region_to_view(&ar->v2d, rect.xmin, rect.ymin, &userdata.rect.xmin, &userdata.rect.ymin);
353         UI_view2d_region_to_view(&ar->v2d, rect.xmax, rect.ymax, &userdata.rect.xmax, &userdata.rect.ymax);
354
355         userdata.change = FALSE;
356         userdata.mode = RNA_int_get(op->ptr, "gesture_mode");
357         userdata.extend = RNA_boolean_get(op->ptr, "extend");
358
359         clip_graph_tracking_values_iterate_track(sc, act_track, &userdata, border_select_cb, NULL, NULL);
360
361         if (userdata.change) {
362                 WM_event_add_notifier(C, NC_GEOM | ND_SELECT, NULL);
363
364                 return OPERATOR_FINISHED;
365         }
366
367         return OPERATOR_CANCELLED;
368 }
369
370 void CLIP_OT_graph_select_border(wmOperatorType *ot)
371 {
372         /* identifiers */
373         ot->name = "Border Select";
374         ot->description = "Select curve points using border selection";
375         ot->idname = "CLIP_OT_graph_select_border";
376
377         /* api callbacks */
378         ot->invoke = WM_border_select_invoke;
379         ot->exec = border_select_graph_exec;
380         ot->modal = WM_border_select_modal;
381         ot->poll = ED_space_clip_graph_poll;
382
383         /* flags */
384         ot->flag = OPTYPE_UNDO;
385
386         /* properties */
387         WM_operator_properties_gesture_border(ot, TRUE);
388 }
389
390 /********************** select all operator *********************/
391
392 static int graph_select_all_markers_exec(bContext *C, wmOperator *op)
393 {
394         SpaceClip *sc = CTX_wm_space_clip(C);
395         MovieClip *clip = ED_space_clip(sc);
396         MovieTracking *tracking = &clip->tracking;
397         MovieTrackingTrack *act_track = BKE_tracking_active_track(tracking);
398         MovieTrackingMarker *marker;
399         int action = RNA_enum_get(op->ptr, "action");
400         int a;
401
402         if (!act_track)
403                 return OPERATOR_CANCELLED;
404
405         if (action == SEL_TOGGLE) {
406                 action = SEL_SELECT;
407
408                 for (a = 0; a < act_track->markersnr; a++) {
409                         marker = &act_track->markers[a];
410
411                         if (marker->flag & MARKER_GRAPH_SEL) {
412                                 action = SEL_DESELECT;
413                                 break;
414                         }
415                 }
416         }
417
418         for (a = 0; a < act_track->markersnr; a++) {
419                 marker = &act_track->markers[a];
420
421                 switch (action) {
422                         case SEL_SELECT:
423                                 marker->flag |= MARKER_GRAPH_SEL;
424                                 break;
425                         case SEL_DESELECT:
426                                 marker->flag &= ~MARKER_GRAPH_SEL;
427                                 break;
428                         case SEL_INVERT:
429                                 marker->flag ^= MARKER_GRAPH_SEL;
430                                 break;
431                 }
432         }
433
434         WM_event_add_notifier(C, NC_GEOM | ND_SELECT, NULL);
435
436         return OPERATOR_FINISHED;
437 }
438
439 void CLIP_OT_graph_select_all_markers(wmOperatorType *ot)
440 {
441         /* identifiers */
442         ot->name = "(De)select All Markers";
443         ot->description = "Change selection of all markers of active track";
444         ot->idname = "CLIP_OT_graph_select_all_markers";
445
446         /* api callbacks */
447         ot->exec = graph_select_all_markers_exec;
448         ot->poll = ED_space_clip_graph_poll;
449
450         /* flags */
451         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
452
453         WM_operator_properties_select_all(ot);
454 }
455
456 /******************** delete curve operator ********************/
457
458 static int delete_curve_exec(bContext *C, wmOperator *UNUSED(op))
459 {
460         SpaceClip *sc = CTX_wm_space_clip(C);
461         MovieClip *clip = ED_space_clip(sc);
462         MovieTracking *tracking = &clip->tracking;
463         ListBase *tracksbase = BKE_tracking_get_tracks(tracking);
464         MovieTrackingTrack *act_track = BKE_tracking_active_track(tracking);
465
466         if (act_track)
467                 clip_delete_track(C, clip, tracksbase, act_track);
468
469         return OPERATOR_FINISHED;
470 }
471
472 void CLIP_OT_graph_delete_curve(wmOperatorType *ot)
473 {
474         /* identifiers */
475         ot->name = "Delete Curve";
476         ot->description = "Delete selected curves";
477         ot->idname = "CLIP_OT_graph_delete_curve";
478
479         /* api callbacks */
480         ot->invoke = WM_operator_confirm;
481         ot->exec = delete_curve_exec;
482         ot->poll = ED_space_clip_tracking_poll;
483
484         /* flags */
485         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
486 }
487
488 /******************** delete knot operator ********************/
489
490 static int delete_knot_exec(bContext *C, wmOperator *UNUSED(op))
491 {
492         SpaceClip *sc = CTX_wm_space_clip(C);
493         MovieClip *clip = ED_space_clip(sc);
494         MovieTracking *tracking = &clip->tracking;
495         ListBase *tracksbase = BKE_tracking_get_tracks(tracking);
496         MovieTrackingTrack *act_track = BKE_tracking_active_track(tracking);
497
498         if (act_track) {
499                 int a = 0;
500
501                 while (a < act_track->markersnr) {
502                         MovieTrackingMarker *marker = &act_track->markers[a];
503
504                         if (marker->flag & MARKER_GRAPH_SEL)
505                                 clip_delete_marker(C, clip, tracksbase, act_track, marker);
506                         else
507                                 a++;
508                 }
509         }
510
511         return OPERATOR_FINISHED;
512 }
513
514 void CLIP_OT_graph_delete_knot(wmOperatorType *ot)
515 {
516         /* identifiers */
517         ot->name = "Delete Knot";
518         ot->description = "Delete curve knots";
519         ot->idname = "CLIP_OT_graph_delete_knot";
520
521         /* api callbacks */
522         ot->exec = delete_knot_exec;
523         ot->poll = ED_space_clip_graph_poll;
524
525         /* flags */
526         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
527 }
528
529 /******************** view all operator ********************/
530
531 typedef struct {
532         float min, max;
533 } ViewAllUserData;
534
535 static void view_all_cb(void *userdata, MovieTrackingTrack *UNUSED(track), MovieTrackingMarker *UNUSED(marker),
536                         int UNUSED(coord), int UNUSED(scene_framenr), float val)
537 {
538         ViewAllUserData *data = (ViewAllUserData *) userdata;
539
540         if (val < data->min)
541                 data->min = val;
542
543         if (val > data->max)
544                 data->max = val;
545 }
546
547 static int view_all_exec(bContext *C, wmOperator *UNUSED(op))
548 {
549         Scene *scene = CTX_data_scene(C);
550         ARegion *ar = CTX_wm_region(C);
551         SpaceClip *sc = CTX_wm_space_clip(C);
552         View2D *v2d = &ar->v2d;
553         ViewAllUserData userdata;
554         float extra;
555
556         userdata.max = -FLT_MAX;
557         userdata.min = FLT_MAX;
558
559         clip_graph_tracking_values_iterate(sc, &userdata, view_all_cb, NULL, NULL);
560
561         /* set extents of view to start/end frames */
562         v2d->cur.xmin = (float) SFRA;
563         v2d->cur.xmax = (float) EFRA;
564
565         if (userdata.min < userdata.max) {
566                 v2d->cur.ymin = userdata.min;
567                 v2d->cur.ymax = userdata.max;
568         }
569         else {
570                 v2d->cur.ymin = -10;
571                 v2d->cur.ymax = 10;
572         }
573
574         /* we need an extra "buffer" factor on either side so that the endpoints are visible */
575         extra = 0.01f * (v2d->cur.xmax - v2d->cur.xmin);
576         v2d->cur.xmin -= extra;
577         v2d->cur.xmax += extra;
578
579         extra = 0.01f * (v2d->cur.ymax - v2d->cur.ymin);
580         v2d->cur.ymin -= extra;
581         v2d->cur.ymax += extra;
582
583         ED_region_tag_redraw(ar);
584
585         return OPERATOR_FINISHED;
586 }
587
588 void CLIP_OT_graph_view_all(wmOperatorType *ot)
589 {
590         /* identifiers */
591         ot->name = "View All";
592         ot->description = "View all curves in editor";
593         ot->idname = "CLIP_OT_graph_view_all";
594
595         /* api callbacks */
596         ot->exec = view_all_exec;
597         ot->poll = ED_space_clip_graph_poll;
598 }
599
600 /******************** jump to current frame operator ********************/
601
602 void ED_clip_graph_center_current_frame(Scene *scene, ARegion *ar)
603 {
604         View2D *v2d = &ar->v2d;
605         float extra = (v2d->cur.xmax - v2d->cur.xmin) / 2.0f;
606
607         /* set extents of view to start/end frames */
608         v2d->cur.xmin = (float)CFRA - extra;
609         v2d->cur.xmax = (float)CFRA + extra;
610 }
611
612 static int center_current_frame_exec(bContext *C, wmOperator *UNUSED(op))
613 {
614         Scene *scene = CTX_data_scene(C);
615         ARegion *ar = CTX_wm_region(C);
616
617         ED_clip_graph_center_current_frame(scene, ar);
618
619         ED_region_tag_redraw(ar);
620
621         return OPERATOR_FINISHED;
622 }
623
624 void CLIP_OT_graph_center_current_frame(wmOperatorType *ot)
625 {
626         /* identifiers */
627         ot->name = "Center Current Frame";
628         ot->description = "Scroll view so current frame would be centered";
629         ot->idname = "CLIP_OT_graph_center_current_frame";
630
631         /* api callbacks */
632         ot->exec = center_current_frame_exec;
633         ot->poll = ED_space_clip_graph_poll;
634 }
635
636 /********************** disable markers operator *********************/
637
638 static int graph_disable_markers_exec(bContext *C, wmOperator *op)
639 {
640         SpaceClip *sc = CTX_wm_space_clip(C);
641         MovieClip *clip = ED_space_clip(sc);
642         MovieTracking *tracking = &clip->tracking;
643         MovieTrackingTrack *act_track = BKE_tracking_active_track(tracking);
644         MovieTrackingMarker *marker;
645         int action = RNA_enum_get(op->ptr, "action");
646         int a;
647
648         if (!act_track || (act_track->flag & TRACK_LOCKED))
649                 return OPERATOR_CANCELLED;
650
651         for (a = 0; a < act_track->markersnr; a++) {
652                 marker = &act_track->markers[a];
653
654                 if (marker->flag & MARKER_GRAPH_SEL) {
655                         if (action == 0)
656                                 marker->flag |= MARKER_DISABLED;
657                         else if (action == 1)
658                                 marker->flag &= ~MARKER_DISABLED;
659                         else
660                                 marker->flag ^= MARKER_DISABLED;
661                 }
662         }
663
664         DAG_id_tag_update(&clip->id, 0);
665
666         WM_event_add_notifier(C, NC_MOVIECLIP | NA_EVALUATED, clip);
667
668         return OPERATOR_FINISHED;
669 }
670
671 void CLIP_OT_graph_disable_markers(wmOperatorType *ot)
672 {
673         static EnumPropertyItem actions_items[] = {
674                         {0, "DISABLE", 0, "Disable", "Disable selected markers"},
675                         {1, "ENABLE", 0, "Enable", "Enable selected markers"},
676                         {2, "TOGGLE", 0, "Toggle", "Toggle disabled flag for selected markers"},
677                         {0, NULL, 0, NULL, NULL}
678         };
679
680         /* identifiers */
681         ot->name = "Disable Markers";
682         ot->description = "Disable/enable selected markers";
683         ot->idname = "CLIP_OT_graph_disable_markers";
684
685         /* api callbacks */
686         ot->exec = graph_disable_markers_exec;
687         ot->poll = ED_space_clip_graph_poll;
688
689         /* flags */
690         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
691
692         /* properties */
693         RNA_def_enum(ot->srna, "action", actions_items, 0, "Action", "Disable action to execute");
694 }