2298 lines
67 KiB
C
2298 lines
67 KiB
C
/*
|
|
* This program is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU General Public License
|
|
* as published by the Free Software Foundation; either version 2
|
|
* of the License, or (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software Foundation,
|
|
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
*
|
|
* The Original Code is Copyright (C) 2011 Blender Foundation.
|
|
* All rights reserved.
|
|
*/
|
|
|
|
/** \file
|
|
* \ingroup spclip
|
|
*/
|
|
|
|
#include "MEM_guardedalloc.h"
|
|
|
|
#include "DNA_screen_types.h"
|
|
#include "DNA_space_types.h"
|
|
|
|
#include "BLI_blenlib.h"
|
|
#include "BLI_ghash.h"
|
|
#include "BLI_math.h"
|
|
#include "BLI_utildefines.h"
|
|
|
|
#include "BKE_context.h"
|
|
#include "BKE_movieclip.h"
|
|
#include "BKE_report.h"
|
|
#include "BKE_tracking.h"
|
|
|
|
#include "DEG_depsgraph.h"
|
|
|
|
#include "WM_api.h"
|
|
#include "WM_types.h"
|
|
|
|
#include "ED_clip.h"
|
|
#include "ED_screen.h"
|
|
|
|
#include "RNA_access.h"
|
|
#include "RNA_define.h"
|
|
|
|
#include "BLT_translation.h"
|
|
|
|
#include "clip_intern.h"
|
|
#include "tracking_ops_intern.h"
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/** \name Add Marker Operator
|
|
* \{ */
|
|
|
|
static bool add_marker(const bContext *C, float x, float y)
|
|
{
|
|
SpaceClip *sc = CTX_wm_space_clip(C);
|
|
MovieClip *clip = ED_space_clip_get_clip(sc);
|
|
MovieTracking *tracking = &clip->tracking;
|
|
ListBase *tracksbase = BKE_tracking_get_active_tracks(tracking);
|
|
ListBase *plane_tracks_base = BKE_tracking_get_active_plane_tracks(tracking);
|
|
MovieTrackingTrack *track;
|
|
int width, height;
|
|
int framenr = ED_space_clip_get_clip_frame_number(sc);
|
|
|
|
ED_space_clip_get_size(sc, &width, &height);
|
|
|
|
if (width == 0 || height == 0) {
|
|
return false;
|
|
}
|
|
|
|
track = BKE_tracking_track_add(tracking, tracksbase, x, y, framenr, width, height);
|
|
|
|
BKE_tracking_track_select(tracksbase, track, TRACK_AREA_ALL, 0);
|
|
BKE_tracking_plane_tracks_deselect_all(plane_tracks_base);
|
|
|
|
clip->tracking.act_track = track;
|
|
clip->tracking.act_plane_track = NULL;
|
|
|
|
return true;
|
|
}
|
|
|
|
static int add_marker_exec(bContext *C, wmOperator *op)
|
|
{
|
|
SpaceClip *sc = CTX_wm_space_clip(C);
|
|
MovieClip *clip = ED_space_clip_get_clip(sc);
|
|
float pos[2];
|
|
|
|
ClipViewLockState lock_state;
|
|
ED_clip_view_lock_state_store(C, &lock_state);
|
|
|
|
RNA_float_get_array(op->ptr, "location", pos);
|
|
|
|
if (!add_marker(C, pos[0], pos[1])) {
|
|
return OPERATOR_CANCELLED;
|
|
}
|
|
|
|
ED_clip_view_lock_state_restore_no_jump(C, &lock_state);
|
|
|
|
WM_event_add_notifier(C, NC_MOVIECLIP | NA_EDITED, clip);
|
|
|
|
return OPERATOR_FINISHED;
|
|
}
|
|
|
|
static int add_marker_invoke(bContext *C, wmOperator *op, const wmEvent *event)
|
|
{
|
|
SpaceClip *sc = CTX_wm_space_clip(C);
|
|
ARegion *region = CTX_wm_region(C);
|
|
|
|
if (!RNA_struct_property_is_set(op->ptr, "location")) {
|
|
/* If location is not set, use mouse position as default. */
|
|
float co[2];
|
|
ED_clip_mouse_pos(sc, region, event->mval, co);
|
|
RNA_float_set_array(op->ptr, "location", co);
|
|
}
|
|
|
|
return add_marker_exec(C, op);
|
|
}
|
|
|
|
void CLIP_OT_add_marker(wmOperatorType *ot)
|
|
{
|
|
/* identifiers */
|
|
ot->name = "Add Marker";
|
|
ot->idname = "CLIP_OT_add_marker";
|
|
ot->description = "Place new marker at specified location";
|
|
|
|
/* api callbacks */
|
|
ot->invoke = add_marker_invoke;
|
|
ot->exec = add_marker_exec;
|
|
ot->poll = ED_space_clip_tracking_poll;
|
|
|
|
/* flags */
|
|
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
|
|
|
|
/* properties */
|
|
RNA_def_float_vector(ot->srna,
|
|
"location",
|
|
2,
|
|
NULL,
|
|
-FLT_MAX,
|
|
FLT_MAX,
|
|
"Location",
|
|
"Location of marker on frame",
|
|
-1.0f,
|
|
1.0f);
|
|
}
|
|
|
|
/** \} */
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/** \name Add Marker Operator
|
|
* \{ */
|
|
|
|
static int add_marker_at_click_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
|
|
{
|
|
ED_workspace_status_text(C, TIP_("Use LMB click to define location where place the marker"));
|
|
|
|
/* Add modal handler for ESC. */
|
|
WM_event_add_modal_handler(C, op);
|
|
|
|
return OPERATOR_RUNNING_MODAL;
|
|
}
|
|
|
|
static int add_marker_at_click_modal(bContext *C, wmOperator *UNUSED(op), const wmEvent *event)
|
|
{
|
|
switch (event->type) {
|
|
case MOUSEMOVE:
|
|
return OPERATOR_RUNNING_MODAL;
|
|
|
|
case LEFTMOUSE: {
|
|
SpaceClip *sc = CTX_wm_space_clip(C);
|
|
MovieClip *clip = ED_space_clip_get_clip(sc);
|
|
ARegion *region = CTX_wm_region(C);
|
|
float pos[2];
|
|
|
|
ED_workspace_status_text(C, NULL);
|
|
|
|
ED_clip_point_stable_pos(sc,
|
|
region,
|
|
event->x - region->winrct.xmin,
|
|
event->y - region->winrct.ymin,
|
|
&pos[0],
|
|
&pos[1]);
|
|
|
|
if (!add_marker(C, pos[0], pos[1])) {
|
|
return OPERATOR_CANCELLED;
|
|
}
|
|
|
|
WM_event_add_notifier(C, NC_MOVIECLIP | NA_EDITED, clip);
|
|
return OPERATOR_FINISHED;
|
|
}
|
|
|
|
case EVT_ESCKEY:
|
|
ED_workspace_status_text(C, NULL);
|
|
return OPERATOR_CANCELLED;
|
|
}
|
|
|
|
return OPERATOR_PASS_THROUGH;
|
|
}
|
|
|
|
void CLIP_OT_add_marker_at_click(wmOperatorType *ot)
|
|
{
|
|
/* identifiers */
|
|
ot->name = "Add Marker at Click";
|
|
ot->idname = "CLIP_OT_add_marker_at_click";
|
|
ot->description = "Place new marker at the desired (clicked) position";
|
|
|
|
/* api callbacks */
|
|
ot->invoke = add_marker_at_click_invoke;
|
|
ot->poll = ED_space_clip_tracking_poll;
|
|
ot->modal = add_marker_at_click_modal;
|
|
|
|
/* flags */
|
|
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_BLOCKING;
|
|
}
|
|
|
|
/** \} */
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/** \name Delete Track Operator
|
|
* \{ */
|
|
|
|
static int delete_track_exec(bContext *C, wmOperator *UNUSED(op))
|
|
{
|
|
SpaceClip *sc = CTX_wm_space_clip(C);
|
|
MovieClip *clip = ED_space_clip_get_clip(sc);
|
|
MovieTracking *tracking = &clip->tracking;
|
|
bool changed = false;
|
|
/* Delete selected plane tracks. */
|
|
ListBase *plane_tracks_base = BKE_tracking_get_active_plane_tracks(tracking);
|
|
for (MovieTrackingPlaneTrack *plane_track = plane_tracks_base->first, *next_plane_track;
|
|
plane_track != NULL;
|
|
plane_track = next_plane_track) {
|
|
next_plane_track = plane_track->next;
|
|
if (PLANE_TRACK_VIEW_SELECTED(plane_track)) {
|
|
clip_delete_plane_track(C, clip, plane_track);
|
|
changed = true;
|
|
}
|
|
}
|
|
/* Remove selected point tracks (they'll also be removed from planes which
|
|
* uses them).
|
|
*/
|
|
ListBase *tracksbase = BKE_tracking_get_active_tracks(tracking);
|
|
for (MovieTrackingTrack *track = tracksbase->first, *next_track; track != NULL;
|
|
track = next_track) {
|
|
next_track = track->next;
|
|
if (TRACK_VIEW_SELECTED(sc, track)) {
|
|
clip_delete_track(C, clip, track);
|
|
changed = true;
|
|
}
|
|
}
|
|
if (changed) {
|
|
WM_event_add_notifier(C, NC_MOVIECLIP | NA_EDITED, clip);
|
|
}
|
|
return OPERATOR_FINISHED;
|
|
}
|
|
|
|
void CLIP_OT_delete_track(wmOperatorType *ot)
|
|
{
|
|
/* identifiers */
|
|
ot->name = "Delete Track";
|
|
ot->idname = "CLIP_OT_delete_track";
|
|
ot->description = "Delete selected tracks";
|
|
|
|
/* api callbacks */
|
|
ot->invoke = WM_operator_confirm;
|
|
ot->exec = delete_track_exec;
|
|
ot->poll = ED_space_clip_tracking_poll;
|
|
|
|
/* flags */
|
|
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
|
|
}
|
|
|
|
/** \} */
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/** \name Delete Marker Operator
|
|
* \{ */
|
|
|
|
static int delete_marker_exec(bContext *C, wmOperator *UNUSED(op))
|
|
{
|
|
SpaceClip *sc = CTX_wm_space_clip(C);
|
|
MovieClip *clip = ED_space_clip_get_clip(sc);
|
|
MovieTracking *tracking = &clip->tracking;
|
|
const int framenr = ED_space_clip_get_clip_frame_number(sc);
|
|
bool has_selection = false;
|
|
bool changed = false;
|
|
|
|
ListBase *tracksbase = BKE_tracking_get_active_tracks(tracking);
|
|
for (MovieTrackingTrack *track = tracksbase->first, *next_track; track != NULL;
|
|
track = next_track) {
|
|
next_track = track->next;
|
|
if (TRACK_VIEW_SELECTED(sc, track)) {
|
|
MovieTrackingMarker *marker = BKE_tracking_marker_get_exact(track, framenr);
|
|
if (marker != NULL) {
|
|
has_selection |= track->markersnr > 1;
|
|
clip_delete_marker(C, clip, track, marker);
|
|
changed = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
ListBase *plane_tracks_base = BKE_tracking_get_active_plane_tracks(tracking);
|
|
for (MovieTrackingPlaneTrack *plane_track = plane_tracks_base->first, *plane_track_next;
|
|
plane_track != NULL;
|
|
plane_track = plane_track_next) {
|
|
plane_track_next = plane_track->next;
|
|
if (PLANE_TRACK_VIEW_SELECTED(plane_track)) {
|
|
MovieTrackingPlaneMarker *plane_marker = BKE_tracking_plane_marker_get_exact(plane_track,
|
|
framenr);
|
|
if (plane_marker != NULL) {
|
|
if (plane_track->markersnr == 1) {
|
|
BKE_tracking_plane_track_free(plane_track);
|
|
BLI_freelinkN(plane_tracks_base, plane_track);
|
|
}
|
|
else {
|
|
BKE_tracking_plane_marker_delete(plane_track, framenr);
|
|
}
|
|
changed = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!changed) {
|
|
return OPERATOR_CANCELLED;
|
|
}
|
|
|
|
return OPERATOR_FINISHED;
|
|
}
|
|
|
|
void CLIP_OT_delete_marker(wmOperatorType *ot)
|
|
{
|
|
/* identifiers */
|
|
ot->name = "Delete Marker";
|
|
ot->idname = "CLIP_OT_delete_marker";
|
|
ot->description = "Delete marker for current frame from selected tracks";
|
|
|
|
/* api callbacks */
|
|
ot->invoke = WM_operator_confirm;
|
|
ot->exec = delete_marker_exec;
|
|
ot->poll = ED_space_clip_tracking_poll;
|
|
|
|
/* flags */
|
|
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
|
|
}
|
|
|
|
/** \} */
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/** \name Slide Marker Operator
|
|
* \{ */
|
|
|
|
enum {
|
|
SLIDE_ACTION_POS = 0,
|
|
SLIDE_ACTION_SIZE,
|
|
SLIDE_ACTION_OFFSET,
|
|
SLIDE_ACTION_TILT_SIZE,
|
|
};
|
|
|
|
typedef struct {
|
|
short area, action;
|
|
MovieTrackingTrack *track;
|
|
MovieTrackingMarker *marker;
|
|
|
|
int mval[2];
|
|
int width, height;
|
|
float *min, *max, *pos, *offset, (*corners)[2];
|
|
float spos[2];
|
|
|
|
bool lock, accurate;
|
|
|
|
/* Data to restore on cancel. */
|
|
float old_search_min[2], old_search_max[2], old_pos[2], old_offset[2];
|
|
float old_corners[4][2];
|
|
float (*old_markers)[2];
|
|
} SlideMarkerData;
|
|
|
|
static void slide_marker_tilt_slider(const MovieTrackingMarker *marker, float r_slider[2])
|
|
{
|
|
add_v2_v2v2(r_slider, marker->pattern_corners[1], marker->pattern_corners[2]);
|
|
add_v2_v2(r_slider, marker->pos);
|
|
}
|
|
|
|
static SlideMarkerData *create_slide_marker_data(SpaceClip *sc,
|
|
MovieTrackingTrack *track,
|
|
MovieTrackingMarker *marker,
|
|
const wmEvent *event,
|
|
int area,
|
|
int corner,
|
|
int action,
|
|
int width,
|
|
int height)
|
|
{
|
|
SlideMarkerData *data = MEM_callocN(sizeof(SlideMarkerData), "slide marker data");
|
|
int framenr = ED_space_clip_get_clip_frame_number(sc);
|
|
|
|
marker = BKE_tracking_marker_ensure(track, framenr);
|
|
|
|
data->area = area;
|
|
data->action = action;
|
|
data->track = track;
|
|
data->marker = marker;
|
|
|
|
if (area == TRACK_AREA_POINT) {
|
|
data->pos = marker->pos;
|
|
data->offset = track->offset;
|
|
}
|
|
else if (area == TRACK_AREA_PAT) {
|
|
if (action == SLIDE_ACTION_SIZE) {
|
|
data->corners = marker->pattern_corners;
|
|
}
|
|
else if (action == SLIDE_ACTION_OFFSET) {
|
|
data->pos = marker->pos;
|
|
data->offset = track->offset;
|
|
data->old_markers = MEM_callocN(sizeof(*data->old_markers) * track->markersnr,
|
|
"slide marekrs");
|
|
for (int a = 0; a < track->markersnr; a++) {
|
|
copy_v2_v2(data->old_markers[a], track->markers[a].pos);
|
|
}
|
|
}
|
|
else if (action == SLIDE_ACTION_POS) {
|
|
data->corners = marker->pattern_corners;
|
|
data->pos = marker->pattern_corners[corner];
|
|
copy_v2_v2(data->spos, data->pos);
|
|
}
|
|
else if (action == SLIDE_ACTION_TILT_SIZE) {
|
|
data->corners = marker->pattern_corners;
|
|
slide_marker_tilt_slider(marker, data->spos);
|
|
}
|
|
}
|
|
else if (area == TRACK_AREA_SEARCH) {
|
|
data->min = marker->search_min;
|
|
data->max = marker->search_max;
|
|
}
|
|
|
|
data->mval[0] = event->mval[0];
|
|
data->mval[1] = event->mval[1];
|
|
|
|
data->width = width;
|
|
data->height = height;
|
|
|
|
if (action == SLIDE_ACTION_SIZE) {
|
|
data->lock = true;
|
|
}
|
|
|
|
/* Backup marker's settings. */
|
|
memcpy(data->old_corners, marker->pattern_corners, sizeof(data->old_corners));
|
|
copy_v2_v2(data->old_search_min, marker->search_min);
|
|
copy_v2_v2(data->old_search_max, marker->search_max);
|
|
copy_v2_v2(data->old_pos, marker->pos);
|
|
copy_v2_v2(data->old_offset, track->offset);
|
|
|
|
return data;
|
|
}
|
|
|
|
static float mouse_to_slide_zone_distance_squared(const float co[2],
|
|
const float slide_zone[2],
|
|
int width,
|
|
int height)
|
|
{
|
|
const float pixel_co[2] = {co[0] * width, co[1] * height},
|
|
pixel_slide_zone[2] = {slide_zone[0] * width, slide_zone[1] * height};
|
|
return square_f(pixel_co[0] - pixel_slide_zone[0]) + square_f(pixel_co[1] - pixel_slide_zone[1]);
|
|
}
|
|
|
|
static float mouse_to_search_corner_distance_squared(
|
|
const MovieTrackingMarker *marker, const float co[2], int corner, int width, int height)
|
|
{
|
|
float side_zone[2];
|
|
if (corner == 0) {
|
|
side_zone[0] = marker->pos[0] + marker->search_max[0];
|
|
side_zone[1] = marker->pos[1] + marker->search_min[1];
|
|
}
|
|
else {
|
|
side_zone[0] = marker->pos[0] + marker->search_min[0];
|
|
side_zone[1] = marker->pos[1] + marker->search_max[1];
|
|
}
|
|
return mouse_to_slide_zone_distance_squared(co, side_zone, width, height);
|
|
}
|
|
|
|
static float mouse_to_closest_pattern_corner_distance_squared(
|
|
const MovieTrackingMarker *marker, const float co[2], int width, int height, int *r_corner)
|
|
{
|
|
float min_distance_squared = FLT_MAX;
|
|
for (int i = 0; i < 4; i++) {
|
|
float corner_co[2];
|
|
add_v2_v2v2(corner_co, marker->pattern_corners[i], marker->pos);
|
|
float distance_squared = mouse_to_slide_zone_distance_squared(co, corner_co, width, height);
|
|
if (distance_squared < min_distance_squared) {
|
|
min_distance_squared = distance_squared;
|
|
*r_corner = i;
|
|
}
|
|
}
|
|
return min_distance_squared;
|
|
}
|
|
|
|
static float mouse_to_offset_distance_squared(const MovieTrackingTrack *track,
|
|
const MovieTrackingMarker *marker,
|
|
const float co[2],
|
|
int width,
|
|
int height)
|
|
{
|
|
float pos[2];
|
|
add_v2_v2v2(pos, marker->pos, track->offset);
|
|
return mouse_to_slide_zone_distance_squared(co, pos, width, height);
|
|
}
|
|
|
|
static int mouse_to_tilt_distance_squared(const MovieTrackingMarker *marker,
|
|
const float co[2],
|
|
int width,
|
|
int height)
|
|
{
|
|
float slider[2];
|
|
slide_marker_tilt_slider(marker, slider);
|
|
return mouse_to_slide_zone_distance_squared(co, slider, width, height);
|
|
}
|
|
|
|
static bool slide_check_corners(float (*corners)[2])
|
|
{
|
|
float cross = 0.0f;
|
|
const float p[2] = {0.0f, 0.0f};
|
|
|
|
if (!isect_point_quad_v2(p, corners[0], corners[1], corners[2], corners[3])) {
|
|
return false;
|
|
}
|
|
|
|
for (int i = 0; i < 4; i++) {
|
|
float v1[2], v2[2];
|
|
|
|
int next = (i + 1) % 4;
|
|
int prev = (4 + i - 1) % 4;
|
|
|
|
sub_v2_v2v2(v1, corners[i], corners[prev]);
|
|
sub_v2_v2v2(v2, corners[next], corners[i]);
|
|
|
|
float cur_cross = cross_v2v2(v1, v2);
|
|
|
|
if (fabsf(cur_cross) > FLT_EPSILON) {
|
|
if (cross == 0.0f) {
|
|
cross = cur_cross;
|
|
}
|
|
else if (cross * cur_cross < 0.0f) {
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
MovieTrackingTrack *tracking_marker_check_slide(
|
|
bContext *C, const wmEvent *event, int *r_area, int *r_action, int *r_corner)
|
|
{
|
|
const float distance_clip_squared = 12.0f * 12.0f;
|
|
SpaceClip *sc = CTX_wm_space_clip(C);
|
|
ARegion *region = CTX_wm_region(C);
|
|
|
|
MovieClip *clip = ED_space_clip_get_clip(sc);
|
|
MovieTrackingTrack *track;
|
|
int width, height;
|
|
float co[2];
|
|
ListBase *tracksbase = BKE_tracking_get_active_tracks(&clip->tracking);
|
|
int framenr = ED_space_clip_get_clip_frame_number(sc);
|
|
float global_min_distance_squared = FLT_MAX;
|
|
|
|
/* Sliding zone designator which is the closest to the mouse
|
|
* across all the tracks.
|
|
*/
|
|
int min_action = -1, min_area = 0, min_corner = -1;
|
|
MovieTrackingTrack *min_track = NULL;
|
|
|
|
ED_space_clip_get_size(sc, &width, &height);
|
|
|
|
if (width == 0 || height == 0) {
|
|
return NULL;
|
|
}
|
|
|
|
ED_clip_mouse_pos(sc, region, event->mval, co);
|
|
|
|
track = tracksbase->first;
|
|
while (track) {
|
|
if (TRACK_VIEW_SELECTED(sc, track) && (track->flag & TRACK_LOCKED) == 0) {
|
|
const MovieTrackingMarker *marker = BKE_tracking_marker_get(track, framenr);
|
|
/* Sliding zone designator which is the closest to the mouse for
|
|
* the current tracks.
|
|
*/
|
|
float min_distance_squared = FLT_MAX;
|
|
int action = -1, area = 0, corner = -1;
|
|
|
|
if ((marker->flag & MARKER_DISABLED) == 0) {
|
|
float distance_squared;
|
|
|
|
/* We start checking with whether the mouse is close enough
|
|
* to the pattern offset area.
|
|
*/
|
|
distance_squared = mouse_to_offset_distance_squared(track, marker, co, width, height);
|
|
area = TRACK_AREA_POINT;
|
|
action = SLIDE_ACTION_POS;
|
|
|
|
/* NOTE: All checks here are assuming there's no maximum distance
|
|
* limit, so checks are quite simple here.
|
|
* Actual distance clipping happens later once all the sliding
|
|
* zones are checked.
|
|
*/
|
|
min_distance_squared = distance_squared;
|
|
|
|
/* If search area is visible, check how close to its sliding
|
|
* zones mouse is.
|
|
*/
|
|
if (sc->flag & SC_SHOW_MARKER_SEARCH) {
|
|
distance_squared = mouse_to_search_corner_distance_squared(marker, co, 1, width, height);
|
|
if (distance_squared < min_distance_squared) {
|
|
area = TRACK_AREA_SEARCH;
|
|
action = SLIDE_ACTION_OFFSET;
|
|
min_distance_squared = distance_squared;
|
|
}
|
|
|
|
distance_squared = mouse_to_search_corner_distance_squared(marker, co, 0, width, height);
|
|
if (distance_squared < min_distance_squared) {
|
|
area = TRACK_AREA_SEARCH;
|
|
action = SLIDE_ACTION_SIZE;
|
|
min_distance_squared = distance_squared;
|
|
}
|
|
}
|
|
|
|
/* If pattern area is visible, check which corner is closest to
|
|
* the mouse.
|
|
*/
|
|
if (sc->flag & SC_SHOW_MARKER_PATTERN) {
|
|
int current_corner = -1;
|
|
distance_squared = mouse_to_closest_pattern_corner_distance_squared(
|
|
marker, co, width, height, ¤t_corner);
|
|
if (distance_squared < min_distance_squared) {
|
|
area = TRACK_AREA_PAT;
|
|
action = SLIDE_ACTION_POS;
|
|
corner = current_corner;
|
|
min_distance_squared = distance_squared;
|
|
}
|
|
|
|
/* Here we also check whether the mouse is actually closer to
|
|
* the widget which controls scale and tilt.
|
|
*/
|
|
distance_squared = mouse_to_tilt_distance_squared(marker, co, width, height);
|
|
if (distance_squared < min_distance_squared) {
|
|
area = TRACK_AREA_PAT;
|
|
action = SLIDE_ACTION_TILT_SIZE;
|
|
min_distance_squared = distance_squared;
|
|
}
|
|
}
|
|
|
|
if (min_distance_squared < global_min_distance_squared) {
|
|
min_area = area;
|
|
min_action = action;
|
|
min_corner = corner;
|
|
min_track = track;
|
|
global_min_distance_squared = min_distance_squared;
|
|
}
|
|
}
|
|
}
|
|
|
|
track = track->next;
|
|
}
|
|
|
|
if (global_min_distance_squared < distance_clip_squared / sc->zoom) {
|
|
if (r_area) {
|
|
*r_area = min_area;
|
|
}
|
|
if (r_action) {
|
|
*r_action = min_action;
|
|
}
|
|
if (r_corner) {
|
|
*r_corner = min_corner;
|
|
}
|
|
return min_track;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
static void *slide_marker_customdata(bContext *C, const wmEvent *event)
|
|
{
|
|
SpaceClip *sc = CTX_wm_space_clip(C);
|
|
ARegion *region = CTX_wm_region(C);
|
|
|
|
MovieTrackingTrack *track;
|
|
int width, height;
|
|
float co[2];
|
|
void *customdata = NULL;
|
|
int framenr = ED_space_clip_get_clip_frame_number(sc);
|
|
int area, action, corner;
|
|
|
|
ED_space_clip_get_size(sc, &width, &height);
|
|
|
|
if (width == 0 || height == 0) {
|
|
return NULL;
|
|
}
|
|
|
|
ED_clip_mouse_pos(sc, region, event->mval, co);
|
|
|
|
track = tracking_marker_check_slide(C, event, &area, &action, &corner);
|
|
if (track != NULL) {
|
|
MovieTrackingMarker *marker = BKE_tracking_marker_get(track, framenr);
|
|
customdata = create_slide_marker_data(
|
|
sc, track, marker, event, area, corner, action, width, height);
|
|
}
|
|
|
|
return customdata;
|
|
}
|
|
|
|
static int slide_marker_invoke(bContext *C, wmOperator *op, const wmEvent *event)
|
|
{
|
|
SlideMarkerData *slidedata = slide_marker_customdata(C, event);
|
|
if (slidedata != NULL) {
|
|
SpaceClip *sc = CTX_wm_space_clip(C);
|
|
MovieClip *clip = ED_space_clip_get_clip(sc);
|
|
MovieTracking *tracking = &clip->tracking;
|
|
|
|
tracking->act_track = slidedata->track;
|
|
tracking->act_plane_track = NULL;
|
|
|
|
op->customdata = slidedata;
|
|
|
|
clip_tracking_hide_cursor(C);
|
|
WM_event_add_modal_handler(C, op);
|
|
|
|
WM_event_add_notifier(C, NC_GEOM | ND_SELECT, NULL);
|
|
|
|
return OPERATOR_RUNNING_MODAL;
|
|
}
|
|
|
|
return OPERATOR_PASS_THROUGH;
|
|
}
|
|
|
|
static void cancel_mouse_slide(SlideMarkerData *data)
|
|
{
|
|
MovieTrackingTrack *track = data->track;
|
|
MovieTrackingMarker *marker = data->marker;
|
|
|
|
memcpy(marker->pattern_corners, data->old_corners, sizeof(marker->pattern_corners));
|
|
copy_v2_v2(marker->search_min, data->old_search_min);
|
|
copy_v2_v2(marker->search_max, data->old_search_max);
|
|
copy_v2_v2(marker->pos, data->old_pos);
|
|
copy_v2_v2(track->offset, data->old_offset);
|
|
|
|
if (data->old_markers != NULL) {
|
|
for (int a = 0; a < data->track->markersnr; a++) {
|
|
copy_v2_v2(data->track->markers[a].pos, data->old_markers[a]);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void apply_mouse_slide(bContext *C, SlideMarkerData *data)
|
|
{
|
|
if (data->area == TRACK_AREA_POINT) {
|
|
SpaceClip *sc = CTX_wm_space_clip(C);
|
|
MovieClip *clip = ED_space_clip_get_clip(sc);
|
|
MovieTracking *tracking = &clip->tracking;
|
|
ListBase *plane_tracks_base = BKE_tracking_get_active_plane_tracks(tracking);
|
|
int framenr = ED_space_clip_get_clip_frame_number(sc);
|
|
|
|
for (MovieTrackingPlaneTrack *plane_track = plane_tracks_base->first; plane_track != NULL;
|
|
plane_track = plane_track->next) {
|
|
if ((plane_track->flag & PLANE_TRACK_AUTOKEY) == 0) {
|
|
if (BKE_tracking_plane_track_has_point_track(plane_track, data->track)) {
|
|
BKE_tracking_track_plane_from_existing_motion(plane_track, framenr);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static void free_slide_data(SlideMarkerData *data)
|
|
{
|
|
if (data->old_markers != NULL) {
|
|
MEM_freeN(data->old_markers);
|
|
}
|
|
MEM_freeN(data);
|
|
}
|
|
|
|
static int slide_marker_modal(bContext *C, wmOperator *op, const wmEvent *event)
|
|
{
|
|
SpaceClip *sc = CTX_wm_space_clip(C);
|
|
ARegion *region = CTX_wm_region(C);
|
|
|
|
SlideMarkerData *data = (SlideMarkerData *)op->customdata;
|
|
float dx, dy, mdelta[2];
|
|
|
|
switch (event->type) {
|
|
case EVT_LEFTCTRLKEY:
|
|
case EVT_RIGHTCTRLKEY:
|
|
case EVT_LEFTSHIFTKEY:
|
|
case EVT_RIGHTSHIFTKEY:
|
|
if (data->action == SLIDE_ACTION_SIZE) {
|
|
if (ELEM(event->type, EVT_LEFTCTRLKEY, EVT_RIGHTCTRLKEY)) {
|
|
data->lock = event->val == KM_RELEASE;
|
|
}
|
|
}
|
|
|
|
if (ELEM(event->type, EVT_LEFTSHIFTKEY, EVT_RIGHTSHIFTKEY)) {
|
|
data->accurate = event->val == KM_PRESS;
|
|
}
|
|
ATTR_FALLTHROUGH;
|
|
case MOUSEMOVE:
|
|
mdelta[0] = event->mval[0] - data->mval[0];
|
|
mdelta[1] = event->mval[1] - data->mval[1];
|
|
|
|
dx = mdelta[0] / data->width / sc->zoom;
|
|
|
|
if (data->lock) {
|
|
dy = -dx / data->height * data->width;
|
|
}
|
|
else {
|
|
dy = mdelta[1] / data->height / sc->zoom;
|
|
}
|
|
|
|
if (data->accurate) {
|
|
dx /= 5.0f;
|
|
dy /= 5.0f;
|
|
}
|
|
|
|
if (data->area == TRACK_AREA_POINT) {
|
|
if (data->action == SLIDE_ACTION_OFFSET) {
|
|
data->offset[0] = data->old_offset[0] + dx;
|
|
data->offset[1] = data->old_offset[1] + dy;
|
|
}
|
|
else {
|
|
data->pos[0] = data->old_pos[0] + dx;
|
|
data->pos[1] = data->old_pos[1] + dy;
|
|
}
|
|
|
|
WM_event_add_notifier(C, NC_OBJECT | ND_TRANSFORM, NULL);
|
|
DEG_id_tag_update(&sc->clip->id, 0);
|
|
}
|
|
else if (data->area == TRACK_AREA_PAT) {
|
|
if (data->action == SLIDE_ACTION_SIZE) {
|
|
float start[2], end[2];
|
|
float scale;
|
|
|
|
ED_clip_point_stable_pos(sc, region, data->mval[0], data->mval[1], &start[0], &start[1]);
|
|
|
|
sub_v2_v2(start, data->old_pos);
|
|
|
|
if (len_squared_v2(start) != 0.0f) {
|
|
float mval[2];
|
|
|
|
if (data->accurate) {
|
|
mval[0] = data->mval[0] + (event->mval[0] - data->mval[0]) / 5.0f;
|
|
mval[1] = data->mval[1] + (event->mval[1] - data->mval[1]) / 5.0f;
|
|
}
|
|
else {
|
|
mval[0] = event->mval[0];
|
|
mval[1] = event->mval[1];
|
|
}
|
|
|
|
ED_clip_point_stable_pos(sc, region, mval[0], mval[1], &end[0], &end[1]);
|
|
|
|
sub_v2_v2(end, data->old_pos);
|
|
scale = len_v2(end) / len_v2(start);
|
|
|
|
if (scale > 0.0f) {
|
|
for (int a = 0; a < 4; a++) {
|
|
mul_v2_v2fl(data->corners[a], data->old_corners[a], scale);
|
|
}
|
|
}
|
|
}
|
|
|
|
BKE_tracking_marker_clamp(data->marker, CLAMP_PAT_DIM);
|
|
}
|
|
else if (data->action == SLIDE_ACTION_OFFSET) {
|
|
const float d[2] = {dx, dy};
|
|
for (int a = 0; a < data->track->markersnr; a++) {
|
|
add_v2_v2v2(data->track->markers[a].pos, data->old_markers[a], d);
|
|
}
|
|
sub_v2_v2v2(data->offset, data->old_offset, d);
|
|
}
|
|
else if (data->action == SLIDE_ACTION_POS) {
|
|
float spos[2];
|
|
|
|
copy_v2_v2(spos, data->pos);
|
|
|
|
data->pos[0] = data->spos[0] + dx;
|
|
data->pos[1] = data->spos[1] + dy;
|
|
|
|
if (!slide_check_corners(data->corners)) {
|
|
copy_v2_v2(data->pos, spos);
|
|
}
|
|
|
|
/* Currently only patterns are allowed to have such
|
|
* combination of event and data.
|
|
*/
|
|
BKE_tracking_marker_clamp(data->marker, CLAMP_PAT_DIM);
|
|
}
|
|
else if (data->action == SLIDE_ACTION_TILT_SIZE) {
|
|
float start[2], end[2];
|
|
float scale = 1.0f, angle = 0.0f;
|
|
float mval[2];
|
|
|
|
if (data->accurate) {
|
|
mval[0] = data->mval[0] + (event->mval[0] - data->mval[0]) / 5.0f;
|
|
mval[1] = data->mval[1] + (event->mval[1] - data->mval[1]) / 5.0f;
|
|
}
|
|
else {
|
|
mval[0] = event->mval[0];
|
|
mval[1] = event->mval[1];
|
|
}
|
|
|
|
sub_v2_v2v2(start, data->spos, data->old_pos);
|
|
|
|
ED_clip_point_stable_pos(sc, region, mval[0], mval[1], &end[0], &end[1]);
|
|
sub_v2_v2(end, data->old_pos);
|
|
|
|
if (len_squared_v2(start) != 0.0f) {
|
|
scale = len_v2(end) / len_v2(start);
|
|
|
|
if (scale < 0.0f) {
|
|
scale = 0.0;
|
|
}
|
|
}
|
|
|
|
angle = -angle_signed_v2v2(start, end);
|
|
|
|
for (int a = 0; a < 4; a++) {
|
|
float vec[2];
|
|
|
|
mul_v2_v2fl(data->corners[a], data->old_corners[a], scale);
|
|
|
|
copy_v2_v2(vec, data->corners[a]);
|
|
vec[0] *= data->width;
|
|
vec[1] *= data->height;
|
|
|
|
data->corners[a][0] = (vec[0] * cosf(angle) - vec[1] * sinf(angle)) / data->width;
|
|
data->corners[a][1] = (vec[1] * cosf(angle) + vec[0] * sinf(angle)) / data->height;
|
|
}
|
|
|
|
BKE_tracking_marker_clamp(data->marker, CLAMP_PAT_DIM);
|
|
}
|
|
}
|
|
else if (data->area == TRACK_AREA_SEARCH) {
|
|
if (data->action == SLIDE_ACTION_SIZE) {
|
|
data->min[0] = data->old_search_min[0] - dx;
|
|
data->max[0] = data->old_search_max[0] + dx;
|
|
|
|
data->min[1] = data->old_search_min[1] + dy;
|
|
data->max[1] = data->old_search_max[1] - dy;
|
|
|
|
BKE_tracking_marker_clamp(data->marker, CLAMP_SEARCH_DIM);
|
|
}
|
|
else if (data->area == TRACK_AREA_SEARCH) {
|
|
const float d[2] = {dx, dy};
|
|
add_v2_v2v2(data->min, data->old_search_min, d);
|
|
add_v2_v2v2(data->max, data->old_search_max, d);
|
|
}
|
|
|
|
BKE_tracking_marker_clamp(data->marker, CLAMP_SEARCH_POS);
|
|
}
|
|
|
|
data->marker->flag &= ~MARKER_TRACKED;
|
|
|
|
WM_event_add_notifier(C, NC_MOVIECLIP | NA_EDITED, NULL);
|
|
|
|
break;
|
|
|
|
case LEFTMOUSE:
|
|
if (event->val == KM_RELEASE) {
|
|
apply_mouse_slide(C, op->customdata);
|
|
free_slide_data(op->customdata);
|
|
|
|
clip_tracking_show_cursor(C);
|
|
|
|
return OPERATOR_FINISHED;
|
|
}
|
|
|
|
break;
|
|
|
|
case EVT_ESCKEY:
|
|
cancel_mouse_slide(op->customdata);
|
|
|
|
free_slide_data(op->customdata);
|
|
|
|
clip_tracking_show_cursor(C);
|
|
|
|
WM_event_add_notifier(C, NC_MOVIECLIP | NA_EDITED, NULL);
|
|
|
|
return OPERATOR_CANCELLED;
|
|
}
|
|
|
|
return OPERATOR_RUNNING_MODAL;
|
|
}
|
|
|
|
void CLIP_OT_slide_marker(wmOperatorType *ot)
|
|
{
|
|
/* identifiers */
|
|
ot->name = "Slide Marker";
|
|
ot->description = "Slide marker areas";
|
|
ot->idname = "CLIP_OT_slide_marker";
|
|
|
|
/* api callbacks */
|
|
ot->poll = ED_space_clip_tracking_poll;
|
|
ot->invoke = slide_marker_invoke;
|
|
ot->modal = slide_marker_modal;
|
|
|
|
/* flags */
|
|
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_GRAB_CURSOR_XY | OPTYPE_BLOCKING;
|
|
|
|
/* properties */
|
|
RNA_def_float_vector(ot->srna,
|
|
"offset",
|
|
2,
|
|
NULL,
|
|
-FLT_MAX,
|
|
FLT_MAX,
|
|
"Offset",
|
|
"Offset in floating-point units, 1.0 is the width and height of the image",
|
|
-FLT_MAX,
|
|
FLT_MAX);
|
|
}
|
|
|
|
/** \} */
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/** \name Clear Track Operator
|
|
* \{ */
|
|
|
|
static int clear_track_path_exec(bContext *C, wmOperator *op)
|
|
{
|
|
SpaceClip *sc = CTX_wm_space_clip(C);
|
|
MovieClip *clip = ED_space_clip_get_clip(sc);
|
|
MovieTracking *tracking = &clip->tracking;
|
|
ListBase *tracksbase = BKE_tracking_get_active_tracks(tracking);
|
|
int action = RNA_enum_get(op->ptr, "action");
|
|
const bool clear_active = RNA_boolean_get(op->ptr, "clear_active");
|
|
int framenr = ED_space_clip_get_clip_frame_number(sc);
|
|
|
|
if (clear_active) {
|
|
MovieTrackingTrack *track = BKE_tracking_track_get_active(tracking);
|
|
if (track != NULL) {
|
|
BKE_tracking_track_path_clear(track, framenr, action);
|
|
}
|
|
}
|
|
else {
|
|
for (MovieTrackingTrack *track = tracksbase->first; track != NULL; track = track->next) {
|
|
if (TRACK_VIEW_SELECTED(sc, track)) {
|
|
BKE_tracking_track_path_clear(track, framenr, action);
|
|
}
|
|
}
|
|
}
|
|
|
|
BKE_tracking_dopesheet_tag_update(tracking);
|
|
WM_event_add_notifier(C, NC_MOVIECLIP | NA_EVALUATED, clip);
|
|
|
|
return OPERATOR_FINISHED;
|
|
}
|
|
|
|
void CLIP_OT_clear_track_path(wmOperatorType *ot)
|
|
{
|
|
static const EnumPropertyItem clear_path_actions[] = {
|
|
{TRACK_CLEAR_UPTO, "UPTO", 0, "Clear Up To", "Clear path up to current frame"},
|
|
{TRACK_CLEAR_REMAINED,
|
|
"REMAINED",
|
|
0,
|
|
"Clear Remained",
|
|
"Clear path at remaining frames (after current)"},
|
|
{TRACK_CLEAR_ALL, "ALL", 0, "Clear All", "Clear the whole path"},
|
|
{0, NULL, 0, NULL, NULL},
|
|
};
|
|
|
|
/* identifiers */
|
|
ot->name = "Clear Track Path";
|
|
ot->description = "Clear tracks after/before current position or clear the whole track";
|
|
ot->idname = "CLIP_OT_clear_track_path";
|
|
|
|
/* api callbacks */
|
|
ot->exec = clear_track_path_exec;
|
|
ot->poll = ED_space_clip_tracking_poll;
|
|
|
|
/* flags */
|
|
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
|
|
|
|
/* properties */
|
|
RNA_def_enum(ot->srna,
|
|
"action",
|
|
clear_path_actions,
|
|
TRACK_CLEAR_REMAINED,
|
|
"Action",
|
|
"Clear action to execute");
|
|
RNA_def_boolean(ot->srna,
|
|
"clear_active",
|
|
0,
|
|
"Clear Active",
|
|
"Clear active track only instead of all selected tracks");
|
|
}
|
|
|
|
/** \} */
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/** \name Disable Markers Operator
|
|
* \{ */
|
|
|
|
enum {
|
|
MARKER_OP_DISABLE = 0,
|
|
MARKER_OP_ENABLE = 1,
|
|
MARKER_OP_TOGGLE = 2,
|
|
};
|
|
|
|
static int disable_markers_exec(bContext *C, wmOperator *op)
|
|
{
|
|
SpaceClip *sc = CTX_wm_space_clip(C);
|
|
MovieClip *clip = ED_space_clip_get_clip(sc);
|
|
MovieTracking *tracking = &clip->tracking;
|
|
ListBase *tracksbase = BKE_tracking_get_active_tracks(tracking);
|
|
int action = RNA_enum_get(op->ptr, "action");
|
|
int framenr = ED_space_clip_get_clip_frame_number(sc);
|
|
|
|
for (MovieTrackingTrack *track = tracksbase->first; track != NULL; track = track->next) {
|
|
if (TRACK_VIEW_SELECTED(sc, track) && (track->flag & TRACK_LOCKED) == 0) {
|
|
MovieTrackingMarker *marker = BKE_tracking_marker_ensure(track, framenr);
|
|
switch (action) {
|
|
case MARKER_OP_DISABLE:
|
|
marker->flag |= MARKER_DISABLED;
|
|
break;
|
|
case MARKER_OP_ENABLE:
|
|
marker->flag &= ~MARKER_DISABLED;
|
|
break;
|
|
case MARKER_OP_TOGGLE:
|
|
marker->flag ^= MARKER_DISABLED;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
DEG_id_tag_update(&clip->id, 0);
|
|
|
|
WM_event_add_notifier(C, NC_MOVIECLIP | NA_EVALUATED, clip);
|
|
|
|
return OPERATOR_FINISHED;
|
|
}
|
|
|
|
void CLIP_OT_disable_markers(wmOperatorType *ot)
|
|
{
|
|
static const EnumPropertyItem actions_items[] = {
|
|
{MARKER_OP_DISABLE, "DISABLE", 0, "Disable", "Disable selected markers"},
|
|
{MARKER_OP_ENABLE, "ENABLE", 0, "Enable", "Enable selected markers"},
|
|
{MARKER_OP_TOGGLE, "TOGGLE", 0, "Toggle", "Toggle disabled flag for selected markers"},
|
|
{0, NULL, 0, NULL, NULL},
|
|
};
|
|
|
|
/* identifiers */
|
|
ot->name = "Disable Markers";
|
|
ot->description = "Disable/enable selected markers";
|
|
ot->idname = "CLIP_OT_disable_markers";
|
|
|
|
/* api callbacks */
|
|
ot->exec = disable_markers_exec;
|
|
ot->poll = ED_space_clip_tracking_poll;
|
|
|
|
/* flags */
|
|
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
|
|
|
|
/* properties */
|
|
RNA_def_enum(ot->srna, "action", actions_items, 0, "Action", "Disable action to execute");
|
|
}
|
|
|
|
/** \} */
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/** \name Set Principal Center Operator
|
|
* \{ */
|
|
|
|
static int set_center_principal_exec(bContext *C, wmOperator *UNUSED(op))
|
|
{
|
|
SpaceClip *sc = CTX_wm_space_clip(C);
|
|
MovieClip *clip = ED_space_clip_get_clip(sc);
|
|
int width, height;
|
|
|
|
BKE_movieclip_get_size(clip, &sc->user, &width, &height);
|
|
|
|
if (width == 0 || height == 0) {
|
|
return OPERATOR_CANCELLED;
|
|
}
|
|
|
|
clip->tracking.camera.principal[0] = ((float)width) / 2.0f;
|
|
clip->tracking.camera.principal[1] = ((float)height) / 2.0f;
|
|
|
|
WM_event_add_notifier(C, NC_MOVIECLIP | NA_EDITED, clip);
|
|
|
|
return OPERATOR_FINISHED;
|
|
}
|
|
|
|
void CLIP_OT_set_center_principal(wmOperatorType *ot)
|
|
{
|
|
/* identifiers */
|
|
ot->name = "Set Principal to Center";
|
|
ot->description = "Set optical center to center of footage";
|
|
ot->idname = "CLIP_OT_set_center_principal";
|
|
|
|
/* api callbacks */
|
|
ot->exec = set_center_principal_exec;
|
|
ot->poll = ED_space_clip_tracking_poll;
|
|
|
|
/* flags */
|
|
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
|
|
}
|
|
|
|
/** \} */
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/** \name Hide Tracks Operator
|
|
* \{ */
|
|
|
|
static int hide_tracks_exec(bContext *C, wmOperator *op)
|
|
{
|
|
SpaceClip *sc = CTX_wm_space_clip(C);
|
|
MovieClip *clip = ED_space_clip_get_clip(sc);
|
|
MovieTracking *tracking = &clip->tracking;
|
|
int unselected;
|
|
|
|
unselected = RNA_boolean_get(op->ptr, "unselected");
|
|
|
|
/* Hide point tracks. */
|
|
ListBase *tracksbase = BKE_tracking_get_active_tracks(tracking);
|
|
MovieTrackingTrack *act_track = BKE_tracking_track_get_active(tracking);
|
|
for (MovieTrackingTrack *track = tracksbase->first; track != NULL; track = track->next) {
|
|
if (unselected == 0 && TRACK_VIEW_SELECTED(sc, track)) {
|
|
track->flag |= TRACK_HIDDEN;
|
|
}
|
|
else if (unselected == 1 && !TRACK_VIEW_SELECTED(sc, track)) {
|
|
track->flag |= TRACK_HIDDEN;
|
|
}
|
|
}
|
|
|
|
if (act_track != NULL && act_track->flag & TRACK_HIDDEN) {
|
|
clip->tracking.act_track = NULL;
|
|
}
|
|
|
|
/* Hide place tracks. */
|
|
ListBase *plane_tracks_base = BKE_tracking_get_active_plane_tracks(tracking);
|
|
MovieTrackingPlaneTrack *act_plane_track = BKE_tracking_plane_track_get_active(tracking);
|
|
for (MovieTrackingPlaneTrack *plane_track = plane_tracks_base->first; plane_track != NULL;
|
|
plane_track = plane_track->next) {
|
|
if (unselected == 0 && plane_track->flag & SELECT) {
|
|
plane_track->flag |= PLANE_TRACK_HIDDEN;
|
|
}
|
|
else if (unselected == 1 && (plane_track->flag & SELECT) == 0) {
|
|
plane_track->flag |= PLANE_TRACK_HIDDEN;
|
|
}
|
|
}
|
|
if (act_plane_track != NULL && act_plane_track->flag & TRACK_HIDDEN) {
|
|
clip->tracking.act_plane_track = NULL;
|
|
}
|
|
|
|
BKE_tracking_dopesheet_tag_update(tracking);
|
|
WM_event_add_notifier(C, NC_MOVIECLIP | ND_DISPLAY, NULL);
|
|
|
|
return OPERATOR_FINISHED;
|
|
}
|
|
|
|
void CLIP_OT_hide_tracks(wmOperatorType *ot)
|
|
{
|
|
/* identifiers */
|
|
ot->name = "Hide Tracks";
|
|
ot->description = "Hide selected tracks";
|
|
ot->idname = "CLIP_OT_hide_tracks";
|
|
|
|
/* api callbacks */
|
|
ot->exec = hide_tracks_exec;
|
|
ot->poll = ED_space_clip_tracking_poll;
|
|
|
|
/* flags */
|
|
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
|
|
|
|
/* properties */
|
|
RNA_def_boolean(ot->srna, "unselected", 0, "Unselected", "Hide unselected tracks");
|
|
}
|
|
|
|
/** \} */
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/** \name Hide Tracks Clear Operator
|
|
* \{ */
|
|
|
|
static int hide_tracks_clear_exec(bContext *C, wmOperator *UNUSED(op))
|
|
{
|
|
SpaceClip *sc = CTX_wm_space_clip(C);
|
|
MovieClip *clip = ED_space_clip_get_clip(sc);
|
|
MovieTracking *tracking = &clip->tracking;
|
|
|
|
/* Unhide point tracks. */
|
|
ListBase *tracksbase = BKE_tracking_get_active_tracks(tracking);
|
|
for (MovieTrackingTrack *track = tracksbase->first; track != NULL; track = track->next) {
|
|
track->flag &= ~TRACK_HIDDEN;
|
|
}
|
|
|
|
/* Unhide plane tracks. */
|
|
ListBase *plane_tracks_base = BKE_tracking_get_active_plane_tracks(tracking);
|
|
for (MovieTrackingPlaneTrack *plane_track = plane_tracks_base->first; plane_track != NULL;
|
|
plane_track = plane_track->next) {
|
|
plane_track->flag &= ~PLANE_TRACK_HIDDEN;
|
|
}
|
|
|
|
BKE_tracking_dopesheet_tag_update(tracking);
|
|
|
|
WM_event_add_notifier(C, NC_MOVIECLIP | ND_DISPLAY, NULL);
|
|
|
|
return OPERATOR_FINISHED;
|
|
}
|
|
|
|
void CLIP_OT_hide_tracks_clear(wmOperatorType *ot)
|
|
{
|
|
/* identifiers */
|
|
ot->name = "Hide Tracks Clear";
|
|
ot->description = "Clear hide selected tracks";
|
|
ot->idname = "CLIP_OT_hide_tracks_clear";
|
|
|
|
/* api callbacks */
|
|
ot->exec = hide_tracks_clear_exec;
|
|
ot->poll = ED_space_clip_tracking_poll;
|
|
|
|
/* flags */
|
|
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
|
|
}
|
|
|
|
/** \} */
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/** \name Frame Jump Operator
|
|
* \{ */
|
|
|
|
static bool frame_jump_poll(bContext *C)
|
|
{
|
|
SpaceClip *space_clip = CTX_wm_space_clip(C);
|
|
return space_clip != NULL;
|
|
}
|
|
|
|
static int frame_jump_exec(bContext *C, wmOperator *op)
|
|
{
|
|
Scene *scene = CTX_data_scene(C);
|
|
SpaceClip *sc = CTX_wm_space_clip(C);
|
|
MovieClip *clip = ED_space_clip_get_clip(sc);
|
|
MovieTracking *tracking = &clip->tracking;
|
|
int pos = RNA_enum_get(op->ptr, "position");
|
|
int delta;
|
|
|
|
if (pos <= 1) { /* jump to path */
|
|
MovieTrackingTrack *track = BKE_tracking_track_get_active(tracking);
|
|
if (track == NULL) {
|
|
return OPERATOR_CANCELLED;
|
|
}
|
|
|
|
delta = pos == 1 ? 1 : -1;
|
|
while (sc->user.framenr + delta >= SFRA && sc->user.framenr + delta <= EFRA) {
|
|
int framenr = BKE_movieclip_remap_scene_to_clip_frame(clip, sc->user.framenr + delta);
|
|
MovieTrackingMarker *marker = BKE_tracking_marker_get_exact(track, framenr);
|
|
|
|
if (marker == NULL || marker->flag & MARKER_DISABLED) {
|
|
break;
|
|
}
|
|
|
|
sc->user.framenr += delta;
|
|
}
|
|
}
|
|
else { /* to failed frame */
|
|
if (tracking->reconstruction.flag & TRACKING_RECONSTRUCTED) {
|
|
int framenr = ED_space_clip_get_clip_frame_number(sc);
|
|
MovieTrackingObject *object = BKE_tracking_object_get_active(tracking);
|
|
|
|
delta = pos == 3 ? 1 : -1;
|
|
framenr += delta;
|
|
|
|
while (framenr + delta >= SFRA && framenr + delta <= EFRA) {
|
|
MovieReconstructedCamera *cam = BKE_tracking_camera_get_reconstructed(
|
|
tracking, object, framenr);
|
|
|
|
if (cam == NULL) {
|
|
sc->user.framenr = BKE_movieclip_remap_clip_to_scene_frame(clip, framenr);
|
|
break;
|
|
}
|
|
|
|
framenr += delta;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (CFRA != sc->user.framenr) {
|
|
CFRA = sc->user.framenr;
|
|
DEG_id_tag_update(&scene->id, ID_RECALC_AUDIO_SEEK);
|
|
|
|
WM_event_add_notifier(C, NC_SCENE | ND_FRAME, scene);
|
|
}
|
|
|
|
WM_event_add_notifier(C, NC_MOVIECLIP | ND_DISPLAY, NULL);
|
|
|
|
return OPERATOR_FINISHED;
|
|
}
|
|
|
|
void CLIP_OT_frame_jump(wmOperatorType *ot)
|
|
{
|
|
static const EnumPropertyItem position_items[] = {
|
|
{0, "PATHSTART", 0, "Path Start", "Jump to start of current path"},
|
|
{1, "PATHEND", 0, "Path End", "Jump to end of current path"},
|
|
{2, "FAILEDPREV", 0, "Previous Failed", "Jump to previous failed frame"},
|
|
{2, "FAILNEXT", 0, "Next Failed", "Jump to next failed frame"},
|
|
{0, NULL, 0, NULL, NULL},
|
|
};
|
|
|
|
/* identifiers */
|
|
ot->name = "Jump to Frame";
|
|
ot->description = "Jump to special frame";
|
|
ot->idname = "CLIP_OT_frame_jump";
|
|
|
|
/* api callbacks */
|
|
ot->exec = frame_jump_exec;
|
|
ot->poll = frame_jump_poll;
|
|
|
|
/* flags */
|
|
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
|
|
|
|
/* properties */
|
|
RNA_def_enum(ot->srna, "position", position_items, 0, "Position", "Position to jump to");
|
|
}
|
|
|
|
/** \} */
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/** \name Join Tracks Operator
|
|
* \{ */
|
|
|
|
static int join_tracks_exec(bContext *C, wmOperator *op)
|
|
{
|
|
SpaceClip *sc = CTX_wm_space_clip(C);
|
|
MovieClip *clip = ED_space_clip_get_clip(sc);
|
|
MovieTracking *tracking = &clip->tracking;
|
|
MovieTrackingStabilization *stab = &tracking->stabilization;
|
|
ListBase *tracksbase = BKE_tracking_get_active_tracks(tracking);
|
|
ListBase *plane_tracks_base = BKE_tracking_get_active_plane_tracks(tracking);
|
|
bool update_stabilization = false;
|
|
|
|
MovieTrackingTrack *act_track = BKE_tracking_track_get_active(tracking);
|
|
if (act_track == NULL) {
|
|
BKE_report(op->reports, RPT_ERROR, "No active track to join to");
|
|
return OPERATOR_CANCELLED;
|
|
}
|
|
|
|
GSet *point_tracks = BLI_gset_ptr_new(__func__);
|
|
|
|
for (MovieTrackingTrack *track = tracksbase->first, *next_track; track != NULL;
|
|
track = next_track) {
|
|
next_track = track->next;
|
|
if (TRACK_VIEW_SELECTED(sc, track) && track != act_track) {
|
|
BKE_tracking_tracks_join(tracking, act_track, track);
|
|
|
|
if (track->flag & TRACK_USE_2D_STAB) {
|
|
update_stabilization = true;
|
|
if ((act_track->flag & TRACK_USE_2D_STAB) == 0) {
|
|
act_track->flag |= TRACK_USE_2D_STAB;
|
|
}
|
|
else {
|
|
stab->tot_track--;
|
|
}
|
|
BLI_assert(0 <= stab->tot_track);
|
|
}
|
|
if (track->flag & TRACK_USE_2D_STAB_ROT) {
|
|
update_stabilization = true;
|
|
if ((act_track->flag & TRACK_USE_2D_STAB_ROT) == 0) {
|
|
act_track->flag |= TRACK_USE_2D_STAB_ROT;
|
|
}
|
|
else {
|
|
stab->tot_rot_track--;
|
|
}
|
|
BLI_assert(0 <= stab->tot_rot_track);
|
|
}
|
|
|
|
for (MovieTrackingPlaneTrack *plane_track = plane_tracks_base->first; plane_track != NULL;
|
|
plane_track = plane_track->next) {
|
|
if (BKE_tracking_plane_track_has_point_track(plane_track, track)) {
|
|
BKE_tracking_plane_track_replace_point_track(plane_track, track, act_track);
|
|
if ((plane_track->flag & PLANE_TRACK_AUTOKEY) == 0) {
|
|
BLI_gset_insert(point_tracks, plane_track);
|
|
}
|
|
}
|
|
}
|
|
|
|
BKE_tracking_track_free(track);
|
|
BLI_freelinkN(tracksbase, track);
|
|
}
|
|
}
|
|
|
|
if (update_stabilization) {
|
|
WM_event_add_notifier(C, NC_MOVIECLIP | ND_DISPLAY, clip);
|
|
}
|
|
|
|
GSetIterator gs_iter;
|
|
int framenr = ED_space_clip_get_clip_frame_number(sc);
|
|
GSET_ITER (gs_iter, point_tracks) {
|
|
MovieTrackingPlaneTrack *plane_track = BLI_gsetIterator_getKey(&gs_iter);
|
|
BKE_tracking_track_plane_from_existing_motion(plane_track, framenr);
|
|
}
|
|
|
|
BLI_gset_free(point_tracks, NULL);
|
|
DEG_id_tag_update(&clip->id, 0);
|
|
|
|
WM_event_add_notifier(C, NC_MOVIECLIP | NA_EDITED, clip);
|
|
|
|
return OPERATOR_FINISHED;
|
|
}
|
|
|
|
void CLIP_OT_join_tracks(wmOperatorType *ot)
|
|
{
|
|
/* identifiers */
|
|
ot->name = "Join Tracks";
|
|
ot->description = "Join selected tracks";
|
|
ot->idname = "CLIP_OT_join_tracks";
|
|
|
|
/* api callbacks */
|
|
ot->exec = join_tracks_exec;
|
|
ot->poll = ED_space_clip_tracking_poll;
|
|
|
|
/* flags */
|
|
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
|
|
}
|
|
|
|
/** \} */
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/** \name Average Tracks Operator
|
|
* \{ */
|
|
|
|
static int average_tracks_exec(bContext *C, wmOperator *op)
|
|
{
|
|
SpaceClip *space_clip = CTX_wm_space_clip(C);
|
|
MovieClip *clip = ED_space_clip_get_clip(space_clip);
|
|
MovieTracking *tracking = &clip->tracking;
|
|
|
|
/* Collect source tracks. */
|
|
int num_source_tracks;
|
|
MovieTrackingTrack **source_tracks = BKE_tracking_selected_tracks_in_active_object(
|
|
tracking, &num_source_tracks);
|
|
if (num_source_tracks == 0) {
|
|
return OPERATOR_CANCELLED;
|
|
}
|
|
|
|
/* Create new empty track, which will be the averaged result.
|
|
* Makes it simple to average all selection to it. */
|
|
ListBase *tracks_list = BKE_tracking_get_active_tracks(tracking);
|
|
MovieTrackingTrack *result_track = BKE_tracking_track_add_empty(tracking, tracks_list);
|
|
|
|
/* Perform averaging. */
|
|
BKE_tracking_tracks_average(result_track, source_tracks, num_source_tracks);
|
|
|
|
const bool keep_original = RNA_boolean_get(op->ptr, "keep_original");
|
|
if (!keep_original) {
|
|
for (int i = 0; i < num_source_tracks; i++) {
|
|
clip_delete_track(C, clip, source_tracks[i]);
|
|
}
|
|
}
|
|
|
|
/* Update selection, making the result track active and selected. */
|
|
/* TODO(sergey): Should become some sort of utility function available for all operators. */
|
|
|
|
BKE_tracking_track_select(tracks_list, result_track, TRACK_AREA_ALL, 0);
|
|
ListBase *plane_tracks_list = BKE_tracking_get_active_plane_tracks(tracking);
|
|
BKE_tracking_plane_tracks_deselect_all(plane_tracks_list);
|
|
|
|
clip->tracking.act_track = result_track;
|
|
clip->tracking.act_plane_track = NULL;
|
|
|
|
/* Inform the dependency graph and interface about changes. */
|
|
DEG_id_tag_update(&clip->id, 0);
|
|
WM_event_add_notifier(C, NC_MOVIECLIP | NA_EDITED, clip);
|
|
|
|
/* Free memory. */
|
|
MEM_freeN(source_tracks);
|
|
|
|
return OPERATOR_FINISHED;
|
|
}
|
|
|
|
static int average_tracks_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
|
|
{
|
|
PropertyRNA *prop_keep_original = RNA_struct_find_property(op->ptr, "keep_original");
|
|
if (!RNA_property_is_set(op->ptr, prop_keep_original)) {
|
|
SpaceClip *space_clip = CTX_wm_space_clip(C);
|
|
MovieClip *clip = ED_space_clip_get_clip(space_clip);
|
|
MovieTracking *tracking = &clip->tracking;
|
|
|
|
const int num_selected_tracks = BKE_tracking_count_selected_tracks_in_active_object(tracking);
|
|
|
|
if (num_selected_tracks == 1) {
|
|
RNA_property_boolean_set(op->ptr, prop_keep_original, false);
|
|
}
|
|
}
|
|
|
|
return average_tracks_exec(C, op);
|
|
}
|
|
|
|
void CLIP_OT_average_tracks(wmOperatorType *ot)
|
|
{
|
|
/* Identifiers. */
|
|
ot->name = "Average Tracks";
|
|
ot->description = "Average selected tracks into active";
|
|
ot->idname = "CLIP_OT_average_tracks";
|
|
|
|
/* API callbacks. */
|
|
ot->exec = average_tracks_exec;
|
|
ot->invoke = average_tracks_invoke;
|
|
ot->poll = ED_space_clip_tracking_poll;
|
|
|
|
/* Flags. */
|
|
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
|
|
|
|
/* Properties. */
|
|
PropertyRNA *prop;
|
|
|
|
prop = RNA_def_boolean(ot->srna, "keep_original", 1, "Keep Original", "Keep original tracks");
|
|
RNA_def_property_flag(prop, PROP_SKIP_SAVE);
|
|
}
|
|
|
|
/** \} */
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/** \name Lock Tracks Operator
|
|
* \{ */
|
|
|
|
enum {
|
|
TRACK_ACTION_LOCK = 0,
|
|
TRACK_ACTION_UNLOCK = 1,
|
|
TRACK_ACTION_TOGGLE = 2,
|
|
};
|
|
|
|
static int lock_tracks_exec(bContext *C, wmOperator *op)
|
|
{
|
|
SpaceClip *sc = CTX_wm_space_clip(C);
|
|
MovieClip *clip = ED_space_clip_get_clip(sc);
|
|
MovieTracking *tracking = &clip->tracking;
|
|
ListBase *tracksbase = BKE_tracking_get_active_tracks(tracking);
|
|
int action = RNA_enum_get(op->ptr, "action");
|
|
|
|
for (MovieTrackingTrack *track = tracksbase->first; track != NULL; track = track->next) {
|
|
if (TRACK_VIEW_SELECTED(sc, track)) {
|
|
switch (action) {
|
|
case TRACK_ACTION_LOCK:
|
|
track->flag |= TRACK_LOCKED;
|
|
break;
|
|
case TRACK_ACTION_UNLOCK:
|
|
track->flag &= ~TRACK_LOCKED;
|
|
break;
|
|
case TRACK_ACTION_TOGGLE:
|
|
track->flag ^= TRACK_LOCKED;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
WM_event_add_notifier(C, NC_MOVIECLIP | NA_EVALUATED, clip);
|
|
|
|
return OPERATOR_FINISHED;
|
|
}
|
|
|
|
void CLIP_OT_lock_tracks(wmOperatorType *ot)
|
|
{
|
|
static const EnumPropertyItem actions_items[] = {
|
|
{TRACK_ACTION_LOCK, "LOCK", 0, "Lock", "Lock selected tracks"},
|
|
{TRACK_ACTION_UNLOCK, "UNLOCK", 0, "Unlock", "Unlock selected tracks"},
|
|
{TRACK_ACTION_TOGGLE, "TOGGLE", 0, "Toggle", "Toggle locked flag for selected tracks"},
|
|
{0, NULL, 0, NULL, NULL},
|
|
};
|
|
|
|
/* identifiers */
|
|
ot->name = "Lock Tracks";
|
|
ot->description = "Lock/unlock selected tracks";
|
|
ot->idname = "CLIP_OT_lock_tracks";
|
|
|
|
/* api callbacks */
|
|
ot->exec = lock_tracks_exec;
|
|
ot->poll = ED_space_clip_tracking_poll;
|
|
|
|
/* flags */
|
|
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
|
|
|
|
/* properties */
|
|
RNA_def_enum(ot->srna, "action", actions_items, 0, "Action", "Lock action to execute");
|
|
}
|
|
|
|
/** \} */
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/** \name Set Keyframe Operator
|
|
* \{ */
|
|
|
|
enum {
|
|
SOLVER_KEYFRAME_A = 0,
|
|
SOLVER_KEYFRAME_B = 1,
|
|
};
|
|
|
|
static int set_solver_keyframe_exec(bContext *C, wmOperator *op)
|
|
{
|
|
SpaceClip *sc = CTX_wm_space_clip(C);
|
|
MovieClip *clip = ED_space_clip_get_clip(sc);
|
|
MovieTracking *tracking = &clip->tracking;
|
|
MovieTrackingObject *object = BKE_tracking_object_get_active(tracking);
|
|
int keyframe = RNA_enum_get(op->ptr, "keyframe");
|
|
int framenr = BKE_movieclip_remap_scene_to_clip_frame(clip, sc->user.framenr);
|
|
|
|
if (keyframe == SOLVER_KEYFRAME_A) {
|
|
object->keyframe1 = framenr;
|
|
}
|
|
else {
|
|
object->keyframe2 = framenr;
|
|
}
|
|
|
|
WM_event_add_notifier(C, NC_MOVIECLIP | ND_DISPLAY, clip);
|
|
|
|
return OPERATOR_FINISHED;
|
|
}
|
|
|
|
void CLIP_OT_set_solver_keyframe(wmOperatorType *ot)
|
|
{
|
|
static const EnumPropertyItem keyframe_items[] = {
|
|
{SOLVER_KEYFRAME_A, "KEYFRAME_A", 0, "Keyframe A", ""},
|
|
{SOLVER_KEYFRAME_B, "KEYFRAME_B", 0, "Keyframe B", ""},
|
|
{0, NULL, 0, NULL, NULL},
|
|
};
|
|
|
|
/* identifiers */
|
|
ot->name = "Set Solver Keyframe";
|
|
ot->description = "Set keyframe used by solver";
|
|
ot->idname = "CLIP_OT_set_solver_keyframe";
|
|
|
|
/* api callbacks */
|
|
ot->exec = set_solver_keyframe_exec;
|
|
ot->poll = ED_space_clip_tracking_poll;
|
|
|
|
/* flags */
|
|
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
|
|
|
|
/* properties */
|
|
RNA_def_enum(ot->srna, "keyframe", keyframe_items, 0, "Keyframe", "Keyframe to set");
|
|
}
|
|
|
|
/** \} */
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/** \name Track Copy Color Operator
|
|
* \{ */
|
|
|
|
static int track_copy_color_exec(bContext *C, wmOperator *UNUSED(op))
|
|
{
|
|
SpaceClip *sc = CTX_wm_space_clip(C);
|
|
MovieClip *clip = ED_space_clip_get_clip(sc);
|
|
MovieTracking *tracking = &clip->tracking;
|
|
ListBase *tracksbase = BKE_tracking_get_active_tracks(tracking);
|
|
|
|
MovieTrackingTrack *act_track = BKE_tracking_track_get_active(tracking);
|
|
if (act_track == NULL) {
|
|
return OPERATOR_CANCELLED;
|
|
}
|
|
|
|
for (MovieTrackingTrack *track = tracksbase->first; track != NULL; track = track->next) {
|
|
if (TRACK_VIEW_SELECTED(sc, track) && track != act_track) {
|
|
track->flag &= ~TRACK_CUSTOMCOLOR;
|
|
if (act_track->flag & TRACK_CUSTOMCOLOR) {
|
|
copy_v3_v3(track->color, act_track->color);
|
|
track->flag |= TRACK_CUSTOMCOLOR;
|
|
}
|
|
}
|
|
}
|
|
|
|
DEG_id_tag_update(&clip->id, 0);
|
|
WM_event_add_notifier(C, NC_MOVIECLIP | ND_DISPLAY, clip);
|
|
|
|
return OPERATOR_FINISHED;
|
|
}
|
|
|
|
void CLIP_OT_track_copy_color(wmOperatorType *ot)
|
|
{
|
|
/* identifiers */
|
|
ot->name = "Copy Color";
|
|
ot->description = "Copy color to all selected tracks";
|
|
ot->idname = "CLIP_OT_track_copy_color";
|
|
|
|
/* api callbacks */
|
|
ot->exec = track_copy_color_exec;
|
|
ot->poll = ED_space_clip_tracking_poll;
|
|
|
|
/* flags */
|
|
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
|
|
}
|
|
|
|
/** \} */
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/** \name Clean Tracks Operator
|
|
* \{ */
|
|
|
|
static bool is_track_clean(MovieTrackingTrack *track, int frames, int del)
|
|
{
|
|
bool ok = true;
|
|
int prev = -1, count = 0;
|
|
MovieTrackingMarker *markers = track->markers, *new_markers = NULL;
|
|
int start_disabled = 0;
|
|
int markersnr = track->markersnr;
|
|
|
|
if (del) {
|
|
new_markers = MEM_callocN(markersnr * sizeof(MovieTrackingMarker), "track cleaned markers");
|
|
}
|
|
|
|
for (int a = 0; a < markersnr; a++) {
|
|
int end = 0;
|
|
|
|
if (prev == -1) {
|
|
if ((markers[a].flag & MARKER_DISABLED) == 0) {
|
|
prev = a;
|
|
}
|
|
else {
|
|
start_disabled = 1;
|
|
}
|
|
}
|
|
|
|
if (prev >= 0) {
|
|
end = a == markersnr - 1;
|
|
end |= (a < markersnr - 1) && (markers[a].framenr != markers[a + 1].framenr - 1 ||
|
|
markers[a].flag & MARKER_DISABLED);
|
|
}
|
|
|
|
if (end) {
|
|
int segok = 1, len = 0;
|
|
|
|
if (a != prev && markers[a].framenr != markers[a - 1].framenr + 1) {
|
|
len = a - prev;
|
|
}
|
|
else if (markers[a].flag & MARKER_DISABLED) {
|
|
len = a - prev;
|
|
}
|
|
else {
|
|
len = a - prev + 1;
|
|
}
|
|
|
|
if (frames) {
|
|
if (len < frames) {
|
|
segok = 0;
|
|
ok = 0;
|
|
|
|
if (!del) {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (del) {
|
|
if (segok) {
|
|
int t = len;
|
|
|
|
if (markers[a].flag & MARKER_DISABLED) {
|
|
t++;
|
|
}
|
|
|
|
/* Place disabled marker in front of current segment. */
|
|
if (start_disabled) {
|
|
memcpy(new_markers + count, markers + prev, sizeof(MovieTrackingMarker));
|
|
new_markers[count].framenr--;
|
|
new_markers[count].flag |= MARKER_DISABLED;
|
|
|
|
count++;
|
|
start_disabled = 0;
|
|
}
|
|
|
|
memcpy(new_markers + count, markers + prev, t * sizeof(MovieTrackingMarker));
|
|
count += t;
|
|
}
|
|
else if (markers[a].flag & MARKER_DISABLED) {
|
|
/* Current segment which would be deleted was finished by
|
|
* disabled marker, so next segment should be started from
|
|
* disabled marker.
|
|
*/
|
|
start_disabled = 1;
|
|
}
|
|
}
|
|
|
|
prev = -1;
|
|
}
|
|
}
|
|
|
|
if (del && count == 0) {
|
|
ok = 0;
|
|
}
|
|
|
|
if (del) {
|
|
MEM_freeN(track->markers);
|
|
|
|
if (count) {
|
|
track->markers = new_markers;
|
|
}
|
|
else {
|
|
track->markers = NULL;
|
|
MEM_freeN(new_markers);
|
|
}
|
|
|
|
track->markersnr = count;
|
|
}
|
|
|
|
return ok;
|
|
}
|
|
|
|
static int clean_tracks_exec(bContext *C, wmOperator *op)
|
|
{
|
|
SpaceClip *sc = CTX_wm_space_clip(C);
|
|
MovieClip *clip = ED_space_clip_get_clip(sc);
|
|
MovieTracking *tracking = &clip->tracking;
|
|
ListBase *tracksbase = BKE_tracking_get_active_tracks(tracking);
|
|
MovieTrackingTrack *act_track = BKE_tracking_track_get_active(tracking);
|
|
int frames = RNA_int_get(op->ptr, "frames");
|
|
int action = RNA_enum_get(op->ptr, "action");
|
|
float error = RNA_float_get(op->ptr, "error");
|
|
|
|
if (error && action == TRACKING_CLEAN_DELETE_SEGMENT) {
|
|
action = TRACKING_CLEAN_DELETE_TRACK;
|
|
}
|
|
|
|
for (MovieTrackingTrack *track = tracksbase->first, *next_track; track != NULL;
|
|
track = next_track) {
|
|
next_track = track->next;
|
|
|
|
if ((track->flag & TRACK_HIDDEN) == 0 && (track->flag & TRACK_LOCKED) == 0) {
|
|
bool ok;
|
|
|
|
ok = (is_track_clean(track, frames, action == TRACKING_CLEAN_DELETE_SEGMENT)) &&
|
|
((error == 0.0f) || (track->flag & TRACK_HAS_BUNDLE) == 0 || (track->error < error));
|
|
|
|
if (!ok) {
|
|
if (action == TRACKING_CLEAN_SELECT) {
|
|
BKE_tracking_track_flag_set(track, TRACK_AREA_ALL, SELECT);
|
|
}
|
|
else if (action == TRACKING_CLEAN_DELETE_TRACK) {
|
|
if (track == act_track) {
|
|
clip->tracking.act_track = NULL;
|
|
}
|
|
BKE_tracking_track_free(track);
|
|
BLI_freelinkN(tracksbase, track);
|
|
track = NULL;
|
|
}
|
|
|
|
/* Happens when all tracking segments are not long enough. */
|
|
if (track && track->markersnr == 0) {
|
|
if (track == act_track) {
|
|
clip->tracking.act_track = NULL;
|
|
}
|
|
BKE_tracking_track_free(track);
|
|
BLI_freelinkN(tracksbase, track);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
DEG_id_tag_update(&clip->id, 0);
|
|
BKE_tracking_dopesheet_tag_update(tracking);
|
|
|
|
WM_event_add_notifier(C, NC_MOVIECLIP | ND_SELECT, clip);
|
|
|
|
return OPERATOR_FINISHED;
|
|
}
|
|
|
|
static int clean_tracks_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
|
|
{
|
|
SpaceClip *sc = CTX_wm_space_clip(C);
|
|
MovieClip *clip = ED_space_clip_get_clip(sc);
|
|
|
|
if (!RNA_struct_property_is_set(op->ptr, "frames")) {
|
|
RNA_int_set(op->ptr, "frames", clip->tracking.settings.clean_frames);
|
|
}
|
|
|
|
if (!RNA_struct_property_is_set(op->ptr, "error")) {
|
|
RNA_float_set(op->ptr, "error", clip->tracking.settings.clean_error);
|
|
}
|
|
|
|
if (!RNA_struct_property_is_set(op->ptr, "action")) {
|
|
RNA_enum_set(op->ptr, "action", clip->tracking.settings.clean_action);
|
|
}
|
|
|
|
return clean_tracks_exec(C, op);
|
|
}
|
|
|
|
void CLIP_OT_clean_tracks(wmOperatorType *ot)
|
|
{
|
|
static const EnumPropertyItem actions_items[] = {
|
|
{TRACKING_CLEAN_SELECT, "SELECT", 0, "Select", "Select unclean tracks"},
|
|
{TRACKING_CLEAN_DELETE_TRACK, "DELETE_TRACK", 0, "Delete Track", "Delete unclean tracks"},
|
|
{TRACKING_CLEAN_DELETE_SEGMENT,
|
|
"DELETE_SEGMENTS",
|
|
0,
|
|
"Delete Segments",
|
|
"Delete unclean segments of tracks"},
|
|
{0, NULL, 0, NULL, NULL},
|
|
};
|
|
|
|
/* identifiers */
|
|
ot->name = "Clean Tracks";
|
|
ot->description = "Clean tracks with high error values or few frames";
|
|
ot->idname = "CLIP_OT_clean_tracks";
|
|
|
|
/* api callbacks */
|
|
ot->exec = clean_tracks_exec;
|
|
ot->invoke = clean_tracks_invoke;
|
|
ot->poll = ED_space_clip_tracking_poll;
|
|
|
|
/* flags */
|
|
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
|
|
|
|
/* properties */
|
|
RNA_def_int(ot->srna,
|
|
"frames",
|
|
0,
|
|
0,
|
|
INT_MAX,
|
|
"Tracked Frames",
|
|
"Effect on tracks which are tracked less than "
|
|
"specified amount of frames",
|
|
0,
|
|
INT_MAX);
|
|
RNA_def_float(ot->srna,
|
|
"error",
|
|
0.0f,
|
|
0.0f,
|
|
FLT_MAX,
|
|
"Reprojection Error",
|
|
"Effect on tracks which have got larger reprojection error",
|
|
0.0f,
|
|
100.0f);
|
|
RNA_def_enum(ot->srna, "action", actions_items, 0, "Action", "Cleanup action to execute");
|
|
}
|
|
|
|
/** \} */
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/** \name Add Tracking Object
|
|
* \{ */
|
|
|
|
static int tracking_object_new_exec(bContext *C, wmOperator *UNUSED(op))
|
|
{
|
|
SpaceClip *sc = CTX_wm_space_clip(C);
|
|
MovieClip *clip = ED_space_clip_get_clip(sc);
|
|
MovieTracking *tracking = &clip->tracking;
|
|
|
|
BKE_tracking_object_add(tracking, "Object");
|
|
|
|
DEG_id_tag_update(&clip->id, ID_RECALC_COPY_ON_WRITE);
|
|
WM_event_add_notifier(C, NC_MOVIECLIP | NA_EDITED, clip);
|
|
|
|
return OPERATOR_FINISHED;
|
|
}
|
|
|
|
void CLIP_OT_tracking_object_new(wmOperatorType *ot)
|
|
{
|
|
/* identifiers */
|
|
ot->name = "Add Tracking Object";
|
|
ot->description = "Add new object for tracking";
|
|
ot->idname = "CLIP_OT_tracking_object_new";
|
|
|
|
/* api callbacks */
|
|
ot->exec = tracking_object_new_exec;
|
|
ot->poll = ED_space_clip_tracking_poll;
|
|
|
|
/* flags */
|
|
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
|
|
}
|
|
|
|
/** \} */
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/** \name Remove Tracking Object
|
|
* \{ */
|
|
|
|
static int tracking_object_remove_exec(bContext *C, wmOperator *op)
|
|
{
|
|
SpaceClip *sc = CTX_wm_space_clip(C);
|
|
MovieClip *clip = ED_space_clip_get_clip(sc);
|
|
MovieTracking *tracking = &clip->tracking;
|
|
MovieTrackingObject *object;
|
|
|
|
object = BKE_tracking_object_get_active(tracking);
|
|
|
|
if (object->flag & TRACKING_OBJECT_CAMERA) {
|
|
BKE_report(op->reports, RPT_WARNING, "Object used for camera tracking cannot be deleted");
|
|
return OPERATOR_CANCELLED;
|
|
}
|
|
|
|
BKE_tracking_object_delete(tracking, object);
|
|
|
|
DEG_id_tag_update(&clip->id, ID_RECALC_COPY_ON_WRITE);
|
|
WM_event_add_notifier(C, NC_MOVIECLIP | NA_EDITED, clip);
|
|
|
|
return OPERATOR_FINISHED;
|
|
}
|
|
|
|
void CLIP_OT_tracking_object_remove(wmOperatorType *ot)
|
|
{
|
|
/* identifiers */
|
|
ot->name = "Remove Tracking Object";
|
|
ot->description = "Remove object for tracking";
|
|
ot->idname = "CLIP_OT_tracking_object_remove";
|
|
|
|
/* api callbacks */
|
|
ot->exec = tracking_object_remove_exec;
|
|
ot->poll = ED_space_clip_tracking_poll;
|
|
|
|
/* flags */
|
|
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
|
|
}
|
|
|
|
/** \} */
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/** \name Copy Tracks to Clipboard Operator
|
|
* \{ */
|
|
|
|
static int copy_tracks_exec(bContext *C, wmOperator *UNUSED(op))
|
|
{
|
|
SpaceClip *sc = CTX_wm_space_clip(C);
|
|
MovieClip *clip = ED_space_clip_get_clip(sc);
|
|
MovieTracking *tracking = &clip->tracking;
|
|
MovieTrackingObject *object = BKE_tracking_object_get_active(tracking);
|
|
|
|
clip_tracking_clear_invisible_track_selection(sc, clip);
|
|
|
|
BKE_tracking_clipboard_copy_tracks(tracking, object);
|
|
|
|
return OPERATOR_FINISHED;
|
|
}
|
|
|
|
void CLIP_OT_copy_tracks(wmOperatorType *ot)
|
|
{
|
|
/* identifiers */
|
|
ot->name = "Copy Tracks";
|
|
ot->description = "Copy selected tracks to clipboard";
|
|
ot->idname = "CLIP_OT_copy_tracks";
|
|
|
|
/* api callbacks */
|
|
ot->exec = copy_tracks_exec;
|
|
ot->poll = ED_space_clip_tracking_poll;
|
|
|
|
/* flags */
|
|
ot->flag = OPTYPE_REGISTER;
|
|
}
|
|
|
|
/** \} */
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/** \name Paste Tracks From Clipboard Operator
|
|
* \{ */
|
|
|
|
static bool paste_tracks_poll(bContext *C)
|
|
{
|
|
if (ED_space_clip_tracking_poll(C)) {
|
|
return BKE_tracking_clipboard_has_tracks();
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int paste_tracks_exec(bContext *C, wmOperator *UNUSED(op))
|
|
{
|
|
SpaceClip *sc = CTX_wm_space_clip(C);
|
|
MovieClip *clip = ED_space_clip_get_clip(sc);
|
|
MovieTracking *tracking = &clip->tracking;
|
|
MovieTrackingObject *object = BKE_tracking_object_get_active(tracking);
|
|
ListBase *tracks_base = BKE_tracking_object_get_tracks(tracking, object);
|
|
|
|
BKE_tracking_tracks_deselect_all(tracks_base);
|
|
BKE_tracking_clipboard_paste_tracks(tracking, object);
|
|
|
|
WM_event_add_notifier(C, NC_MOVIECLIP | NA_EDITED, clip);
|
|
|
|
return OPERATOR_FINISHED;
|
|
}
|
|
|
|
void CLIP_OT_paste_tracks(wmOperatorType *ot)
|
|
{
|
|
/* identifiers */
|
|
ot->name = "Paste Tracks";
|
|
ot->description = "Paste tracks from clipboard";
|
|
ot->idname = "CLIP_OT_paste_tracks";
|
|
|
|
/* api callbacks */
|
|
ot->exec = paste_tracks_exec;
|
|
ot->poll = paste_tracks_poll;
|
|
|
|
/* flags */
|
|
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
|
|
}
|
|
|
|
/** \} */
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/** \name Insert Track Keyframe Operator
|
|
* \{ */
|
|
|
|
static void keyframe_set_flag(bContext *C, bool set)
|
|
{
|
|
SpaceClip *sc = CTX_wm_space_clip(C);
|
|
MovieClip *clip = ED_space_clip_get_clip(sc);
|
|
MovieTracking *tracking = &clip->tracking;
|
|
int framenr = ED_space_clip_get_clip_frame_number(sc);
|
|
|
|
ListBase *tracks_base = BKE_tracking_get_active_tracks(tracking);
|
|
for (MovieTrackingTrack *track = tracks_base->first; track != NULL; track = track->next) {
|
|
if (TRACK_VIEW_SELECTED(sc, track)) {
|
|
if (set) {
|
|
MovieTrackingMarker *marker = BKE_tracking_marker_ensure(track, framenr);
|
|
marker->flag &= ~MARKER_TRACKED;
|
|
}
|
|
else {
|
|
MovieTrackingMarker *marker = BKE_tracking_marker_get_exact(track, framenr);
|
|
if (marker != NULL) {
|
|
marker->flag |= MARKER_TRACKED;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
ListBase *plane_tracks_base = BKE_tracking_get_active_plane_tracks(tracking);
|
|
for (MovieTrackingPlaneTrack *plane_track = plane_tracks_base->first; plane_track != NULL;
|
|
plane_track = plane_track->next) {
|
|
if (PLANE_TRACK_VIEW_SELECTED(plane_track)) {
|
|
if (set) {
|
|
MovieTrackingPlaneMarker *plane_marker = BKE_tracking_plane_marker_ensure(plane_track,
|
|
framenr);
|
|
if (plane_marker->flag & PLANE_MARKER_TRACKED) {
|
|
plane_marker->flag &= ~PLANE_MARKER_TRACKED;
|
|
BKE_tracking_track_plane_from_existing_motion(plane_track, plane_marker->framenr);
|
|
}
|
|
}
|
|
else {
|
|
MovieTrackingPlaneMarker *plane_marker = BKE_tracking_plane_marker_get_exact(plane_track,
|
|
framenr);
|
|
if (plane_marker) {
|
|
if ((plane_marker->flag & PLANE_MARKER_TRACKED) == 0) {
|
|
plane_marker->flag |= PLANE_MARKER_TRACKED;
|
|
BKE_tracking_retrack_plane_from_existing_motion_at_segment(plane_track,
|
|
plane_marker->framenr);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
WM_event_add_notifier(C, NC_MOVIECLIP | NA_EDITED, clip);
|
|
}
|
|
|
|
static int keyframe_insert_exec(bContext *C, wmOperator *UNUSED(op))
|
|
{
|
|
keyframe_set_flag(C, true);
|
|
return OPERATOR_FINISHED;
|
|
}
|
|
|
|
void CLIP_OT_keyframe_insert(wmOperatorType *ot)
|
|
{
|
|
/* identifiers */
|
|
ot->name = "Insert Keyframe";
|
|
ot->description = "Insert a keyframe to selected tracks at current frame";
|
|
ot->idname = "CLIP_OT_keyframe_insert";
|
|
|
|
/* api callbacks */
|
|
ot->poll = ED_space_clip_tracking_poll;
|
|
ot->exec = keyframe_insert_exec;
|
|
|
|
/* flags */
|
|
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
|
|
}
|
|
|
|
/** \} */
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/** \name Delete Track Keyframe Operator
|
|
* \{ */
|
|
|
|
static int keyframe_delete_exec(bContext *C, wmOperator *UNUSED(op))
|
|
{
|
|
keyframe_set_flag(C, false);
|
|
return OPERATOR_FINISHED;
|
|
}
|
|
|
|
void CLIP_OT_keyframe_delete(wmOperatorType *ot)
|
|
{
|
|
/* identifiers */
|
|
ot->name = "Delete Keyframe";
|
|
ot->description = "Delete a keyframe from selected tracks at current frame";
|
|
ot->idname = "CLIP_OT_keyframe_delete";
|
|
|
|
/* api callbacks */
|
|
ot->poll = ED_space_clip_tracking_poll;
|
|
ot->exec = keyframe_delete_exec;
|
|
|
|
/* flags */
|
|
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
|
|
}
|
|
|
|
/** \} */
|