1 /*
2  * This program is free software; you can redistribute it and/or
3  * modify it under the terms of the GNU General Public License
4  * as published by the Free Software Foundation; either version 2
5  * of the License, or (at your option) any later version.
6  *
7  * This program is distributed in the hope that it will be useful,
8  * but WITHOUT ANY WARRANTY; without even the implied warranty of
9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10  * GNU General Public License for more details.
11  *
12  * You should have received a copy of the GNU General Public License
13  * along with this program; if not, write to the Free Software Foundation,
14  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
15  *
16  * The Original Code is Copyright (C) 2011 Blender Foundation.
17  * All rights reserved.
18  */
19 
20 /** \file
21  * \ingroup spclip
22  */
23 
24 #include "MEM_guardedalloc.h"
25 
26 #include "DNA_screen_types.h"
27 #include "DNA_space_types.h"
28 
29 #include "BLI_math.h"
30 #include "BLI_utildefines.h"
31 
32 #include "BKE_context.h"
33 #include "BKE_report.h"
34 #include "BKE_tracking.h"
35 
36 #include "DEG_depsgraph.h"
37 
38 #include "WM_api.h"
39 #include "WM_types.h"
40 
41 #include "ED_clip.h"
42 
43 #include "clip_intern.h"
44 #include "tracking_ops_intern.h"
45 
46 /********************** Create plane track operator *********************/
47 
create_plane_track_tracks_exec(bContext * C,wmOperator * op)48 static int create_plane_track_tracks_exec(bContext *C, wmOperator *op)
49 {
50   SpaceClip *sc = CTX_wm_space_clip(C);
51   MovieClip *clip = ED_space_clip_get_clip(sc);
52   MovieTracking *tracking = &clip->tracking;
53   MovieTrackingPlaneTrack *plane_track;
54   ListBase *tracks_base = BKE_tracking_get_active_tracks(tracking);
55   ListBase *plane_tracks_base = BKE_tracking_get_active_plane_tracks(tracking);
56   int framenr = ED_space_clip_get_clip_frame_number(sc);
57 
58   plane_track = BKE_tracking_plane_track_add(tracking, plane_tracks_base, tracks_base, framenr);
59 
60   if (plane_track == NULL) {
61     BKE_report(op->reports, RPT_ERROR, "Need at least 4 selected point tracks to create a plane");
62     return OPERATOR_CANCELLED;
63   }
64 
65   BKE_tracking_tracks_deselect_all(tracks_base);
66 
67   plane_track->flag |= SELECT;
68   clip->tracking.act_track = NULL;
69   clip->tracking.act_plane_track = plane_track;
70 
71   /* Compute homoraphies and apply them on marker's corner, so we've got
72    * quite nice motion from the very beginning.
73    */
74   BKE_tracking_track_plane_from_existing_motion(plane_track, framenr);
75 
76   DEG_id_tag_update(&clip->id, ID_RECALC_COPY_ON_WRITE);
77   WM_event_add_notifier(C, NC_MOVIECLIP | NA_EDITED, clip);
78 
79   return OPERATOR_FINISHED;
80 }
81 
CLIP_OT_create_plane_track(wmOperatorType * ot)82 void CLIP_OT_create_plane_track(wmOperatorType *ot)
83 {
84   /* identifiers */
85   ot->name = "Create Plane Track";
86   ot->description = "Create new plane track out of selected point tracks";
87   ot->idname = "CLIP_OT_create_plane_track";
88 
89   /* api callbacks */
90   ot->exec = create_plane_track_tracks_exec;
91   ot->poll = ED_space_clip_tracking_poll;
92 
93   /* flags */
94   ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
95 }
96 
97 /********************** Slide plane marker corner operator *********************/
98 
99 typedef struct SlidePlaneMarkerData {
100   int launch_event;
101   MovieTrackingPlaneTrack *plane_track;
102   MovieTrackingPlaneMarker *plane_marker;
103   int width, height;
104   int corner_index;
105   float *corner;
106   int previous_mval[2];
107   float previous_corner[2];
108   float old_corner[2];
109   bool accurate;
110 } SlidePlaneMarkerData;
111 
mouse_to_plane_slide_zone_distance_squared(const float co[2],const float slide_zone[2],int width,int height)112 static float mouse_to_plane_slide_zone_distance_squared(const float co[2],
113                                                         const float slide_zone[2],
114                                                         int width,
115                                                         int height)
116 {
117   const float pixel_co[2] = {co[0] * width, co[1] * height},
118               pixel_slide_zone[2] = {slide_zone[0] * width, slide_zone[1] * height};
119   return square_f(pixel_co[0] - pixel_slide_zone[0]) + square_f(pixel_co[1] - pixel_slide_zone[1]);
120 }
121 
tracking_plane_marker_check_slide(bContext * C,const wmEvent * event,int * r_corner)122 static MovieTrackingPlaneTrack *tracking_plane_marker_check_slide(bContext *C,
123                                                                   const wmEvent *event,
124                                                                   int *r_corner)
125 {
126   const float distance_clip_squared = 12.0f * 12.0f;
127   SpaceClip *sc = CTX_wm_space_clip(C);
128   ARegion *region = CTX_wm_region(C);
129   MovieClip *clip = ED_space_clip_get_clip(sc);
130   MovieTracking *tracking = &clip->tracking;
131   int width, height;
132   float co[2];
133   ListBase *plane_tracks_base = BKE_tracking_get_active_plane_tracks(tracking);
134   int framenr = ED_space_clip_get_clip_frame_number(sc);
135 
136   ED_space_clip_get_size(sc, &width, &height);
137   if (width == 0 || height == 0) {
138     return NULL;
139   }
140 
141   ED_clip_mouse_pos(sc, region, event->mval, co);
142 
143   float min_distance_squared = FLT_MAX;
144   int min_corner = -1;
145   MovieTrackingPlaneTrack *min_plane_track = NULL;
146   for (MovieTrackingPlaneTrack *plane_track = plane_tracks_base->first; plane_track != NULL;
147        plane_track = plane_track->next) {
148     if (PLANE_TRACK_VIEW_SELECTED(plane_track)) {
149       MovieTrackingPlaneMarker *plane_marker = BKE_tracking_plane_marker_get(plane_track, framenr);
150       for (int i = 0; i < 4; i++) {
151         float distance_squared = mouse_to_plane_slide_zone_distance_squared(
152             co, plane_marker->corners[i], width, height);
153 
154         if (distance_squared < min_distance_squared) {
155           min_distance_squared = distance_squared;
156           min_corner = i;
157           min_plane_track = plane_track;
158         }
159       }
160     }
161   }
162 
163   if (min_distance_squared < distance_clip_squared / sc->zoom) {
164     if (r_corner != NULL) {
165       *r_corner = min_corner;
166     }
167     return min_plane_track;
168   }
169 
170   return NULL;
171 }
172 
slide_plane_marker_customdata(bContext * C,const wmEvent * event)173 static void *slide_plane_marker_customdata(bContext *C, const wmEvent *event)
174 {
175   SpaceClip *sc = CTX_wm_space_clip(C);
176   ARegion *region = CTX_wm_region(C);
177   MovieTrackingPlaneTrack *plane_track;
178   int width, height;
179   float co[2];
180   SlidePlaneMarkerData *customdata = NULL;
181   int framenr = ED_space_clip_get_clip_frame_number(sc);
182   int corner;
183 
184   ED_space_clip_get_size(sc, &width, &height);
185   if (width == 0 || height == 0) {
186     return NULL;
187   }
188 
189   ED_clip_mouse_pos(sc, region, event->mval, co);
190 
191   plane_track = tracking_plane_marker_check_slide(C, event, &corner);
192   if (plane_track) {
193     MovieTrackingPlaneMarker *plane_marker;
194 
195     customdata = MEM_callocN(sizeof(SlidePlaneMarkerData), "slide plane marker data");
196 
197     customdata->launch_event = WM_userdef_event_type_from_keymap_type(event->type);
198 
199     plane_marker = BKE_tracking_plane_marker_ensure(plane_track, framenr);
200 
201     customdata->plane_track = plane_track;
202     customdata->plane_marker = plane_marker;
203     customdata->width = width;
204     customdata->height = height;
205 
206     customdata->previous_mval[0] = event->mval[0];
207     customdata->previous_mval[1] = event->mval[1];
208 
209     customdata->corner_index = corner;
210     customdata->corner = plane_marker->corners[corner];
211 
212     copy_v2_v2(customdata->previous_corner, customdata->corner);
213     copy_v2_v2(customdata->old_corner, customdata->corner);
214   }
215 
216   return customdata;
217 }
218 
slide_plane_marker_invoke(bContext * C,wmOperator * op,const wmEvent * event)219 static int slide_plane_marker_invoke(bContext *C, wmOperator *op, const wmEvent *event)
220 {
221   SlidePlaneMarkerData *slidedata = slide_plane_marker_customdata(C, event);
222 
223   if (slidedata) {
224     SpaceClip *sc = CTX_wm_space_clip(C);
225     MovieClip *clip = ED_space_clip_get_clip(sc);
226     MovieTracking *tracking = &clip->tracking;
227 
228     tracking->act_plane_track = slidedata->plane_track;
229     tracking->act_track = NULL;
230 
231     op->customdata = slidedata;
232 
233     clip_tracking_hide_cursor(C);
234     WM_event_add_modal_handler(C, op);
235 
236     WM_event_add_notifier(C, NC_GEOM | ND_SELECT, NULL);
237 
238     return OPERATOR_RUNNING_MODAL;
239   }
240 
241   return OPERATOR_PASS_THROUGH;
242 }
243 
cancel_mouse_slide_plane_marker(SlidePlaneMarkerData * data)244 static void cancel_mouse_slide_plane_marker(SlidePlaneMarkerData *data)
245 {
246   copy_v2_v2(data->corner, data->old_corner);
247 }
248 
free_slide_plane_marker_data(SlidePlaneMarkerData * data)249 static void free_slide_plane_marker_data(SlidePlaneMarkerData *data)
250 {
251   MEM_freeN(data);
252 }
253 
slide_plane_marker_update_homographies(SpaceClip * sc,SlidePlaneMarkerData * data)254 static void slide_plane_marker_update_homographies(SpaceClip *sc, SlidePlaneMarkerData *data)
255 {
256   int framenr = ED_space_clip_get_clip_frame_number(sc);
257 
258   BKE_tracking_track_plane_from_existing_motion(data->plane_track, framenr);
259 }
260 
slide_plane_marker_modal(bContext * C,wmOperator * op,const wmEvent * event)261 static int slide_plane_marker_modal(bContext *C, wmOperator *op, const wmEvent *event)
262 {
263   SpaceClip *sc = CTX_wm_space_clip(C);
264   MovieClip *clip = ED_space_clip_get_clip(sc);
265   SlidePlaneMarkerData *data = (SlidePlaneMarkerData *)op->customdata;
266   float dx, dy, mdelta[2];
267   int next_corner_index, prev_corner_index, diag_corner_index;
268   const float *next_corner, *prev_corner, *diag_corner;
269   float next_edge[2], prev_edge[2], next_diag_edge[2], prev_diag_edge[2];
270 
271   switch (event->type) {
272     case EVT_LEFTCTRLKEY:
273     case EVT_RIGHTCTRLKEY:
274     case EVT_LEFTSHIFTKEY:
275     case EVT_RIGHTSHIFTKEY:
276       if (ELEM(event->type, EVT_LEFTSHIFTKEY, EVT_RIGHTSHIFTKEY)) {
277         data->accurate = event->val == KM_PRESS;
278       }
279       ATTR_FALLTHROUGH;
280     case MOUSEMOVE:
281       mdelta[0] = event->mval[0] - data->previous_mval[0];
282       mdelta[1] = event->mval[1] - data->previous_mval[1];
283 
284       dx = mdelta[0] / data->width / sc->zoom;
285       dy = mdelta[1] / data->height / sc->zoom;
286 
287       if (data->accurate) {
288         dx /= 5.0f;
289         dy /= 5.0f;
290       }
291 
292       data->corner[0] = data->previous_corner[0] + dx;
293       data->corner[1] = data->previous_corner[1] + dy;
294 
295       /*
296        *                              prev_edge
297        *   (Corner 3, current) <-----------------------   (Corner 2, previous)
298        *           |                                              ^
299        *           |                                              |
300        *           |                                              |
301        *           |                                              |
302        * next_edge |                                              | next_diag_edge
303        *           |                                              |
304        *           |                                              |
305        *           |                                              |
306        *           v                                              |
307        *    (Corner 0, next)   ----------------------->   (Corner 1, diagonal)
308        *                             prev_diag_edge
309        */
310 
311       next_corner_index = (data->corner_index + 1) % 4;
312       prev_corner_index = (data->corner_index + 3) % 4;
313       diag_corner_index = (data->corner_index + 2) % 4;
314 
315       next_corner = data->plane_marker->corners[next_corner_index];
316       prev_corner = data->plane_marker->corners[prev_corner_index];
317       diag_corner = data->plane_marker->corners[diag_corner_index];
318 
319       sub_v2_v2v2(next_edge, next_corner, data->corner);
320       sub_v2_v2v2(prev_edge, data->corner, prev_corner);
321       sub_v2_v2v2(next_diag_edge, prev_corner, diag_corner);
322       sub_v2_v2v2(prev_diag_edge, diag_corner, next_corner);
323 
324       if (cross_v2v2(prev_edge, next_edge) < 0.0f) {
325         closest_to_line_v2(data->corner, data->corner, prev_corner, next_corner);
326       }
327 
328       if (cross_v2v2(next_diag_edge, prev_edge) < 0.0f) {
329         closest_to_line_v2(data->corner, data->corner, prev_corner, diag_corner);
330       }
331 
332       if (cross_v2v2(next_edge, prev_diag_edge) < 0.0f) {
333         closest_to_line_v2(data->corner, data->corner, next_corner, diag_corner);
334       }
335 
336       data->previous_mval[0] = event->mval[0];
337       data->previous_mval[1] = event->mval[1];
338       copy_v2_v2(data->previous_corner, data->corner);
339 
340       DEG_id_tag_update(&clip->id, ID_RECALC_COPY_ON_WRITE);
341       WM_event_add_notifier(C, NC_MOVIECLIP | NA_EDITED, NULL);
342 
343       break;
344 
345     case LEFTMOUSE:
346     case RIGHTMOUSE:
347       if (event->type == data->launch_event && event->val == KM_RELEASE) {
348         /* Marker is now keyframed. */
349         data->plane_marker->flag &= ~PLANE_MARKER_TRACKED;
350 
351         slide_plane_marker_update_homographies(sc, data);
352 
353         free_slide_plane_marker_data(op->customdata);
354 
355         clip_tracking_show_cursor(C);
356 
357         DEG_id_tag_update(&clip->id, ID_RECALC_COPY_ON_WRITE);
358         WM_event_add_notifier(C, NC_MOVIECLIP | NA_EDITED, clip);
359 
360         return OPERATOR_FINISHED;
361       }
362 
363       break;
364 
365     case EVT_ESCKEY:
366       cancel_mouse_slide_plane_marker(op->customdata);
367 
368       free_slide_plane_marker_data(op->customdata);
369 
370       clip_tracking_show_cursor(C);
371 
372       WM_event_add_notifier(C, NC_MOVIECLIP | NA_EDITED, clip);
373 
374       return OPERATOR_CANCELLED;
375   }
376 
377   return OPERATOR_RUNNING_MODAL;
378 }
379 
CLIP_OT_slide_plane_marker(wmOperatorType * ot)380 void CLIP_OT_slide_plane_marker(wmOperatorType *ot)
381 {
382   /* identifiers */
383   ot->name = "Slide Plane Marker";
384   ot->description = "Slide plane marker areas";
385   ot->idname = "CLIP_OT_slide_plane_marker";
386 
387   /* api callbacks */
388   ot->poll = ED_space_clip_tracking_poll;
389   ot->invoke = slide_plane_marker_invoke;
390   ot->modal = slide_plane_marker_modal;
391 
392   /* flags */
393   ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_GRAB_CURSOR_XY | OPTYPE_BLOCKING;
394 }
395