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