Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSergey Sharybin <sergey.vfx@gmail.com>2011-11-07 16:55:18 +0400
committerSergey Sharybin <sergey.vfx@gmail.com>2011-11-07 16:55:18 +0400
commit27d42c63d9b507b1771ed5a7923c389c719b877b (patch)
tree8dd4ca61e197a7053633f62b4a5d8091957724c4 /source/blender/editors/space_clip
parente122dc0748f6a4d77b236e26beba93e2a9a36bf0 (diff)
Camera tracking integration
=========================== Commiting camera tracking integration gsoc project into trunk. This commit includes: - Bundled version of libmv library (with some changes against official repo, re-sync with libmv repo a bit later) - New datatype ID called MovieClip which is optimized to work with movie clips (both of movie files and image sequences) and doing camera/motion tracking operations. - New editor called Clip Editor which is currently used for motion/tracking stuff only, but which can be easily extended to work with masks too. This editor supports: * Loading movie files/image sequences * Build proxies with different size for loaded movie clip, also supports building undistorted proxies to increase speed of playback in undistorted mode. * Manual lens distortion mode calibration using grid and grease pencil * Supervised 2D tracking using two different algorithms KLT and SAD. * Basic algorithm for feature detection * Camera motion solving. scene orientation - New constraints to "link" scene objects with solved motions from clip: * Follow Track (make object follow 2D motion of track with given name or parent object to reconstructed 3D position of track) * Camera Solver to make camera moving in the same way as reconstructed camera This commit NOT includes changes from tomato branch: - New nodes (they'll be commited as separated patch) - Automatic image offset guessing for image input node and image editor (need to do more tests and gather more feedback) - Code cleanup in libmv-capi. It's not so critical cleanup, just increasing readability and understanadability of code. Better to make this chaneg when Keir will finish his current patch. More details about this project can be found on this page: http://wiki.blender.org/index.php/User:Nazg-gul/GSoC-2011 Further development of small features would be done in trunk, bigger/experimental features would first be implemented in tomato branch.
Diffstat (limited to 'source/blender/editors/space_clip')
-rw-r--r--source/blender/editors/space_clip/CMakeLists.txt56
-rw-r--r--source/blender/editors/space_clip/SConscript9
-rw-r--r--source/blender/editors/space_clip/clip_buttons.c436
-rw-r--r--source/blender/editors/space_clip/clip_draw.c1325
-rw-r--r--source/blender/editors/space_clip/clip_editor.c302
-rw-r--r--source/blender/editors/space_clip/clip_graph_draw.c255
-rw-r--r--source/blender/editors/space_clip/clip_graph_ops.c356
-rw-r--r--source/blender/editors/space_clip/clip_intern.h144
-rw-r--r--source/blender/editors/space_clip/clip_ops.c1004
-rw-r--r--source/blender/editors/space_clip/clip_toolbar.c244
-rw-r--r--source/blender/editors/space_clip/clip_utils.c219
-rw-r--r--source/blender/editors/space_clip/space_clip.c949
-rw-r--r--source/blender/editors/space_clip/tracking_ops.c2940
13 files changed, 8239 insertions, 0 deletions
diff --git a/source/blender/editors/space_clip/CMakeLists.txt b/source/blender/editors/space_clip/CMakeLists.txt
new file mode 100644
index 00000000000..4f9819e8e77
--- /dev/null
+++ b/source/blender/editors/space_clip/CMakeLists.txt
@@ -0,0 +1,56 @@
+# ***** BEGIN GPL LICENSE BLOCK *****
+#
+# 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.
+#
+# Contributor(s): Blender Foundation,
+# Sergey Sharybin
+#
+# ***** END GPL LICENSE BLOCK *****
+
+set(INC
+ ../include
+ ../../blenkernel
+ ../../blenloader
+ ../../blenfont
+ ../../blenlib
+ ../../imbuf
+ ../../makesdna
+ ../../makesrna
+ ../../windowmanager
+ ../../../../intern/guardedalloc
+ ${GLEW_INCLUDE_PATH}
+)
+
+set(INC_SYS
+)
+
+set(SRC
+ space_clip.c
+ clip_draw.c
+ clip_toolbar.c
+ clip_ops.c
+ clip_graph_ops.c
+ clip_graph_draw.c
+ clip_editor.c
+ clip_buttons.c
+ clip_utils.c
+ tracking_ops.c
+
+ clip_intern.h
+)
+
+blender_add_lib(bf_editor_space_clip "${SRC}" "${INC}" "${INC_SYS}")
diff --git a/source/blender/editors/space_clip/SConscript b/source/blender/editors/space_clip/SConscript
new file mode 100644
index 00000000000..70331b0ec4a
--- /dev/null
+++ b/source/blender/editors/space_clip/SConscript
@@ -0,0 +1,9 @@
+#!/usr/bin/python
+Import ('env')
+
+sources = env.Glob('*.c')
+defs = []
+incs = '../include ../../blenkernel ../../blenloader ../../blenfont ../../blenlib ../../imbuf ../../makesdna'
+incs += ' ../../makesrna ../../windowmanager #/intern/guardedalloc #/extern/glew/include'
+
+env.BlenderLib ( 'bf_editors_space_clip', sources, Split(incs), defs, libtype=['core'], priority=[95] )
diff --git a/source/blender/editors/space_clip/clip_buttons.c b/source/blender/editors/space_clip/clip_buttons.c
new file mode 100644
index 00000000000..149aa9106b0
--- /dev/null
+++ b/source/blender/editors/space_clip/clip_buttons.c
@@ -0,0 +1,436 @@
+/*
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * 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.
+ *
+ *
+ * Contributor(s): Blender Foundation,
+ * Sergey Sharybin
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+/** \file blender/editors/space_clip/clip_buttons.c
+ * \ingroup spclip
+ */
+
+#include <string.h>
+#include <stdio.h>
+
+#include "MEM_guardedalloc.h"
+
+#include "DNA_scene_types.h"
+#include "DNA_screen_types.h"
+#include "DNA_space_types.h"
+
+#include "BLI_math.h"
+#include "BLI_utildefines.h"
+#include "BLI_listbase.h"
+
+#include "BKE_context.h"
+#include "BKE_depsgraph.h"
+#include "BKE_screen.h"
+#include "BKE_movieclip.h"
+#include "BKE_tracking.h"
+
+#include "ED_clip.h"
+#include "ED_gpencil.h"
+
+#include "UI_interface.h"
+#include "UI_resources.h"
+
+#include "RNA_access.h"
+
+#include "WM_api.h"
+#include "WM_types.h"
+
+#include "clip_intern.h" // own include
+
+/* Panels */
+
+void ED_clip_buttons_register(ARegionType *art)
+{
+ PanelType *pt;
+
+ pt= MEM_callocN(sizeof(PanelType), "spacetype clip panel gpencil");
+ strcpy(pt->idname, "CLIP_PT_gpencil");
+ strcpy(pt->label, "Grease Pencil");
+ pt->draw= gpencil_panel_standard;
+ pt->flag|= PNL_DEFAULT_CLOSED;
+ BLI_addtail(&art->paneltypes, pt);
+}
+
+/********************* MovieClip Template ************************/
+
+void uiTemplateMovieClip(uiLayout *layout, bContext *C, PointerRNA *ptr, const char *propname, int compact)
+{
+ PropertyRNA *prop;
+ PointerRNA clipptr;
+ MovieClip *clip;
+ uiLayout *row, *split;
+ uiBlock *block;
+
+ if(!ptr->data)
+ return;
+
+ prop= RNA_struct_find_property(ptr, propname);
+ if(!prop) {
+ printf("uiTemplateMovieClip: property not found: %s.%s\n", RNA_struct_identifier(ptr->type), propname);
+ return;
+ }
+
+ if(RNA_property_type(prop) != PROP_POINTER) {
+ printf("uiTemplateMovieClip: expected pointer property for %s.%s\n", RNA_struct_identifier(ptr->type), propname);
+ return;
+ }
+
+ clipptr= RNA_property_pointer_get(ptr, prop);
+ clip= clipptr.data;
+
+ uiLayoutSetContextPointer(layout, "edit_movieclip", &clipptr);
+
+ if(!compact)
+ uiTemplateID(layout, C, ptr, propname, NULL, "CLIP_OT_open", NULL);
+
+ if(clip) {
+ row= uiLayoutRow(layout, 0);
+ block= uiLayoutGetBlock(row);
+ uiDefBut(block, LABEL, 0, "File Path:", 0, 19, 145, 19, NULL, 0, 0, 0, 0, "");
+
+ row= uiLayoutRow(layout, 0);
+ split = uiLayoutSplit(row, 0.0, 0);
+ row= uiLayoutRow(split, 1);
+
+ uiItemR(row, &clipptr, "filepath", 0, "", ICON_NONE);
+ uiItemO(row, "", ICON_FILE_REFRESH, "clip.reload");
+ }
+}
+
+/********************* Track Template ************************/
+
+void uiTemplateTrack(uiLayout *layout, PointerRNA *ptr, const char *propname)
+{
+ PropertyRNA *prop;
+ PointerRNA scopesptr;
+ uiBlock *block;
+ rctf rect;
+ MovieClipScopes *scopes;
+
+ if(!ptr->data)
+ return;
+
+ prop= RNA_struct_find_property(ptr, propname);
+ if(!prop) {
+ printf("uiTemplateTrack: property not found: %s.%s\n", RNA_struct_identifier(ptr->type), propname);
+ return;
+ }
+
+ if(RNA_property_type(prop) != PROP_POINTER) {
+ printf("uiTemplateTrack: expected pointer property for %s.%s\n", RNA_struct_identifier(ptr->type), propname);
+ return;
+ }
+
+ scopesptr= RNA_property_pointer_get(ptr, prop);
+ scopes= (MovieClipScopes *)scopesptr.data;
+
+ rect.xmin= 0; rect.xmax= 200;
+ rect.ymin= 0; rect.ymax= 120;
+
+ block= uiLayoutAbsoluteBlock(layout);
+
+ scopes->track_preview_height= (scopes->track_preview_height<=UI_UNIT_Y)?UI_UNIT_Y:scopes->track_preview_height;
+
+ uiDefBut(block, TRACKPREVIEW, 0, "", rect.xmin, rect.ymin, rect.xmax-rect.xmin, scopes->track_preview_height, scopes, 0, 0, 0, 0, "");
+}
+
+/********************* Marker Template ************************/
+
+#define B_MARKER_POS 3
+#define B_MARKER_OFFSET 4
+#define B_MARKER_PAT_DIM 5
+#define B_MARKER_SEARCH_POS 6
+#define B_MARKER_SEARCH_DIM 7
+#define B_MARKER_FLAG 8
+
+typedef struct {
+ int compact; /* compact mode */
+
+ MovieClip *clip;
+ MovieClipUser *user; /* user of clip */
+ MovieTrackingTrack *track;
+
+ int framenr; /* current frame number */
+ float marker_pos[2]; /* position of marker in pixel coords */
+ float track_pat[2]; /* position and dimensions of marker pattern in pixel coords */
+ float track_offset[2]; /* offset of "parenting" point */
+ float track_search_pos[2], track_search[2]; /* position and dimensions of marker search in pixel coords */
+ int marker_flag; /* marker's flags */
+} MarkerUpdateCb;
+
+static void to_pixel_space(float r[2], float a[2], int width, int height)
+{
+ copy_v2_v2(r, a);
+ r[0]*= width;
+ r[1]*= height;
+}
+
+static void marker_update_cb(bContext *C, void *arg_cb, void *UNUSED(arg))
+{
+ MarkerUpdateCb *cb= (MarkerUpdateCb*) arg_cb;
+ MovieTrackingMarker *marker;
+
+ if(!cb->compact)
+ return;
+
+ marker= BKE_tracking_ensure_marker(cb->track, cb->framenr);
+
+ marker->flag= cb->marker_flag;
+
+ WM_event_add_notifier(C, NC_MOVIECLIP|NA_EDITED, NULL);
+}
+
+static void marker_block_handler(bContext *C, void *arg_cb, int event)
+{
+ MarkerUpdateCb *cb= (MarkerUpdateCb*) arg_cb;
+ MovieTrackingMarker *marker;
+ int width, height, ok= 0;
+
+ BKE_movieclip_get_size(cb->clip, cb->user, &width, &height);
+
+ marker= BKE_tracking_ensure_marker(cb->track, cb->framenr);
+
+ if(event==B_MARKER_POS) {
+ marker->pos[0]= cb->marker_pos[0]/width;
+ marker->pos[1]= cb->marker_pos[1]/height;
+
+ /* to update position of "parented" objects */
+ DAG_id_tag_update(&cb->clip->id, 0);
+ WM_event_add_notifier(C, NC_SPACE|ND_SPACE_VIEW3D, NULL);
+
+ ok= 1;
+ }
+ else if(event==B_MARKER_PAT_DIM) {
+ float dim[2], pat_dim[2];
+
+ sub_v2_v2v2(pat_dim, cb->track->pat_max, cb->track->pat_min);
+
+ dim[0]= cb->track_pat[0]/width;
+ dim[1]= cb->track_pat[1]/height;
+
+ sub_v2_v2(dim, pat_dim);
+ mul_v2_fl(dim, 0.5f);
+
+ cb->track->pat_min[0]-= dim[0];
+ cb->track->pat_min[1]-= dim[1];
+
+ cb->track->pat_max[0]+= dim[0];
+ cb->track->pat_max[1]+= dim[1];
+
+ BKE_tracking_clamp_track(cb->track, CLAMP_PAT_DIM);
+
+ ok= 1;
+ }
+ else if(event==B_MARKER_SEARCH_POS) {
+ float delta[2], side[2];
+
+ sub_v2_v2v2(side, cb->track->search_max, cb->track->search_min);
+ mul_v2_fl(side, 0.5f);
+
+ delta[0]= cb->track_search_pos[0]/width;
+ delta[1]= cb->track_search_pos[1]/height;
+
+ sub_v2_v2v2(cb->track->search_min, delta, side);
+ add_v2_v2v2(cb->track->search_max, delta, side);
+
+ BKE_tracking_clamp_track(cb->track, CLAMP_SEARCH_POS);
+
+ ok= 1;
+ }
+ else if(event==B_MARKER_SEARCH_DIM) {
+ float dim[2], search_dim[2];
+
+ sub_v2_v2v2(search_dim, cb->track->search_max, cb->track->search_min);
+
+ dim[0]= cb->track_search[0]/width;
+ dim[1]= cb->track_search[1]/height;
+
+ sub_v2_v2(dim, search_dim);
+ mul_v2_fl(dim, 0.5f);
+
+ cb->track->search_min[0]-= dim[0];
+ cb->track->search_min[1]-= dim[1];
+
+ cb->track->search_max[0]+= dim[0];
+ cb->track->search_max[1]+= dim[1];
+
+ BKE_tracking_clamp_track(cb->track, CLAMP_SEARCH_DIM);
+
+ ok= 1;
+ } else if(event==B_MARKER_FLAG) {
+ marker->flag= cb->marker_flag;
+
+ ok= 1;
+ } else if(event==B_MARKER_OFFSET) {
+ float offset[2], delta[2];
+ int i;
+
+ offset[0]= cb->track_offset[0]/width;
+ offset[1]= cb->track_offset[1]/height;
+
+ sub_v2_v2v2(delta, offset, cb->track->offset);
+ copy_v2_v2(cb->track->offset, offset);
+
+ for(i=0; i<cb->track->markersnr; i++)
+ sub_v2_v2(cb->track->markers[i].pos, delta);
+
+ /* to update position of "parented" objects */
+ DAG_id_tag_update(&cb->clip->id, 0);
+ WM_event_add_notifier(C, NC_SPACE|ND_SPACE_VIEW3D, NULL);
+
+ ok= 1;
+ }
+
+ if(ok)
+ WM_event_add_notifier(C, NC_MOVIECLIP|NA_EDITED, cb->clip);
+}
+
+void uiTemplateMarker(uiLayout *layout, PointerRNA *ptr, const char *propname, PointerRNA *userptr, PointerRNA *trackptr, int compact)
+{
+ PropertyRNA *prop;
+ uiBlock *block;
+ uiBut *bt;
+ PointerRNA clipptr;
+ MovieClip *clip;
+ MovieClipUser *user;
+ MovieTrackingTrack *track;
+ MovieTrackingMarker *marker;
+ MarkerUpdateCb *cb;
+
+ if(!ptr->data)
+ return;
+
+ prop= RNA_struct_find_property(ptr, propname);
+ if(!prop) {
+ printf("uiTemplateMarker: property not found: %s.%s\n", RNA_struct_identifier(ptr->type), propname);
+ return;
+ }
+
+ if(RNA_property_type(prop) != PROP_POINTER) {
+ printf("uiTemplateMarker: expected pointer property for %s.%s\n", RNA_struct_identifier(ptr->type), propname);
+ return;
+ }
+
+ clipptr= RNA_property_pointer_get(ptr, prop);
+ clip= (MovieClip *)clipptr.data;
+ user= userptr->data;
+ track= trackptr->data;
+
+ marker= BKE_tracking_get_marker(track, user->framenr);
+
+ cb= MEM_callocN(sizeof(MarkerUpdateCb), "uiTemplateMarker update_cb");
+ cb->compact= compact;
+ cb->clip= clip;
+ cb->user= user;
+ cb->track= track;
+ cb->marker_flag= marker->flag;
+ cb->framenr= user->framenr;
+
+ if(compact) {
+ block= uiLayoutGetBlock(layout);
+
+ bt= uiDefIconButBitI(block, TOGN, MARKER_DISABLED, 0, ICON_RESTRICT_VIEW_OFF, 0, 0, 20, 20, &cb->marker_flag, 0, 0, 1, 0, "Marker is disabled for current frame.");
+ uiButSetNFunc(bt, marker_update_cb, cb, NULL);
+ } else {
+ int width, height, step, digits;
+ float pat_dim[2], pat_pos[2], search_dim[2], search_pos[2];
+ uiLayout *col;
+
+ BKE_movieclip_get_size(clip, user, &width, &height);
+
+ if(track->flag&TRACK_LOCKED) {
+ uiLayoutSetActive(layout, 0);
+ block= uiLayoutAbsoluteBlock(layout);
+ uiDefBut(block, LABEL, 0, "Track is locked", 0, 0, 300, 19, NULL, 0, 0, 0, 0, "");
+
+ return;
+ }
+
+ step= 100;
+ digits= 2;
+
+ sub_v2_v2v2(pat_dim, track->pat_max, track->pat_min);
+ sub_v2_v2v2(search_dim, track->search_max, track->search_min);
+
+ add_v2_v2v2(search_pos, track->search_max, track->search_min);
+ mul_v2_fl(search_pos, 0.5);
+
+ add_v2_v2v2(pat_pos, track->pat_max, track->pat_min);
+ mul_v2_fl(pat_pos, 0.5);
+
+ to_pixel_space(cb->marker_pos, marker->pos, width, height);
+ to_pixel_space(cb->track_pat, pat_dim, width, height);
+ to_pixel_space(cb->track_search, search_dim, width, height);
+ to_pixel_space(cb->track_search_pos, search_pos, width, height);
+ to_pixel_space(cb->track_offset, track->offset, width, height);
+
+ cb->marker_flag= marker->flag;
+
+ block= uiLayoutAbsoluteBlock(layout);
+ uiBlockSetHandleFunc(block, marker_block_handler, cb);
+ uiBlockSetNFunc(block, marker_update_cb, cb, NULL);
+
+ uiDefButBitI(block, OPTIONN, MARKER_DISABLED, B_MARKER_FLAG, "Enabled", 10, 190, 145, 19, &cb->marker_flag,
+ 0, 0, 0, 0, "Marker is disabled for current frame.");
+
+ col= uiLayoutColumn(layout, 1);
+ uiLayoutSetActive(col, (cb->marker_flag&MARKER_DISABLED)==0);
+
+ block= uiLayoutAbsoluteBlock(col);
+ uiBlockBeginAlign(block);
+
+ uiDefBut(block, LABEL, 0, "Position:", 0, 190, 300, 19, NULL, 0, 0, 0, 0, "");
+ uiDefButF(block, NUM, B_MARKER_POS, "X:", 10, 171, 145, 19, &cb->marker_pos[0],
+ -10*width, 10.0*width, step, digits, "X-position of marker at frame in screen coordinates.");
+ uiDefButF(block, NUM, B_MARKER_POS, "Y:", 165, 171, 145, 19, &cb->marker_pos[1],
+ -10*height, 10.0*height, step, digits, "Y-position of marker at frame in screen coordinates.");
+
+ uiDefBut(block, LABEL, 0, "Offset:", 0, 152, 300, 19, NULL, 0, 0, 0, 0, "");
+ uiDefButF(block, NUM, B_MARKER_OFFSET, "X:", 10, 133, 145, 19, &cb->track_offset[0],
+ -10*width, 10.0*width, step, digits, "X-offset to parenting point.");
+ uiDefButF(block, NUM, B_MARKER_OFFSET, "Y:", 165, 133, 145, 19, &cb->track_offset[1],
+ -10*height, 10.0*height, step, digits, "Y-offset to parenting point.");
+
+ uiDefBut(block, LABEL, 0, "Pattern Area:", 0, 114, 300, 19, NULL, 0, 0, 0, 0, "");
+ uiDefButF(block, NUM, B_MARKER_PAT_DIM, "Width:", 10, 95, 300, 19, &cb->track_pat[0], 3.0f,
+ 10.0*width, step, digits, "Width of marker's pattern in screen soordinates.");
+ uiDefButF(block, NUM, B_MARKER_PAT_DIM, "Height:", 10, 76, 300, 19, &cb->track_pat[1], 3.0f,
+ 10.0*height, step, digits, "Height of marker's pattern in screen soordinates.");
+
+ uiDefBut(block, LABEL, 0, "Search Area:", 0, 57, 300, 19, NULL, 0, 0, 0, 0, "");
+ uiDefButF(block, NUM, B_MARKER_SEARCH_POS, "X:", 10, 38, 145, 19, &cb->track_search_pos[0],
+ -width, width, step, digits, "X-position of search at frame relative to marker's position");
+ uiDefButF(block, NUM, B_MARKER_SEARCH_POS, "Y:", 165, 38, 145, 19, &cb->track_search_pos[1],
+ -height, height, step, digits, "X-position of search at frame relative to marker's position");
+ uiDefButF(block, NUM, B_MARKER_SEARCH_DIM, "Width:", 10, 19, 300, 19, &cb->track_search[0], 3.0f,
+ 10.0*width, step, digits, "Width of marker's search in screen soordinates.");
+ uiDefButF(block, NUM, B_MARKER_SEARCH_DIM, "Height:", 10, 0, 300, 19, &cb->track_search[1], 3.0f,
+ 10.0*height, step, digits, "Height of marker's search in screen soordinates.");
+
+ uiBlockEndAlign(block);
+ }
+}
diff --git a/source/blender/editors/space_clip/clip_draw.c b/source/blender/editors/space_clip/clip_draw.c
new file mode 100644
index 00000000000..0da68953593
--- /dev/null
+++ b/source/blender/editors/space_clip/clip_draw.c
@@ -0,0 +1,1325 @@
+/*
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * 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.
+ *
+ *
+ * Contributor(s): Blender Foundation,
+ * Sergey Sharybin
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+/** \file blender/editors/space_clip/clip_draw.c
+ * \ingroup spclip
+ */
+
+#include "DNA_gpencil_types.h"
+#include "DNA_movieclip_types.h"
+#include "DNA_scene_types.h"
+#include "DNA_object_types.h" /* SELECT */
+
+#include "MEM_guardedalloc.h"
+
+#include "BKE_context.h"
+#include "BKE_movieclip.h"
+#include "BKE_tracking.h"
+
+#include "IMB_imbuf_types.h"
+#include "IMB_imbuf.h"
+
+#include "BLI_utildefines.h"
+#include "BLI_math.h"
+#include "BLI_string.h"
+#include "BLI_rect.h"
+#include "BLI_math_base.h"
+
+#include "ED_screen.h"
+#include "ED_clip.h"
+#include "ED_gpencil.h"
+
+#include "BIF_gl.h"
+#include "BIF_glutil.h"
+
+#include "WM_api.h"
+#include "WM_types.h"
+
+#include "UI_interface.h"
+#include "UI_resources.h"
+#include "UI_view2d.h"
+
+#include "RNA_access.h"
+
+#include "BLF_api.h"
+
+#include "clip_intern.h" // own include
+
+/*********************** main area drawing *************************/
+
+void clip_draw_curfra_label(SpaceClip *sc, float x, float y)
+{
+ uiStyle *style= UI_GetStyle();
+ int fontid= style->widget.uifont_id;
+ char str[32];
+ float fontsize, fontwidth;
+
+ /* frame number */
+ BLF_size(fontid, 11.0f, U.dpi);
+ BLI_snprintf(str, sizeof(str), "%d", sc->user.framenr);
+ fontsize= BLF_height(fontid, str);
+ fontwidth= BLF_width(fontid, str);
+
+ glRecti(x, y, x+fontwidth+6, y+fontsize+4);
+
+ UI_ThemeColor(TH_TEXT);
+ BLF_position(fontid, x+2.0f, y+2.0f, 0.0f);
+ BLF_draw(fontid, str, strlen(str));
+}
+
+static void draw_movieclip_cache(SpaceClip *sc, ARegion *ar, MovieClip *clip, Scene *scene)
+{
+ float x;
+ int *points, totseg, i, a;
+ float sfra= SFRA, efra= EFRA, framelen= ar->winx/(efra-sfra+1);
+
+ glEnable(GL_BLEND);
+
+ /* cache background */
+ glColor4ub(128, 128, 255, 64);
+ glRecti(0, 0, ar->winx, 8);
+
+ /* cached segments -- could be usefu lto debug caching strategies */
+ BKE_movieclip_get_cache_segments(clip, &sc->user, &totseg, &points);
+ if(totseg) {
+ glColor4ub(128, 128, 255, 128);
+
+ for(a= 0; a<totseg; a++) {
+ float x1, x2;
+
+ x1= (points[a*2]-sfra)/(efra-sfra+1)*ar->winx;
+ x2= (points[a*2+1]-sfra+1)/(efra-sfra+1)*ar->winx;
+
+ glRecti(x1, 0, x2, 8);
+ }
+ }
+
+ /* track */
+ if(clip->tracking.act_track) {
+ MovieTrackingTrack *track= clip->tracking.act_track;
+
+ for(i= sfra, a= 0; i <= efra; i++) {
+ int framenr;
+ MovieTrackingMarker *marker;
+
+ while(a<track->markersnr) {
+ if(track->markers[a].framenr>=i)
+ break;
+
+ if(a<track->markersnr-1 && track->markers[a+1].framenr>i)
+ break;
+
+ a++;
+ }
+
+ if(a<track->markersnr) marker= &track->markers[a];
+ else marker= &track->markers[track->markersnr-1];
+
+ if((marker->flag&MARKER_DISABLED)==0) {
+ framenr= marker->framenr;
+
+ if(framenr!=i) glColor4ub(128, 128, 0, 96);
+ else if((marker->flag&MARKER_TRACKED)==0) glColor4ub(255, 255, 0, 196);
+ else glColor4ub(255, 255, 0, 96);
+
+ glRecti((i-sfra)*framelen, 0, (i-sfra+1)*framelen, 4);
+ }
+ }
+ }
+
+ /* failed frames */
+ if(clip->tracking.reconstruction.flag&TRACKING_RECONSTRUCTED) {
+ int n= clip->tracking.reconstruction.camnr;
+ MovieReconstructedCamera *cameras= clip->tracking.reconstruction.cameras;
+
+ glColor4ub(255, 0, 0, 96);
+
+ for(i= sfra, a= 0; i <= efra; i++) {
+ int ok= 0;
+
+ while(a<n) {
+ if(cameras[a].framenr==i) {
+ ok= 1;
+ break;
+ }
+ else if(cameras[a].framenr>i) {
+ break;
+ }
+
+ a++;
+ }
+
+ if(!ok)
+ glRecti((i-sfra)*framelen, 0, (i-sfra+1)*framelen, 8);
+ }
+ }
+
+ glDisable(GL_BLEND);
+
+ /* current frame */
+ x= (sc->user.framenr-sfra)/(efra-sfra+1)*ar->winx;
+
+ UI_ThemeColor(TH_CFRAME);
+ glRecti(x, 0, x+framelen, 8);
+
+ clip_draw_curfra_label(sc, x, 8.0f);
+}
+
+static void draw_movieclip_notes(SpaceClip *sc, ARegion *ar)
+{
+ char str[256]= {0};
+
+ if(sc->flag&SC_LOCK_SELECTION)
+ strcpy(str, "Locked");
+
+ if(str[0]) {
+ uiStyle *style= UI_GetStyle();
+ int fontsize, fontwidth;
+ int fontid= style->widget.uifont_id;
+
+ BLF_size(fontid, 11.0f, U.dpi);
+ fontsize= BLF_height(fontid, str);
+ fontwidth= BLF_width(fontid, str);
+
+ glEnable(GL_BLEND);
+
+ glColor4f(0.0f, 0.0f, 0.0f, 0.6f);
+ glRecti(0, ar->winy-fontsize-9, fontwidth+12, ar->winy);
+
+ glColor3f(1.0f, 1.0f, 1.0f);
+ BLF_position(fontid, 6.0f, ar->winy-fontsize-5.0f, 0.0f);
+ BLF_draw(fontid, str, strlen(str));
+
+ glDisable(GL_BLEND);
+ }
+}
+
+static void draw_movieclip_buffer(SpaceClip *sc, ARegion *ar, ImBuf *ibuf,
+ int width, int height, float zoomx, float zoomy)
+{
+ int x, y;
+ MovieClip *clip= ED_space_clip(sc);
+
+ /* set zoom */
+ glPixelZoom(zoomx*width/ibuf->x, zoomy*height/ibuf->y);
+
+ /* find window pixel coordinates of origin */
+ UI_view2d_to_region_no_clip(&ar->v2d, 0.0f, 0.0f, &x, &y);
+
+ if(sc->flag&SC_MUTE_FOOTAGE) {
+ glColor3f(0.0f, 0.0f, 0.0f);
+ glRectf(x, y, x+zoomx*width, y+zoomy*height);
+ } else {
+ if(ibuf->rect_float && !ibuf->rect) {
+ IMB_rect_from_float(ibuf);
+ }
+
+ if(ibuf->rect)
+ glaDrawPixelsSafe(x, y, ibuf->x, ibuf->y, ibuf->x, GL_RGBA, GL_UNSIGNED_BYTE, ibuf->rect);
+ }
+
+ /* draw boundary border for frame if stabilization is enabled */
+ if(sc->flag&SC_SHOW_STABLE && clip->tracking.stabilization.flag&TRACKING_2D_STABILIZATION) {
+ glColor3f(0.0f, 0.0f, 0.0f);
+ glLineStipple(3, 0xaaaa);
+ glEnable(GL_LINE_STIPPLE);
+ glEnable(GL_COLOR_LOGIC_OP);
+ glLogicOp(GL_NOR);
+
+ glPushMatrix();
+ glTranslatef(x, y, 0);
+
+ glScalef(zoomx, zoomy, 0);
+ glMultMatrixf(sc->stabmat);
+
+ glBegin(GL_LINE_LOOP);
+ glVertex2f(0.0f, 0.0f);
+ glVertex2f(ibuf->x, 0.0f);
+ glVertex2f(ibuf->x, ibuf->y);
+ glVertex2f(0.0f, ibuf->y);
+ glEnd();
+
+ glPopMatrix();
+
+ glDisable(GL_COLOR_LOGIC_OP);
+ glDisable(GL_LINE_STIPPLE);
+ }
+
+
+ /* reset zoom */
+ glPixelZoom(1.0f, 1.0f);
+}
+
+static void draw_track_path(SpaceClip *sc, MovieClip *UNUSED(clip), MovieTrackingTrack *track)
+{
+ int count= sc->path_length;
+ int i, a, b, curindex= -1;
+ float path[102][2];
+ int tiny= sc->flag&SC_SHOW_TINY_MARKER, framenr;
+ MovieTrackingMarker *marker;
+
+ if(count==0)
+ return;
+
+ marker= BKE_tracking_get_marker(track, sc->user.framenr);
+ if(marker->framenr!=sc->user.framenr || marker->flag&MARKER_DISABLED)
+ return;
+
+ framenr= marker->framenr;
+
+ a= count;
+ i= framenr-1;
+ while(i>=framenr-count) {
+ marker= BKE_tracking_get_marker(track, i);
+
+ if(!marker || marker->flag&MARKER_DISABLED)
+ break;
+
+ if(marker->framenr==i) {
+ add_v2_v2v2(path[--a], marker->pos, track->offset);
+ ED_clip_point_undistorted_pos(sc, path[a], path[a]);
+
+ if(marker->framenr==sc->user.framenr)
+ curindex= a;
+ } else
+ break;
+
+ i--;
+ }
+
+ b= count;
+ i= framenr;
+ while(i<=framenr+count) {
+ marker= BKE_tracking_get_marker(track, i);
+
+ if(!marker || marker->flag&MARKER_DISABLED)
+ break;
+
+ if(marker->framenr==i) {
+ if(marker->framenr==sc->user.framenr)
+ curindex= b;
+
+ add_v2_v2v2(path[b++], marker->pos, track->offset);
+ ED_clip_point_undistorted_pos(sc, path[b-1], path[b-1]);
+ } else
+ break;
+
+ i++;
+ }
+
+ if(!tiny) {
+ UI_ThemeColor(TH_MARKER_OUTLINE);
+
+ if(TRACK_VIEW_SELECTED(sc, track)) {
+ glPointSize(5.0f);
+ glBegin(GL_POINTS);
+ for(i= a; i<b; i++) {
+ if(i!=curindex)
+ glVertex2f(path[i][0], path[i][1]);
+ }
+ glEnd();
+ }
+
+ glLineWidth(3.0f);
+ glBegin(GL_LINE_STRIP);
+ for(i= a; i<b; i++)
+ glVertex2f(path[i][0], path[i][1]);
+ glEnd();
+ glLineWidth(1.0f);
+ }
+
+ UI_ThemeColor(TH_PATH_BEFORE);
+
+ if(TRACK_VIEW_SELECTED(sc, track)) {
+ glPointSize(3.0f);
+ glBegin(GL_POINTS);
+ for(i= a; i<b; i++) {
+ if(i==count+1)
+ UI_ThemeColor(TH_PATH_AFTER);
+
+ if(i!=curindex)
+ glVertex2f(path[i][0], path[i][1]);
+ }
+ glEnd();
+ }
+
+ UI_ThemeColor(TH_PATH_BEFORE);
+
+ glBegin(GL_LINE_STRIP);
+ for(i= a; i<b; i++) {
+ if(i==count+1)
+ UI_ThemeColor(TH_PATH_AFTER);
+
+ glVertex2f(path[i][0], path[i][1]);
+ }
+ glEnd();
+ glPointSize(1.0f);
+}
+
+static void draw_marker_outline(SpaceClip *sc, MovieTrackingTrack *track, MovieTrackingMarker *marker, float marker_pos[2], int width, int height)
+{
+ int tiny= sc->flag&SC_SHOW_TINY_MARKER;
+ int show_search= 0;
+ float px[2];
+
+ UI_ThemeColor(TH_MARKER_OUTLINE);
+
+ px[0]= 1.0f/width/sc->zoom;
+ px[1]= 1.0f/height/sc->zoom;
+
+ if((marker->flag&MARKER_DISABLED)==0) {
+ float pos[2];
+ rctf r;
+
+ BLI_init_rctf(&r, track->pat_min[0], track->pat_max[0], track->pat_min[1], track->pat_max[1]);
+ add_v2_v2v2(pos, marker->pos, track->offset);
+
+ ED_clip_point_undistorted_pos(sc, pos, pos);
+
+ if(BLI_in_rctf(&r, pos[0]-marker_pos[0], pos[1]-marker_pos[1])) {
+ if(tiny) glPointSize(3.0f);
+ else glPointSize(4.0f);
+ glBegin(GL_POINTS);
+ glVertex2f(pos[0], pos[1]);
+ glEnd();
+ glPointSize(1.0f);
+ } else {
+ if(!tiny) glLineWidth(3.0f);
+ glBegin(GL_LINES);
+ glVertex2f(pos[0] + px[0]*2, pos[1]);
+ glVertex2f(pos[0] + px[0]*8, pos[1]);
+
+ glVertex2f(pos[0] - px[0]*2, pos[1]);
+ glVertex2f(pos[0] - px[0]*8, pos[1]);
+
+ glVertex2f(pos[0], pos[1] - px[1]*2);
+ glVertex2f(pos[0], pos[1] - px[1]*8);
+
+ glVertex2f(pos[0], pos[1] + px[1]*2);
+ glVertex2f(pos[0], pos[1] + px[1]*8);
+ glEnd();
+ if(!tiny) glLineWidth(1.0f);
+ }
+ }
+
+ /* pattern and search outline */
+ glPushMatrix();
+ glTranslatef(marker_pos[0], marker_pos[1], 0);
+
+ if(!tiny) glLineWidth(3.0f);
+
+ if(sc->flag&SC_SHOW_MARKER_PATTERN) {
+ glBegin(GL_LINE_LOOP);
+ glVertex2f(track->pat_min[0], track->pat_min[1]);
+ glVertex2f(track->pat_max[0], track->pat_min[1]);
+ glVertex2f(track->pat_max[0], track->pat_max[1]);
+ glVertex2f(track->pat_min[0], track->pat_max[1]);
+ glEnd();
+ }
+
+ show_search= TRACK_VIEW_SELECTED(sc, track) && ((marker->flag&MARKER_DISABLED)==0 || (sc->flag&SC_SHOW_MARKER_PATTERN)==0);
+ if(sc->flag&SC_SHOW_MARKER_SEARCH && show_search) {
+ glBegin(GL_LINE_LOOP);
+ glVertex2f(track->search_min[0], track->search_min[1]);
+ glVertex2f(track->search_max[0], track->search_min[1]);
+ glVertex2f(track->search_max[0], track->search_max[1]);
+ glVertex2f(track->search_min[0], track->search_max[1]);
+ glEnd();
+ }
+ glPopMatrix();
+
+ if(!tiny) glLineWidth(1.0f);
+}
+
+static void track_colors(MovieTrackingTrack *track, int act, float col[3], float scol[3])
+{
+ if(track->flag&TRACK_CUSTOMCOLOR) {
+ if(act) UI_GetThemeColor3fv(TH_ACT_MARKER, scol);
+ else copy_v3_v3(scol, track->color);
+
+ mul_v3_v3fl(col, track->color, 0.5f);
+ } else {
+ UI_GetThemeColor3fv(TH_MARKER, col);
+
+ if(act) UI_GetThemeColor3fv(TH_ACT_MARKER, scol);
+ else UI_GetThemeColor3fv(TH_SEL_MARKER, scol);
+ }
+}
+
+static void draw_marker_areas(SpaceClip *sc, MovieTrackingTrack *track, MovieTrackingMarker *marker, float marker_pos[2], int width, int height, int act, int sel)
+{
+ int tiny= sc->flag&SC_SHOW_TINY_MARKER;
+ int show_search= 0;
+ float col[3], scol[3], px[2];
+
+ track_colors(track, act, col, scol);
+
+ px[0]= 1.0f/width/sc->zoom;
+ px[1]= 1.0f/height/sc->zoom;
+
+ /* marker position and offset position */
+ if((track->flag&SELECT)==sel && (marker->flag&MARKER_DISABLED)==0) {
+ float pos[2];
+ rctf r;
+
+ if(track->flag&TRACK_LOCKED) {
+ if(act) UI_ThemeColor(TH_ACT_MARKER);
+ else if(track->flag&SELECT) UI_ThemeColorShade(TH_LOCK_MARKER, 64);
+ else UI_ThemeColor(TH_LOCK_MARKER);
+ } else {
+ if(track->flag&SELECT) glColor3fv(scol);
+ else glColor3fv(col);
+ }
+
+ BLI_init_rctf(&r, track->pat_min[0], track->pat_max[0], track->pat_min[1], track->pat_max[1]);
+ add_v2_v2v2(pos, marker->pos, track->offset);
+ ED_clip_point_undistorted_pos(sc, pos, pos);
+
+ if(BLI_in_rctf(&r, pos[0]-marker_pos[0], pos[1]-marker_pos[1])) {
+ if(!tiny) glPointSize(2.0f);
+ glBegin(GL_POINTS);
+ glVertex2f(pos[0], pos[1]);
+ glEnd();
+ if(!tiny) glPointSize(1.0f);
+ } else {
+ glBegin(GL_LINES);
+ glVertex2f(pos[0] + px[0]*3, pos[1]);
+ glVertex2f(pos[0] + px[0]*7, pos[1]);
+
+ glVertex2f(pos[0] - px[0]*3, pos[1]);
+ glVertex2f(pos[0] - px[0]*7, pos[1]);
+
+ glVertex2f(pos[0], pos[1] - px[1]*3);
+ glVertex2f(pos[0], pos[1] - px[1]*7);
+
+ glVertex2f(pos[0], pos[1] + px[1]*3);
+ glVertex2f(pos[0], pos[1] + px[1]*7);
+ glEnd();
+
+ glColor3f(0.0f, 0.0f, 0.0f);
+ glLineStipple(3, 0xaaaa);
+ glEnable(GL_LINE_STIPPLE);
+ glEnable(GL_COLOR_LOGIC_OP);
+ glLogicOp(GL_NOR);
+
+ glBegin(GL_LINES);
+ glVertex2fv(pos);
+ glVertex2fv(marker_pos);
+ glEnd();
+
+ glDisable(GL_COLOR_LOGIC_OP);
+ glDisable(GL_LINE_STIPPLE);
+ }
+ }
+
+ /* pattern */
+ glPushMatrix();
+ glTranslatef(marker_pos[0], marker_pos[1], 0);
+
+ if(tiny) {
+ glLineStipple(3, 0xaaaa);
+ glEnable(GL_LINE_STIPPLE);
+ }
+
+ if((track->pat_flag&SELECT)==sel && (sc->flag&SC_SHOW_MARKER_PATTERN)) {
+ if(track->flag&TRACK_LOCKED) {
+ if(act) UI_ThemeColor(TH_ACT_MARKER);
+ else if(track->pat_flag&SELECT) UI_ThemeColorShade(TH_LOCK_MARKER, 64);
+ else UI_ThemeColor(TH_LOCK_MARKER);
+ }
+ else if(marker->flag&MARKER_DISABLED) {
+ if(act) UI_ThemeColor(TH_ACT_MARKER);
+ else if(track->pat_flag&SELECT) UI_ThemeColorShade(TH_DIS_MARKER, 128);
+ else UI_ThemeColor(TH_DIS_MARKER);
+ } else {
+ if(track->pat_flag&SELECT) glColor3fv(scol);
+ else glColor3fv(col);
+ }
+
+ glBegin(GL_LINE_LOOP);
+ glVertex2f(track->pat_min[0], track->pat_min[1]);
+ glVertex2f(track->pat_max[0], track->pat_min[1]);
+ glVertex2f(track->pat_max[0], track->pat_max[1]);
+ glVertex2f(track->pat_min[0], track->pat_max[1]);
+ glEnd();
+ }
+
+ /* search */
+ show_search= TRACK_VIEW_SELECTED(sc, track) && ((marker->flag&MARKER_DISABLED)==0 || (sc->flag&SC_SHOW_MARKER_PATTERN)==0);
+ if((track->search_flag&SELECT)==sel && (sc->flag&SC_SHOW_MARKER_SEARCH) && show_search) {
+ if(track->flag&TRACK_LOCKED) {
+ if(act) UI_ThemeColor(TH_ACT_MARKER);
+ else if(track->search_flag&SELECT) UI_ThemeColorShade(TH_LOCK_MARKER, 64);
+ else UI_ThemeColor(TH_LOCK_MARKER);
+ }
+ else if(marker->flag&MARKER_DISABLED) {
+ if(act) UI_ThemeColor(TH_ACT_MARKER);
+ else if(track->search_flag&SELECT) UI_ThemeColorShade(TH_DIS_MARKER, 128);
+ else UI_ThemeColor(TH_DIS_MARKER);
+ } else {
+ if(track->search_flag&SELECT) glColor3fv(scol);
+ else glColor3fv(col);
+ }
+
+ glBegin(GL_LINE_LOOP);
+ glVertex2f(track->search_min[0], track->search_min[1]);
+ glVertex2f(track->search_max[0], track->search_min[1]);
+ glVertex2f(track->search_max[0], track->search_max[1]);
+ glVertex2f(track->search_min[0], track->search_max[1]);
+ glEnd();
+ }
+
+ /* pyramid */
+ if((sel == TRACK_SELECTED(track) && sel && (sc->flag&SC_SHOW_PYRAMID_LEVELS) && (track->tracker==TRACKER_KLT))) {
+ if(track->flag&TRACK_LOCKED) {
+ if(act) UI_ThemeColor(TH_ACT_MARKER);
+ else if(track->pat_flag&SELECT) UI_ThemeColorShade(TH_LOCK_MARKER, 64);
+ else UI_ThemeColor(TH_LOCK_MARKER);
+ }
+ else if(marker->flag&MARKER_DISABLED) {
+ if(act) UI_ThemeColor(TH_ACT_MARKER);
+ else if(track->pat_flag&SELECT) UI_ThemeColorShade(TH_DIS_MARKER, 128);
+ else UI_ThemeColor(TH_DIS_MARKER);
+ } else {
+ if(track->pat_flag&SELECT) glColor3fv(scol);
+ else glColor3fv(col);
+ }
+
+ {
+ int i = 0;
+ glPushMatrix();
+ glEnable(GL_LINE_STIPPLE);
+ for (i = 1; i < track->pyramid_levels; ++i) {
+ glScalef(2.0f, 2.0f, 1.0);
+ }
+ /* only draw a pattern for the coarsest level */
+ glBegin(GL_LINE_LOOP);
+ glVertex2f(track->pat_min[0], track->pat_min[1]);
+ glVertex2f(track->pat_max[0], track->pat_min[1]);
+ glVertex2f(track->pat_max[0], track->pat_max[1]);
+ glVertex2f(track->pat_min[0], track->pat_max[1]);
+ glEnd();
+ glDisable(GL_LINE_STIPPLE);
+ glPopMatrix();
+ }
+ }
+
+ if(tiny)
+ glDisable(GL_LINE_STIPPLE);
+
+ glPopMatrix();
+}
+
+static void draw_marker_slide_zones(SpaceClip *sc, MovieTrackingTrack *track, MovieTrackingMarker *marker,
+ float marker_pos[2], int outline, int sel, int act, int width, int height)
+{
+ float x, y, dx, dy, patdx, patdy, searchdx, searchdy, tdx, tdy;
+ int tiny= sc->flag&SC_SHOW_TINY_MARKER;
+ float col[3], scol[3], px[2];
+
+ if((tiny && outline) || (marker->flag&MARKER_DISABLED))
+ return;
+
+ if(!TRACK_VIEW_SELECTED(sc, track) || track->flag&TRACK_LOCKED)
+ return;
+
+ track_colors(track, act, col, scol);
+
+ if(outline) {
+ glLineWidth(3.0f);
+ UI_ThemeColor(TH_MARKER_OUTLINE);
+ }
+
+ glPushMatrix();
+ glTranslatef(marker_pos[0], marker_pos[1], 0);
+
+ dx= 6.0f/width/sc->zoom;
+ dy= 6.0f/height/sc->zoom;
+
+ patdx= MIN2(dx*2.0f/3.0f, (track->pat_max[0]-track->pat_min[0])/6.0f);
+ patdy= MIN2(dy*2.0f/3.0f, (track->pat_max[1]-track->pat_min[1])/6.0f);
+
+ searchdx= MIN2(dx, (track->search_max[0]-track->search_min[0])/6.0f);
+ searchdy= MIN2(dy, (track->search_max[1]-track->search_min[1])/6.0f);
+
+ px[0]= 1.0f/sc->zoom/width/sc->scale;
+ px[1]= 1.0f/sc->zoom/height/sc->scale;
+
+ if((sc->flag&SC_SHOW_MARKER_SEARCH) && ((track->search_flag&SELECT)==sel || outline)) {
+ if(!outline) {
+ if(track->search_flag&SELECT) glColor3fv(scol);
+ else glColor3fv(col);
+ }
+
+ /* search offset square */
+ x= track->search_min[0];
+ y= track->search_max[1];
+
+ tdx= searchdx;
+ tdy= searchdy;
+
+ if(outline) {
+ tdx+= px[0];
+ tdy+= px[1];
+ }
+
+ glBegin(GL_QUADS);
+ glVertex3f(x-tdx, y+tdy, 0);
+ glVertex3f(x+tdx, y+tdy, 0);
+ glVertex3f(x+tdx, y-tdy, 0);
+ glVertex3f(x-tdx, y-tdy, 0);
+ glEnd();
+
+ /* search resizing triangle */
+ x= track->search_max[0];
+ y= track->search_min[1];
+
+ tdx= searchdx*2.0f;
+ tdy= searchdy*2.0f;
+
+ if(outline) {
+ tdx+= px[0];
+ tdy+= px[1];
+ }
+
+ glBegin(GL_TRIANGLES);
+ glVertex3f(x, y, 0);
+ glVertex3f(x-tdx, y, 0);
+ glVertex3f(x, y+tdy, 0);
+ glEnd();
+ }
+
+ if((sc->flag&SC_SHOW_MARKER_PATTERN) && ((track->pat_flag&SELECT)==sel || outline)) {
+ if(!outline) {
+ if(track->pat_flag&SELECT) glColor3fv(scol);
+ else glColor3fv(col);
+ }
+
+ /* pattern offset square */
+ x= track->pat_min[0];
+ y= track->pat_max[1];
+
+ tdx= patdx;
+ tdy= patdy;
+
+ if(outline) {
+ tdx+= px[0];
+ tdy+= px[1];
+ }
+
+ glBegin(GL_QUADS);
+ glVertex3f(x-tdx, y+tdy, 0);
+ glVertex3f(x+tdx, y+tdy, 0);
+ glVertex3f(x+tdx, y-tdy, 0);
+ glVertex3f(x-tdx, y-tdy, 0);
+ glEnd();
+
+ /* pattern resizing triangle */
+ x= track->pat_max[0];
+ y= track->pat_min[1];
+
+ tdx= patdx*2.0f;
+ tdy= patdy*2.0f;
+
+ if(outline) {
+ tdx+= px[0];
+ tdy+= px[1];
+ }
+
+ glBegin(GL_TRIANGLES);
+ glVertex3f(x, y, 0);
+ glVertex3f(x-tdx, y, 0);
+ glVertex3f(x, y+tdy, 0);
+ glEnd();
+ }
+
+ glPopMatrix();
+
+ if(outline)
+ glLineWidth(1.0f);
+}
+
+static void draw_marker_texts(SpaceClip *sc, MovieTrackingTrack *track, MovieTrackingMarker *marker, float marker_pos[2], int act,
+ int width, int height, float zoomx, float zoomy)
+{
+ char str[128]= {0}, state[64]= {0};
+ float dx= 0.0f, dy= 0.0f, fontsize, pos[3];
+ uiStyle *style= U.uistyles.first;
+ int fontid= style->widget.uifont_id;
+
+ if(!TRACK_VIEW_SELECTED(sc, track))
+ return;
+
+ BLF_size(fontid, 11.0f, U.dpi);
+ fontsize= BLF_height_max(fontid);
+
+ if(marker->flag&MARKER_DISABLED) {
+ if(act) UI_ThemeColor(TH_ACT_MARKER);
+ else UI_ThemeColorShade(TH_DIS_MARKER, 128);
+ } else {
+ if(act) UI_ThemeColor(TH_ACT_MARKER);
+ else UI_ThemeColor(TH_SEL_MARKER);
+ }
+
+ if(sc->flag&SC_SHOW_MARKER_SEARCH) {
+ dx= track->search_min[0];
+ dy= track->search_min[1];
+ } else if(sc->flag&SC_SHOW_MARKER_PATTERN) {
+ dx= track->pat_min[0];
+ dy= track->pat_min[1];
+ }
+
+ pos[0]= (marker_pos[0]+dx)*width;
+ pos[1]= (marker_pos[1]+dy)*height;
+ pos[2]= 0.0f;
+
+ mul_m4_v3(sc->stabmat, pos);
+
+ pos[0]= pos[0]*zoomx;
+ pos[1]= pos[1]*zoomy - fontsize;
+
+ if(marker->flag&MARKER_DISABLED) strcpy(state, "disabled");
+ else if(marker->framenr!=sc->user.framenr) strcpy(state, "estimated");
+ else if(marker->flag&MARKER_TRACKED) strcpy(state, "tracked");
+ else strcpy(state, "keyframed");
+
+ if(state[0])
+ BLI_snprintf(str, sizeof(str), "%s: %s", track->name, state);
+ else
+ BLI_snprintf(str, sizeof(str), "%s", track->name);
+
+ BLF_position(fontid, pos[0], pos[1], 0.0f);
+ BLF_draw(fontid, str, strlen(str));
+ pos[1]-= fontsize;
+
+ if(track->flag&TRACK_HAS_BUNDLE) {
+ BLI_snprintf(str, sizeof(str), "Average error: %.3f", track->error);
+ BLF_position(fontid, pos[0], pos[1], 0.0f);
+ BLF_draw(fontid, str, strlen(str));
+ pos[1]-= fontsize;
+ }
+
+ if(track->flag&TRACK_LOCKED) {
+ BLF_position(fontid, pos[0], pos[1], 0.0f);
+ BLF_draw(fontid, "locked", 6);
+ }
+}
+
+static void view2d_to_region_float(View2D *v2d, float x, float y, float *regionx, float *regiony)
+{
+ /* express given coordinates as proportional values */
+ x= -v2d->cur.xmin / (v2d->cur.xmax-v2d->cur.xmin);
+ y= -v2d->cur.ymin / (v2d->cur.ymax-v2d->cur.ymin);
+
+ /* convert proportional distances to screen coordinates */
+ *regionx= v2d->mask.xmin + x*(v2d->mask.xmax-v2d->mask.xmin);
+ *regiony= v2d->mask.ymin + y*(v2d->mask.ymax-v2d->mask.ymin);
+}
+
+static void draw_tracking_tracks(SpaceClip *sc, ARegion *ar, MovieClip *clip,
+ int width, int height, float zoomx, float zoomy)
+{
+ float x, y;
+ MovieTracking* tracking= &clip->tracking;
+ MovieTrackingMarker *marker;
+ MovieTrackingTrack *track, *act_track;
+ int framenr= sc->user.framenr;
+ int undistort= sc->user.render_flag&MCLIP_PROXY_RENDER_UNDISTORT;
+ float *marker_pos= NULL, *fp, *active_pos= NULL, cur_pos[2];
+
+ /* ** find window pixel coordinates of origin ** */
+
+ /* UI_view2d_to_region_no_clip return integer values, this could
+ lead to 1px flickering when view is locked to selection during playbeck.
+ to avoid this flickering, calclate base point in the same way as it happens
+ in UI_view2d_to_region_no_clip, but do it in floats here */
+
+ view2d_to_region_float(&ar->v2d, 0.0f, 0.0f, &x, &y);
+
+ glPushMatrix();
+ glTranslatef(x, y, 0);
+
+ glPushMatrix();
+ glScalef(zoomx, zoomy, 0);
+ glMultMatrixf(sc->stabmat);
+ glScalef(width, height, 0);
+
+ act_track= clip->tracking.act_track;
+
+ if(sc->user.render_flag&MCLIP_PROXY_RENDER_UNDISTORT) {
+ int count= 0;
+
+ /* count */
+ track= tracking->tracks.first;
+ while(track) {
+ if((track->flag&TRACK_HIDDEN)==0) {
+ marker= BKE_tracking_get_marker(track, framenr);
+
+ if(MARKER_VISIBLE(sc, marker))
+ count++;
+ }
+
+ track= track->next;
+ }
+
+ /* undistort */
+ if(count) {
+ marker_pos= MEM_callocN(2*sizeof(float)*count, "draw_tracking_tracks marker_pos");
+
+ track= tracking->tracks.first;
+ fp= marker_pos;
+ while(track) {
+ if((track->flag&TRACK_HIDDEN)==0) {
+ marker= BKE_tracking_get_marker(track, framenr);
+
+ if(MARKER_VISIBLE(sc, marker)) {
+ ED_clip_point_undistorted_pos(sc, marker->pos, fp);
+
+ if(track==act_track)
+ active_pos= fp;
+
+ fp+= 2;
+ }
+ }
+
+ track= track->next;
+ }
+ }
+ }
+
+ if(sc->flag&SC_SHOW_TRACK_PATH) {
+ track= tracking->tracks.first;
+ while(track) {
+ if((track->flag&TRACK_HIDDEN)==0)
+ draw_track_path(sc, clip, track);
+
+ track= track->next;
+ }
+ }
+
+ /* markers outline and non-selected areas */
+ track= tracking->tracks.first;
+ fp= marker_pos;
+ while(track) {
+ if((track->flag&TRACK_HIDDEN)==0) {
+ marker= BKE_tracking_get_marker(track, framenr);
+
+ if(MARKER_VISIBLE(sc, marker)) {
+ copy_v2_v2(cur_pos, fp ? fp : marker->pos);
+
+ draw_marker_outline(sc, track, marker, cur_pos, width, height);
+ draw_marker_areas(sc, track, marker, cur_pos, width, height, 0, 0);
+ draw_marker_slide_zones(sc, track, marker, cur_pos, 1, 0, 0, width, height);
+ draw_marker_slide_zones(sc, track, marker, cur_pos, 0, 0, 0, width, height);
+
+ if(fp) fp+= 2;
+ }
+ }
+
+ track= track->next;
+ }
+
+ /* selected areas only, so selection wouldn't be overlapped by
+ non-selected areas */
+ track= tracking->tracks.first;
+ fp= marker_pos;
+ while(track) {
+ if((track->flag&TRACK_HIDDEN)==0) {
+ int act= track==act_track;
+
+ if(!act) {
+ marker= BKE_tracking_get_marker(track, framenr);
+
+ if(MARKER_VISIBLE(sc, marker)) {
+ copy_v2_v2(cur_pos, fp ? fp : marker->pos);
+
+ draw_marker_areas(sc, track, marker, cur_pos, width, height, 0, 1);
+ draw_marker_slide_zones(sc, track, marker, cur_pos, 0, 1, 0, width, height);
+ }
+ }
+
+ if(MARKER_VISIBLE(sc, marker) && fp)
+ fp+= 2;
+ }
+
+ track= track->next;
+ }
+
+ /* active marker would be displayed on top of everything else */
+ if(act_track) {
+ if((act_track->flag&TRACK_HIDDEN)==0) {
+ marker= BKE_tracking_get_marker(act_track, framenr);
+
+ if(MARKER_VISIBLE(sc, marker)) {
+ copy_v2_v2(cur_pos, active_pos ? active_pos : marker->pos);
+
+ draw_marker_areas(sc, act_track, marker, cur_pos, width, height, 1, 1);
+ draw_marker_slide_zones(sc, act_track, marker, cur_pos, 0, 1, 1, width, height);
+ }
+ }
+ }
+
+ if(sc->flag&SC_SHOW_BUNDLES) {
+ float pos[4], vec[4], mat[4][4], aspy;
+
+ glEnable(GL_POINT_SMOOTH);
+ glPointSize(3.0f);
+
+ aspy= 1.0f/clip->tracking.camera.pixel_aspect;
+ BKE_tracking_projection_matrix(tracking, framenr, width, height, mat);
+
+ track= tracking->tracks.first;
+ while(track) {
+ if((track->flag&TRACK_HIDDEN)==0 && track->flag&TRACK_HAS_BUNDLE) {
+ marker= BKE_tracking_get_marker(track, framenr);
+
+ if(MARKER_VISIBLE(sc, marker)) {
+ float npos[2];
+ copy_v4_v4(vec, track->bundle_pos);
+ vec[3]=1;
+
+ mul_v4_m4v4(pos, mat, vec);
+
+ pos[0]= (pos[0]/(pos[3]*2.0f)+0.5f)*width;
+ pos[1]= (pos[1]/(pos[3]*2.0f)+0.5f)*height*aspy;
+
+ BKE_tracking_apply_intrinsics(tracking, pos, npos);
+
+ if(npos[0]>=0.0f && npos[1]>=0.0f && npos[0]<=width && npos[1]<=height*aspy) {
+ vec[0]= (marker->pos[0]+track->offset[0])*width;
+ vec[1]= (marker->pos[1]+track->offset[1])*height*aspy;
+
+ sub_v2_v2(vec, npos);
+
+ if(len_v2(vec)<3) glColor3f(0.0f, 1.0f, 0.0f);
+ else glColor3f(1.0f, 0.0f, 0.0f);
+
+ glBegin(GL_POINTS);
+ if(undistort) glVertex3f(pos[0]/width, pos[1]/(height*aspy), 0);
+ else glVertex3f(npos[0]/width, npos[1]/(height*aspy), 0);
+ glEnd();
+ }
+ }
+ }
+
+ track= track->next;
+ }
+
+ glPointSize(1.0f);
+ glDisable(GL_POINT_SMOOTH);
+ }
+
+ glPopMatrix();
+
+ if(sc->flag&SC_SHOW_NAMES) {
+ /* scaling should be cleared before drawing texts, otherwise font would also be scaled */
+ track= tracking->tracks.first;
+ fp= marker_pos;
+ while(track) {
+ if((track->flag&TRACK_HIDDEN)==0) {
+ marker= BKE_tracking_get_marker(track, framenr);
+
+ if(MARKER_VISIBLE(sc, marker)) {
+ int act= track==act_track;
+
+ copy_v2_v2(cur_pos, fp ? fp : marker->pos);
+
+ draw_marker_texts(sc, track, marker, cur_pos, act, width, height, zoomx, zoomy);
+
+ if(fp) fp+= 2;
+ }
+ }
+
+ track= track->next;
+ }
+ }
+
+ glPopMatrix();
+
+ if(marker_pos)
+ MEM_freeN(marker_pos);
+}
+
+static void draw_distortion(SpaceClip *sc, ARegion *ar, MovieClip *clip, int width, int height, float zoomx, float zoomy)
+{
+ float x, y;
+ const int n= 10;
+ int i, j, a;
+ float pos[2], tpos[2], grid[11][11][2];
+ MovieTracking *tracking= &clip->tracking;
+ float aspy= 1.0f/tracking->camera.pixel_aspect;
+ float dx= (float)width/n, dy= (float)height/n*aspy;
+
+ if(sc->mode!=SC_MODE_DISTORTION)
+ return;
+
+ if(!tracking->camera.focal)
+ return;
+
+ if((sc->flag&SC_SHOW_GRID)==0 && (sc->flag&SC_MANUAL_CALIBRATION)==0)
+ return;
+
+ view2d_to_region_float(&ar->v2d, 0.0f, 0.0f, &x, &y);
+
+ glPushMatrix();
+ glTranslatef(x, y, 0);
+ glScalef(zoomx, zoomy, 0);
+ glMultMatrixf(sc->stabmat);
+ glScalef(width, height, 0);
+
+ /* grid */
+ if(sc->flag&SC_SHOW_GRID) {
+ float val[4][2], idx[4][2];
+ float min[2], max[2];
+
+ for(a=0; a<4; a++) {
+ if(a<2) val[a][a%2]= FLT_MAX;
+ else val[a][a%2]= -FLT_MAX;
+ }
+
+ zero_v2(pos);
+ for(i= 0; i<=n; i++) {
+ for(j= 0; j<=n; j++) {
+ if(i==0 || j==0 || i==n || j==n) {
+ BKE_tracking_apply_intrinsics(tracking, pos, tpos);
+
+ for(a=0; a<4; a++) {
+ int ok;
+
+ if(a<2) ok= tpos[a%2] < val[a][a%2];
+ else ok= tpos[a%2] > val[a][a%2];
+
+ if(ok) {
+ copy_v2_v2(val[a], tpos);
+ idx[a][0]= j;
+ idx[a][1]= i;
+ }
+ }
+ }
+
+ pos[0]+= dx;
+ }
+
+ pos[0]= 0.0f;
+ pos[1]+= dy;
+ }
+
+ INIT_MINMAX2(min, max);
+
+ for(a= 0; a<4; a++) {
+ pos[0]= idx[a][0]*dx;
+ pos[1]= idx[a][1]*dy;
+
+ BKE_tracking_invert_intrinsics(tracking, pos, tpos);
+
+ DO_MINMAX2(tpos, min, max);
+ }
+
+ copy_v2_v2(pos, min);
+ dx= (max[0]-min[0])/n;
+ dy= (max[1]-min[1])/n;
+
+ for(i= 0; i<=n; i++) {
+ for(j= 0; j<=n; j++) {
+ BKE_tracking_apply_intrinsics(tracking, pos, grid[i][j]);
+
+ grid[i][j][0]/= width;
+ grid[i][j][1]/= height*aspy;
+
+ pos[0]+= dx;
+ }
+
+ pos[0]= min[0];
+ pos[1]+= dy;
+ }
+
+ glColor3f(1.0f, 0.0f, 0.0f);
+
+ for(i= 0; i<=n; i++) {
+ glBegin(GL_LINE_STRIP);
+ for(j= 0; j<=n; j++) {
+ glVertex2fv(grid[i][j]);
+ }
+ glEnd();
+ }
+
+ for(j= 0; j<=n; j++) {
+ glBegin(GL_LINE_STRIP);
+ for(i= 0; i<=n; i++) {
+ glVertex2fv(grid[i][j]);
+ }
+ glEnd();
+ }
+ }
+
+ if(sc->flag&SC_MANUAL_CALIBRATION && clip->gpd) {
+ bGPDlayer *layer= clip->gpd->layers.first;
+
+ while(layer) {
+ bGPDframe *frame= layer->frames.first;
+
+ glColor4fv(layer->color);
+ glLineWidth(layer->thickness);
+ glPointSize((float)(layer->thickness + 2));
+
+ while(frame) {
+ bGPDstroke *stroke= frame->strokes.first;
+
+ while(stroke) {
+ if(stroke->flag&GP_STROKE_2DSPACE) {
+ if(stroke->totpoints>1) {
+ glBegin(GL_LINE_STRIP);
+ for(i= 0; i<stroke->totpoints-1; i++) {
+ float npos[2], dpos[2], len;
+ int steps;
+
+ pos[0]= stroke->points[i].x*width;
+ pos[1]= stroke->points[i].y*height*aspy;
+
+ npos[0]= stroke->points[i+1].x*width;
+ npos[1]= stroke->points[i+1].y*height*aspy;
+
+ len= len_v2v2(pos, npos);
+ steps= ceil(len/5.0f);
+
+ /* we want to distort only long straight lines */
+ if(stroke->totpoints==2) {
+ BKE_tracking_invert_intrinsics(tracking, pos, pos);
+ BKE_tracking_invert_intrinsics(tracking, npos, npos);
+ }
+
+ sub_v2_v2v2(dpos, npos, pos);
+ mul_v2_fl(dpos, 1.0f/steps);
+
+ for(j= 0; j<=steps; j++) {
+ BKE_tracking_apply_intrinsics(tracking, pos, tpos);
+ glVertex2f(tpos[0]/width, tpos[1]/(height*aspy));
+
+ add_v2_v2(pos, dpos);
+ }
+ }
+ glEnd();
+ }
+ else if(stroke->totpoints==1) {
+ glBegin(GL_POINTS);
+ glVertex2f(stroke->points[0].x, stroke->points[0].y);
+ glEnd();
+ }
+ }
+
+ stroke= stroke->next;
+ }
+
+ frame= frame->next;
+ }
+
+ layer= layer->next;
+ }
+
+ glLineWidth(1.0f);
+ glPointSize(1.0f);
+ }
+
+ glPopMatrix();
+}
+
+void clip_draw_main(SpaceClip *sc, ARegion *ar, Scene *scene)
+{
+ MovieClip *clip= ED_space_clip(sc);
+ ImBuf *ibuf;
+ int width, height;
+ float zoomx, zoomy;
+
+ /* if no clip, nothing to do */
+ if(!clip)
+ return;
+
+ ED_space_clip_size(sc, &width, &height);
+ ED_space_clip_zoom(sc, ar, &zoomx, &zoomy);
+
+ if(sc->flag&SC_SHOW_STABLE) {
+ float smat[4][4], ismat[4][4];
+
+ ibuf= ED_space_clip_get_stable_buffer(sc, sc->loc, &sc->scale, &sc->angle);
+ BKE_tracking_stabdata_to_mat4(width, height, sc->loc, sc->scale, sc->angle, sc->stabmat);
+
+ unit_m4(smat);
+ smat[0][0]= 1.0f/width;
+ smat[1][1]= 1.0f/height;
+ invert_m4_m4(ismat, smat);
+
+ mul_serie_m4(sc->unistabmat, smat, sc->stabmat, ismat, NULL, NULL, NULL, NULL, NULL);
+ } else {
+ ibuf= ED_space_clip_get_buffer(sc);
+
+ zero_v2(sc->loc);
+ sc->scale= 1.0f;
+ unit_m4(sc->stabmat);
+ unit_m4(sc->unistabmat);
+ }
+
+ if(ibuf) {
+ draw_movieclip_buffer(sc, ar, ibuf, width, height, zoomx, zoomy);
+ IMB_freeImBuf(ibuf);
+
+ draw_tracking_tracks(sc, ar, clip, width, height, zoomx, zoomy);
+ draw_distortion(sc, ar, clip, width, height, zoomx, zoomy);
+ }
+
+ draw_movieclip_cache(sc, ar, clip, scene);
+ draw_movieclip_notes(sc, ar);
+}
+
+/* draw grease pencil */
+void clip_draw_grease_pencil(bContext *C, int onlyv2d)
+{
+ SpaceClip *sc= CTX_wm_space_clip(C);
+ MovieClip *clip= ED_space_clip(sc);
+ ImBuf *ibuf;
+
+ if((sc->flag&SC_SHOW_GPENCIL)==0 || !clip)
+ return;
+
+ if(onlyv2d) {
+ /* if manual calibration is used then grase pencil data is already
+ drawed in draw_distortion */
+ if((sc->flag&SC_MANUAL_CALIBRATION)==0 || sc->mode!=SC_MODE_DISTORTION) {
+ ibuf= ED_space_clip_get_buffer(sc);
+
+ if(ibuf) {
+ glPushMatrix();
+ glMultMatrixf(sc->unistabmat);
+ draw_gpencil_2dimage(C, ibuf);
+
+ IMB_freeImBuf(ibuf);
+ glPopMatrix();
+ }
+ }
+ } else {
+ draw_gpencil_view2d(C, 0);
+ }
+}
diff --git a/source/blender/editors/space_clip/clip_editor.c b/source/blender/editors/space_clip/clip_editor.c
new file mode 100644
index 00000000000..0de7fed1f1a
--- /dev/null
+++ b/source/blender/editors/space_clip/clip_editor.c
@@ -0,0 +1,302 @@
+/*
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * 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.
+ *
+ *
+ * Contributor(s): Blender Foundation,
+ * Sergey Sharybin
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+/** \file blender/editors/space_clip/clip_editor.c
+ * \ingroup spclip
+ */
+
+#include <stddef.h>
+
+#include "BKE_main.h"
+#include "BKE_movieclip.h"
+#include "BKE_context.h"
+#include "BKE_tracking.h"
+#include "DNA_object_types.h" /* SELECT */
+
+#include "BLI_utildefines.h"
+#include "BLI_math.h"
+
+#include "IMB_imbuf_types.h"
+#include "IMB_imbuf.h"
+
+#include "ED_screen.h"
+#include "ED_clip.h"
+
+#include "BIF_gl.h"
+
+#include "WM_api.h"
+#include "WM_types.h"
+
+#include "UI_view2d.h"
+
+#include "clip_intern.h" // own include
+
+int ED_space_clip_poll(bContext *C)
+{
+ SpaceClip *sc= CTX_wm_space_clip(C);
+
+ if(sc && sc->clip)
+ return 1;
+
+ return 0;
+}
+
+void ED_space_clip_set(bContext *C, SpaceClip *sc, MovieClip *clip)
+{
+ sc->clip= clip;
+
+ if(sc->clip && sc->clip->id.us==0)
+ sc->clip->id.us= 1;
+
+ if(C)
+ WM_event_add_notifier(C, NC_MOVIECLIP|NA_SELECTED, sc->clip);
+}
+
+MovieClip *ED_space_clip(SpaceClip *sc)
+{
+ return sc->clip;
+}
+
+ImBuf *ED_space_clip_get_buffer(SpaceClip *sc)
+{
+ if(sc->clip) {
+ ImBuf *ibuf;
+
+ ibuf= BKE_movieclip_get_ibuf(sc->clip, &sc->user);
+
+ if(ibuf && (ibuf->rect || ibuf->rect_float))
+ return ibuf;
+
+ if(ibuf)
+ IMB_freeImBuf(ibuf);
+ }
+
+ return NULL;
+}
+
+ImBuf *ED_space_clip_get_stable_buffer(SpaceClip *sc, float loc[2], float *scale, float *angle)
+{
+ if(sc->clip) {
+ ImBuf *ibuf;
+
+ ibuf= BKE_movieclip_get_stable_ibuf(sc->clip, &sc->user, loc, scale, angle);
+
+ if(ibuf && (ibuf->rect || ibuf->rect_float))
+ return ibuf;
+
+ if(ibuf)
+ IMB_freeImBuf(ibuf);
+ }
+
+ return NULL;
+}
+
+void ED_space_clip_size(SpaceClip *sc, int *width, int *height)
+{
+ if(!sc->clip) {
+ *width= 0;
+ *height= 0;
+ } else
+ BKE_movieclip_get_size(sc->clip, &sc->user, width, height);
+}
+
+void ED_space_clip_zoom(SpaceClip *sc, ARegion *ar, float *zoomx, float *zoomy)
+{
+ int width, height;
+
+ ED_space_clip_size(sc, &width, &height);
+
+ *zoomx= (float)(ar->winrct.xmax - ar->winrct.xmin + 1)/(float)((ar->v2d.cur.xmax - ar->v2d.cur.xmin)*width);
+ *zoomy= (float)(ar->winrct.ymax - ar->winrct.ymin + 1)/(float)((ar->v2d.cur.ymax - ar->v2d.cur.ymin)*height);
+}
+
+void ED_space_clip_aspect(SpaceClip *sc, float *aspx, float *aspy)
+{
+ MovieClip *clip= ED_space_clip(sc);
+
+ if(clip)
+ BKE_movieclip_aspect(clip, aspx, aspy);
+ else
+ *aspx= *aspy= 1.0f;
+}
+
+void ED_clip_update_frame(const Main *mainp, int cfra)
+{
+ wmWindowManager *wm;
+ wmWindow *win;
+
+ /* image window, compo node users */
+ for(wm=mainp->wm.first; wm; wm= wm->id.next) { /* only 1 wm */
+ for(win= wm->windows.first; win; win= win->next) {
+ ScrArea *sa;
+ for(sa= win->screen->areabase.first; sa; sa= sa->next) {
+ if(sa->spacetype==SPACE_CLIP) {
+ SpaceClip *sc= sa->spacedata.first;
+
+ sc->scopes.ok= 0;
+
+ BKE_movieclip_user_set_frame(&sc->user, cfra);
+ }
+ }
+ }
+ }
+}
+
+static int selected_boundbox(SpaceClip *sc, float min[2], float max[2])
+{
+ MovieClip *clip= ED_space_clip(sc);
+ MovieTrackingTrack *track;
+ int width, height, ok= 0;
+
+ INIT_MINMAX2(min, max);
+
+ ED_space_clip_size(sc, &width, &height);
+
+ track= clip->tracking.tracks.first;
+ while(track) {
+ if(TRACK_VIEW_SELECTED(sc, track)) {
+ MovieTrackingMarker *marker= BKE_tracking_get_marker(track, sc->user.framenr);
+
+ if(marker) {
+ float pos[3];
+
+ pos[0]= (marker->pos[0]+track->offset[0])*width;
+ pos[1]= (marker->pos[1]+track->offset[1])*height;
+ pos[2]= 0.0f;
+
+ mul_v3_m4v3(pos, sc->stabmat, pos);
+
+ DO_MINMAX2(pos, min, max);
+
+ ok= 1;
+ }
+ }
+
+ track= track->next;
+ }
+
+ return ok;
+}
+
+int ED_clip_view_selection(SpaceClip *sc, ARegion *ar, int fit)
+{
+ int w, h, frame_width, frame_height;
+ float min[2], max[2];
+
+ ED_space_clip_size(sc, &frame_width, &frame_height);
+
+ if(frame_width==0 || frame_height==0) return 0;
+
+ if(!selected_boundbox(sc, min, max))
+ return 0;
+
+ /* center view */
+ clip_view_center_to_point(sc, (max[0]+min[0])/(2*frame_width), (max[1]+min[1])/(2*frame_height));
+
+ w= max[0]-min[0];
+ h= max[1]-min[1];
+
+ /* set zoom to see all selection */
+ if(w>0 && h>0) {
+ int width, height;
+ float zoomx, zoomy, newzoom, aspx, aspy;
+
+ ED_space_clip_aspect(sc, &aspx, &aspy);
+
+ width= ar->winrct.xmax - ar->winrct.xmin + 1;
+ height= ar->winrct.ymax - ar->winrct.ymin + 1;
+
+ zoomx= (float)width/w/aspx;
+ zoomy= (float)height/h/aspy;
+
+ newzoom= 1.0f/power_of_2(1/MIN2(zoomx, zoomy));
+
+ if(fit || sc->zoom>newzoom)
+ sc->zoom= newzoom;
+ }
+
+ return 1;
+}
+
+void ED_clip_point_undistorted_pos(SpaceClip *sc, float co[2], float nco[2])
+{
+ copy_v2_v2(nco, co);
+
+ if(sc->user.render_flag&MCLIP_PROXY_RENDER_UNDISTORT) {
+ MovieClip *clip= ED_space_clip(sc);
+ float aspy= 1.0f/clip->tracking.camera.pixel_aspect;
+ int width, height;
+
+ ED_space_clip_size(sc, &width, &height);
+
+ nco[0]*= width;
+ nco[1]*= height*aspy;
+
+ BKE_tracking_invert_intrinsics(&clip->tracking, nco, nco);
+ nco[0]/= width;
+ nco[1]/= height*aspy;
+ }
+}
+
+void ED_clip_point_stable_pos(bContext *C, float x, float y, float *xr, float *yr)
+{
+ ARegion *ar= CTX_wm_region(C);
+ SpaceClip *sc= CTX_wm_space_clip(C);
+ int sx, sy, width, height;
+ float zoomx, zoomy, pos[3]={0.0f, 0.0f, 0.0f}, imat[4][4];
+
+ ED_space_clip_zoom(sc, ar, &zoomx, &zoomy);
+ ED_space_clip_size(sc, &width, &height);
+
+ UI_view2d_to_region_no_clip(&ar->v2d, 0.0f, 0.0f, &sx, &sy);
+
+ pos[0]= (x-sx)/zoomx;
+ pos[1]= (y-sy)/zoomy;
+
+ invert_m4_m4(imat, sc->stabmat);
+ mul_v3_m4v3(pos, imat, pos);
+
+ *xr= pos[0]/width;
+ *yr= pos[1]/height;
+
+ if(sc->user.render_flag&MCLIP_PROXY_RENDER_UNDISTORT) {
+ MovieClip *clip= ED_space_clip(sc);
+ MovieTracking *tracking= &clip->tracking;
+ float aspy= 1.0f/tracking->camera.pixel_aspect;
+ float tmp[2]= {*xr*width, *yr*height*aspy};
+
+ BKE_tracking_apply_intrinsics(tracking, tmp, tmp);
+
+ *xr= tmp[0]/width;
+ *yr= tmp[1]/(height*aspy);
+ }
+}
+
+void ED_clip_mouse_pos(bContext *C, wmEvent *event, float co[2])
+{
+ ED_clip_point_stable_pos(C, event->mval[0], event->mval[1], &co[0], &co[1]);
+}
diff --git a/source/blender/editors/space_clip/clip_graph_draw.c b/source/blender/editors/space_clip/clip_graph_draw.c
new file mode 100644
index 00000000000..7b14783d4ca
--- /dev/null
+++ b/source/blender/editors/space_clip/clip_graph_draw.c
@@ -0,0 +1,255 @@
+/*
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * 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.
+ *
+ *
+ * Contributor(s): Blender Foundation,
+ * Sergey Sharybin
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+/** \file blender/editors/space_clip/clip_graph_draw.c
+ * \ingroup spclip
+ */
+
+#include "DNA_movieclip_types.h"
+#include "DNA_scene_types.h"
+#include "DNA_object_types.h" /* SELECT */
+
+#include "MEM_guardedalloc.h"
+
+#include "BKE_context.h"
+#include "BKE_movieclip.h"
+#include "BKE_tracking.h"
+
+#include "BLI_utildefines.h"
+#include "BLI_math.h"
+#include "BLI_string.h"
+
+#include "ED_screen.h"
+#include "ED_clip.h"
+
+#include "BIF_gl.h"
+
+#include "WM_types.h"
+
+#include "UI_interface.h"
+#include "UI_resources.h"
+#include "UI_view2d.h"
+
+#include "BLF_api.h"
+
+#include "clip_intern.h" // own include
+
+static void draw_curve_knot(float x, float y, float xscale, float yscale, float hsize)
+{
+ static GLuint displist=0;
+
+ /* initialise round circle shape */
+ if (displist == 0) {
+ GLUquadricObj *qobj;
+
+ displist= glGenLists(1);
+ glNewList(displist, GL_COMPILE);
+
+ qobj= gluNewQuadric();
+ gluQuadricDrawStyle(qobj, GLU_SILHOUETTE);
+ gluDisk(qobj, 0, 0.7, 8, 1);
+ gluDeleteQuadric(qobj);
+
+ glEndList();
+ }
+
+ glPushMatrix();
+
+ glTranslatef(x, y, 0.0f);
+ glScalef(1.0f/xscale*hsize, 1.0f/yscale*hsize, 1.0f);
+ glCallList(displist);
+
+ glPopMatrix();
+}
+
+static void draw_graph_cfra(SpaceClip *sc, ARegion *ar, Scene *scene)
+{
+ View2D *v2d= &ar->v2d;
+ float xscale, yscale;
+ float vec[2];
+
+ /* Draw a light green line to indicate current frame */
+ vec[0]= (float)(sc->user.framenr * scene->r.framelen);
+
+ UI_ThemeColor(TH_CFRAME);
+ glLineWidth(2.0);
+
+ glBegin(GL_LINE_STRIP);
+ vec[1]= v2d->cur.ymin;
+ glVertex2fv(vec);
+
+ vec[1]= v2d->cur.ymax;
+ glVertex2fv(vec);
+ glEnd();
+
+ glLineWidth(1.0);
+
+ UI_view2d_view_orthoSpecial(ar, v2d, 1);
+
+ /* because the frame number text is subject to the same scaling as the contents of the view */
+ UI_view2d_getscale(v2d, &xscale, &yscale);
+ glScalef(1.0f/xscale, 1.0f, 1.0f);
+
+ clip_draw_curfra_label(sc, (float)sc->user.framenr * xscale, 18);
+
+ /* restore view transform */
+ glScalef(xscale, 1.0, 1.0);
+}
+
+static void tracking_segment_point_cb(void *UNUSED(userdata), MovieTrackingTrack *UNUSED(track),
+ MovieTrackingMarker *marker, int UNUSED(coord), float val)
+{
+ glVertex2f(marker->framenr, val);
+}
+
+void tracking_segment_start_cb(void *userdata, MovieTrackingTrack *track, int coord)
+{
+ static float colors[2][3] = {{1.0f, 0.0f, 0.0f},
+ {0.0f, 1.0f, 0.0f}};
+ float col[4];
+
+ copy_v3_v3(col, colors[coord]);
+
+ if(track==userdata) {
+ col[3]= 1.0f;
+ glLineWidth(2.0f);
+ } else {
+ col[3]= 0.5f;
+ glLineWidth(1.0f);
+ }
+
+ glColor4fv(col);
+
+ glBegin(GL_LINE_STRIP);
+}
+
+void tracking_segment_end_cb(void *UNUSED(userdata))
+{
+ glEnd();
+
+ glLineWidth(1.0f);
+}
+
+static void tracking_segment_knot_cb(void *userdata, MovieTrackingTrack *track,
+ MovieTrackingMarker *marker, int UNUSED(coord), float val)
+{
+ struct { MovieTrackingTrack *act_track; int sel; float xscale, yscale, hsize; } *data = userdata;
+ int sel= 0;
+
+ if(track!=data->act_track)
+ return;
+
+ sel= (marker->flag&MARKER_GRAPH_SEL) ? 1 : 0;
+
+ if(sel == data->sel) {
+ if(sel) UI_ThemeColor(TH_HANDLE_VERTEX_SELECT);
+ else UI_ThemeColor(TH_HANDLE_VERTEX);
+
+ draw_curve_knot(marker->framenr, val, data->xscale, data->yscale, data->hsize);
+ }
+}
+
+static void draw_tracks_curves(View2D *v2d, SpaceClip *sc)
+{
+ MovieClip *clip= ED_space_clip(sc);
+ MovieTracking *tracking= &clip->tracking;
+ int width, height;
+ struct { MovieTrackingTrack *act_track; int sel; float xscale, yscale, hsize; } userdata;
+
+ BKE_movieclip_get_size(clip, &sc->user, &width, &height);
+
+ if(!width || !height)
+ return;
+
+ /* non-selected knot handles */
+ userdata.hsize= UI_GetThemeValuef(TH_HANDLE_VERTEX_SIZE);
+ userdata.sel= 0;
+ userdata.act_track= clip->tracking.act_track;
+ UI_view2d_getscale(v2d, &userdata.xscale, &userdata.yscale);
+ clip_graph_tracking_values_iterate(sc, &userdata, tracking_segment_knot_cb, NULL, NULL);
+
+ /* draw graph lines */
+ glEnable(GL_BLEND);
+ clip_graph_tracking_values_iterate(sc, tracking->act_track, tracking_segment_point_cb, tracking_segment_start_cb, tracking_segment_end_cb);
+ glDisable(GL_BLEND);
+
+ /* selected knot handles on top of curves */
+ userdata.sel= 1;
+ clip_graph_tracking_values_iterate(sc, &userdata, tracking_segment_knot_cb, NULL, NULL);
+}
+
+static void draw_frame_curves(SpaceClip *sc)
+{
+ MovieClip *clip= ED_space_clip(sc);
+ MovieTracking *tracking= &clip->tracking;
+ MovieTrackingReconstruction *reconstruction= &tracking->reconstruction;
+ int i, lines= 0, prevfra= 0;
+
+ glColor3f(0.0f, 0.0f, 1.0f);
+
+ for(i= 0; i<reconstruction->camnr; i++) {
+ MovieReconstructedCamera *camera= &reconstruction->cameras[i];
+
+ if(lines && camera->framenr!=prevfra+1) {
+ glEnd();
+ lines= 0;
+ }
+
+ if(!lines) {
+ glBegin(GL_LINE_STRIP);
+ lines= 1;
+ }
+
+ glVertex2f(camera->framenr, camera->error);
+
+ prevfra= camera->framenr;
+ }
+
+ if(lines)
+ glEnd();
+}
+
+void clip_draw_graph(SpaceClip *sc, ARegion *ar, Scene *scene)
+{
+ View2D *v2d= &ar->v2d;
+ View2DGrid *grid;
+ short unitx= V2D_UNIT_FRAMESCALE, unity= V2D_UNIT_VALUES;
+
+ /* grid */
+ grid= UI_view2d_grid_calc(scene, v2d, unitx, V2D_GRID_NOCLAMP, unity, V2D_GRID_NOCLAMP, ar->winx, ar->winy);
+ UI_view2d_grid_draw(v2d, grid, V2D_GRIDLINES_ALL);
+ UI_view2d_grid_free(grid);
+
+ if(sc->flag&SC_SHOW_GRAPH_TRACKS)
+ draw_tracks_curves(v2d, sc);
+
+ if(sc->flag&SC_SHOW_GRAPH_FRAMES)
+ draw_frame_curves(sc);
+
+ /* current frame */
+ draw_graph_cfra(sc, ar, scene);
+}
diff --git a/source/blender/editors/space_clip/clip_graph_ops.c b/source/blender/editors/space_clip/clip_graph_ops.c
new file mode 100644
index 00000000000..831b225386a
--- /dev/null
+++ b/source/blender/editors/space_clip/clip_graph_ops.c
@@ -0,0 +1,356 @@
+/*
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * 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.
+ *
+ *
+ * Contributor(s): Blender Foundation,
+ * Sergey Sharybin
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+/** \file blender/editors/space_clip/clip_graph_ops.c
+ * \ingroup spclip
+ */
+
+#include "DNA_object_types.h" /* SELECT */
+
+#include "MEM_guardedalloc.h"
+
+#include "BLI_utildefines.h"
+#include "BLI_math.h"
+#include "BLI_listbase.h"
+
+#include "BKE_context.h"
+#include "BKE_movieclip.h"
+#include "BKE_tracking.h"
+
+#include "WM_api.h"
+#include "WM_types.h"
+
+#include "ED_screen.h"
+#include "ED_clip.h"
+
+#include "UI_interface.h"
+
+#include "RNA_access.h"
+#include "RNA_define.h"
+
+#include "UI_view2d.h"
+
+#include "clip_intern.h" // own include
+
+/******************** common graph-editing utilities ********************/
+
+typedef struct {
+ int action;
+} SelectUserData;
+
+static void toggle_selection_cb(void *userdata, MovieTrackingMarker *marker)
+{
+ SelectUserData *data= (SelectUserData *)userdata;
+
+ switch(data->action) {
+ case SEL_SELECT:
+ marker->flag|= MARKER_GRAPH_SEL;
+ break;
+ case SEL_DESELECT:
+ marker->flag&= ~MARKER_GRAPH_SEL;
+ break;
+ case SEL_INVERT:
+ marker->flag^= MARKER_GRAPH_SEL;
+ break;
+ }
+}
+
+/******************** mouse select operator ********************/
+
+typedef struct {
+ int coord, /* coordinate index of found entuty (0 = X-axis, 1 = Y-axis) */
+ has_prev; /* if there's valid coordinate of previous point of curve segment */
+
+ float min_dist, /* minimal distance between mouse and currently found entuty */
+ mouse_co[2], /* mouse coordinate */
+ prev_co[2], /* coordinate of previeous point of segment */
+ min_co[2]; /* coordinate of entity with minimal distance */
+
+ MovieTrackingTrack *track; /* nearest found track */
+ MovieTrackingMarker *marker; /* nearest found marker */
+} MouseSelectUserData;
+
+static void find_nearest_tracking_segment_cb(void *userdata, MovieTrackingTrack *track,
+ MovieTrackingMarker *marker, int coord, float val)
+{
+ MouseSelectUserData *data= userdata;
+ float co[2]= {marker->framenr, val};
+
+ if(data->has_prev) {
+ float d= dist_to_line_segment_v2(data->mouse_co, data->prev_co, co);
+
+ if(data->track==NULL || d<data->min_dist) {
+ data->track= track;
+ data->min_dist= d;
+ data->coord= coord;
+ copy_v2_v2(data->min_co, co);
+ }
+ }
+
+ data->has_prev= 1;
+ copy_v2_v2(data->prev_co, co);
+}
+
+void find_nearest_tracking_segment_end_cb(void *userdata)
+{
+ MouseSelectUserData *data= userdata;
+
+ data->has_prev= 0;
+}
+
+static void find_nearest_tracking_knot_cb(void *userdata, MovieTrackingTrack *track,
+ MovieTrackingMarker *marker, int coord, float val)
+{
+ MouseSelectUserData *data= userdata;
+ float dx= marker->framenr-data->mouse_co[0], dy= val-data->mouse_co[1];
+ float d= dx*dx+dy*dy;
+
+ if(data->marker==NULL || d<data->min_dist) {
+ float co[2]= {marker->framenr, val};
+
+ data->track= track;
+ data->marker= marker;
+ data->min_dist= d;
+ data->coord= coord;
+ copy_v2_v2(data->min_co, co);
+ }
+
+}
+
+static void mouse_select_init_data(MouseSelectUserData *userdata, float *co)
+{
+ memset(userdata, 0, sizeof(MouseSelectUserData));
+ userdata->min_dist= FLT_MAX;
+ copy_v2_v2(userdata->mouse_co, co);
+}
+
+static int mouse_select_knot(bContext *C, float co[2], int extend)
+{
+ SpaceClip *sc= CTX_wm_space_clip(C);
+ MovieClip *clip= ED_space_clip(sc);
+ ARegion *ar= CTX_wm_region(C);
+ View2D *v2d= &ar->v2d;
+ MovieTracking *tracking= &clip->tracking;
+ static const int delta= 6;
+
+ if(tracking->act_track) {
+ MouseSelectUserData userdata;
+
+ mouse_select_init_data(&userdata, co);
+ clip_graph_tracking_values_iterate_track(sc, tracking->act_track,
+ &userdata, find_nearest_tracking_knot_cb, NULL, NULL);
+
+ if(userdata.marker) {
+ int x1, y1, x2, y2;
+
+ UI_view2d_view_to_region(v2d, co[0], co[1], &x1, &y1);
+ UI_view2d_view_to_region(v2d, userdata.min_co[0], userdata.min_co[1], &x2, &y2);
+
+ if(abs(x2-x1)<=delta && abs(y2-y1)<=delta) {
+ if(!extend) {
+ SelectUserData selectdata = {SEL_DESELECT};
+ clip_graph_tracking_iterate(sc, &selectdata, toggle_selection_cb);
+ }
+
+ userdata.marker->flag|= MARKER_GRAPH_SEL;
+
+ return 1;
+ }
+ }
+ }
+
+ return 0;
+}
+
+static int mouse_select_curve(bContext *C, float co[2], int extend)
+{
+ SpaceClip *sc= CTX_wm_space_clip(C);
+ MovieClip *clip= ED_space_clip(sc);
+ MovieTracking *tracking= &clip->tracking;
+ MouseSelectUserData userdata;
+
+ mouse_select_init_data(&userdata, co);
+ clip_graph_tracking_values_iterate(sc, &userdata, find_nearest_tracking_segment_cb, NULL, find_nearest_tracking_segment_end_cb);
+
+ if(userdata.track) {
+ if(extend) {
+ if(tracking->act_track==userdata.track) {
+ /* currently only single curve can be selected (selected curve represents active track) */
+ tracking->act_track= NULL;
+ }
+ }
+ else if(tracking->act_track!=userdata.track) {
+ MovieTrackingMarker *marker;
+ SelectUserData selectdata = {SEL_DESELECT};
+
+ tracking->act_track= userdata.track;
+
+ /* make active track be centered to screen */
+ marker= BKE_tracking_get_marker(userdata.track, sc->user.framenr);
+
+ clip_view_center_to_point(sc, marker->pos[0], marker->pos[1]);
+
+ /* deselect all knots on newly selected curve */
+ clip_graph_tracking_iterate(sc, &selectdata, toggle_selection_cb);
+ }
+
+ return 1;
+ }
+
+ return 0;
+}
+
+static int mouse_select(bContext *C, float co[2], int extend)
+{
+ int sel= 0;
+
+ /* first try to select knot on selected curves */
+ sel= mouse_select_knot(C, co, extend);
+
+ if(!sel) {
+ /* if there's no close enough knot to mouse osition, select nearest curve */
+ sel= mouse_select_curve(C, co, extend);
+ }
+
+ if(sel)
+ WM_event_add_notifier(C, NC_GEOM|ND_SELECT, NULL);
+
+ return OPERATOR_FINISHED;
+}
+
+static int select_exec(bContext *C, wmOperator *op)
+{
+ float co[2];
+ int extend= RNA_boolean_get(op->ptr, "extend");
+
+ RNA_float_get_array(op->ptr, "location", co);
+
+ return mouse_select(C, co, extend);
+}
+
+static int select_invoke(bContext *C, wmOperator *op, wmEvent *event)
+{
+ ARegion *ar= CTX_wm_region(C);
+ float co[2];
+
+ UI_view2d_region_to_view(&ar->v2d, event->mval[0], event->mval[1], &co[0], &co[1]);
+ RNA_float_set_array(op->ptr, "location", co);
+
+ return select_exec(C, op);
+}
+
+void CLIP_OT_graph_select(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name= "Select";
+ ot->description= "Select graph curves";
+ ot->idname= "CLIP_OT_graph_select";
+
+ /* api callbacks */
+ ot->exec= select_exec;
+ ot->invoke= select_invoke;
+ ot->poll= ED_space_clip_poll;
+
+ /* flags */
+ ot->flag= OPTYPE_UNDO;
+
+ /* properties */
+ RNA_def_float_vector(ot->srna, "location", 2, NULL, -FLT_MAX, FLT_MAX,
+ "Location", "Mouse location to select nearest entity closest to", -100.0f, 100.0f);
+ RNA_def_boolean(ot->srna, "extend", 0,
+ "Extend", "Extend selection rather than clearing the existing selection");
+}
+
+/******************** delete curve operator ********************/
+
+static int delete_curve_exec(bContext *C, wmOperator *UNUSED(op))
+{
+ SpaceClip *sc= CTX_wm_space_clip(C);
+ MovieClip *clip= ED_space_clip(sc);
+ MovieTracking *tracking= &clip->tracking;
+
+ if(tracking->act_track)
+ clip_delete_track(C, clip, tracking->act_track);
+
+ return OPERATOR_FINISHED;
+}
+
+void CLIP_OT_graph_delete_curve(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name= "Delete Curve";
+ ot->description= "Delete selected curves";
+ ot->idname= "CLIP_OT_graph_delete_curve";
+
+ /* api callbacks */
+ ot->invoke= WM_operator_confirm;
+ ot->exec= delete_curve_exec;
+ ot->poll= ED_space_clip_poll;
+
+ /* flags */
+ ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
+}
+
+/******************** delete knot operator ********************/
+
+static int delete_knot_exec(bContext *C, wmOperator *UNUSED(op))
+{
+ SpaceClip *sc= CTX_wm_space_clip(C);
+ MovieClip *clip= ED_space_clip(sc);
+ MovieTracking *tracking= &clip->tracking;
+
+ if(tracking->act_track) {
+ int a= 0;
+ MovieTrackingTrack *track= tracking->act_track;
+
+ while(a<track->markersnr) {
+ MovieTrackingMarker *marker= &track->markers[a];
+
+ if(marker->flag&MARKER_GRAPH_SEL)
+ clip_delete_marker(C, clip, track, marker);
+ else
+ a++;
+ }
+ }
+
+ return OPERATOR_FINISHED;
+}
+
+void CLIP_OT_graph_delete_knot(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name= "Delete Knot";
+ ot->description= "Delete curve knots";
+ ot->idname= "CLIP_OT_graph_delete_knot";
+
+ /* api callbacks */
+ ot->exec= delete_knot_exec;
+ ot->poll= ED_space_clip_poll;
+
+ /* flags */
+ ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
+}
diff --git a/source/blender/editors/space_clip/clip_intern.h b/source/blender/editors/space_clip/clip_intern.h
new file mode 100644
index 00000000000..da0b589652e
--- /dev/null
+++ b/source/blender/editors/space_clip/clip_intern.h
@@ -0,0 +1,144 @@
+/*
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * 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.
+ *
+ *
+ * Contributor(s): Blender Foundation,
+ * Sergey Sharybin
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+/** \file blender/editors/space_clip/clip_intern.h
+ * \ingroup spclip
+ */
+
+#ifndef ED_CLIP_INTERN_H
+#define ED_CLIP_INTERN_H
+
+struct bContext;
+struct ARegion;
+struct MovieClip;
+struct MovieTrackingMarker;
+struct MovieTrackingTrack;
+struct Scene;
+struct SpaceClip;
+struct wmOperatorType;
+
+/* internal exports only */
+
+/* clip_buttons.c */
+void ED_clip_buttons_register(struct ARegionType *art);
+
+/* clip_draw.c */
+void clip_draw_main(struct SpaceClip *sc, struct ARegion *ar, struct Scene *scene);
+void clip_draw_grease_pencil(struct bContext *C, int onlyv2d);
+void clip_draw_curfra_label(struct SpaceClip *sc, float x, float y);
+
+/* clip_graph_draw.c */
+void clip_draw_graph(struct SpaceClip *sc, struct ARegion *ar, struct Scene *scene);
+
+/* clip_graph_ops.c */
+void CLIP_OT_graph_select(struct wmOperatorType *ot);
+void CLIP_OT_graph_delete_curve(struct wmOperatorType *ot);
+void CLIP_OT_graph_delete_knot(struct wmOperatorType *ot);
+
+/* clip_ops.c */
+void CLIP_OT_open(struct wmOperatorType *ot);
+void CLIP_OT_reload(struct wmOperatorType *ot);
+void CLIP_OT_view_pan(struct wmOperatorType *ot);
+void CLIP_OT_view_zoom(wmOperatorType *ot);
+void CLIP_OT_view_zoom_in(struct wmOperatorType *ot);
+void CLIP_OT_view_zoom_out(struct wmOperatorType *ot);
+void CLIP_OT_view_zoom_ratio(struct wmOperatorType *ot);
+void CLIP_OT_view_all(struct wmOperatorType *ot);
+void CLIP_OT_view_selected(struct wmOperatorType *ot);
+void CLIP_OT_change_frame(wmOperatorType *ot);
+void CLIP_OT_rebuild_proxy(struct wmOperatorType *ot);
+void CLIP_OT_mode_set(struct wmOperatorType *ot);
+
+/* clip_toolbar.c */
+void CLIP_OT_tools(struct wmOperatorType *ot);
+void CLIP_OT_properties(struct wmOperatorType *ot);
+void ED_clip_tool_props_register(struct ARegionType *art);
+
+/* clip_utils.c */
+void clip_graph_tracking_values_iterate_track(struct SpaceClip *sc, struct MovieTrackingTrack *track, void *userdata,
+ void (*func) (void *userdata, struct MovieTrackingTrack *track, struct MovieTrackingMarker *marker, int coord, float val),
+ void (*segment_start) (void *userdata, struct MovieTrackingTrack *track, int coord),
+ void (*segment_end) (void *userdata));
+
+void clip_graph_tracking_values_iterate(struct SpaceClip *sc, void *userdata,
+ void (*func) (void *userdata, struct MovieTrackingTrack *track, struct MovieTrackingMarker *marker, int coord, float val),
+ void (*segment_start) (void *userdata, struct MovieTrackingTrack *track, int coord),
+ void (*segment_end) (void *userdata));
+
+void clip_graph_tracking_iterate(struct SpaceClip *sc, void *userdata,
+ void (*func) (void *userdata, struct MovieTrackingMarker *marker));
+
+void clip_delete_track(struct bContext *C, struct MovieClip *clip, struct MovieTrackingTrack *track);
+void clip_delete_marker(struct bContext *C, struct MovieClip *clip, struct MovieTrackingTrack *track, struct MovieTrackingMarker *marker);
+
+void clip_view_center_to_point(struct SpaceClip *sc, float x, float y);
+
+/* tracking_ops.c */
+void CLIP_OT_select(struct wmOperatorType *ot);
+void CLIP_OT_select_all(struct wmOperatorType *ot);
+void CLIP_OT_select_border(struct wmOperatorType *ot);
+void CLIP_OT_select_circle(struct wmOperatorType *ot);
+void CLIP_OT_select_grouped(struct wmOperatorType *ot);
+
+void CLIP_OT_add_marker(struct wmOperatorType *ot);
+void CLIP_OT_delete_track(struct wmOperatorType *ot);
+void CLIP_OT_delete_marker(struct wmOperatorType *ot);
+
+void CLIP_OT_track_markers(struct wmOperatorType *ot);
+void CLIP_OT_solve_camera(struct wmOperatorType *ot);
+void CLIP_OT_clear_solution(struct wmOperatorType *ot);
+
+void CLIP_OT_clear_track_path(struct wmOperatorType *ot);
+void CLIP_OT_join_tracks(struct wmOperatorType *ot);
+
+void CLIP_OT_disable_markers(struct wmOperatorType *ot);
+void CLIP_OT_hide_tracks(struct wmOperatorType *ot);
+void CLIP_OT_hide_tracks_clear(struct wmOperatorType *ot);
+void CLIP_OT_lock_tracks(struct wmOperatorType *ot);
+
+void CLIP_OT_set_origin(struct wmOperatorType *ot);
+void CLIP_OT_set_floor(struct wmOperatorType *ot);
+void CLIP_OT_set_axis(struct wmOperatorType *ot);
+void CLIP_OT_set_scale(struct wmOperatorType *ot);
+
+void CLIP_OT_set_center_principal(struct wmOperatorType *ot);
+
+void CLIP_OT_slide_marker(struct wmOperatorType *ot);
+
+void CLIP_OT_frame_jump(struct wmOperatorType *ot);
+void CLIP_OT_track_copy_color(struct wmOperatorType *ot);
+
+void CLIP_OT_detect_features(struct wmOperatorType *ot);
+
+void CLIP_OT_stabilize_2d_add(struct wmOperatorType *ot);
+void CLIP_OT_stabilize_2d_remove(struct wmOperatorType *ot);
+void CLIP_OT_stabilize_2d_select(struct wmOperatorType *ot);
+void CLIP_OT_stabilize_2d_set_rotation(struct wmOperatorType *ot);
+
+void CLIP_OT_clean_tracks(wmOperatorType *ot);
+
+#endif /* ED_CLIP_INTERN_H */
diff --git a/source/blender/editors/space_clip/clip_ops.c b/source/blender/editors/space_clip/clip_ops.c
new file mode 100644
index 00000000000..c52346fd4b6
--- /dev/null
+++ b/source/blender/editors/space_clip/clip_ops.c
@@ -0,0 +1,1004 @@
+/*
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * 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.
+ *
+ *
+ * Contributor(s): Blender Foundation,
+ * Sergey Sharybin
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+/** \file blender/editors/space_clip/clip_ops.c
+ * \ingroup spclip
+ */
+
+#include <errno.h>
+
+#include "MEM_guardedalloc.h"
+
+#include "DNA_userdef_types.h"
+#include "DNA_scene_types.h" /* min/max frames */
+
+#include "BLI_utildefines.h"
+#include "BLI_math.h"
+
+#include "BKE_context.h"
+#include "BKE_global.h"
+#include "BKE_report.h"
+#include "BKE_main.h"
+#include "BKE_library.h"
+#include "BKE_movieclip.h"
+#include "BKE_sound.h"
+#include "BKE_tracking.h"
+
+#include "WM_api.h"
+#include "WM_types.h"
+
+#include "IMB_imbuf_types.h"
+#include "IMB_imbuf.h"
+
+#include "ED_screen.h"
+#include "ED_clip.h"
+
+#include "UI_interface.h"
+
+#include "RNA_access.h"
+#include "RNA_define.h"
+
+#include "UI_view2d.h"
+
+#include "clip_intern.h" // own include
+
+/******************** view navigation utilities *********************/
+
+static void sclip_zoom_set(SpaceClip *sc, ARegion *ar, float zoom)
+{
+ float oldzoom= sc->zoom;
+ int width, height;
+
+ sc->zoom= zoom;
+
+ if (sc->zoom > 0.1f && sc->zoom < 4.0f)
+ return;
+
+ /* check zoom limits */
+ ED_space_clip_size(sc, &width, &height);
+
+ width*= sc->zoom;
+ height*= sc->zoom;
+
+ if((width < 4) && (height < 4))
+ sc->zoom= oldzoom;
+ else if((ar->winrct.xmax - ar->winrct.xmin) <= sc->zoom)
+ sc->zoom= oldzoom;
+ else if((ar->winrct.ymax - ar->winrct.ymin) <= sc->zoom)
+ sc->zoom= oldzoom;
+}
+
+static void sclip_zoom_set_factor(SpaceClip *sc, ARegion *ar, float zoomfac)
+{
+ sclip_zoom_set(sc, ar, sc->zoom*zoomfac);
+}
+
+
+/******************** open clip operator ********************/
+
+static void clip_filesel(bContext *C, wmOperator *op, const char *path)
+{
+ RNA_string_set(op->ptr, "filepath", path);
+ WM_event_add_fileselect(C, op);
+}
+
+static void open_init(bContext *C, wmOperator *op)
+{
+ PropertyPointerRNA *pprop;
+
+ op->customdata= pprop= MEM_callocN(sizeof(PropertyPointerRNA), "OpenPropertyPointerRNA");
+ uiIDContextProperty(C, &pprop->ptr, &pprop->prop);
+}
+
+static int open_cancel(bContext *UNUSED(C), wmOperator *op)
+{
+ MEM_freeN(op->customdata);
+ op->customdata= NULL;
+
+ return OPERATOR_CANCELLED;
+}
+
+static int open_exec(bContext *C, wmOperator *op)
+{
+ SpaceClip *sc= CTX_wm_space_clip(C);
+ PropertyPointerRNA *pprop;
+ PointerRNA idptr;
+ MovieClip *clip= NULL;
+ char str[FILE_MAX];
+
+ RNA_string_get(op->ptr, "filepath", str);
+ /* default to frame 1 if there's no scene in context */
+
+ errno= 0;
+
+ clip= BKE_add_movieclip_file(str);
+
+ if(!clip) {
+ if(op->customdata)
+ MEM_freeN(op->customdata);
+
+ BKE_reportf(op->reports, RPT_ERROR, "Can't read: \"%s\", %s.", str, errno ? strerror(errno) : "Unsupported movie clip format");
+
+ return OPERATOR_CANCELLED;
+ }
+
+ if(!op->customdata)
+ open_init(C, op);
+
+ /* hook into UI */
+ pprop= op->customdata;
+
+ if(pprop->prop) {
+ /* when creating new ID blocks, use is already 1, but RNA
+ * pointer se also increases user, so this compensates it */
+ clip->id.us--;
+
+ RNA_id_pointer_create(&clip->id, &idptr);
+ RNA_property_pointer_set(&pprop->ptr, pprop->prop, idptr);
+ RNA_property_update(C, &pprop->ptr, pprop->prop);
+ }
+ else if(sc) {
+ ED_space_clip_set(C, sc, clip);
+ }
+
+ WM_event_add_notifier(C, NC_MOVIECLIP|NA_ADDED, clip);
+
+ MEM_freeN(op->customdata);
+
+ return OPERATOR_FINISHED;
+}
+
+static int open_invoke(bContext *C, wmOperator *op, wmEvent *UNUSED(event))
+{
+ SpaceClip *sc= CTX_wm_space_clip(C);
+ char *path= U.textudir;
+ MovieClip *clip= NULL;
+
+ if(sc)
+ clip= ED_space_clip(sc);
+
+ if(clip)
+ path= clip->name;
+
+ if(!RNA_property_is_set(op->ptr, "relative_path"))
+ RNA_boolean_set(op->ptr, "relative_path", U.flag & USER_RELPATHS);
+
+ if(RNA_property_is_set(op->ptr, "filepath"))
+ return open_exec(C, op);
+
+ open_init(C, op);
+
+ clip_filesel(C, op, path);
+
+ return OPERATOR_RUNNING_MODAL;
+}
+
+void CLIP_OT_open(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name= "Open Clip";
+ ot->description= "Open clip";
+ ot->idname= "CLIP_OT_open";
+
+ /* api callbacks */
+ ot->exec= open_exec;
+ ot->invoke= open_invoke;
+ ot->cancel= open_cancel;
+
+ /* flags */
+ ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
+
+ /* properties */
+ WM_operator_properties_filesel(ot, FOLDERFILE|IMAGEFILE|MOVIEFILE, FILE_SPECIAL, FILE_OPENFILE, WM_FILESEL_FILEPATH|WM_FILESEL_RELPATH);
+}
+
+/******************* reload clip operator *********************/
+
+static int reload_exec(bContext *C, wmOperator *UNUSED(op))
+{
+ SpaceClip *sc= CTX_wm_space_clip(C);
+ MovieClip *clip= CTX_data_edit_movieclip(C);
+
+ if(!clip)
+ return OPERATOR_CANCELLED;
+
+ sc->scopes.ok= 0;
+
+ BKE_movieclip_reload(clip);
+
+ WM_event_add_notifier(C, NC_MOVIECLIP|NA_EDITED, clip);
+
+ return OPERATOR_FINISHED;
+}
+
+void CLIP_OT_reload(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name= "Reload Clip";
+ ot->description= "Reload clip";
+ ot->idname= "CLIP_OT_reload";
+
+ /* api callbacks */
+ ot->exec= reload_exec;
+}
+
+/********************** view pan operator *********************/
+
+typedef struct ViewPanData {
+ float x, y;
+ float xof, yof, xorig, yorig;
+ int event_type;
+ float *vec;
+} ViewPanData;
+
+static void view_pan_init(bContext *C, wmOperator *op, wmEvent *event)
+{
+ SpaceClip *sc= CTX_wm_space_clip(C);
+ ViewPanData *vpd;
+
+ op->customdata= vpd= MEM_callocN(sizeof(ViewPanData), "ClipViewPanData");
+ WM_cursor_modal(CTX_wm_window(C), BC_NSEW_SCROLLCURSOR);
+
+ vpd->x= event->x;
+ vpd->y= event->y;
+
+ if(sc->flag&SC_LOCK_SELECTION) vpd->vec= &sc->xlockof;
+ else vpd->vec= &sc->xof;
+
+ copy_v2_v2(&vpd->xof, vpd->vec);
+ copy_v2_v2(&vpd->xorig, &vpd->xof);
+
+ vpd->event_type= event->type;
+
+ WM_event_add_modal_handler(C, op);
+}
+
+static void view_pan_exit(bContext *C, wmOperator *op, int cancel)
+{
+ ViewPanData *vpd= op->customdata;
+
+ if(cancel) {
+ copy_v2_v2(vpd->vec, &vpd->xorig);
+
+ ED_region_tag_redraw(CTX_wm_region(C));
+ }
+
+ WM_cursor_restore(CTX_wm_window(C));
+ MEM_freeN(op->customdata);
+}
+
+static int view_pan_exec(bContext *C, wmOperator *op)
+{
+ SpaceClip *sc= CTX_wm_space_clip(C);
+ float offset[2];
+
+ RNA_float_get_array(op->ptr, "offset", offset);
+
+ if(sc->flag&SC_LOCK_SELECTION) {
+ sc->xlockof+= offset[0];
+ sc->ylockof+= offset[1];
+ } else {
+ sc->xof+= offset[0];
+ sc->yof+= offset[1];
+ }
+
+ ED_region_tag_redraw(CTX_wm_region(C));
+
+ return OPERATOR_FINISHED;
+}
+
+static int view_pan_invoke(bContext *C, wmOperator *op, wmEvent *event)
+{
+ if (event->type==MOUSEPAN) {
+ SpaceClip *sc= CTX_wm_space_clip(C);
+ float offset[2];
+
+ offset[0]= (event->x - event->prevx)/sc->zoom;
+ offset[1]= (event->y - event->prevy)/sc->zoom;
+
+ RNA_float_set_array(op->ptr, "offset", offset);
+
+ view_pan_exec(C, op);
+ return OPERATOR_FINISHED;
+ }
+ else {
+ view_pan_init(C, op, event);
+ return OPERATOR_RUNNING_MODAL;
+ }
+}
+
+static int view_pan_modal(bContext *C, wmOperator *op, wmEvent *event)
+{
+ SpaceClip *sc= CTX_wm_space_clip(C);
+ ViewPanData *vpd= op->customdata;
+ float offset[2];
+
+ switch(event->type) {
+ case MOUSEMOVE:
+ copy_v2_v2(vpd->vec, &vpd->xorig);
+ offset[0]= (vpd->x - event->x)/sc->zoom;
+ offset[1]= (vpd->y - event->y)/sc->zoom;
+ RNA_float_set_array(op->ptr, "offset", offset);
+ view_pan_exec(C, op);
+ break;
+ case ESCKEY:
+ view_pan_exit(C, op, 1);
+ return OPERATOR_CANCELLED;
+ case SPACEKEY:
+ view_pan_exit(C, op, 0);
+ return OPERATOR_FINISHED;
+ default:
+ if(event->type==vpd->event_type && event->val==KM_RELEASE) {
+ view_pan_exit(C, op, 0);
+ return OPERATOR_FINISHED;
+ }
+ break;
+ }
+
+ return OPERATOR_RUNNING_MODAL;
+}
+
+static int view_pan_cancel(bContext *C, wmOperator *op)
+{
+ view_pan_exit(C, op, 1);
+
+ return OPERATOR_CANCELLED;
+}
+
+void CLIP_OT_view_pan(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name= "View Pan";
+ ot->idname= "CLIP_OT_view_pan";
+
+ /* api callbacks */
+ ot->exec= view_pan_exec;
+ ot->invoke= view_pan_invoke;
+ ot->modal= view_pan_modal;
+ ot->cancel= view_pan_cancel;
+ ot->poll= ED_space_clip_poll;
+
+ /* flags */
+ ot->flag= 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);
+}
+
+/********************** view zoom operator *********************/
+
+typedef struct ViewZoomData {
+ float x, y;
+ float zoom;
+ int event_type;
+} ViewZoomData;
+
+static void view_zoom_init(bContext *C, wmOperator *op, wmEvent *event)
+{
+ SpaceClip *sc= CTX_wm_space_clip(C);
+ ViewZoomData *vpd;
+
+ op->customdata= vpd= MEM_callocN(sizeof(ViewZoomData), "ClipViewZoomData");
+ WM_cursor_modal(CTX_wm_window(C), BC_NSEW_SCROLLCURSOR);
+
+ vpd->x= event->x;
+ vpd->y= event->y;
+ vpd->zoom= sc->zoom;
+ vpd->event_type= event->type;
+
+ WM_event_add_modal_handler(C, op);
+}
+
+static void view_zoom_exit(bContext *C, wmOperator *op, int cancel)
+{
+ SpaceClip *sc= CTX_wm_space_clip(C);
+ ViewZoomData *vpd= op->customdata;
+
+ if(cancel) {
+ sc->zoom= vpd->zoom;
+ ED_region_tag_redraw(CTX_wm_region(C));
+ }
+
+ WM_cursor_restore(CTX_wm_window(C));
+ MEM_freeN(op->customdata);
+}
+
+static int view_zoom_exec(bContext *C, wmOperator *op)
+{
+ SpaceClip *sc= CTX_wm_space_clip(C);
+ ARegion *ar= CTX_wm_region(C);
+
+ sclip_zoom_set_factor(sc, ar, RNA_float_get(op->ptr, "factor"));
+
+ ED_region_tag_redraw(CTX_wm_region(C));
+
+ return OPERATOR_FINISHED;
+}
+
+static int view_zoom_invoke(bContext *C, wmOperator *op, wmEvent *event)
+{
+ if (event->type==MOUSEZOOM) {
+ SpaceClip *sc= CTX_wm_space_clip(C);
+ ARegion *ar= CTX_wm_region(C);
+ float factor;
+
+ factor= 1.0f + (event->x-event->prevx+event->y-event->prevy)/300.0f;
+ RNA_float_set(op->ptr, "factor", factor);
+ sclip_zoom_set(sc, ar, sc->zoom*factor);
+ ED_region_tag_redraw(CTX_wm_region(C));
+
+ return OPERATOR_FINISHED;
+ }
+ else {
+ view_zoom_init(C, op, event);
+ return OPERATOR_RUNNING_MODAL;
+ }
+}
+
+static int view_zoom_modal(bContext *C, wmOperator *op, wmEvent *event)
+{
+ SpaceClip *sc= CTX_wm_space_clip(C);
+ ARegion *ar= CTX_wm_region(C);
+ ViewZoomData *vpd= op->customdata;
+ float factor;
+
+ switch(event->type) {
+ case MOUSEMOVE:
+ factor= 1.0f + (vpd->x-event->x+vpd->y-event->y)/300.0f;
+ RNA_float_set(op->ptr, "factor", factor);
+ sclip_zoom_set(sc, ar, vpd->zoom*factor);
+ ED_region_tag_redraw(CTX_wm_region(C));
+ break;
+ default:
+ if(event->type==vpd->event_type && event->val==KM_RELEASE) {
+ view_zoom_exit(C, op, 0);
+ return OPERATOR_FINISHED;
+ }
+ break;
+ }
+
+ return OPERATOR_RUNNING_MODAL;
+}
+
+static int view_zoom_cancel(bContext *C, wmOperator *op)
+{
+ view_zoom_exit(C, op, 1);
+ return OPERATOR_CANCELLED;
+}
+
+void CLIP_OT_view_zoom(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name= "View Zoom";
+ ot->idname= "CLIP_OT_view_zoom";
+
+ /* api callbacks */
+ ot->exec= view_zoom_exec;
+ ot->invoke= view_zoom_invoke;
+ ot->modal= view_zoom_modal;
+ ot->cancel= view_zoom_cancel;
+ ot->poll= ED_space_clip_poll;
+
+ /* flags */
+ ot->flag= OPTYPE_BLOCKING|OPTYPE_GRAB_POINTER;
+
+ /* properties */
+ RNA_def_float(ot->srna, "factor", 0.0f, 0.0f, FLT_MAX,
+ "Factor", "Zoom factor, values higher than 1.0 zoom in, lower values zoom out", -FLT_MAX, FLT_MAX);
+}
+
+/********************** view zoom in/out operator *********************/
+
+static int view_zoom_in_exec(bContext *C, wmOperator *UNUSED(op))
+{
+ SpaceClip *sc= CTX_wm_space_clip(C);
+ ARegion *ar= CTX_wm_region(C);
+
+ sclip_zoom_set_factor(sc, ar, 1.25f);
+
+ ED_region_tag_redraw(CTX_wm_region(C));
+
+ return OPERATOR_FINISHED;
+}
+
+static int view_zoom_out_exec(bContext *C, wmOperator *UNUSED(op))
+{
+ SpaceClip *sc= CTX_wm_space_clip(C);
+ ARegion *ar= CTX_wm_region(C);
+
+ sclip_zoom_set_factor(sc, ar, 0.8f);
+
+ ED_region_tag_redraw(CTX_wm_region(C));
+
+ return OPERATOR_FINISHED;
+}
+
+static int view_zoom_inout_invoke(bContext *C, wmOperator *op, wmEvent *event, int out)
+{
+ SpaceClip *sc= CTX_wm_space_clip(C);
+ float co[2], oldzoom= sc->zoom;
+
+ ED_clip_mouse_pos(C, event, co);
+
+ if(out)
+ view_zoom_out_exec(C, op);
+ else
+ view_zoom_in_exec(C, op);
+
+ if(U.uiflag&USER_ZOOM_TO_MOUSEPOS) {
+ int width, height;
+
+ ED_space_clip_size(sc, &width, &height);
+
+ sc->xof+= ((co[0]-0.5)*width-sc->xof)*(sc->zoom-oldzoom)/sc->zoom;
+ sc->yof+= ((co[1]-0.5)*height-sc->yof)*(sc->zoom-oldzoom)/sc->zoom;
+ }
+
+ return OPERATOR_FINISHED;
+}
+
+static int view_zoom_in_invoke(bContext *C, wmOperator *op, wmEvent *event)
+{
+ return view_zoom_inout_invoke(C, op, event, 0);
+}
+
+void CLIP_OT_view_zoom_in(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name= "View Zoom In";
+ ot->idname= "CLIP_OT_view_zoom_in";
+
+ /* api callbacks */
+ ot->exec= view_zoom_in_exec;
+ ot->invoke= view_zoom_in_invoke;
+ ot->poll= ED_space_clip_poll;
+}
+
+static int view_zoom_out_invoke(bContext *C, wmOperator *op, wmEvent *event)
+{
+ return view_zoom_inout_invoke(C, op, event, 1);
+}
+
+void CLIP_OT_view_zoom_out(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name= "View Zoom Out";
+ ot->idname= "CLIP_OT_view_zoom_out";
+
+ /* api callbacks */
+ ot->exec= view_zoom_out_exec;
+ ot->invoke= view_zoom_out_invoke;
+ ot->poll= ED_space_clip_poll;
+}
+
+/********************** view zoom ratio operator *********************/
+
+static int view_zoom_ratio_exec(bContext *C, wmOperator *op)
+{
+ SpaceClip *sc= CTX_wm_space_clip(C);
+ ARegion *ar= CTX_wm_region(C);
+
+ sclip_zoom_set(sc, ar, RNA_float_get(op->ptr, "ratio"));
+
+ /* ensure pixel exact locations for draw */
+ sc->xof= (int)sc->xof;
+ sc->yof= (int)sc->yof;
+
+ ED_region_tag_redraw(CTX_wm_region(C));
+
+ return OPERATOR_FINISHED;
+}
+
+void CLIP_OT_view_zoom_ratio(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name= "View Zoom Ratio";
+ ot->idname= "CLIP_OT_view_zoom_ratio";
+
+ /* api callbacks */
+ ot->exec= view_zoom_ratio_exec;
+ ot->poll= ED_space_clip_poll;
+
+ /* properties */
+ RNA_def_float(ot->srna, "ratio", 0.0f, 0.0f, FLT_MAX,
+ "Ratio", "Zoom ratio, 1.0 is 1:1, higher is zoomed in, lower is zoomed out", -FLT_MAX, FLT_MAX);
+}
+
+/********************** view all operator *********************/
+
+static int view_all_exec(bContext *C, wmOperator *UNUSED(op))
+{
+ SpaceClip *sc;
+ ARegion *ar;
+ int w, h, width, height;
+ float aspx, aspy;
+
+ /* retrieve state */
+ sc= CTX_wm_space_clip(C);
+ ar= CTX_wm_region(C);
+
+ ED_space_clip_size(sc, &w, &h);
+ ED_space_clip_aspect(sc, &aspx, &aspy);
+
+ w= w*aspx;
+ h= h*aspy;
+
+ /* check if the image will fit in the image with zoom==1 */
+ width= ar->winrct.xmax - ar->winrct.xmin + 1;
+ height= ar->winrct.ymax - ar->winrct.ymin + 1;
+
+ if((w >= width || h >= height) && (width > 0 && height > 0)) {
+ float zoomx, zoomy;
+
+ /* find the zoom value that will fit the image in the image space */
+ zoomx= (float)width/w;
+ zoomy= (float)height/h;
+ sclip_zoom_set(sc, ar, 1.0f/power_of_2(1/MIN2(zoomx, zoomy)));
+ }
+ else
+ sclip_zoom_set(sc, ar, 1.0f);
+
+ sc->xof= sc->yof= 0.0f;
+
+ ED_region_tag_redraw(CTX_wm_region(C));
+
+ return OPERATOR_FINISHED;
+}
+
+void CLIP_OT_view_all(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name= "View All";
+ ot->idname= "CLIP_OT_view_all";
+
+ /* api callbacks */
+ ot->exec= view_all_exec;
+ ot->poll= ED_space_clip_poll;
+}
+
+/********************** view selected operator *********************/
+
+static int view_selected_exec(bContext *C, wmOperator *UNUSED(op))
+{
+ SpaceClip *sc= CTX_wm_space_clip(C);
+ ARegion *ar= CTX_wm_region(C);
+
+ sc->xlockof= 0.0f;
+ sc->ylockof= 0.0f;
+
+ ED_clip_view_selection(sc, ar, 1);
+ ED_region_tag_redraw(CTX_wm_region(C));
+
+ return OPERATOR_FINISHED;
+}
+
+void CLIP_OT_view_selected(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name= "View Selected";
+ ot->idname= "CLIP_OT_view_selected";
+
+ /* api callbacks */
+ ot->exec= view_selected_exec;
+ ot->poll= ED_space_clip_poll;
+}
+
+/********************** change frame operator *********************/
+
+static int change_frame_poll(bContext *C)
+{
+ /* prevent changes during render */
+ if(G.rendering)
+ return 0;
+
+ return ED_space_clip_poll(C);
+}
+
+static void change_frame_apply(bContext *C, wmOperator *op)
+{
+ Scene *scene= CTX_data_scene(C);
+
+ /* set the new frame number */
+ CFRA= RNA_int_get(op->ptr, "frame");
+ FRAMENUMBER_MIN_CLAMP(CFRA);
+ SUBFRA = 0.0f;
+
+ /* do updates */
+ sound_seek_scene(CTX_data_main(C), CTX_data_scene(C));
+ WM_event_add_notifier(C, NC_SCENE|ND_FRAME, scene);
+}
+
+static int change_frame_exec(bContext *C, wmOperator *op)
+{
+ change_frame_apply(C, op);
+
+ return OPERATOR_FINISHED;
+}
+
+static int frame_from_event(bContext *C, wmEvent *event)
+{
+ ARegion *ar= CTX_wm_region(C);
+ Scene *scene= CTX_data_scene(C);
+ int framenr= 0;
+
+ if(ar->regiontype == RGN_TYPE_WINDOW) {
+ float sfra= SFRA, efra= EFRA, framelen= ar->winx/(efra-sfra+1);
+
+ framenr= sfra+event->mval[0]/framelen;
+ } else {
+ float viewx, viewy;
+
+ UI_view2d_region_to_view(&ar->v2d, event->mval[0], event->mval[1], &viewx, &viewy);
+
+ framenr= (int)floor(viewx+0.5f);
+ }
+
+ return framenr;
+}
+
+static int change_frame_invoke(bContext *C, wmOperator *op, wmEvent *event)
+{
+ ARegion *ar= CTX_wm_region(C);
+
+ if(ar->regiontype == RGN_TYPE_WINDOW) {
+ if(event->mval[1]>16)
+ return OPERATOR_PASS_THROUGH;
+ }
+
+ RNA_int_set(op->ptr, "frame", frame_from_event(C, event));
+
+ change_frame_apply(C, op);
+
+ /* add temp handler */
+ WM_event_add_modal_handler(C, op);
+
+ return OPERATOR_RUNNING_MODAL;
+}
+
+static int change_frame_modal(bContext *C, wmOperator *op, wmEvent *event)
+{
+ switch (event->type) {
+ case ESCKEY:
+ return OPERATOR_FINISHED;
+
+ case MOUSEMOVE:
+ RNA_int_set(op->ptr, "frame", frame_from_event(C, event));
+ change_frame_apply(C, op);
+ break;
+
+ case LEFTMOUSE:
+ case RIGHTMOUSE:
+ if (event->val==KM_RELEASE)
+ return OPERATOR_FINISHED;
+ break;
+ }
+
+ return OPERATOR_RUNNING_MODAL;
+}
+
+void CLIP_OT_change_frame(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name= "Change frame";
+ ot->idname= "CLIP_OT_change_frame";
+ ot->description= "Interactively change the current frame number";
+
+ /* api callbacks */
+ ot->exec= change_frame_exec;
+ ot->invoke= change_frame_invoke;
+ ot->modal= change_frame_modal;
+ ot->poll= change_frame_poll;
+
+ /* flags */
+ ot->flag= OPTYPE_BLOCKING|OPTYPE_UNDO;
+
+ /* rna */
+ RNA_def_int(ot->srna, "frame", 0, MINAFRAME, MAXFRAME, "Frame", "", MINAFRAME, MAXFRAME);
+}
+
+/********************** rebuild proxies operator *********************/
+
+typedef struct ProxyBuildJob {
+ Scene *scene;
+ struct Main *main;
+ MovieClip *clip;
+} ProxyJob;
+
+static void proxy_freejob(void *pjv)
+{
+ ProxyJob *pj= pjv;
+
+ MEM_freeN(pj);
+}
+
+/* only this runs inside thread */
+static void proxy_startjob(void *pjv, short *stop, short *do_update, float *progress)
+{
+ ProxyJob *pj= pjv;
+ Scene *scene=pj->scene;
+ MovieClip *clip= pj->clip;
+ struct MovieDistortion *distortion= NULL;
+ int cfra, undistort;
+ short tc_flag, size_flag, quality, build_flag;
+ int sfra= SFRA, efra= EFRA;
+ int build_sizes[4], build_count= 0;
+
+ tc_flag= clip->proxy.build_tc_flag;
+ size_flag= clip->proxy.build_size_flag;
+ quality= clip->proxy.quality;
+ build_flag= clip->proxy.build_flag;
+ undistort= build_flag&MCLIP_PROXY_RENDER_UNDISTORT;
+
+ if(clip->source == MCLIP_SRC_MOVIE) {
+ if(clip->anim)
+ IMB_anim_index_rebuild(clip->anim, tc_flag, size_flag, quality, stop, do_update, progress);
+
+ if(!undistort) {
+ return;
+ }
+ else {
+ sfra= 1;
+ efra= IMB_anim_get_duration(clip->anim, IMB_TC_NONE);
+ }
+ }
+
+ if(size_flag&IMB_PROXY_25) build_sizes[build_count++]= MCLIP_PROXY_RENDER_SIZE_25;
+ if(size_flag&IMB_PROXY_50) build_sizes[build_count++]= MCLIP_PROXY_RENDER_SIZE_50;
+ if(size_flag&IMB_PROXY_75) build_sizes[build_count++]= MCLIP_PROXY_RENDER_SIZE_75;
+ if(size_flag&IMB_PROXY_100) build_sizes[build_count++]= MCLIP_PROXY_RENDER_SIZE_100;
+
+ if(undistort)
+ distortion= BKE_tracking_distortion_create();
+
+ for(cfra= sfra; cfra<=efra; cfra++) {
+ if(clip->source != MCLIP_SRC_MOVIE)
+ BKE_movieclip_build_proxy_frame(clip, NULL, cfra, build_sizes, build_count, 0);
+
+ if(undistort)
+ BKE_movieclip_build_proxy_frame(clip, distortion, cfra, build_sizes, build_count, 1);
+
+ if(*stop || G.afbreek)
+ break;
+
+ *do_update= 1;
+ *progress= ((float)cfra)/(efra-sfra);
+ }
+
+ if(distortion)
+ BKE_tracking_distortion_destroy(distortion);
+}
+
+static int sequencer_rebuild_proxy_exec(bContext *C, wmOperator *UNUSED(op))
+{
+ wmJob * steve;
+ ProxyJob *pj;
+ Scene *scene= CTX_data_scene(C);
+ ScrArea *sa= CTX_wm_area(C);
+ SpaceClip *sc= CTX_wm_space_clip(C);
+ MovieClip *clip= ED_space_clip(sc);
+
+ if((clip->flag&MCLIP_USE_PROXY)==0)
+ return OPERATOR_CANCELLED;
+
+ steve= WM_jobs_get(CTX_wm_manager(C), CTX_wm_window(C), sa, "Building Proxies", WM_JOB_PROGRESS);
+
+ pj= MEM_callocN(sizeof(ProxyJob), "proxy rebuild job");
+ pj->scene= scene;
+ pj->main= CTX_data_main(C);
+ pj->clip= clip;
+
+ WM_jobs_customdata(steve, pj, proxy_freejob);
+ WM_jobs_timer(steve, 0.2, NC_MOVIECLIP|ND_DISPLAY, 0);
+ WM_jobs_callbacks(steve, proxy_startjob, NULL, NULL, NULL);
+
+ G.afbreek= 0;
+ WM_jobs_start(CTX_wm_manager(C), steve);
+
+ ED_area_tag_redraw(CTX_wm_area(C));
+
+ return OPERATOR_FINISHED;
+}
+
+void CLIP_OT_rebuild_proxy(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name= "Rebuild Proxy and Timecode Indices";
+ ot->idname= "CLIP_OT_rebuild_proxy";
+ ot->description="Rebuild all selected proxies and timecode indeces using the job system";
+
+ /* api callbacks */
+ ot->exec= sequencer_rebuild_proxy_exec;
+ ot->poll= ED_space_clip_poll;
+
+ /* flags */
+ ot->flag= OPTYPE_REGISTER;
+}
+
+/********************** mode set operator *********************/
+
+static int mode_set_exec(bContext *C, wmOperator *op)
+{
+ SpaceClip *sc= CTX_wm_space_clip(C);
+ int mode= RNA_enum_get(op->ptr, "mode");
+ int toggle= RNA_boolean_get(op->ptr, "toggle");
+
+ if(sc->mode==mode) {
+ if(toggle)
+ sc->mode= SC_MODE_TRACKING;
+ } else {
+ sc->mode= mode;
+ }
+
+ WM_event_add_notifier(C, NC_SPACE|ND_SPACE_CLIP, NULL);
+
+ return OPERATOR_FINISHED;
+}
+
+void CLIP_OT_mode_set(wmOperatorType *ot)
+{
+ static EnumPropertyItem mode_items[] = {
+ {SC_MODE_TRACKING, "TRACKING", 0, "Tracking", "Show tracking and solving tools"},
+ {SC_MODE_RECONSTRUCTION, "RECONSTRUCTION", 0, "Reconstruction", "Show tracking/reconstruction tools"},
+ {SC_MODE_DISTORTION, "DISTORTION", 0, "Distortion", "Show distortion tools"},
+ {0, NULL, 0, NULL, NULL}};
+
+
+ /* identifiers */
+ ot->name= "Set Clip Mode";
+ ot->description = "Sets the clip interaction mode";
+ ot->idname= "CLIP_OT_mode_set";
+
+ /* api callbacks */
+ ot->exec= mode_set_exec;
+
+ ot->poll= ED_space_clip_poll;
+
+ /* properties */
+ RNA_def_enum(ot->srna, "mode", mode_items, SC_MODE_TRACKING, "Mode", "");
+ RNA_def_boolean(ot->srna, "toggle", 0, "Toggle", "");
+}
+
+/********************** macroses *********************/
+
+void ED_operatormacros_clip(void)
+{
+ wmOperatorType *ot;
+ wmOperatorTypeMacro *otmacro;
+
+ ot= WM_operatortype_append_macro("CLIP_OT_add_marker_move", "Add Marker and Move", OPTYPE_UNDO|OPTYPE_REGISTER);
+ ot->description = "Add new marker and move it on movie";
+ WM_operatortype_macro_define(ot, "CLIP_OT_add_marker");
+ otmacro= WM_operatortype_macro_define(ot, "TRANSFORM_OT_translate");
+ RNA_struct_idprops_unset(otmacro->ptr, "release_confirm");
+
+ ot= WM_operatortype_append_macro("CLIP_OT_add_marker_slide", "Add Marker and Slide", OPTYPE_UNDO|OPTYPE_REGISTER);
+ ot->description = "Add new marker and slide it with mouse until mouse button release";
+ WM_operatortype_macro_define(ot, "CLIP_OT_add_marker");
+ otmacro= WM_operatortype_macro_define(ot, "TRANSFORM_OT_translate");
+ RNA_boolean_set(otmacro->ptr, "release_confirm", 1);
+}
diff --git a/source/blender/editors/space_clip/clip_toolbar.c b/source/blender/editors/space_clip/clip_toolbar.c
new file mode 100644
index 00000000000..c8113c5ea7b
--- /dev/null
+++ b/source/blender/editors/space_clip/clip_toolbar.c
@@ -0,0 +1,244 @@
+/*
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * 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.
+ *
+ *
+ * Contributor(s): Blender Foundation,
+ * Sergey Sharybin
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+/** \file blender/editors/space_clip/clip_header.c
+ * \ingroup spclip
+ */
+
+#include <string.h>
+
+#include "DNA_windowmanager_types.h"
+
+#include "MEM_guardedalloc.h"
+
+#include "BLI_blenlib.h"
+#include "BLI_utildefines.h"
+
+#include "BKE_context.h"
+#include "BKE_screen.h"
+
+#include "ED_screen.h"
+#include "ED_util.h"
+
+#include "WM_types.h"
+#include "WM_api.h"
+
+#include "UI_interface.h"
+#include "UI_resources.h"
+
+/* ************************ header area region *********************** */
+
+/************************** properties ******************************/
+
+static ARegion *clip_has_properties_region(ScrArea *sa)
+{
+ ARegion *ar, *arnew;
+
+ ar= BKE_area_find_region_type(sa, RGN_TYPE_UI);
+ if(ar)
+ return ar;
+
+ /* add subdiv level; after header */
+ ar= BKE_area_find_region_type(sa, RGN_TYPE_HEADER);
+
+ /* is error! */
+ if(ar==NULL)
+ return NULL;
+
+ arnew= MEM_callocN(sizeof(ARegion), "clip properties region");
+
+ BLI_insertlinkafter(&sa->regionbase, ar, arnew);
+ arnew->regiontype= RGN_TYPE_UI;
+ arnew->alignment= RGN_ALIGN_RIGHT;
+
+ arnew->flag= RGN_FLAG_HIDDEN;
+
+ return arnew;
+}
+
+static int properties_poll(bContext *C)
+{
+ return (CTX_wm_space_clip(C) != NULL);
+}
+
+static int properties_exec(bContext *C, wmOperator *UNUSED(op))
+{
+ ScrArea *sa= CTX_wm_area(C);
+ ARegion *ar= clip_has_properties_region(sa);
+
+ if(ar)
+ ED_region_toggle_hidden(C, ar);
+
+ return OPERATOR_FINISHED;
+}
+
+void CLIP_OT_properties(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name= "Properties";
+ ot->description= "Toggle clip properties panel";
+ ot->idname= "CLIP_OT_properties";
+
+ /* api callbacks */
+ ot->exec= properties_exec;
+ ot->poll= properties_poll;
+}
+
+/************************** tools ******************************/
+
+static ARegion *clip_has_tools_region(ScrArea *sa)
+{
+ ARegion *ar, *artool=NULL, *arprops=NULL, *arhead;
+
+ for(ar= sa->regionbase.first; ar; ar= ar->next) {
+ if(ar->regiontype==RGN_TYPE_TOOLS)
+ artool= ar;
+ if(ar->regiontype==RGN_TYPE_TOOL_PROPS)
+ arprops= ar;
+ }
+
+ /* tool region hide/unhide also hides props */
+ if(arprops && artool)
+ return artool;
+
+ if(artool==NULL) {
+ /* add subdiv level; after header */
+ arhead= BKE_area_find_region_type(sa, RGN_TYPE_HEADER);
+
+ /* is error! */
+ if(arhead==NULL)
+ return NULL;
+
+ artool= MEM_callocN(sizeof(ARegion), "clip tools region");
+
+ BLI_insertlinkafter(&sa->regionbase, arhead, artool);
+ artool->regiontype= RGN_TYPE_TOOLS;
+ artool->alignment= RGN_ALIGN_LEFT;
+
+ artool->flag= RGN_FLAG_HIDDEN;
+ }
+
+ if(arprops==NULL) {
+ /* add extra subdivided region for tool properties */
+ arprops= MEM_callocN(sizeof(ARegion), "tool props for clip");
+
+ BLI_insertlinkafter(&sa->regionbase, artool, arprops);
+ arprops->regiontype= RGN_TYPE_TOOL_PROPS;
+ arprops->alignment= RGN_ALIGN_BOTTOM|RGN_SPLIT_PREV;
+ }
+
+ return artool;
+}
+
+static int tools_poll(bContext *C)
+{
+ return (CTX_wm_space_clip(C) != NULL);
+}
+
+static int tools_exec(bContext *C, wmOperator *UNUSED(op))
+{
+ ScrArea *sa= CTX_wm_area(C);
+ ARegion *ar= clip_has_tools_region(sa);
+
+ if(ar)
+ ED_region_toggle_hidden(C, ar);
+
+ return OPERATOR_FINISHED;
+}
+
+void CLIP_OT_tools(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name= "Tools";
+ ot->description= "Toggle clip tools panel";
+ ot->idname= "CLIP_OT_tools";
+
+ /* api callbacks */
+ ot->exec= tools_exec;
+ ot->poll= tools_poll;
+}
+
+/************************** redo panel ******************************/
+
+static void clip_panel_operator_redo_buts(const bContext *C, Panel *pa, wmOperator *op)
+{
+ uiLayoutOperatorButs(C, pa->layout, op, NULL, 'V', 0);
+}
+
+static void clip_panel_operator_redo_header(const bContext *C, Panel *pa)
+{
+ wmOperator *op= WM_operator_last_redo(C);
+
+ if(op) BLI_strncpy(pa->drawname, op->type->name, sizeof(pa->drawname));
+ else BLI_strncpy(pa->drawname, "Operator", sizeof(pa->drawname));
+}
+
+static void clip_panel_operator_redo_operator(const bContext *C, Panel *pa, wmOperator *op)
+{
+ if(op->type->flag & OPTYPE_MACRO) {
+ for(op= op->macro.first; op; op= op->next) {
+ uiItemL(pa->layout, op->type->name, ICON_NONE);
+ clip_panel_operator_redo_operator(C, pa, op);
+ }
+ }
+ else {
+ clip_panel_operator_redo_buts(C, pa, op);
+ }
+}
+
+static void clip_panel_operator_redo(const bContext *C, Panel *pa)
+{
+ wmOperator *op= WM_operator_last_redo(C);
+ uiBlock *block;
+
+ if(op==NULL)
+ return;
+ if(WM_operator_poll((bContext*)C, op->type) == 0)
+ return;
+
+ block= uiLayoutGetBlock(pa->layout);
+
+ if(ED_undo_valid(C, op->type->name)==0)
+ uiLayoutSetEnabled(pa->layout, 0);
+
+ /* note, blockfunc is a default but->func, use Handle func to allow button callbacks too */
+ uiBlockSetHandleFunc(block, ED_undo_operator_repeat_cb_evt, op);
+
+ clip_panel_operator_redo_operator(C, pa, op);
+}
+
+void ED_clip_tool_props_register(ARegionType *art)
+{
+ PanelType *pt;
+
+ pt= MEM_callocN(sizeof(PanelType), "spacetype clip panel last operator");
+ strcpy(pt->idname, "CLIP_PT_last_operator");
+ strcpy(pt->label, "Operator");
+ pt->draw_header= clip_panel_operator_redo_header;
+ pt->draw= clip_panel_operator_redo;
+ BLI_addtail(&art->paneltypes, pt);
+}
diff --git a/source/blender/editors/space_clip/clip_utils.c b/source/blender/editors/space_clip/clip_utils.c
new file mode 100644
index 00000000000..649b278ab3d
--- /dev/null
+++ b/source/blender/editors/space_clip/clip_utils.c
@@ -0,0 +1,219 @@
+/*
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * 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.
+ *
+ *
+ * Contributor(s): Blender Foundation,
+ * Sergey Sharybin
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+/** \file blender/editors/space_clip/clip_utils.c
+ * \ingroup spclip
+ */
+
+#include "DNA_object_types.h" /* SELECT */
+
+#include "MEM_guardedalloc.h"
+
+#include "BLI_utildefines.h"
+#include "BLI_math.h"
+#include "BLI_listbase.h"
+
+#include "BKE_context.h"
+#include "BKE_movieclip.h"
+#include "BKE_tracking.h"
+#include "BKE_depsgraph.h"
+
+#include "WM_api.h"
+#include "WM_types.h"
+
+#include "ED_screen.h"
+#include "ED_clip.h"
+
+#include "UI_interface.h"
+
+#include "RNA_access.h"
+#include "RNA_define.h"
+
+#include "UI_view2d.h"
+
+#include "clip_intern.h" // own include
+
+void clip_graph_tracking_values_iterate_track(SpaceClip *sc, MovieTrackingTrack *track, void *userdata,
+ void (*func) (void *userdata, MovieTrackingTrack *track, MovieTrackingMarker *marker, int coord, float val),
+ void (*segment_start) (void *userdata, MovieTrackingTrack *track, int coord),
+ void (*segment_end) (void *userdata))
+{
+ MovieClip *clip= ED_space_clip(sc);
+ int width, height, coord;
+
+ BKE_movieclip_get_size(clip, &sc->user, &width, &height);
+
+ for(coord= 0; coord<2; coord++) {
+ int i, open= 0, prevfra= 0;
+ float prevval= 0.0f;
+
+ for(i= 0; i<track->markersnr; i++) {
+ MovieTrackingMarker *marker= &track->markers[i];
+ float val;
+
+ if(marker->flag&MARKER_DISABLED) {
+ if(open) {
+ if(segment_end)
+ segment_end(userdata);
+
+ open= 0;
+ }
+
+ continue;
+ }
+
+ if(!open) {
+ if(segment_start)
+ segment_start(userdata, track, coord);
+
+ open= 1;
+ prevval= marker->pos[coord];
+ }
+
+ /* value is a pixels per frame speed */
+ val= (marker->pos[coord] - prevval) * ((i==0) ? (width) : (height));
+ val/= marker->framenr-prevfra;
+
+ if(func)
+ func(userdata, track, marker, coord, val);
+
+ prevval= marker->pos[coord];
+ prevfra= marker->framenr;
+ }
+
+ if(open) {
+ if(segment_end)
+ segment_end(userdata);
+ }
+ }
+}
+
+void clip_graph_tracking_values_iterate(SpaceClip *sc, void *userdata,
+ void (*func) (void *userdata, MovieTrackingTrack *track, MovieTrackingMarker *marker, int coord, float val),
+ void (*segment_start) (void *userdata, MovieTrackingTrack *track, int coord),
+ void (*segment_end) (void *userdata))
+{
+ MovieClip *clip= ED_space_clip(sc);
+ MovieTracking *tracking= &clip->tracking;
+ MovieTrackingTrack *track;
+
+ track= tracking->tracks.first;
+ while(track) {
+ if(TRACK_VIEW_SELECTED(sc, track)) {
+ clip_graph_tracking_values_iterate_track(sc, track, userdata, func, segment_start, segment_end);
+ }
+
+ track= track->next;
+ }
+}
+
+void clip_graph_tracking_iterate(SpaceClip *sc, void *userdata,
+ void (*func) (void *userdata, MovieTrackingMarker *marker))
+{
+ MovieClip *clip= ED_space_clip(sc);
+ MovieTracking *tracking= &clip->tracking;
+ MovieTrackingTrack *track;
+
+ track= tracking->tracks.first;
+ while(track) {
+ if(TRACK_VIEW_SELECTED(sc, track)) {
+ int i;
+
+ for(i= 0; i<track->markersnr; i++) {
+ MovieTrackingMarker *marker= &track->markers[i];
+
+ if(marker->flag&MARKER_DISABLED)
+ continue;
+
+ if(func)
+ func(userdata, marker);
+ }
+ }
+
+ track= track->next;
+ }
+}
+
+void clip_delete_track(bContext *C, MovieClip *clip, MovieTrackingTrack *track)
+{
+ MovieTracking *tracking= &clip->tracking;
+ MovieTrackingStabilization *stab= &tracking->stabilization;
+
+ int has_bundle= 0, update_stab= 0;
+
+ if(track==tracking->act_track)
+ tracking->act_track= NULL;
+
+ if(track==stab->rot_track) {
+ stab->rot_track= NULL;
+
+ update_stab= 1;
+ }
+
+ /* handle reconstruction display in 3d viewport */
+ if(track->flag&TRACK_HAS_BUNDLE)
+ has_bundle= 1;
+
+ BKE_tracking_free_track(track);
+ BLI_freelinkN(&tracking->tracks, track);
+
+ WM_event_add_notifier(C, NC_MOVIECLIP|NA_EDITED, clip);
+
+ if(update_stab) {
+ tracking->stabilization.ok= 0;
+
+ DAG_id_tag_update(&clip->id, 0);
+ WM_event_add_notifier(C, NC_MOVIECLIP|ND_DISPLAY, clip);
+ }
+
+ if(has_bundle)
+ WM_event_add_notifier(C, NC_SPACE|ND_SPACE_VIEW3D, NULL);
+}
+
+void clip_delete_marker(bContext *C, MovieClip *clip, MovieTrackingTrack *track, MovieTrackingMarker *marker)
+{
+ if(track->markersnr==1) {
+ clip_delete_track(C, clip, track);
+ }
+ else {
+ BKE_tracking_delete_marker(track, marker->framenr);
+
+ WM_event_add_notifier(C, NC_MOVIECLIP|NA_EDITED, clip);
+ }
+}
+
+void clip_view_center_to_point(SpaceClip *sc, float x, float y)
+{
+ int width, height;
+ float aspx, aspy;
+
+ ED_space_clip_size(sc, &width, &height);
+ ED_space_clip_aspect(sc, &aspx, &aspy);
+
+ sc->xof= (x-0.5f)*width*aspx;
+ sc->yof= (y-0.5f)*height*aspy;
+}
diff --git a/source/blender/editors/space_clip/space_clip.c b/source/blender/editors/space_clip/space_clip.c
new file mode 100644
index 00000000000..c8577f7760e
--- /dev/null
+++ b/source/blender/editors/space_clip/space_clip.c
@@ -0,0 +1,949 @@
+/*
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * 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.
+ *
+ *
+ * Contributor(s): Blender Foundation,
+ * Sergey Sharybin
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+/** \file blender/editors/space_clip/space_clip.c
+ * \ingroup spclip
+ */
+
+#include <string.h>
+#include <stdio.h>
+
+#include "DNA_scene_types.h"
+#include "DNA_movieclip_types.h"
+
+#include "MEM_guardedalloc.h"
+
+#include "BLI_blenlib.h"
+#include "BLI_utildefines.h"
+#include "BLI_math.h"
+
+#include "BKE_main.h"
+#include "BKE_context.h"
+#include "BKE_screen.h"
+#include "BKE_movieclip.h"
+#include "BKE_tracking.h"
+
+#include "IMB_imbuf_types.h"
+
+#include "ED_screen.h"
+#include "ED_clip.h"
+#include "ED_transform.h"
+
+#include "IMB_imbuf.h"
+
+#include "BIF_gl.h"
+
+#include "WM_api.h"
+#include "WM_types.h"
+
+#include "UI_interface.h"
+#include "UI_resources.h"
+#include "UI_view2d.h"
+
+#include "RNA_access.h"
+
+
+#include "clip_intern.h" // own include
+
+static void init_preview_region(const bContext *C, ARegion *ar)
+{
+ Scene *scene= CTX_data_scene(C);
+
+ ar->regiontype= RGN_TYPE_PREVIEW;
+ ar->alignment= RGN_ALIGN_TOP;
+ ar->flag|= RGN_FLAG_HIDDEN;
+
+ ar->v2d.tot.xmin= 0.0f;
+ ar->v2d.tot.ymin= (float)scene->r.sfra - 10.0f;
+ ar->v2d.tot.xmax= (float)scene->r.efra;
+ ar->v2d.tot.ymax= 10.0f;
+
+ ar->v2d.cur= ar->v2d.tot;
+
+ ar->v2d.min[0]= FLT_MIN;
+ ar->v2d.min[1]= FLT_MIN;
+
+ ar->v2d.max[0]= MAXFRAMEF;
+ ar->v2d.max[1]= FLT_MAX;
+
+ ar->v2d.scroll= (V2D_SCROLL_BOTTOM|V2D_SCROLL_SCALE_HORIZONTAL);
+ ar->v2d.scroll |= (V2D_SCROLL_LEFT|V2D_SCROLL_SCALE_VERTICAL);
+
+ ar->v2d.keeptot= 0;
+}
+
+static ARegion *clip_has_preview_region(const bContext *C, ScrArea *sa)
+{
+ ARegion *ar, *arnew;
+
+ ar= BKE_area_find_region_type(sa, RGN_TYPE_PREVIEW);
+ if(ar)
+ return ar;
+
+ /* add subdiv level; after header */
+ ar= BKE_area_find_region_type(sa, RGN_TYPE_WINDOW);
+
+ /* is error! */
+ if(ar==NULL)
+ return NULL;
+
+ arnew= MEM_callocN(sizeof(ARegion), "clip preview region");
+
+ BLI_insertlinkbefore(&sa->regionbase, ar, arnew);
+ init_preview_region(C, arnew);
+
+ return arnew;
+}
+
+static void clip_scopes_tag_refresh(ScrArea *sa)
+{
+ SpaceClip *sc= (SpaceClip *)sa->spacedata.first;
+ ARegion *ar;
+
+ if(sc->mode!=SC_MODE_TRACKING)
+ return;
+
+ /* only while proeprties are visible */
+ for (ar=sa->regionbase.first; ar; ar=ar->next) {
+ if (ar->regiontype == RGN_TYPE_UI && ar->flag & RGN_FLAG_HIDDEN)
+ return;
+ }
+
+ sc->scopes.ok= 0;
+}
+
+static void clip_stabilization_tag_refresh(ScrArea *sa)
+{
+ SpaceClip *sc= (SpaceClip *)sa->spacedata.first;
+ MovieClip *clip= ED_space_clip(sc);
+
+ if(clip) {
+ MovieTrackingStabilization *stab= &clip->tracking.stabilization;
+
+ stab->ok= 0;
+ }
+}
+
+/* ******************** default callbacks for clip space ***************** */
+
+static SpaceLink *clip_new(const bContext *C)
+{
+ ARegion *ar;
+ SpaceClip *sc;
+
+ sc= MEM_callocN(sizeof(SpaceClip), "initclip");
+ sc->spacetype= SPACE_CLIP;
+ sc->flag= SC_SHOW_MARKER_PATTERN|SC_SHOW_TRACK_PATH|SC_SHOW_GPENCIL|SC_MANUAL_CALIBRATION|SC_SHOW_GRAPH_TRACKS|SC_SHOW_GRAPH_FRAMES;
+ sc->zoom= 1.0f;
+ sc->path_length= 20;
+ sc->scopes.track_preview_height= 120;
+
+ /* header */
+ ar= MEM_callocN(sizeof(ARegion), "header for clip");
+
+ BLI_addtail(&sc->regionbase, ar);
+ ar->regiontype= RGN_TYPE_HEADER;
+ ar->alignment= RGN_ALIGN_BOTTOM;
+
+ /* tools view */
+ ar= MEM_callocN(sizeof(ARegion), "tools for clip");
+
+ BLI_addtail(&sc->regionbase, ar);
+ ar->regiontype= RGN_TYPE_TOOLS;
+ ar->alignment= RGN_ALIGN_LEFT;
+
+ /* tool properties */
+ ar= MEM_callocN(sizeof(ARegion), "tool properties for clip");
+
+ BLI_addtail(&sc->regionbase, ar);
+ ar->regiontype= RGN_TYPE_TOOL_PROPS;
+ ar->alignment= RGN_ALIGN_BOTTOM|RGN_SPLIT_PREV;
+
+ /* properties view */
+ ar= MEM_callocN(sizeof(ARegion), "properties for clip");
+
+ BLI_addtail(&sc->regionbase, ar);
+ ar->regiontype= RGN_TYPE_UI;
+ ar->alignment= RGN_ALIGN_RIGHT;
+
+ /* preview view */
+ ar= MEM_callocN(sizeof(ARegion), "preview for clip");
+
+ BLI_addtail(&sc->regionbase, ar);
+ init_preview_region(C, ar);
+
+ /* main area */
+ ar= MEM_callocN(sizeof(ARegion), "main area for clip");
+
+ BLI_addtail(&sc->regionbase, ar);
+ ar->regiontype= RGN_TYPE_WINDOW;
+
+ return (SpaceLink *)sc;
+}
+
+/* not spacelink itself */
+static void clip_free(SpaceLink *sl)
+{
+ SpaceClip *sc= (SpaceClip*) sl;
+
+ sc->clip= NULL;
+
+ if(sc->scopes.track_preview)
+ IMB_freeImBuf(sc->scopes.track_preview);
+}
+
+/* spacetype; init callback */
+static void clip_init(struct wmWindowManager *UNUSED(wm), ScrArea *UNUSED(sa))
+{
+
+}
+
+static SpaceLink *clip_duplicate(SpaceLink *sl)
+{
+ SpaceClip *scn= MEM_dupallocN(sl);
+
+ /* clear or remove stuff from old */
+ scn->scopes.track_preview= NULL;
+ scn->scopes.ok= 0;
+
+ return (SpaceLink *)scn;
+}
+
+static void clip_listener(ScrArea *sa, wmNotifier *wmn)
+{
+ /* context changes */
+ switch(wmn->category) {
+ case NC_SCENE:
+ switch(wmn->data) {
+ case ND_FRAME:
+ clip_scopes_tag_refresh(sa);
+ /* no break! */
+
+ case ND_FRAME_RANGE:
+ ED_area_tag_refresh(sa);
+ ED_area_tag_redraw(sa);
+ break;
+ }
+ break;
+ case NC_MOVIECLIP:
+ switch(wmn->data) {
+ case ND_DISPLAY:
+ case ND_SELECT:
+ clip_scopes_tag_refresh(sa);
+ ED_area_tag_redraw(sa);
+ break;
+ }
+ switch(wmn->action) {
+ case NA_REMOVED:
+ case NA_EDITED:
+ case NA_EVALUATED:
+ clip_stabilization_tag_refresh(sa);
+ /* no break! */
+
+ case NA_SELECTED:
+ clip_scopes_tag_refresh(sa);
+ ED_area_tag_redraw(sa);
+ break;
+ }
+ break;
+ case NC_GEOM:
+ switch(wmn->data) {
+ case ND_SELECT:
+ clip_scopes_tag_refresh(sa);
+ ED_area_tag_redraw(sa);
+ break;
+ }
+ break;
+ case NC_SCREEN:
+ if(wmn->data==ND_ANIMPLAY) {
+ ED_area_tag_redraw(sa);
+ }
+ break;
+ case NC_SPACE:
+ if(wmn->data==ND_SPACE_CLIP) {
+ clip_scopes_tag_refresh(sa);
+ clip_stabilization_tag_refresh(sa);
+ ED_area_tag_redraw(sa);
+ }
+ break;
+ }
+}
+
+static void clip_operatortypes(void)
+{
+ /* ** clip_ops.c ** */
+ WM_operatortype_append(CLIP_OT_open);
+ WM_operatortype_append(CLIP_OT_reload);
+ WM_operatortype_append(CLIP_OT_view_pan);
+ WM_operatortype_append(CLIP_OT_view_zoom);
+ WM_operatortype_append(CLIP_OT_view_zoom_in);
+ WM_operatortype_append(CLIP_OT_view_zoom_out);
+ WM_operatortype_append(CLIP_OT_view_zoom_ratio);
+ WM_operatortype_append(CLIP_OT_view_all);
+ WM_operatortype_append(CLIP_OT_view_selected);
+ WM_operatortype_append(CLIP_OT_change_frame);
+ WM_operatortype_append(CLIP_OT_rebuild_proxy);
+ WM_operatortype_append(CLIP_OT_mode_set);
+
+ /* ** clip_toolbar.c ** */
+ WM_operatortype_append(CLIP_OT_tools);
+ WM_operatortype_append(CLIP_OT_properties);
+
+ /* ** tracking_ops.c ** */
+
+ /* navigation */
+ WM_operatortype_append(CLIP_OT_frame_jump);
+
+ /* foorage */
+ WM_operatortype_append(CLIP_OT_set_center_principal);
+
+ /* selection */
+ WM_operatortype_append(CLIP_OT_select);
+ WM_operatortype_append(CLIP_OT_select_all);
+ WM_operatortype_append(CLIP_OT_select_border);
+ WM_operatortype_append(CLIP_OT_select_circle);
+ WM_operatortype_append(CLIP_OT_select_grouped);
+
+ /* markers */
+ WM_operatortype_append(CLIP_OT_add_marker);
+ WM_operatortype_append(CLIP_OT_slide_marker);
+ WM_operatortype_append(CLIP_OT_delete_track);
+ WM_operatortype_append(CLIP_OT_delete_marker);
+
+ /* track */
+ WM_operatortype_append(CLIP_OT_track_markers);
+
+ /* solving */
+ WM_operatortype_append(CLIP_OT_solve_camera);
+ WM_operatortype_append(CLIP_OT_clear_solution);
+
+ WM_operatortype_append(CLIP_OT_disable_markers);
+ WM_operatortype_append(CLIP_OT_hide_tracks);
+ WM_operatortype_append(CLIP_OT_hide_tracks_clear);
+ WM_operatortype_append(CLIP_OT_lock_tracks);
+
+ /* orientation */
+ WM_operatortype_append(CLIP_OT_set_origin);
+ WM_operatortype_append(CLIP_OT_set_floor);
+ WM_operatortype_append(CLIP_OT_set_axis);
+ WM_operatortype_append(CLIP_OT_set_scale);
+
+ /* detect */
+ WM_operatortype_append(CLIP_OT_detect_features);
+
+ /* stabilization */
+ WM_operatortype_append(CLIP_OT_stabilize_2d_add);
+ WM_operatortype_append(CLIP_OT_stabilize_2d_remove);
+ WM_operatortype_append(CLIP_OT_stabilize_2d_select);
+ WM_operatortype_append(CLIP_OT_stabilize_2d_set_rotation);
+
+ /* clean-up */
+ WM_operatortype_append(CLIP_OT_clear_track_path);
+ WM_operatortype_append(CLIP_OT_join_tracks);
+ WM_operatortype_append(CLIP_OT_track_copy_color);
+
+ WM_operatortype_append(CLIP_OT_clean_tracks);
+
+ /* graph editing */
+ WM_operatortype_append(CLIP_OT_graph_select);
+ WM_operatortype_append(CLIP_OT_graph_delete_curve);
+ WM_operatortype_append(CLIP_OT_graph_delete_knot);
+}
+
+static void clip_keymap(struct wmKeyConfig *keyconf)
+{
+ wmKeyMap *keymap;
+ wmKeyMapItem *kmi;
+
+ /* ******** Global hotkeys avalaible for all regions ******** */
+
+ keymap= WM_keymap_find(keyconf, "Clip", SPACE_CLIP, 0);
+
+ WM_keymap_add_item(keymap, "CLIP_OT_open", OKEY, KM_PRESS, KM_ALT, 0);
+
+ WM_keymap_add_item(keymap, "CLIP_OT_tools", TKEY, KM_PRESS, 0, 0);
+ WM_keymap_add_item(keymap, "CLIP_OT_properties", NKEY, KM_PRESS, 0, 0);
+
+ /* 2d tracking */
+ kmi= WM_keymap_add_item(keymap, "CLIP_OT_track_markers", LEFTARROWKEY, KM_PRESS, KM_ALT, 0);
+ RNA_boolean_set(kmi->ptr, "backwards", 1);
+ WM_keymap_add_item(keymap, "CLIP_OT_track_markers", RIGHTARROWKEY, KM_PRESS, KM_ALT, 0);
+ kmi= WM_keymap_add_item(keymap, "CLIP_OT_track_markers", TKEY, KM_PRESS, KM_CTRL, 0);
+ RNA_boolean_set(kmi->ptr, "sequence", 1);
+ kmi= WM_keymap_add_item(keymap, "CLIP_OT_track_markers", TKEY, KM_PRESS, KM_SHIFT|KM_CTRL, 0);
+ RNA_boolean_set(kmi->ptr, "backwards", 1);
+ RNA_boolean_set(kmi->ptr, "sequence", 1);
+
+ /* mode */
+ kmi= WM_keymap_add_item(keymap, "CLIP_OT_mode_set", TABKEY, KM_PRESS, 0, 0);
+ RNA_enum_set(kmi->ptr, "mode", SC_MODE_RECONSTRUCTION);
+ RNA_boolean_set(kmi->ptr, "toggle", 1);
+
+ kmi= WM_keymap_add_item(keymap, "CLIP_OT_mode_set", TABKEY, KM_PRESS, KM_CTRL, 0);
+ RNA_enum_set(kmi->ptr, "mode", SC_MODE_DISTORTION);
+ RNA_boolean_set(kmi->ptr, "toggle", 1);
+
+ /* ******** Hotkeys avalaible for main region only ******** */
+
+ keymap= WM_keymap_find(keyconf, "Clip Editor", SPACE_CLIP, 0);
+
+ /* ** View/navigation ** */
+
+ WM_keymap_add_item(keymap, "CLIP_OT_view_pan", MIDDLEMOUSE, KM_PRESS, 0, 0);
+ WM_keymap_add_item(keymap, "CLIP_OT_view_pan", MIDDLEMOUSE, KM_PRESS, KM_SHIFT, 0);
+ WM_keymap_add_item(keymap, "CLIP_OT_view_pan", MOUSEPAN, 0, 0, 0);
+
+ WM_keymap_add_item(keymap, "CLIP_OT_view_zoom", MIDDLEMOUSE, KM_PRESS, KM_CTRL, 0);
+ WM_keymap_add_item(keymap, "CLIP_OT_view_zoom", MOUSEZOOM, 0, 0, 0);
+ WM_keymap_add_item(keymap, "CLIP_OT_view_zoom_in", WHEELINMOUSE, KM_PRESS, 0, 0);
+ WM_keymap_add_item(keymap, "CLIP_OT_view_zoom_out", WHEELOUTMOUSE, KM_PRESS, 0, 0);
+ WM_keymap_add_item(keymap, "CLIP_OT_view_zoom_in", PADPLUSKEY, KM_PRESS, 0, 0);
+ WM_keymap_add_item(keymap, "CLIP_OT_view_zoom_out", PADMINUS, KM_PRESS, 0, 0);
+
+ RNA_float_set(WM_keymap_add_item(keymap, "CLIP_OT_view_zoom_ratio", PAD8, KM_PRESS, KM_SHIFT, 0)->ptr, "ratio", 8.0f);
+ RNA_float_set(WM_keymap_add_item(keymap, "CLIP_OT_view_zoom_ratio", PAD4, KM_PRESS, KM_SHIFT, 0)->ptr, "ratio", 4.0f);
+ RNA_float_set(WM_keymap_add_item(keymap, "CLIP_OT_view_zoom_ratio", PAD2, KM_PRESS, KM_SHIFT, 0)->ptr, "ratio", 2.0f);
+ RNA_float_set(WM_keymap_add_item(keymap, "CLIP_OT_view_zoom_ratio", PAD1, KM_PRESS, 0, 0)->ptr, "ratio", 1.0f);
+ RNA_float_set(WM_keymap_add_item(keymap, "CLIP_OT_view_zoom_ratio", PAD2, KM_PRESS, 0, 0)->ptr, "ratio", 0.5f);
+ RNA_float_set(WM_keymap_add_item(keymap, "CLIP_OT_view_zoom_ratio", PAD4, KM_PRESS, 0, 0)->ptr, "ratio", 0.25f);
+ RNA_float_set(WM_keymap_add_item(keymap, "CLIP_OT_view_zoom_ratio", PAD8, KM_PRESS, 0, 0)->ptr, "ratio", 0.125f);
+
+ WM_keymap_add_item(keymap, "CLIP_OT_view_all", HOMEKEY, KM_PRESS, 0, 0);
+ WM_keymap_add_item(keymap, "CLIP_OT_view_selected", PADPERIOD, KM_PRESS, 0, 0);
+
+ /* jump to special frame */
+ kmi= WM_keymap_add_item(keymap, "CLIP_OT_frame_jump", LEFTARROWKEY, KM_PRESS, KM_CTRL|KM_SHIFT, 0);
+ RNA_enum_set(kmi->ptr, "position", 0);
+
+ kmi= WM_keymap_add_item(keymap, "CLIP_OT_frame_jump", RIGHTARROWKEY, KM_PRESS, KM_CTRL|KM_SHIFT, 0);
+ RNA_enum_set(kmi->ptr, "position", 1);
+
+ kmi= WM_keymap_add_item(keymap, "CLIP_OT_frame_jump", LEFTARROWKEY, KM_PRESS, KM_ALT|KM_SHIFT, 0);
+ RNA_enum_set(kmi->ptr, "position", 2);
+
+ kmi= WM_keymap_add_item(keymap, "CLIP_OT_frame_jump", RIGHTARROWKEY, KM_PRESS, KM_ALT|KM_SHIFT, 0);
+ RNA_enum_set(kmi->ptr, "position", 3);
+
+ /* "timeline" */
+ WM_keymap_add_item(keymap, "CLIP_OT_change_frame", LEFTMOUSE, KM_PRESS, 0, 0);
+
+ /* selection */
+ WM_keymap_add_item(keymap, "CLIP_OT_select", SELECTMOUSE, KM_PRESS, 0, 0);
+ RNA_boolean_set(WM_keymap_add_item(keymap, "CLIP_OT_select", SELECTMOUSE, KM_PRESS, KM_SHIFT, 0)->ptr, "extend", 1);
+ WM_keymap_add_item(keymap, "CLIP_OT_select_all", AKEY, KM_PRESS, 0, 0);
+ RNA_enum_set(WM_keymap_add_item(keymap, "CLIP_OT_select_all", IKEY, KM_PRESS, KM_CTRL, 0)->ptr, "action", SEL_INVERT);
+ WM_keymap_add_item(keymap, "CLIP_OT_select_border", BKEY, KM_PRESS, 0, 0);
+ WM_keymap_add_item(keymap, "CLIP_OT_select_circle", CKEY, KM_PRESS, 0, 0);
+ WM_keymap_add_menu(keymap, "CLIP_MT_select_grouped", GKEY, KM_PRESS, KM_SHIFT, 0);
+
+ /* marker */
+ WM_keymap_add_item(keymap, "CLIP_OT_add_marker_slide", LEFTMOUSE, KM_PRESS, KM_CTRL, 0);
+
+ WM_keymap_add_item(keymap, "CLIP_OT_delete_marker", DELKEY, KM_PRESS, KM_SHIFT, 0);
+ WM_keymap_add_item(keymap, "CLIP_OT_delete_marker", XKEY, KM_PRESS, KM_SHIFT, 0);
+
+ WM_keymap_add_item(keymap, "CLIP_OT_slide_marker", LEFTMOUSE, KM_PRESS, 0, 0);
+
+ kmi= WM_keymap_add_item(keymap, "CLIP_OT_disable_markers", DKEY, KM_PRESS, KM_SHIFT, 0);
+ RNA_enum_set(kmi->ptr, "action", 2); /* toggle */
+
+ /* tracks */
+ WM_keymap_add_item(keymap, "CLIP_OT_delete_track", DELKEY, KM_PRESS, 0, 0);
+ WM_keymap_add_item(keymap, "CLIP_OT_delete_track", XKEY, KM_PRESS, 0, 0);
+
+ kmi= WM_keymap_add_item(keymap, "CLIP_OT_lock_tracks", LKEY, KM_PRESS, KM_CTRL, 0);
+ RNA_enum_set(kmi->ptr, "action", 0); /* lock */
+
+ kmi= WM_keymap_add_item(keymap, "CLIP_OT_lock_tracks", LKEY, KM_PRESS, KM_ALT, 0);
+ RNA_enum_set(kmi->ptr, "action", 1); /* unlock */
+
+ WM_keymap_add_item(keymap, "CLIP_OT_hide_tracks", HKEY, KM_PRESS, 0, 0);
+
+ kmi= WM_keymap_add_item(keymap, "CLIP_OT_hide_tracks", HKEY, KM_PRESS, KM_SHIFT, 0);
+ RNA_boolean_set(kmi->ptr, "unselected", 1);
+
+ WM_keymap_add_item(keymap, "CLIP_OT_hide_tracks_clear", HKEY, KM_PRESS, KM_ALT, 0);
+
+ /* clean-up */
+ kmi= WM_keymap_add_item(keymap, "CLIP_OT_clear_track_path", TKEY, KM_PRESS, KM_ALT, 0);
+ RNA_enum_set(kmi->ptr, "action", TRACK_CLEAR_REMAINED);
+ kmi= WM_keymap_add_item(keymap, "CLIP_OT_clear_track_path", TKEY, KM_PRESS, KM_SHIFT, 0);
+ RNA_enum_set(kmi->ptr, "action", TRACK_CLEAR_UPTO);
+ kmi= WM_keymap_add_item(keymap, "CLIP_OT_clear_track_path", TKEY, KM_PRESS, KM_ALT|KM_SHIFT, 0);
+ RNA_enum_set(kmi->ptr, "action", TRACK_CLEAR_ALL);
+
+ WM_keymap_add_item(keymap, "CLIP_OT_join_tracks", JKEY, KM_PRESS, KM_CTRL, 0);
+
+ /* menus */
+ WM_keymap_add_menu(keymap, "CLIP_MT_tracking_specials", WKEY, KM_PRESS, 0, 0);
+
+ /* display */
+ kmi= WM_keymap_add_item(keymap, "WM_OT_context_toggle", LKEY, KM_PRESS, 0, 0);
+ RNA_string_set(kmi->ptr, "data_path", "space_data.lock_selection");
+
+ kmi= WM_keymap_add_item(keymap, "WM_OT_context_toggle", MKEY, KM_PRESS, 0, 0);
+ RNA_string_set(kmi->ptr, "data_path", "space_data.use_mute_footage");
+
+ transform_keymap_for_space(keyconf, keymap, SPACE_CLIP);
+
+ /* ******** Hotkeys avalaible for preview region only ******** */
+
+ keymap= WM_keymap_find(keyconf, "Clip Graph Editor", SPACE_CLIP, 0);
+
+ /* "timeline" */
+ WM_keymap_add_item(keymap, "CLIP_OT_change_frame", ACTIONMOUSE, KM_PRESS, 0, 0);
+
+ /* selection */
+ WM_keymap_add_item(keymap, "CLIP_OT_graph_select", SELECTMOUSE, KM_PRESS, 0, 0);
+ RNA_boolean_set(WM_keymap_add_item(keymap, "CLIP_OT_graph_select", SELECTMOUSE, KM_PRESS, KM_SHIFT, 0)->ptr, "extend", 1);
+
+ /* delete */
+ WM_keymap_add_item(keymap, "CLIP_OT_graph_delete_curve", DELKEY, KM_PRESS, 0, 0);
+ WM_keymap_add_item(keymap, "CLIP_OT_graph_delete_curve", XKEY, KM_PRESS, 0, 0);
+
+ WM_keymap_add_item(keymap, "CLIP_OT_graph_delete_knot", DELKEY, KM_PRESS, KM_SHIFT, 0);
+ WM_keymap_add_item(keymap, "CLIP_OT_graph_delete_knot", XKEY, KM_PRESS, KM_SHIFT, 0);
+}
+
+const char *clip_context_dir[]= {"edit_movieclip", NULL};
+
+static int clip_context(const bContext *C, const char *member, bContextDataResult *result)
+{
+ SpaceClip *sc= CTX_wm_space_clip(C);
+
+ if(CTX_data_dir(member)) {
+ CTX_data_dir_set(result, clip_context_dir);
+ return 1;
+ }
+ else if(CTX_data_equals(member, "edit_movieclip")) {
+ CTX_data_id_pointer_set(result, &sc->clip->id);
+ return 1;
+ }
+
+ return 0;
+}
+
+static void clip_refresh(const bContext *C, ScrArea *sa)
+{
+ wmWindowManager *wm= CTX_wm_manager(C);
+ wmWindow *window= CTX_wm_window(C);
+ SpaceClip *sc= (SpaceClip *)sa->spacedata.first;
+ ARegion *ar_main= BKE_area_find_region_type(sa, RGN_TYPE_WINDOW);
+ ARegion *ar_preview= clip_has_preview_region(C, sa);
+ int view_changed= 0;
+
+ switch (sc->view) {
+ case SC_VIEW_CLIP:
+ if (ar_preview && !(ar_preview->flag & RGN_FLAG_HIDDEN)) {
+ ar_preview->flag |= RGN_FLAG_HIDDEN;
+ ar_preview->v2d.flag &= ~V2D_IS_INITIALISED;
+ WM_event_remove_handlers((bContext*)C, &ar_preview->handlers);
+ view_changed= 1;
+ }
+ if (ar_main && ar_main->alignment != RGN_ALIGN_NONE) {
+ ar_main->alignment= RGN_ALIGN_NONE;
+ view_changed= 1;
+ }
+ if (ar_preview && ar_preview->alignment != RGN_ALIGN_NONE) {
+ ar_preview->alignment= RGN_ALIGN_NONE;
+ view_changed= 1;
+ }
+ break;
+ case SC_VIEW_GRAPH:
+ if (ar_preview && (ar_preview->flag & RGN_FLAG_HIDDEN)) {
+ ar_preview->flag &= ~RGN_FLAG_HIDDEN;
+ ar_preview->v2d.flag &= ~V2D_IS_INITIALISED;
+ ar_preview->v2d.cur = ar_preview->v2d.tot;
+ view_changed= 1;
+ }
+ if (ar_main && ar_main->alignment != RGN_ALIGN_NONE) {
+ ar_main->alignment= RGN_ALIGN_NONE;
+ view_changed= 1;
+ }
+ if (ar_preview && ar_preview->alignment != RGN_ALIGN_TOP) {
+ ar_preview->alignment= RGN_ALIGN_TOP;
+ view_changed= 1;
+ }
+ break;
+ }
+
+ if(view_changed) {
+ ED_area_initialize(wm, window, sa);
+ ED_area_tag_redraw(sa);
+ }
+
+ BKE_movieclip_user_set_frame(&sc->user, CTX_data_scene(C)->r.cfra);
+}
+
+/********************* main region ********************/
+
+/* sets up the fields of the View2D from zoom and offset */
+static void movieclip_main_area_set_view2d(SpaceClip *sc, ARegion *ar)
+{
+ MovieClip *clip= ED_space_clip(sc);
+ float x1, y1, w, h;
+ int width, height, winx, winy;
+
+ ED_space_clip_size(sc, &width, &height);
+
+ w= width;
+ h= height;
+
+ if(clip)
+ h*= clip->aspy/clip->aspx/clip->tracking.camera.pixel_aspect;
+
+ winx= ar->winrct.xmax - ar->winrct.xmin + 1;
+ winy= ar->winrct.ymax - ar->winrct.ymin + 1;
+
+ ar->v2d.tot.xmin= 0;
+ ar->v2d.tot.ymin= 0;
+ ar->v2d.tot.xmax= w;
+ ar->v2d.tot.ymax= h;
+
+ ar->v2d.mask.xmin= ar->v2d.mask.ymin= 0;
+ ar->v2d.mask.xmax= winx;
+ ar->v2d.mask.ymax= winy;
+
+ /* which part of the image space do we see? */
+ x1= ar->winrct.xmin+(winx-sc->zoom*w)/2.0f;
+ y1= ar->winrct.ymin+(winy-sc->zoom*h)/2.0f;
+
+ x1-= sc->zoom*sc->xof;
+ y1-= sc->zoom*sc->yof;
+
+ /* relative display right */
+ ar->v2d.cur.xmin= ((ar->winrct.xmin - (float)x1)/sc->zoom);
+ ar->v2d.cur.xmax= ar->v2d.cur.xmin + ((float)winx/sc->zoom);
+
+ /* relative display left */
+ ar->v2d.cur.ymin= ((ar->winrct.ymin-(float)y1)/sc->zoom);
+ ar->v2d.cur.ymax= ar->v2d.cur.ymin + ((float)winy/sc->zoom);
+
+ /* normalize 0.0..1.0 */
+ ar->v2d.cur.xmin /= w;
+ ar->v2d.cur.xmax /= w;
+ ar->v2d.cur.ymin /= h;
+ ar->v2d.cur.ymax /= h;
+}
+
+/* add handlers, stuff you only do once or on area/region changes */
+static void clip_main_area_init(wmWindowManager *wm, ARegion *ar)
+{
+ wmKeyMap *keymap;
+
+ UI_view2d_region_reinit(&ar->v2d, V2D_COMMONVIEW_STANDARD, ar->winx, ar->winy);
+
+ /* own keymap */
+ keymap= WM_keymap_find(wm->defaultconf, "Clip", SPACE_CLIP, 0);
+ WM_event_add_keymap_handler_bb(&ar->handlers, keymap, &ar->v2d.mask, &ar->winrct);
+
+ keymap= WM_keymap_find(wm->defaultconf, "Clip Editor", SPACE_CLIP, 0);
+ WM_event_add_keymap_handler_bb(&ar->handlers, keymap, &ar->v2d.mask, &ar->winrct);
+}
+
+static void clip_main_area_draw(const bContext *C, ARegion *ar)
+{
+ /* draw entirely, view changes should be handled here */
+ SpaceClip *sc= CTX_wm_space_clip(C);
+ Scene *scene= CTX_data_scene(C);
+ MovieClip *clip= ED_space_clip(sc);
+
+ /* if trcking is in progress, we should sunchronize framenr from clipuser
+ so latest tracked frame would be shown */
+ if(clip && clip->tracking_context)
+ BKE_tracking_sync_user(&sc->user, clip->tracking_context);
+
+ if(sc->flag&SC_LOCK_SELECTION) {
+ ImBuf *tmpibuf= NULL;
+
+ if(clip && clip->tracking.stabilization.flag&TRACKING_2D_STABILIZATION) {
+ tmpibuf= ED_space_clip_get_stable_buffer(sc, NULL, NULL, NULL);
+ }
+
+ if(ED_clip_view_selection(sc, ar, 0)) {
+ sc->xof+= sc->xlockof;
+ sc->yof+= sc->ylockof;
+ }
+
+ if(tmpibuf)
+ IMB_freeImBuf(tmpibuf);
+ }
+
+ /* clear and setup matrix */
+ UI_ThemeClearColor(TH_BACK);
+ glClear(GL_COLOR_BUFFER_BIT);
+
+ /* data... */
+ movieclip_main_area_set_view2d(sc, ar);
+
+ clip_draw_main(sc, ar, scene);
+
+ /* Grease Pencil */
+ clip_draw_grease_pencil((bContext *)C, 1);
+
+ /* reset view matrix */
+ UI_view2d_view_restore(C);
+
+ /* draw Grease Pencil - screen space only */
+ clip_draw_grease_pencil((bContext *)C, 0);
+}
+
+static void clip_main_area_listener(ARegion *ar, wmNotifier *wmn)
+{
+ /* context changes */
+ switch(wmn->category) {
+ case NC_SCREEN:
+ if (wmn->data==ND_GPENCIL)
+ ED_region_tag_redraw(ar);
+ break;
+ }
+}
+
+/****************** preview region ******************/
+
+static void clip_preview_area_init(wmWindowManager *wm, ARegion *ar)
+{
+ wmKeyMap *keymap;
+
+ UI_view2d_region_reinit(&ar->v2d, V2D_COMMONVIEW_CUSTOM, ar->winx, ar->winy);
+
+ /* own keymap */
+ keymap= WM_keymap_find(wm->defaultconf, "Clip", SPACE_CLIP, 0);
+ WM_event_add_keymap_handler_bb(&ar->handlers, keymap, &ar->v2d.mask, &ar->winrct);
+
+ keymap= WM_keymap_find(wm->defaultconf, "Clip Graph Editor", SPACE_CLIP, 0);
+ WM_event_add_keymap_handler_bb(&ar->handlers, keymap, &ar->v2d.mask, &ar->winrct);
+}
+
+static void clip_preview_area_draw(const bContext *C, ARegion *ar)
+{
+ View2D *v2d= &ar->v2d;
+ View2DScrollers *scrollers;
+ SpaceClip *sc= CTX_wm_space_clip(C);
+ Scene *scene= CTX_data_scene(C);
+ short unitx= V2D_UNIT_FRAMESCALE, unity= V2D_UNIT_VALUES;
+
+ /* clear and setup matrix */
+ UI_ThemeClearColor(TH_BACK);
+ glClear(GL_COLOR_BUFFER_BIT);
+
+ UI_view2d_view_ortho(v2d);
+
+ /* data... */
+ clip_draw_graph(sc, ar, scene);
+
+ /* reset view matrix */
+ UI_view2d_view_restore(C);
+
+ /* scrollers */
+ scrollers= UI_view2d_scrollers_calc(C, v2d, unitx, V2D_GRID_NOCLAMP, unity, V2D_GRID_NOCLAMP);
+ UI_view2d_scrollers_draw(C, v2d, scrollers);
+ UI_view2d_scrollers_free(scrollers);
+}
+
+static void clip_preview_area_listener(ARegion *UNUSED(ar), wmNotifier *UNUSED(wmn))
+{
+}
+
+/****************** header region ******************/
+
+/* add handlers, stuff you only do once or on area/region changes */
+static void clip_header_area_init(wmWindowManager *UNUSED(wm), ARegion *ar)
+{
+ ED_region_header_init(ar);
+}
+
+static void clip_header_area_draw(const bContext *C, ARegion *ar)
+{
+ ED_region_header(C, ar);
+}
+
+/****************** tools region ******************/
+
+/* add handlers, stuff you only do once or on area/region changes */
+static void clip_tools_area_init(wmWindowManager *wm, ARegion *ar)
+{
+ ED_region_panels_init(wm, ar);
+}
+
+static void clip_tools_area_draw(const bContext *C, ARegion *ar)
+{
+ ED_region_panels(C, ar, 1, NULL, -1);
+}
+
+/****************** tool properties region ******************/
+
+static void clip_props_area_listener(ARegion *ar, wmNotifier *wmn)
+{
+ /* context changes */
+ switch(wmn->category) {
+ case NC_WM:
+ if(wmn->data == ND_HISTORY)
+ ED_region_tag_redraw(ar);
+ break;
+ case NC_SCENE:
+ if(wmn->data == ND_MODE)
+ ED_region_tag_redraw(ar);
+ break;
+ case NC_SPACE:
+ if(wmn->data == ND_SPACE_CLIP)
+ ED_region_tag_redraw(ar);
+ break;
+ case NC_SCREEN:
+ if(wmn->data == ND_GPENCIL)
+ ED_region_tag_redraw(ar);
+ break;
+ }
+}
+
+/****************** properties region ******************/
+
+/* add handlers, stuff you only do once or on area/region changes */
+static void clip_properties_area_init(wmWindowManager *wm, ARegion *ar)
+{
+ wmKeyMap *keymap;
+
+ ED_region_panels_init(wm, ar);
+
+ keymap= WM_keymap_find(wm->defaultconf, "Clip", SPACE_CLIP, 0);
+ WM_event_add_keymap_handler(&ar->handlers, keymap);
+}
+
+static void clip_properties_area_draw(const bContext *C, ARegion *ar)
+{
+ SpaceClip *sc= CTX_wm_space_clip(C);
+
+ BKE_movieclip_update_scopes(sc->clip, &sc->user, &sc->scopes);
+
+ ED_region_panels(C, ar, 1, NULL, -1);
+}
+
+static void clip_properties_area_listener(ARegion *ar, wmNotifier *wmn)
+{
+ /* context changes */
+ switch(wmn->category) {
+ case NC_SCREEN:
+ if (wmn->data==ND_GPENCIL)
+ ED_region_tag_redraw(ar);
+ break;
+ case NC_BRUSH:
+ if(wmn->action==NA_EDITED)
+ ED_region_tag_redraw(ar);
+ break;
+ }
+}
+
+/********************* registration ********************/
+
+/* only called once, from space/spacetypes.c */
+void ED_spacetype_clip(void)
+{
+ SpaceType *st= MEM_callocN(sizeof(SpaceType), "spacetype clip");
+ ARegionType *art;
+
+ st->spaceid= SPACE_CLIP;
+ strncpy(st->name, "Clip", BKE_ST_MAXNAME);
+
+ st->new= clip_new;
+ st->free= clip_free;
+ st->init= clip_init;
+ st->duplicate= clip_duplicate;
+ st->operatortypes= clip_operatortypes;
+ st->keymap= clip_keymap;
+ st->listener= clip_listener;
+ st->context= clip_context;
+ st->refresh= clip_refresh;
+
+ /* regions: main window */
+ art= MEM_callocN(sizeof(ARegionType), "spacetype clip region");
+ art->regionid= RGN_TYPE_WINDOW;
+ art->init= clip_main_area_init;
+ art->draw= clip_main_area_draw;
+ art->listener= clip_main_area_listener;
+ art->keymapflag= ED_KEYMAP_FRAMES|ED_KEYMAP_UI|ED_KEYMAP_GPENCIL;
+
+ BLI_addhead(&st->regiontypes, art);
+
+ /* preview */
+ art= MEM_callocN(sizeof(ARegionType), "spacetype clip region preview");
+ art->regionid = RGN_TYPE_PREVIEW;
+ art->prefsizey = 240;
+ art->init= clip_preview_area_init;
+ art->draw= clip_preview_area_draw;
+ art->listener= clip_preview_area_listener;
+ art->keymapflag= ED_KEYMAP_FRAMES|ED_KEYMAP_UI|ED_KEYMAP_VIEW2D;
+
+ BLI_addhead(&st->regiontypes, art);
+
+ /* regions: properties */
+ art= MEM_callocN(sizeof(ARegionType), "spacetype clip region properties");
+ art->regionid= RGN_TYPE_UI;
+ art->prefsizex= UI_COMPACT_PANEL_WIDTH;
+ art->keymapflag= ED_KEYMAP_FRAMES|ED_KEYMAP_UI;
+ art->init= clip_properties_area_init;
+ art->draw= clip_properties_area_draw;
+ art->listener= clip_properties_area_listener;
+ BLI_addhead(&st->regiontypes, art);
+ ED_clip_buttons_register(art);
+
+ /* regions: tools */
+ art= MEM_callocN(sizeof(ARegionType), "spacetype clip region tools");
+ art->regionid= RGN_TYPE_TOOLS;
+ art->prefsizex= UI_COMPACT_PANEL_WIDTH;
+ art->keymapflag= ED_KEYMAP_FRAMES|ED_KEYMAP_UI;
+ art->listener= clip_props_area_listener;
+ art->init= clip_tools_area_init;
+ art->draw= clip_tools_area_draw;
+
+ BLI_addhead(&st->regiontypes, art);
+
+ /* tool properties */
+ art= MEM_callocN(sizeof(ARegionType), "spacetype clip tool properties region");
+ art->regionid = RGN_TYPE_TOOL_PROPS;
+ art->prefsizex= 0;
+ art->prefsizey= 120;
+ art->keymapflag= ED_KEYMAP_FRAMES|ED_KEYMAP_UI;
+ art->listener= clip_props_area_listener;
+ art->init= clip_tools_area_init;
+ art->draw= clip_tools_area_draw;
+ ED_clip_tool_props_register(art);
+
+ BLI_addhead(&st->regiontypes, art);
+
+ /* regions: header */
+ art= MEM_callocN(sizeof(ARegionType), "spacetype clip region");
+ art->regionid= RGN_TYPE_HEADER;
+ art->prefsizey= HEADERY;
+ art->keymapflag= ED_KEYMAP_FRAMES|ED_KEYMAP_UI|ED_KEYMAP_VIEW2D|ED_KEYMAP_HEADER;
+
+ art->init= clip_header_area_init;
+ art->draw= clip_header_area_draw;
+
+ BLI_addhead(&st->regiontypes, art);
+
+ BKE_spacetype_register(st);
+}
diff --git a/source/blender/editors/space_clip/tracking_ops.c b/source/blender/editors/space_clip/tracking_ops.c
new file mode 100644
index 00000000000..aab7188c069
--- /dev/null
+++ b/source/blender/editors/space_clip/tracking_ops.c
@@ -0,0 +1,2940 @@
+/*
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * 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.
+ *
+ *
+ * Contributor(s): Blender Foundation,
+ * Sergey Sharybin
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+/** \file blender/editors/space_clip/tracking_ops.c
+ * \ingroup spclip
+ */
+
+#include "MEM_guardedalloc.h"
+
+#include "DNA_camera_types.h"
+#include "DNA_gpencil_types.h"
+#include "DNA_movieclip_types.h"
+#include "DNA_object_types.h" /* SELECT */
+#include "DNA_scene_types.h"
+
+#include "BLI_utildefines.h"
+#include "BLI_math.h"
+#include "BLI_listbase.h"
+#include "BLI_rect.h"
+#include "BLI_blenlib.h"
+
+#include "BKE_main.h"
+#include "BKE_context.h"
+#include "BKE_movieclip.h"
+#include "BKE_tracking.h"
+#include "BKE_global.h"
+#include "BKE_depsgraph.h"
+#include "BKE_object.h"
+#include "BKE_report.h"
+#include "BKE_scene.h"
+#include "BKE_library.h"
+#include "BKE_sound.h"
+
+#include "WM_api.h"
+#include "WM_types.h"
+
+#include "ED_screen.h"
+#include "ED_clip.h"
+#include "ED_keyframing.h"
+
+#include "IMB_imbuf_types.h"
+#include "IMB_imbuf.h"
+
+#include "UI_interface.h"
+
+#include "RNA_access.h"
+#include "RNA_define.h"
+
+#include "PIL_time.h"
+
+#include "UI_view2d.h"
+
+#include "clip_intern.h" // own include
+
+static int space_clip_frame_poll(bContext *C)
+{
+ SpaceClip *sc= CTX_wm_space_clip(C);
+
+ if(sc) {
+ MovieClip *clip= ED_space_clip(sc);
+
+ if(clip)
+ return BKE_movieclip_has_frame(clip, &sc->user);
+ }
+
+ return 0;
+}
+
+static int space_clip_frame_camera_poll(bContext *C)
+{
+ Scene *scene= CTX_data_scene(C);
+
+ if(space_clip_frame_poll(C)) {
+ return scene->camera != NULL;
+ }
+
+ return 0;
+}
+
+static int space_clip_camera_poll(bContext *C)
+{
+ SpaceClip *sc= CTX_wm_space_clip(C);
+ Scene *scene= CTX_data_scene(C);
+
+ if(sc && sc->clip && scene->camera)
+ return 1;
+
+ return 0;
+}
+
+/********************** add marker operator *********************/
+
+static void add_marker(SpaceClip *sc, float x, float y)
+{
+ MovieClip *clip= ED_space_clip(sc);
+ MovieTrackingTrack *track;
+ int width, height;
+
+ ED_space_clip_size(sc, &width, &height);
+
+ track= BKE_tracking_add_track(&clip->tracking, x, y, sc->user.framenr, width, height);
+
+ BKE_tracking_select_track(&clip->tracking, track, TRACK_AREA_ALL, 0);
+
+ clip->tracking.act_track= track;
+}
+
+static int add_marker_exec(bContext *C, wmOperator *op)
+{
+ SpaceClip *sc= CTX_wm_space_clip(C);
+ MovieClip *clip= ED_space_clip(sc);
+ float pos[2];
+ int width, height;
+
+ ED_space_clip_size(sc, &width, &height);
+ if(!width || !height)
+ return OPERATOR_CANCELLED;
+
+ RNA_float_get_array(op->ptr, "location", pos);
+
+ add_marker(sc, pos[0], pos[1]);
+
+ /* reset offset from locked position, so frame jumping wouldn't be so confusing */
+ sc->xlockof= 0;
+ sc->ylockof= 0;
+
+ WM_event_add_notifier(C, NC_MOVIECLIP|NA_EDITED, clip);
+
+ return OPERATOR_FINISHED;
+}
+
+static int add_marker_invoke(bContext *C, wmOperator *op, wmEvent *event)
+{
+ float co[2];
+
+ ED_clip_mouse_pos(C, event, 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= space_clip_frame_poll;
+
+ /* flags */
+ ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
+
+ /* properties */
+ RNA_def_float_vector(ot->srna, "location", 2, NULL, -FLT_MIN, FLT_MAX,
+ "Location", "Location of marker on frame", -1.0f, 1.0f);
+}
+
+/********************** 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(sc);
+ MovieTracking *tracking= &clip->tracking;
+ MovieTrackingTrack *track= tracking->tracks.first, *next;
+
+ while(track) {
+ next= track->next;
+
+ if(TRACK_VIEW_SELECTED(sc, track))
+ clip_delete_track(C, clip, track);
+
+ track= next;
+ }
+
+ /* nothing selected now, unlock view so it can be scrolled nice again */
+ sc->flag&= ~SC_LOCK_SELECTION;
+
+ 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_poll;
+
+ /* flags */
+ ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
+}
+
+/********************** 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(sc);
+ MovieTrackingTrack *track= clip->tracking.tracks.first, *next;
+ int framenr= sc->user.framenr;
+ int has_selection= 0;
+
+ while(track) {
+ next= track->next;
+
+ if(TRACK_VIEW_SELECTED(sc, track)) {
+ MovieTrackingMarker *marker= BKE_tracking_exact_marker(track, framenr);
+
+ if(marker) {
+ has_selection|= track->markersnr>1;
+
+ clip_delete_marker(C, clip, track, marker);
+ }
+ }
+
+ track= next;
+ }
+
+ if(!has_selection) {
+ /* nothing selected now, unlock view so it can be scrolled nice again */
+ sc->flag&= ~SC_LOCK_SELECTION;
+ }
+
+ 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_poll;
+
+ /* flags */
+ ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
+}
+
+/********************** slide marker operator *********************/
+
+#define SLIDE_ACTION_POS 0
+#define SLIDE_ACTION_SIZE 1
+#define SLIDE_ACTION_OFFSET 2
+
+typedef struct {
+ int area, action;
+ MovieTrackingTrack *track;
+ MovieTrackingMarker *marker;
+
+ int mval[2];
+ int width, height;
+ float *min, *max, *pos, *offset;
+ float smin[2], smax[2], spos[2], soff[2];
+ float (*smarkers)[2];
+
+ int lock, accurate;
+} SlideMarkerData;
+
+static SlideMarkerData *create_slide_marker_data(SpaceClip *sc, MovieTrackingTrack *track,
+ MovieTrackingMarker *marker, wmEvent *event, int area, int action, int width, int height)
+{
+ SlideMarkerData *data= MEM_callocN(sizeof(SlideMarkerData), "slide marker data");
+
+ marker= BKE_tracking_ensure_marker(track, sc->user.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;
+ copy_v2_v2(data->spos, marker->pos);
+ copy_v2_v2(data->soff, track->offset);
+ } else if(area==TRACK_AREA_PAT) {
+ if(action==SLIDE_ACTION_SIZE) {
+ data->min= track->pat_min;
+ data->max= track->pat_max;
+ } else {
+ int a;
+
+ data->pos= marker->pos;
+ data->offset= track->offset;
+
+ copy_v2_v2(data->soff, track->offset);
+
+ data->smarkers= MEM_callocN(sizeof(*data->smarkers)*track->markersnr, "slide marekrs");
+ for(a= 0; a<track->markersnr; a++)
+ copy_v2_v2(data->smarkers[a], track->markers[a].pos);
+ }
+ } else if(area==TRACK_AREA_SEARCH) {
+ data->min= track->search_min;
+ data->max= track->search_max;
+ }
+
+ if(area==TRACK_AREA_SEARCH || (area==TRACK_AREA_PAT && action!=SLIDE_ACTION_OFFSET)) {
+ copy_v2_v2(data->smin, data->min);
+ copy_v2_v2(data->smax, data->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= 1;
+
+ return data;
+}
+
+/* corner = 0: right-bottom corner,
+ corner = 1: left-top corner */
+static int mouse_on_corner(SpaceClip *sc, MovieTrackingTrack *track, MovieTrackingMarker *marker,
+ int area, float co[2], int corner, int width, int height)
+{
+ int inside= 0;
+ float size= 12.0f;
+ float min[2], max[2];
+ float crn[2], dx, dy, tdx, tdy;
+
+ if(area==TRACK_AREA_SEARCH) {
+ copy_v2_v2(min, track->search_min);
+ copy_v2_v2(max, track->search_max);
+ } else {
+ copy_v2_v2(min, track->pat_min);
+ copy_v2_v2(max, track->pat_max);
+ }
+
+ dx= size/width/sc->zoom;
+ dy= size/height/sc->zoom;
+
+ tdx= 5.0f/width/sc->zoom;
+ tdy= 5.0f/height/sc->zoom;
+
+ dx= MIN2(dx, (max[0]-min[0])/6.0f) + tdx;
+ dy= MIN2(dy, (max[1]-min[1])/6.0f) + tdy;
+
+ if(corner==0) {
+ crn[0]= marker->pos[0]+max[0];
+ crn[1]= marker->pos[1]+min[1];
+
+ inside= co[0]>=crn[0]-dx && co[0]<=crn[0]+tdx && co[1]>=crn[1]-tdy && co[1]<=crn[1]+dy;
+ } else {
+ crn[0]= marker->pos[0]+min[0];
+ crn[1]= marker->pos[1]+max[1];
+
+ inside= co[0]>=crn[0]-dx && co[0]<=crn[0]+dx && co[1]>=crn[1]-dy && co[1]<=crn[1]+dy;
+ }
+
+ return inside;
+}
+
+static int mouse_on_offset(SpaceClip *sc, MovieTrackingTrack *track, MovieTrackingMarker *marker,
+ float co[2], int width, int height)
+{
+ float pos[2], dx, dy;
+
+ add_v2_v2v2(pos, marker->pos, track->offset);
+
+ dx= 12.0f/width/sc->zoom;
+ dy= 12.0f/height/sc->zoom;
+
+ dx=MIN2(dx, (track->pat_max[0]-track->pat_min[0])/2.0f);
+ dy=MIN2(dy, (track->pat_max[1]-track->pat_min[1])/2.0f);
+
+ return co[0]>=pos[0]-dx && co[0]<=pos[0]+dx && co[1]>=pos[1]-dy && co[1]<=pos[1]+dy;
+}
+
+static void hide_cursor(bContext *C)
+{
+ wmWindow *win= CTX_wm_window(C);
+
+ WM_cursor_set(win, CURSOR_NONE);
+}
+
+static void show_cursor(bContext *C)
+{
+ wmWindow *win= CTX_wm_window(C);
+
+ WM_cursor_set(win, CURSOR_STD);
+}
+
+static void *slide_marker_customdata(bContext *C, wmEvent *event)
+{
+ SpaceClip *sc= CTX_wm_space_clip(C);
+ MovieClip *clip= ED_space_clip(sc);
+ MovieTrackingTrack *track;
+ int width, height;
+ float co[2];
+ void *customdata= NULL;
+
+ ED_space_clip_size(sc, &width, &height);
+
+ if(width==0 || height==0)
+ return NULL;
+
+ ED_clip_mouse_pos(C, event, co);
+
+ track= clip->tracking.tracks.first;
+ while(track) {
+ if(TRACK_VIEW_SELECTED(sc, track) && (track->flag&TRACK_LOCKED)==0) {
+ MovieTrackingMarker *marker= BKE_tracking_get_marker(track, sc->user.framenr);
+
+ if((marker->flag&MARKER_DISABLED)==0) {
+ if(!customdata)
+ if(mouse_on_offset(sc, track, marker, co, width, height))
+ customdata= create_slide_marker_data(sc, track, marker, event, TRACK_AREA_POINT, SLIDE_ACTION_POS, width, height);
+
+ if(sc->flag&SC_SHOW_MARKER_SEARCH) {
+ if(mouse_on_corner(sc, track, marker, TRACK_AREA_SEARCH, co, 1, width, height))
+ customdata= create_slide_marker_data(sc, track, marker, event, TRACK_AREA_SEARCH, SLIDE_ACTION_OFFSET, width, height);
+ else if(mouse_on_corner(sc, track, marker, TRACK_AREA_SEARCH, co, 0, width, height))
+ customdata= create_slide_marker_data(sc, track, marker, event, TRACK_AREA_SEARCH, SLIDE_ACTION_SIZE, width, height);
+ }
+
+ if(!customdata && sc->flag&SC_SHOW_MARKER_PATTERN) {
+ if(mouse_on_corner(sc, track, marker, TRACK_AREA_PAT, co, 1, width, height))
+ customdata= create_slide_marker_data(sc, track, marker, event, TRACK_AREA_PAT, SLIDE_ACTION_OFFSET, width, height);
+
+ if(!customdata && mouse_on_corner(sc, track, marker, TRACK_AREA_PAT, co, 0, width, height))
+ customdata= create_slide_marker_data(sc, track, marker, event, TRACK_AREA_PAT, SLIDE_ACTION_SIZE, width, height);
+ }
+
+ if(customdata)
+ break;
+ }
+ }
+
+ track= track->next;
+ }
+
+ return customdata;
+}
+
+static int slide_marker_invoke(bContext *C, wmOperator *op, wmEvent *event)
+{
+ SlideMarkerData *slidedata= slide_marker_customdata(C, event);
+
+ if(slidedata) {
+ SpaceClip *sc= CTX_wm_space_clip(C);
+ MovieClip *clip= ED_space_clip(sc);
+ MovieTracking *tracking= &clip->tracking;
+
+ tracking->act_track= slidedata->track;
+
+ op->customdata= slidedata;
+
+ 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)
+{
+ /* cancel sliding */
+ if(data->area == TRACK_AREA_POINT) {
+ if(data->action==SLIDE_ACTION_OFFSET)
+ copy_v2_v2(data->offset, data->soff);
+ else
+ copy_v2_v2(data->pos, data->spos);
+ } else {
+ if(data->action==SLIDE_ACTION_SIZE) {
+ copy_v2_v2(data->min, data->smin);
+ copy_v2_v2(data->max, data->smax);
+ } else {
+ int a;
+
+ for(a= 0; a<data->track->markersnr; a++)
+ copy_v2_v2(data->track->markers[a].pos, data->smarkers[a]);
+
+ copy_v2_v2(data->offset, data->soff);
+ }
+ }
+}
+
+static void free_slide_data(SlideMarkerData *data)
+{
+ if(data->smarkers) MEM_freeN(data->smarkers);
+ MEM_freeN(data);
+}
+
+static int slide_marker_modal(bContext *C, wmOperator *op, wmEvent *event)
+{
+ SpaceClip *sc= CTX_wm_space_clip(C);
+ SlideMarkerData *data= (SlideMarkerData *)op->customdata;
+ float dx, dy, mdelta[2];
+
+ switch(event->type) {
+ case LEFTCTRLKEY:
+ case RIGHTCTRLKEY:
+ case LEFTSHIFTKEY:
+ case RIGHTSHIFTKEY:
+ if(data->action==SLIDE_ACTION_SIZE)
+ if(ELEM(event->type, LEFTCTRLKEY, RIGHTCTRLKEY))
+ data->lock= event->val==KM_RELEASE;
+
+ if(ELEM(event->type, LEFTSHIFTKEY, RIGHTSHIFTKEY))
+ data->accurate= event->val==KM_PRESS;
+
+ /* no break! update area size */
+
+ 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;
+ dy/= 5;
+ }
+
+ if(data->area==TRACK_AREA_POINT) {
+ if(data->action==SLIDE_ACTION_OFFSET) {
+ data->offset[0]= data->soff[0]+dx;
+ data->offset[1]= data->soff[1]+dy;
+ } else {
+ data->pos[0]= data->spos[0]+dx;
+ data->pos[1]= data->spos[1]+dy;
+
+ data->marker->flag&= ~MARKER_TRACKED;
+ }
+
+ WM_event_add_notifier(C, NC_OBJECT|ND_TRANSFORM, NULL);
+ DAG_id_tag_update(&sc->clip->id, 0);
+ } else {
+ if(data->action==SLIDE_ACTION_SIZE) {
+ data->min[0]= data->smin[0]-dx;
+ data->max[0]= data->smax[0]+dx;
+
+ data->min[1]= data->smin[1]+dy;
+ data->max[1]= data->smax[1]-dy;
+
+ if(data->area==TRACK_AREA_SEARCH) BKE_tracking_clamp_track(data->track, CLAMP_SEARCH_DIM);
+ else BKE_tracking_clamp_track(data->track, CLAMP_PAT_DIM);
+ } else {
+ float d[2]={dx, dy};
+
+ if(data->area==TRACK_AREA_SEARCH) {
+ add_v2_v2v2(data->min, data->smin, d);
+ add_v2_v2v2(data->max, data->smax, d);
+ } else {
+ int a;
+
+ for(a= 0; a<data->track->markersnr; a++)
+ add_v2_v2v2(data->track->markers[a].pos, data->smarkers[a], d);
+
+ sub_v2_v2v2(data->offset, data->soff, d);
+ }
+
+ if(data->area==TRACK_AREA_SEARCH)
+ BKE_tracking_clamp_track(data->track, CLAMP_SEARCH_POS);
+ }
+ }
+
+ WM_event_add_notifier(C, NC_MOVIECLIP|NA_EDITED, NULL);
+
+ break;
+
+ case LEFTMOUSE:
+ if(event->val==KM_RELEASE) {
+ free_slide_data(op->customdata);
+
+ show_cursor(C);
+
+ return OPERATOR_FINISHED;
+ }
+
+ break;
+
+ case ESCKEY:
+ cancel_mouse_slide(op->customdata);
+
+ free_slide_data(op->customdata);
+
+ 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= space_clip_frame_poll;
+ ot->invoke= slide_marker_invoke;
+ ot->modal= slide_marker_modal;
+
+ /* flags */
+ ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO|OPTYPE_GRAB_POINTER|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);
+}
+
+/********************** mouse select operator *********************/
+
+static int mouse_on_side(float co[2], float x1, float y1, float x2, float y2, float epsx, float epsy)
+{
+ if(x1>x2) SWAP(float, x1, x2);
+ if(y1>y2) SWAP(float, y1, y2);
+
+ return (co[0]>=x1-epsx && co[0]<=x2+epsx) && (co[1]>=y1-epsy && co[1]<=y2+epsy);
+}
+
+static int mouse_on_rect(float co[2], float pos[2], float min[2], float max[2], float epsx, float epsy)
+{
+ return mouse_on_side(co, pos[0]+min[0], pos[1]+min[1], pos[0]+max[0], pos[1]+min[1], epsx, epsy) ||
+ mouse_on_side(co, pos[0]+min[0], pos[1]+min[1], pos[0]+min[0], pos[1]+max[1], epsx, epsy) ||
+ mouse_on_side(co, pos[0]+min[0], pos[1]+max[1], pos[0]+max[0], pos[1]+max[1], epsx, epsy) ||
+ mouse_on_side(co, pos[0]+max[0], pos[1]+min[1], pos[0]+max[0], pos[1]+max[1], epsx, epsy);
+}
+
+static int track_mouse_area(SpaceClip *sc, float co[2], MovieTrackingTrack *track)
+{
+ MovieTrackingMarker *marker= BKE_tracking_get_marker(track, sc->user.framenr);
+ float epsx, epsy;
+ int width, height;
+
+ ED_space_clip_size(sc, &width, &height);
+
+ epsx= MIN4(track->pat_min[0]-track->search_min[0], track->search_max[0]-track->pat_max[0],
+ fabsf(track->pat_min[0]), fabsf(track->pat_max[0])) / 2;
+ epsy= MIN4(track->pat_min[1]-track->search_min[1], track->search_max[1]-track->pat_max[1],
+ fabsf(track->pat_min[1]), fabsf(track->pat_max[1])) / 2;
+
+ epsx= MAX2(epsy, 2.0f / width);
+ epsy= MAX2(epsy, 2.0f / height);
+
+ if(sc->flag&SC_SHOW_MARKER_SEARCH)
+ if(mouse_on_rect(co, marker->pos, track->search_min, track->search_max, epsx, epsy))
+ return TRACK_AREA_SEARCH;
+
+ if((marker->flag&MARKER_DISABLED)==0) {
+ if(sc->flag&SC_SHOW_MARKER_PATTERN)
+ if(mouse_on_rect(co, marker->pos, track->pat_min, track->pat_max, epsx, epsy))
+ return TRACK_AREA_PAT;
+
+ epsx= 12.0f/width;
+ epsy= 12.0f/height;
+
+ if(fabsf(co[0]-marker->pos[0]-track->offset[0])< epsx && fabsf(co[1]-marker->pos[1]-track->offset[1])<=epsy)
+ return TRACK_AREA_POINT;
+ }
+
+ return TRACK_AREA_NONE;
+}
+
+static float dist_to_rect(float co[2], float pos[2], float min[2], float max[2])
+{
+ float d1, d2, d3, d4;
+ float p[2]= {co[0]-pos[0], co[1]-pos[1]};
+ float v1[2]= {min[0], min[1]}, v2[2]= {max[0], min[1]},
+ v3[2]= {max[0], max[1]}, v4[2]= {min[0], max[1]};
+
+ d1= dist_to_line_segment_v2(p, v1, v2);
+ d2= dist_to_line_segment_v2(p, v2, v3);
+ d3= dist_to_line_segment_v2(p, v3, v4);
+ d4= dist_to_line_segment_v2(p, v4, v1);
+
+ return MIN4(d1, d2, d3, d4);
+}
+
+static MovieTrackingTrack *find_nearest_track(SpaceClip *sc, MovieClip *clip, float co[2])
+{
+ MovieTrackingTrack *track= NULL, *cur;
+ float mindist= 0.0f;
+
+ cur= clip->tracking.tracks.first;
+ while(cur) {
+ MovieTrackingMarker *marker= BKE_tracking_get_marker(cur, sc->user.framenr);
+
+ if(((cur->flag&TRACK_HIDDEN)==0) && MARKER_VISIBLE(sc, marker)) {
+ float dist, d1, d2=FLT_MAX, d3=FLT_MAX;
+
+ d1= sqrtf((co[0]-marker->pos[0]-cur->offset[0])*(co[0]-marker->pos[0]-cur->offset[0])+
+ (co[1]-marker->pos[1]-cur->offset[1])*(co[1]-marker->pos[1]-cur->offset[1])); /* distance to marker point */
+
+ /* distance to pattern boundbox */
+ if(sc->flag&SC_SHOW_MARKER_PATTERN)
+ d2= dist_to_rect(co, marker->pos, cur->pat_min, cur->pat_max);
+
+ /* distance to search boundbox */
+ if(sc->flag&SC_SHOW_MARKER_SEARCH)
+ d3= dist_to_rect(co, marker->pos, cur->search_min, cur->search_max);
+
+ /* choose minimal distance. useful for cases of overlapped markers. */
+ dist= MIN3(d1, d2, d3);
+
+ if(track==NULL || dist<mindist) {
+ track= cur;
+ mindist= dist;
+ }
+ }
+
+ cur= cur->next;
+ }
+
+ return track;
+}
+
+static int mouse_select(bContext *C, float co[2], int extend)
+{
+ SpaceClip *sc= CTX_wm_space_clip(C);
+ MovieClip *clip= ED_space_clip(sc);
+ MovieTracking *tracking= &clip->tracking;
+ MovieTrackingTrack *act_track= tracking->act_track;
+ MovieTrackingTrack *track= NULL; /* selected marker */
+
+ track= find_nearest_track(sc, clip, co);
+
+ if(track) {
+ int area= track_mouse_area(sc, co, track);
+
+ if(!extend || !TRACK_VIEW_SELECTED(sc, track))
+ area= TRACK_AREA_ALL;
+
+ if(extend && TRACK_AREA_SELECTED(track, area)) {
+ if(track==act_track)
+ BKE_tracking_deselect_track(track, area);
+ else
+ clip->tracking.act_track= track;
+ } else {
+ if(area==TRACK_AREA_POINT)
+ area= TRACK_AREA_ALL;
+
+ BKE_tracking_select_track(tracking, track, area, extend);
+ clip->tracking.act_track= track;
+ }
+ }
+
+ if(!extend) {
+ sc->xlockof= 0.0f;
+ sc->ylockof= 0.0f;
+ }
+
+ WM_event_add_notifier(C, NC_GEOM|ND_SELECT, NULL);
+
+ return OPERATOR_FINISHED;
+}
+
+static int select_exec(bContext *C, wmOperator *op)
+{
+ float co[2];
+ int extend;
+
+ RNA_float_get_array(op->ptr, "location", co);
+ extend= RNA_boolean_get(op->ptr, "extend");
+
+ return mouse_select(C, co, extend);
+}
+
+static int select_invoke(bContext *C, wmOperator *op, wmEvent *event)
+{
+ float co[2];
+ int extend= RNA_boolean_get(op->ptr, "extend");
+
+ if(!extend) {
+ SlideMarkerData *slidedata= slide_marker_customdata(C, event);
+
+ if(slidedata) {
+ SpaceClip *sc= CTX_wm_space_clip(C);
+ MovieClip *clip= ED_space_clip(sc);
+
+ clip->tracking.act_track= slidedata->track;
+
+ WM_event_add_notifier(C, NC_GEOM|ND_SELECT, NULL);
+
+ MEM_freeN(slidedata);
+
+ return OPERATOR_PASS_THROUGH;
+ }
+ }
+
+ ED_clip_mouse_pos(C, event, co);
+ RNA_float_set_array(op->ptr, "location", co);
+
+ return select_exec(C, op);
+}
+
+void CLIP_OT_select(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name= "Select";
+ ot->description= "Select tracking markers";
+ ot->idname= "CLIP_OT_select";
+
+ /* api callbacks */
+ ot->exec= select_exec;
+ ot->invoke= select_invoke;
+ ot->poll= ED_space_clip_poll;
+
+ /* flags */
+ ot->flag= OPTYPE_UNDO;
+
+ /* properties */
+ RNA_def_boolean(ot->srna, "extend", 0,
+ "Extend", "Extend selection rather than clearing the existing selection");
+ RNA_def_float_vector(ot->srna, "location", 2, NULL, -FLT_MAX, FLT_MAX,
+ "Location", "Mouse location in normalized coordinates, 0.0 to 1.0 is within the image bounds", -100.0f, 100.0f);
+}
+
+/********************** border select operator *********************/
+
+static int border_select_exec(bContext *C, wmOperator *op)
+{
+ SpaceClip *sc= CTX_wm_space_clip(C);
+ MovieClip *clip= ED_space_clip(sc);
+ MovieTrackingTrack *track;
+ rcti rect;
+ rctf rectf;
+ int change= 0, mode;
+
+ /* get rectangle from operator */
+ rect.xmin= RNA_int_get(op->ptr, "xmin");
+ rect.ymin= RNA_int_get(op->ptr, "ymin");
+ rect.xmax= RNA_int_get(op->ptr, "xmax");
+ rect.ymax= RNA_int_get(op->ptr, "ymax");
+
+ ED_clip_point_stable_pos(C, rect.xmin, rect.ymin, &rectf.xmin, &rectf.ymin);
+ ED_clip_point_stable_pos(C, rect.xmax, rect.ymax, &rectf.xmax, &rectf.ymax);
+
+ mode= RNA_int_get(op->ptr, "gesture_mode");
+
+ /* do actual selection */
+ track= clip->tracking.tracks.first;
+ while(track) {
+ if((track->flag&TRACK_HIDDEN)==0) {
+ MovieTrackingMarker *marker= BKE_tracking_get_marker(track, sc->user.framenr);
+
+ if(MARKER_VISIBLE(sc, marker) && BLI_in_rctf(&rectf, marker->pos[0], marker->pos[1])) {
+ BKE_tracking_track_flag(track, TRACK_AREA_ALL, SELECT, mode!=GESTURE_MODAL_SELECT);
+
+ change= 1;
+ }
+ }
+
+ track= track->next;
+ }
+
+ if(change) {
+ WM_event_add_notifier(C, NC_GEOM|ND_SELECT, NULL);
+
+ return OPERATOR_FINISHED;
+ }
+
+ return OPERATOR_CANCELLED;
+}
+
+void CLIP_OT_select_border(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name= "Border Select";
+ ot->description= "Select markers using border selection";
+ ot->idname= "CLIP_OT_select_border";
+
+ /* api callbacks */
+ ot->invoke= WM_border_select_invoke;
+ ot->exec= border_select_exec;
+ ot->modal= WM_border_select_modal;
+ ot->poll= ED_space_clip_poll;
+
+ /* flags */
+ ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
+
+ /* properties */
+ WM_operator_properties_gesture_border(ot, FALSE);
+}
+
+/********************** circle select operator *********************/
+
+static int marker_inside_ellipse(MovieTrackingMarker *marker, float offset[2], float ellipse[2])
+{
+ /* normalized ellipse: ell[0] = scaleX, ell[1] = scaleY */
+ float x, y;
+
+ x= (marker->pos[0] - offset[0])*ellipse[0];
+ y= (marker->pos[1] - offset[1])*ellipse[1];
+
+ return x*x + y*y < 1.0f;
+}
+
+static int circle_select_exec(bContext *C, wmOperator *op)
+{
+ SpaceClip *sc= CTX_wm_space_clip(C);
+ MovieClip *clip= ED_space_clip(sc);
+ ARegion *ar= CTX_wm_region(C);
+ MovieTrackingTrack *track;
+ int x, y, radius, width, height, mode, change= 0;
+ float zoomx, zoomy, offset[2], ellipse[2];
+
+ /* get operator properties */
+ x= RNA_int_get(op->ptr, "x");
+ y= RNA_int_get(op->ptr, "y");
+ radius= RNA_int_get(op->ptr, "radius");
+
+ mode= RNA_int_get(op->ptr, "gesture_mode");
+
+ /* compute ellipse and position in unified coordinates */
+ ED_space_clip_size(sc, &width, &height);
+ ED_space_clip_zoom(sc, ar, &zoomx, &zoomy);
+
+ ellipse[0]= width*zoomx/radius;
+ ellipse[1]= height*zoomy/radius;
+
+ ED_clip_point_stable_pos(C, x, y, &offset[0], &offset[1]);
+
+ /* do selection */
+ track= clip->tracking.tracks.first;
+ while(track) {
+ if((track->flag&TRACK_HIDDEN)==0) {
+ MovieTrackingMarker *marker= BKE_tracking_get_marker(track, sc->user.framenr);
+
+ if(MARKER_VISIBLE(sc, marker) && marker_inside_ellipse(marker, offset, ellipse)) {
+ BKE_tracking_track_flag(track, TRACK_AREA_ALL, SELECT, mode!=GESTURE_MODAL_SELECT);
+
+ change= 1;
+ }
+ }
+
+ track= track->next;
+ }
+
+ if(change) {
+ WM_event_add_notifier(C, NC_GEOM|ND_SELECT, NULL);
+
+ return OPERATOR_FINISHED;
+ }
+
+ return OPERATOR_CANCELLED;
+}
+
+void CLIP_OT_select_circle(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name= "Circle Select";
+ ot->description= "Select markers using circle selection";
+ ot->idname= "CLIP_OT_select_circle";
+
+ /* api callbacks */
+ ot->invoke= WM_gesture_circle_invoke;
+ ot->modal= WM_gesture_circle_modal;
+ ot->exec= circle_select_exec;
+ ot->poll= ED_space_clip_poll;
+
+ /* flags */
+ ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
+
+ /* properties */
+ RNA_def_int(ot->srna, "x", 0, INT_MIN, INT_MAX, "X", "", INT_MIN, INT_MAX);
+ RNA_def_int(ot->srna, "y", 0, INT_MIN, INT_MAX, "Y", "", INT_MIN, INT_MAX);
+ RNA_def_int(ot->srna, "radius", 0, INT_MIN, INT_MAX, "Radius", "", INT_MIN, INT_MAX);
+ RNA_def_int(ot->srna, "gesture_mode", 0, INT_MIN, INT_MAX, "Gesture Mode", "", INT_MIN, INT_MAX);
+}
+
+/********************** select all operator *********************/
+
+static int select_all_exec(bContext *C, wmOperator *op)
+{
+ SpaceClip *sc= CTX_wm_space_clip(C);
+ MovieClip *clip= ED_space_clip(sc);
+ MovieTrackingTrack *track= NULL; /* selected track */
+ int action= RNA_enum_get(op->ptr, "action");
+ int framenr= sc->user.framenr;
+ int has_selection= 0;
+
+ if(action == SEL_TOGGLE){
+ action= SEL_SELECT;
+ track= clip->tracking.tracks.first;
+ while(track) {
+ if(TRACK_VIEW_SELECTED(sc, track)) {
+ action= SEL_DESELECT;
+ break;
+ }
+
+ track= track->next;
+ }
+ }
+
+ track= clip->tracking.tracks.first;
+ while(track) {
+ if((track->flag&TRACK_HIDDEN)==0) {
+ MovieTrackingMarker *marker= BKE_tracking_get_marker(track, framenr);
+
+ if(marker && MARKER_VISIBLE(sc, marker)) {
+ switch (action) {
+ case SEL_SELECT:
+ track->flag|= SELECT;
+ track->pat_flag|= SELECT;
+ track->search_flag|= SELECT;
+ break;
+ case SEL_DESELECT:
+ track->flag&= ~SELECT;
+ track->pat_flag&= ~SELECT;
+ track->search_flag&= ~SELECT;
+ break;
+ case SEL_INVERT:
+ track->flag^= SELECT;
+ track->pat_flag^= SELECT;
+ track->search_flag^= SELECT;
+ break;
+ }
+ }
+ }
+
+ if(TRACK_VIEW_SELECTED(sc, track))
+ has_selection= 1;
+
+ track= track->next;
+ }
+
+ if(!has_selection)
+ sc->flag&= ~SC_LOCK_SELECTION;
+
+ WM_event_add_notifier(C, NC_GEOM|ND_SELECT, NULL);
+
+ return OPERATOR_FINISHED;
+}
+
+void CLIP_OT_select_all(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name= "Select or Deselect All";
+ ot->description= "Change selection of all tracking markers";
+ ot->idname= "CLIP_OT_select_all";
+
+ /* api callbacks */
+ ot->exec= select_all_exec;
+ ot->poll= ED_space_clip_poll;
+
+ /* flags */
+ ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
+
+ WM_operator_properties_select_all(ot);
+}
+
+/********************** select grouped operator *********************/
+
+static int select_groped_exec(bContext *C, wmOperator *op)
+{
+ SpaceClip *sc= CTX_wm_space_clip(C);
+ MovieClip *clip= ED_space_clip(sc);
+ MovieTrackingTrack *track;
+ MovieTrackingMarker *marker;
+ int group= RNA_enum_get(op->ptr, "group");
+
+ track= clip->tracking.tracks.first;
+ while(track) {
+ int ok= 0;
+
+ marker= BKE_tracking_get_marker(track, sc->user.framenr);
+
+ if(group==0) { /* Keyframed */
+ ok= marker->framenr==sc->user.framenr && (marker->flag&MARKER_TRACKED)==0;
+ }
+ else if(group==1) { /* Estimated */
+ ok= marker->framenr!=sc->user.framenr;
+ }
+ else if(group==2) { /* tracked */
+ ok= marker->framenr==sc->user.framenr && (marker->flag&MARKER_TRACKED);
+ }
+ else if(group==3) { /* locked */
+ ok= track->flag&TRACK_LOCKED;
+ }
+ else if(group==4) { /* disabled */
+ ok= marker->flag&MARKER_DISABLED;
+ }
+ else if(group==5) { /* color */
+ if(clip->tracking.act_track) {
+ ok= (track->flag&TRACK_CUSTOMCOLOR) == (clip->tracking.act_track->flag&TRACK_CUSTOMCOLOR);
+
+ if(ok && track->flag&TRACK_CUSTOMCOLOR)
+ ok= equals_v3v3(track->color, clip->tracking.act_track->color);
+ }
+ }
+ else if(group==6) { /* failed */
+ ok= (track->flag&TRACK_HAS_BUNDLE) == 0;
+ }
+
+ if(ok) {
+ track->flag|= SELECT;
+ if(sc->flag&SC_SHOW_MARKER_PATTERN) track->pat_flag|= SELECT;;
+ if(sc->flag&SC_SHOW_MARKER_SEARCH) track->search_flag|= SELECT;;
+ }
+
+ track= track->next;
+ }
+
+ WM_event_add_notifier(C, NC_MOVIECLIP|ND_DISPLAY, clip);
+
+ return OPERATOR_FINISHED;
+}
+
+void CLIP_OT_select_grouped(wmOperatorType *ot)
+{
+ static EnumPropertyItem select_group_items[] = {
+ {0, "KEYFRAMED", 0, "Keyframed tracks", "Select all keyframed tracks"},
+ {1, "ESTIMATED", 0, "Estimated tracks", "Select all estimated tracks"},
+ {2, "TRACKED", 0, "Tracked tracks", "Select all tracked tracks"},
+ {3, "LOCKED", 0, "Locked tracks", "Select all locked tracks"},
+ {4, "DISABLED", 0, "Disabled tracks", "Select all disabled tracks"},
+ {5, "COLOR", 0, "Tracks with same color", "Select all tracks with same color as actiev track"},
+ {6, "FAILED", 0, "Failed Tracks", "Select all tracks which failed to be reconstructed"},
+ {0, NULL, 0, NULL, NULL}
+ };
+
+ /* identifiers */
+ ot->name= "Select Grouped";
+ ot->description= "Joint Selected Tracks";
+ ot->idname= "CLIP_OT_select_grouped";
+
+ /* api callbacks */
+ ot->exec= select_groped_exec;
+ ot->poll= space_clip_frame_poll;
+
+ /* flags */
+ ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
+
+ /* proeprties */
+ RNA_def_enum(ot->srna, "group", select_group_items, TRACK_CLEAR_REMAINED, "Action", "Clear action to execute");
+}
+
+/********************** track operator *********************/
+
+typedef struct TrackMarkersJob {
+ struct MovieTrackingContext *context; /* tracking context */
+ int sfra, efra, lastfra; /* Start, end and recently tracked frames */
+ int backwards; /* Backwards tracking flag */
+ MovieClip *clip; /* Clip which is tracking */
+ float delay; /* Delay in milliseconds to allow tracking at fixed FPS */
+
+ struct Main *main;
+ struct Scene *scene;
+ struct bScreen *screen;
+} TrackMarkersJob;
+
+static int track_markers_testbreak(void)
+{
+ return G.afbreek;
+}
+
+static int track_count_markers(SpaceClip *sc, MovieClip *clip)
+{
+ int tot= 0;
+ MovieTrackingTrack *track;
+
+ track= clip->tracking.tracks.first;
+ while(track) {
+ if(TRACK_VIEW_SELECTED(sc, track) && (track->flag&TRACK_LOCKED)==0)
+ tot++;
+
+ track= track->next;
+ }
+
+ return tot;
+}
+
+static void track_init_markers(SpaceClip *sc, MovieClip *clip)
+{
+ MovieTrackingTrack *track;
+ int framenr= sc->user.framenr, hidden= 0;
+
+ if((sc->flag&SC_SHOW_MARKER_PATTERN)==0) hidden|= TRACK_AREA_PAT;
+ if((sc->flag&SC_SHOW_MARKER_SEARCH)==0) hidden|= TRACK_AREA_SEARCH;
+
+ track= clip->tracking.tracks.first;
+ while(track) {
+ if(hidden)
+ BKE_tracking_track_flag(track, hidden, SELECT, 1);
+
+ if(TRACK_SELECTED(track)) {
+ if((track->flag&TRACK_HIDDEN)==0 && (track->flag&TRACK_LOCKED)==0)
+ BKE_tracking_ensure_marker(track, framenr);
+ }
+
+ track= track->next;
+ }
+}
+
+static int track_markers_check_direction(int backwards, int curfra, int efra)
+{
+ if(backwards) {
+ if(curfra<efra) return 0;
+ }
+ else {
+ if(curfra>efra) return 0;
+ }
+
+ return 1;
+}
+
+static int track_markers_initjob(bContext *C, TrackMarkersJob *tmj, int backwards)
+{
+ SpaceClip *sc= CTX_wm_space_clip(C);
+ MovieClip *clip= ED_space_clip(sc);
+ Scene *scene= CTX_data_scene(C);
+ MovieTrackingSettings *settings= &clip->tracking.settings;
+
+ tmj->sfra= sc->user.framenr;
+ tmj->clip= clip;
+ tmj->backwards= backwards;
+
+ if(backwards) tmj->efra= SFRA;
+ else tmj->efra= EFRA;
+
+ /* limit frames to be tracked by user setting */
+ if(settings->frames_limit) {
+ if(backwards) tmj->efra= MAX2(tmj->efra, tmj->sfra-settings->frames_limit);
+ else tmj->efra= MIN2(tmj->efra, tmj->sfra+settings->frames_limit);
+ }
+
+ if(settings->speed!=TRACKING_SPEED_FASTEST) {
+ tmj->delay= 1.0f/scene->r.frs_sec*1000.0f;
+
+ if(settings->speed==TRACKING_SPEED_HALF) tmj->delay*= 2;
+ else if(settings->speed==TRACKING_SPEED_QUARTER) tmj->delay*= 4;
+ else if(settings->speed==TRACKING_SPEED_DOUBLE) tmj->delay/= 2;
+ }
+
+ track_init_markers(sc, clip);
+
+ tmj->context= BKE_tracking_context_new(clip, &sc->user, backwards, 1);
+
+ clip->tracking_context= tmj->context;
+
+ tmj->lastfra= tmj->sfra;
+
+ /* XXX: silly to store this, but this data is needed to update scene and movieclip
+ frame numbers when tracking is finished. This introduces better feedback for artists.
+ Maybe there's another way to solve this problem, but can't think better way atm.
+ Anyway, this way isn't more unstable as animation rendering animation
+ which uses the same approach (except storing screen). */
+ tmj->scene= scene;
+ tmj->main= CTX_data_main(C);
+ tmj->screen= CTX_wm_screen(C);
+
+ return track_markers_check_direction(backwards, tmj->sfra, tmj->efra);
+}
+
+static void track_markers_startjob(void *tmv, short *stop, short *do_update, float *progress)
+{
+ TrackMarkersJob *tmj= (TrackMarkersJob *)tmv;
+ int framenr= tmj->sfra;
+ //double t= PIL_check_seconds_timer();
+
+ while(framenr != tmj->efra) {
+ if(tmj->delay>0) {
+ /* tracking should happen with fixed fps. Calculate time
+ using current timer value before tracking frame and after.
+
+ Small (and maybe unneeded optimization): do not calculate exec_time
+ for "Fastest" tracking */
+
+ double start_time= PIL_check_seconds_timer(), exec_time;
+
+ if(!BKE_tracking_next(tmj->context))
+ break;
+
+ exec_time= PIL_check_seconds_timer()-start_time;
+ if(tmj->delay>exec_time)
+ PIL_sleep_ms(tmj->delay-exec_time);
+ } else if(!BKE_tracking_next(tmj->context))
+ break;
+
+ *do_update= 1;
+ *progress=(float)(framenr-tmj->sfra) / (tmj->efra-tmj->sfra);
+
+ if(tmj->backwards) framenr--;
+ else framenr++;
+
+ tmj->lastfra= framenr;
+
+ if(*stop || track_markers_testbreak())
+ break;
+ }
+
+ //printf("Tracking time: %lf\n", PIL_check_seconds_timer()-t);
+}
+
+static void track_markers_updatejob(void *tmv)
+{
+ TrackMarkersJob *tmj= (TrackMarkersJob *)tmv;
+
+ BKE_tracking_sync(tmj->context);
+}
+
+static void track_markers_freejob(void *tmv)
+{
+ TrackMarkersJob *tmj= (TrackMarkersJob *)tmv;
+
+ tmj->clip->tracking_context= NULL;
+ tmj->scene->r.cfra= tmj->lastfra;
+ ED_update_for_newframe(tmj->main, tmj->scene, tmj->screen, 0);
+
+ BKE_tracking_sync(tmj->context);
+ BKE_tracking_context_free(tmj->context);
+
+ MEM_freeN(tmj);
+
+ WM_main_add_notifier(NC_SCENE|ND_FRAME, tmj->scene);
+}
+
+static int track_markers_exec(bContext *C, wmOperator *op)
+{
+ SpaceClip *sc= CTX_wm_space_clip(C);
+ MovieClip *clip= ED_space_clip(sc);
+ Scene *scene= CTX_data_scene(C);
+ struct MovieTrackingContext *context;
+ int framenr= sc->user.framenr;
+ int sfra= framenr, efra;
+ int backwards= RNA_boolean_get(op->ptr, "backwards");
+ int sequence= RNA_boolean_get(op->ptr, "sequence");
+ MovieTrackingSettings *settings= &clip->tracking.settings;
+
+ if(track_count_markers(sc, clip)==0)
+ return OPERATOR_CANCELLED;
+
+ if(backwards) efra= SFRA;
+ else efra= EFRA;
+
+ /* limit frames to be tracked by user setting */
+ if(settings->frames_limit) {
+ if(backwards) efra= MAX2(efra, sfra-settings->frames_limit);
+ else efra= MIN2(efra, sfra+settings->frames_limit);
+ }
+
+ if(!track_markers_check_direction(backwards, framenr, efra))
+ return OPERATOR_CANCELLED;
+
+ track_init_markers(sc, clip);
+
+ /* do not disable tracks due to threshold when tracking frame-by-frame */
+ context= BKE_tracking_context_new(clip, &sc->user, backwards, sequence);
+
+ while(framenr != efra) {
+ if(!BKE_tracking_next(context))
+ break;
+
+ if(backwards) framenr--;
+ else framenr++;
+
+ if(!sequence)
+ break;
+ }
+
+ BKE_tracking_sync(context);
+ BKE_tracking_context_free(context);
+
+ /* update scene current frame to the lastes tracked frame */
+ scene->r.cfra= framenr;
+
+ WM_event_add_notifier(C, NC_MOVIECLIP|NA_EVALUATED, clip);
+ WM_event_add_notifier(C, NC_SCENE|ND_FRAME, scene);
+
+ return OPERATOR_FINISHED;
+}
+
+static int track_markers_invoke(bContext *C, wmOperator *op, wmEvent *UNUSED(event))
+{
+ TrackMarkersJob *tmj;
+ ScrArea *sa= CTX_wm_area(C);
+ SpaceClip *sc= CTX_wm_space_clip(C);
+ MovieClip *clip= ED_space_clip(sc);
+ wmJob *steve;
+ int backwards= RNA_boolean_get(op->ptr, "backwards");
+ int sequence= RNA_boolean_get(op->ptr, "sequence");
+
+ if(clip->tracking_context)
+ return OPERATOR_CANCELLED;
+
+ if(track_count_markers(sc, clip)==0)
+ return OPERATOR_CANCELLED;
+
+ if(!sequence)
+ return track_markers_exec(C, op);
+
+ tmj= MEM_callocN(sizeof(TrackMarkersJob), "TrackMarkersJob data");
+ if(!track_markers_initjob(C, tmj, backwards)) {
+ track_markers_freejob(tmj);
+
+ return OPERATOR_CANCELLED;
+ }
+
+ /* setup job */
+ steve= WM_jobs_get(CTX_wm_manager(C), CTX_wm_window(C), sa, "Track Markers", WM_JOB_PROGRESS);
+ WM_jobs_customdata(steve, tmj, track_markers_freejob);
+
+ /* if there's delay set in tracking job, tracking should happen
+ with fixed FPS. To deal with editor refresh we have to syncronize
+ tracks from job and tracks in clip. Do this in timer callback
+ to prevent threading conflicts. */
+ if(tmj->delay>0) WM_jobs_timer(steve, tmj->delay/1000.0f, NC_MOVIECLIP|NA_EVALUATED, 0);
+ else WM_jobs_timer(steve, 0.2, NC_MOVIECLIP|NA_EVALUATED, 0);
+
+ WM_jobs_callbacks(steve, track_markers_startjob, NULL, track_markers_updatejob, NULL);
+
+ G.afbreek= 0;
+
+ WM_jobs_start(CTX_wm_manager(C), steve);
+ WM_cursor_wait(0);
+
+ /* add modal handler for ESC */
+ WM_event_add_modal_handler(C, op);
+
+ return OPERATOR_RUNNING_MODAL;
+}
+
+static int track_markers_modal(bContext *C, wmOperator *UNUSED(op), wmEvent *event)
+{
+ /* no running blender, remove handler and pass through */
+ if(0==WM_jobs_test(CTX_wm_manager(C), CTX_wm_area(C)))
+ return OPERATOR_FINISHED|OPERATOR_PASS_THROUGH;
+
+ /* running tracking */
+ switch (event->type) {
+ case ESCKEY:
+ return OPERATOR_RUNNING_MODAL;
+ break;
+ }
+
+ return OPERATOR_PASS_THROUGH;
+}
+
+void CLIP_OT_track_markers(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name= "Track Markers";
+ ot->description= "Track selected markers";
+ ot->idname= "CLIP_OT_track_markers";
+
+ /* api callbacks */
+ ot->exec= track_markers_exec;
+ ot->invoke= track_markers_invoke;
+ ot->poll= space_clip_frame_poll;
+ ot->modal= track_markers_modal;
+
+ /* flags */
+ ot->flag= OPTYPE_UNDO;
+
+ /* properties */
+ RNA_def_boolean(ot->srna, "backwards", 0, "Backwards", "Do backwards tracking");
+ RNA_def_boolean(ot->srna, "sequence", 0, "Track Sequence", "Track marker during image sequence rather than single image");
+}
+
+/********************** solve camera operator *********************/
+
+static int check_solve_track_count(MovieTracking *tracking)
+{
+ int tot= 0;
+ int frame1= tracking->settings.keyframe1, frame2= tracking->settings.keyframe2;
+ MovieTrackingTrack *track;
+
+ track= tracking->tracks.first;
+ while(track) {
+ if(BKE_tracking_has_marker(track, frame1))
+ if(BKE_tracking_has_marker(track, frame2))
+ tot++;
+
+ track= track->next;
+ }
+
+ return tot>=8;
+}
+
+static int solve_camera_exec(bContext *C, wmOperator *op)
+{
+ SpaceClip *sc= CTX_wm_space_clip(C);
+ MovieClip *clip= ED_space_clip(sc);
+ Scene *scene= CTX_data_scene(C);
+ MovieTracking *tracking= &clip->tracking;
+ int width, height;
+ float error;
+
+ if(!check_solve_track_count(tracking)) {
+ BKE_report(op->reports, RPT_ERROR, "At least 8 tracks on both of keyframes are needed for reconstruction");
+ return OPERATOR_CANCELLED;
+ }
+
+ /* could fail if footage uses images with different sizes */
+ BKE_movieclip_get_size(clip, NULL, &width, &height);
+
+ error= BKE_tracking_solve_reconstruction(tracking, width, height);
+
+ if(error<0)
+ BKE_report(op->reports, RPT_WARNING, "Some data failed to reconstruct, see console for details");
+ else
+ BKE_reportf(op->reports, RPT_INFO, "Average reprojection error %.3f", error);
+
+ scene->clip= clip;
+ id_us_plus(&clip->id);
+
+ if(!scene->camera)
+ scene->camera= scene_find_camera(scene);
+
+ if(scene->camera) {
+ /* set blender camera focal length so result would look fine there */
+ Camera *camera= (Camera*)scene->camera->data;
+
+ BKE_tracking_camera_to_blender(tracking, scene, camera, width, height);
+
+ WM_event_add_notifier(C, NC_OBJECT, camera);
+ }
+
+ DAG_id_tag_update(&clip->id, 0);
+
+ WM_event_add_notifier(C, NC_MOVIECLIP|NA_EVALUATED, clip);
+ WM_event_add_notifier(C, NC_OBJECT|ND_TRANSFORM, NULL);
+
+ /* update active clip displayed in scene buttons */
+ WM_event_add_notifier(C, NC_SCENE, scene);
+
+ return OPERATOR_FINISHED;
+}
+
+void CLIP_OT_solve_camera(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name= "Solve Camera";
+ ot->description= "Solve camera motion from tracks";
+ ot->idname= "CLIP_OT_solve_camera";
+
+ /* api callbacks */
+ ot->exec= solve_camera_exec;
+ ot->poll= ED_space_clip_poll;
+
+ /* flags */
+ ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
+}
+
+/********************** clear solution operator *********************/
+
+static int clear_solution_exec(bContext *C, wmOperator *UNUSED(op))
+{
+ SpaceClip *sc= CTX_wm_space_clip(C);
+ MovieClip *clip= ED_space_clip(sc);
+ MovieTracking *tracking= &clip->tracking;
+ MovieTrackingTrack *track= tracking->tracks.first;
+
+ while(track) {
+ track->flag&= ~TRACK_HAS_BUNDLE;
+
+ track= track->next;
+ }
+
+ if(tracking->reconstruction.cameras)
+ MEM_freeN(tracking->reconstruction.cameras);
+
+ tracking->reconstruction.cameras= NULL;
+ tracking->reconstruction.camnr= 0;
+
+ tracking->reconstruction.flag&= ~TRACKING_RECONSTRUCTED;
+
+ DAG_id_tag_update(&clip->id, 0);
+
+ WM_event_add_notifier(C, NC_MOVIECLIP|NA_EVALUATED, clip);
+ WM_event_add_notifier(C, NC_SPACE|ND_SPACE_VIEW3D, NULL);
+
+ return OPERATOR_FINISHED;
+}
+
+void CLIP_OT_clear_solution(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name= "Clear Solution";
+ ot->description= "Clear all calculated data";
+ ot->idname= "CLIP_OT_clear_solution";
+
+ /* api callbacks */
+ ot->exec= clear_solution_exec;
+ ot->poll= ED_space_clip_poll;
+
+ /* flags */
+ ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
+}
+
+/********************** 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(sc);
+ MovieTrackingTrack *track;
+ int action= RNA_enum_get(op->ptr, "action");
+
+ track= clip->tracking.tracks.first;
+ while(track) {
+ if(TRACK_VIEW_SELECTED(sc, track))
+ BKE_tracking_clear_path(track, sc->user.framenr, action);
+
+ track= track->next;
+ }
+
+ WM_event_add_notifier(C, NC_MOVIECLIP|NA_EVALUATED, clip);
+
+ return OPERATOR_FINISHED;
+}
+
+void CLIP_OT_clear_track_path(wmOperatorType *ot)
+{
+ static 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 remained 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 path of selected tracks";
+ ot->idname= "CLIP_OT_clear_track_path";
+
+ /* api callbacks */
+ ot->exec= clear_track_path_exec;
+ ot->poll= ED_space_clip_poll;
+
+ /* flags */
+ ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
+
+ /* proeprties */
+ RNA_def_enum(ot->srna, "action", clear_path_actions, TRACK_CLEAR_REMAINED, "Action", "Clear action to execute");
+}
+
+/********************** disable markers operator *********************/
+
+static int disable_markers_exec(bContext *C, wmOperator *op)
+{
+ SpaceClip *sc= CTX_wm_space_clip(C);
+ MovieClip *clip= ED_space_clip(sc);
+ MovieTracking *tracking= &clip->tracking;
+ MovieTrackingTrack *track= tracking->tracks.first;
+ int action= RNA_enum_get(op->ptr, "action");
+
+ while(track) {
+ if(TRACK_VIEW_SELECTED(sc, track) && (track->flag&TRACK_LOCKED)==0) {
+ MovieTrackingMarker *marker= BKE_tracking_ensure_marker(track, sc->user.framenr);
+
+ if(action==0) marker->flag|= MARKER_DISABLED;
+ else if(action==1) marker->flag&= ~MARKER_DISABLED;
+ else marker->flag^= MARKER_DISABLED;
+ }
+
+ track= track->next;
+ }
+
+ DAG_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 EnumPropertyItem actions_items[] = {
+ {0, "DISABLE", 0, "Disable", "Disable selected markers"},
+ {1, "ENABLE", 0, "Enable", "Enable selected markers"},
+ {2, "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_poll;
+
+ /* flags */
+ ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
+
+ /* properties */
+ RNA_def_enum(ot->srna, "action", actions_items, 0, "Action", "Disable action to execute");
+}
+
+/********************** set origin operator *********************/
+
+static int count_selected_bundles(bContext *C)
+{
+ SpaceClip *sc= CTX_wm_space_clip(C);
+ MovieClip *clip= ED_space_clip(sc);
+ MovieTrackingTrack *track;
+ int tot= 0;
+
+ track= clip->tracking.tracks.first;
+ while(track) {
+ if(TRACK_VIEW_SELECTED(sc, track) && (track->flag&TRACK_HAS_BUNDLE))
+ tot++;
+
+ track= track->next;
+ }
+
+ return tot;
+}
+
+static int set_origin_exec(bContext *C, wmOperator *op)
+{
+ SpaceClip *sc= CTX_wm_space_clip(C);
+ MovieClip *clip= ED_space_clip(sc);
+ MovieTrackingTrack *track;
+ Scene *scene= CTX_data_scene(C);
+ Object *parent= scene->camera;
+ float mat[4][4], vec[3];
+
+ if(count_selected_bundles(C)!=1) {
+ BKE_report(op->reports, RPT_ERROR, "Track with bundle should be selected to define origin position");
+ return OPERATOR_CANCELLED;
+ }
+
+ if(scene->camera->parent)
+ parent= scene->camera->parent;
+
+ track= clip->tracking.tracks.first;
+ while(track) {
+ if(TRACK_VIEW_SELECTED(sc, track))
+ break;
+
+ track= track->next;
+ }
+
+ BKE_get_tracking_mat(scene, NULL, mat);
+ mul_v3_m4v3(vec, mat, track->bundle_pos);
+
+ sub_v3_v3(parent->loc, vec);
+
+ DAG_id_tag_update(&clip->id, 0);
+ DAG_id_tag_update(&parent->id, OB_RECALC_OB);
+
+ WM_event_add_notifier(C, NC_MOVIECLIP|NA_EVALUATED, clip);
+ WM_event_add_notifier(C, NC_OBJECT|ND_TRANSFORM, NULL);
+
+ return OPERATOR_FINISHED;
+}
+
+void CLIP_OT_set_origin(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name= "Set Origin";
+ ot->description= "Set active marker as origin by moving camera (or it's parent if present) in 3d space";
+ ot->idname= "CLIP_OT_set_origin";
+
+ /* api callbacks */
+ ot->exec= set_origin_exec;
+ ot->poll= space_clip_frame_camera_poll;
+
+ /* flags */
+ ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
+}
+
+/********************** set floor operator *********************/
+
+static void set_axis(Scene *scene, Object *ob, MovieTrackingTrack *track, char axis)
+{
+ float mat[4][4], vec[3], obmat[4][4];
+
+ BKE_get_tracking_mat(scene, NULL, mat);
+ mul_v3_m4v3(vec, mat, track->bundle_pos);
+
+ if(len_v2(vec)<1e-3)
+ return;
+
+ unit_m4(mat);
+
+ if(axis=='X') {
+ if(fabsf(vec[1])<1e-3) {
+ mat[0][0]= -1.0f; mat[0][1]= 0.0f; mat[0][2]= 0.0f;
+ mat[1][0]= 0.0f; mat[1][1]= -1.0f; mat[1][2]= 0.0f;
+ mat[2][0]= 0.0f; mat[2][1]= 0.0f; mat[2][2]= 1.0f;
+ } else {
+ copy_v3_v3(mat[0], vec);
+ mat[0][2]= 0.0f;
+ mat[2][0]= 0.0f; mat[2][1]= 0.0f; mat[2][2]= 1.0f;
+ cross_v3_v3v3(mat[1], mat[2], mat[0]);
+ }
+ } else {
+ if(fabsf(vec[0])<1e-3) {
+ mat[0][0]= -1.0f; mat[0][1]= 0.0f; mat[0][2]= 0.0f;
+ mat[1][0]= 0.0f; mat[1][1]= -1.0f; mat[1][2]= 0.0f;
+ mat[2][0]= 0.0f; mat[2][1]= 0.0f; mat[2][2]= 1.0f;
+ } else {
+ copy_v3_v3(mat[1], vec);
+ mat[1][2]= 0.0f;
+ mat[2][0]= 0.0f; mat[2][1]= 0.0f; mat[2][2]= 1.0f;
+ cross_v3_v3v3(mat[0], mat[1], mat[2]);
+ }
+ }
+
+ normalize_v3(mat[0]);
+ normalize_v3(mat[1]);
+ normalize_v3(mat[2]);
+
+ invert_m4(mat);
+
+ object_to_mat4(ob, obmat);
+ mul_m4_m4m4(mat, obmat, mat);
+ object_apply_mat4(ob, mat, 0, 0);
+}
+
+static int set_floor_exec(bContext *C, wmOperator *op)
+{
+ SpaceClip *sc= CTX_wm_space_clip(C);
+ MovieClip *clip= ED_space_clip(sc);
+ Scene *scene= CTX_data_scene(C);
+ MovieTrackingTrack *track, *axis_track= NULL;
+ Object *camera= scene->camera;
+ Object *parent= camera;
+ int tot= 0;
+ float vec[3][3], mat[4][4], obmat[4][4], newmat[4][4], orig[3]= {0.0f, 0.0f, 0.0f};
+ float rot[4][4]={{0.0f, 0.0f, -1.0f, 0.0f},
+ {0.0f, 1.0f, 0.0f, 0.0f},
+ {1.0f, 0.0f, 0.0f, 0.0f},
+ {0.0f, 0.0f, 0.0f, 1.0f}}; /* 90 degrees Y-axis rotation matrix */
+
+ if(count_selected_bundles(C)!=3) {
+ BKE_report(op->reports, RPT_ERROR, "Three tracks with bundles are needed to orient the floor");
+ return OPERATOR_CANCELLED;
+ }
+
+ if(scene->camera->parent)
+ parent= scene->camera->parent;
+
+ BKE_get_tracking_mat(scene, NULL, mat);
+
+ /* get 3 bundles to use as reference */
+ track= clip->tracking.tracks.first;
+ while(track && tot<3) {
+ if(track->flag&TRACK_HAS_BUNDLE && TRACK_VIEW_SELECTED(sc, track)) {
+ mul_v3_m4v3(vec[tot], mat, track->bundle_pos);
+
+ if(tot==0 || track==clip->tracking.act_track)
+ copy_v3_v3(orig, vec[tot]);
+ else
+ axis_track= track;
+
+ tot++;
+ }
+
+ track= track->next;
+ }
+
+ sub_v3_v3(vec[1], vec[0]);
+ sub_v3_v3(vec[2], vec[0]);
+
+ /* construct ortho-normal basis */
+ unit_m4(mat);
+
+ cross_v3_v3v3(mat[0], vec[1], vec[2]);
+ copy_v3_v3(mat[1], vec[1]);
+ cross_v3_v3v3(mat[2], mat[0], mat[1]);
+
+ normalize_v3(mat[0]);
+ normalize_v3(mat[1]);
+ normalize_v3(mat[2]);
+
+ /* move to origin point */
+ mat[3][0]= orig[0];
+ mat[3][1]= orig[1];
+ mat[3][2]= orig[2];
+
+ invert_m4(mat);
+
+ object_to_mat4(parent, obmat);
+ mul_m4_m4m4(mat, obmat, mat);
+ mul_m4_m4m4(newmat, mat, rot);
+ object_apply_mat4(parent, newmat, 0, 0);
+
+ /* make camera have positive z-coordinate */
+ mul_v3_m4v3(vec[0], mat, camera->loc);
+ if(camera->loc[2]<0) {
+ invert_m4(rot);
+ mul_m4_m4m4(newmat, mat, rot);
+ object_apply_mat4(camera, newmat, 0, 0);
+ }
+
+ where_is_object(scene, parent);
+ set_axis(scene, parent, axis_track, 'X');
+
+ DAG_id_tag_update(&clip->id, 0);
+ DAG_id_tag_update(&parent->id, OB_RECALC_OB);
+
+ WM_event_add_notifier(C, NC_MOVIECLIP|NA_EVALUATED, clip);
+ WM_event_add_notifier(C, NC_OBJECT|ND_TRANSFORM, NULL);
+
+ return OPERATOR_FINISHED;
+}
+
+void CLIP_OT_set_floor(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name= "Set Floor";
+ ot->description= "Set floor based on 3 selected bundles by moving camera (or it's parent if present) in 3d space";
+ ot->idname= "CLIP_OT_set_floor";
+
+ /* api callbacks */
+ ot->exec= set_floor_exec;
+ ot->poll= space_clip_camera_poll;
+
+ /* flags */
+ ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
+}
+
+/********************** set axis operator *********************/
+
+static int set_axis_exec(bContext *C, wmOperator *op)
+{
+ SpaceClip *sc= CTX_wm_space_clip(C);
+ MovieClip *clip= ED_space_clip(sc);
+ MovieTrackingTrack *track;
+ Scene *scene= CTX_data_scene(C);
+ Object *parent= scene->camera;
+ int axis= RNA_enum_get(op->ptr, "axis");
+
+ if(count_selected_bundles(C)!=1) {
+ BKE_report(op->reports, RPT_ERROR, "Single track with bundle should be selected to define axis");
+
+ return OPERATOR_CANCELLED;
+ }
+
+ if(scene->camera->parent)
+ parent= scene->camera->parent;
+
+ track= clip->tracking.tracks.first;
+ while(track) {
+ if(TRACK_VIEW_SELECTED(sc, track))
+ break;
+
+ track= track->next;
+ }
+
+ set_axis(scene, parent, track, axis==0?'X':'Y');
+
+ DAG_id_tag_update(&clip->id, 0);
+ DAG_id_tag_update(&parent->id, OB_RECALC_OB);
+
+ WM_event_add_notifier(C, NC_MOVIECLIP|NA_EVALUATED, clip);
+ WM_event_add_notifier(C, NC_OBJECT|ND_TRANSFORM, NULL);
+
+ return OPERATOR_FINISHED;
+}
+
+void CLIP_OT_set_axis(wmOperatorType *ot)
+{
+ static EnumPropertyItem axis_actions[] = {
+ {0, "X", 0, "X", "Align bundle align X axis"},
+ {1, "Y", 0, "Y", "Align bundle align Y axis"},
+ {0, NULL, 0, NULL, NULL}
+ };
+
+ /* identifiers */
+ ot->name= "Set Axis";
+ ot->description= "Set direction of scene axis rotating camera (or it's parent if present) and assuming selected track lies on real axis joining it with the origin";
+ ot->idname= "CLIP_OT_set_axis";
+
+ /* api callbacks */
+ ot->exec= set_axis_exec;
+ ot->poll= space_clip_frame_camera_poll;
+
+ /* flags */
+ ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
+
+ /* properties */
+ RNA_def_enum(ot->srna, "axis", axis_actions, 0, "Axis", "Axis to use to align bundle along");
+}
+
+/********************** set scale operator *********************/
+
+static int set_scale_exec(bContext *C, wmOperator *op)
+{
+ SpaceClip *sc= CTX_wm_space_clip(C);
+ MovieClip *clip= ED_space_clip(sc);
+ MovieTrackingTrack *track;
+ Scene *scene= CTX_data_scene(C);
+ Object *parent= scene->camera;
+ int tot= 0;
+ float vec[2][3], mat[4][4], scale;
+ float dist= RNA_float_get(op->ptr, "distance");
+
+ if(count_selected_bundles(C)!=2) {
+ BKE_report(op->reports, RPT_ERROR, "Two tracks with bundles should be selected to scale scene");
+
+ return OPERATOR_CANCELLED;
+ }
+
+ if(scene->camera->parent)
+ parent= scene->camera->parent;
+
+ BKE_get_tracking_mat(scene, NULL, mat);
+
+ track= clip->tracking.tracks.first;
+ while(track) {
+ if(TRACK_VIEW_SELECTED(sc, track)) {
+ mul_v3_m4v3(vec[tot], mat, track->bundle_pos);
+ tot++;
+ }
+
+ track= track->next;
+ }
+
+ sub_v3_v3(vec[0], vec[1]);
+
+ if(len_v3(vec[0])>1e-5) {
+ scale= dist / len_v3(vec[0]);
+
+ mul_v3_fl(parent->size, scale);
+ mul_v3_fl(parent->loc, scale);
+
+ DAG_id_tag_update(&clip->id, 0);
+ DAG_id_tag_update(&parent->id, OB_RECALC_OB);
+
+ WM_event_add_notifier(C, NC_MOVIECLIP|NA_EVALUATED, clip);
+ WM_event_add_notifier(C, NC_OBJECT|ND_TRANSFORM, NULL);
+ }
+
+ return OPERATOR_FINISHED;
+}
+
+static int set_scale_invoke(bContext *C, wmOperator *op, wmEvent *UNUSED(event))
+{
+ SpaceClip *sc= CTX_wm_space_clip(C);
+ MovieClip *clip= ED_space_clip(sc);
+ float dist= RNA_float_get(op->ptr, "distance");
+
+ if(dist==0.0f)
+ RNA_float_set(op->ptr, "distance", clip->tracking.settings.dist);
+
+ return set_scale_exec(C, op);
+}
+
+void CLIP_OT_set_scale(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name= "Set Scale";
+ ot->description= "Set scale of scene by scaling camera (or it's parent if present)";
+ ot->idname= "CLIP_OT_set_scale";
+
+ /* api callbacks */
+ ot->exec= set_scale_exec;
+ ot->invoke= set_scale_invoke;
+ ot->poll= space_clip_frame_camera_poll;
+
+ /* flags */
+ ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
+
+ /* properties */
+ RNA_def_float(ot->srna, "distance", 0.0f, -FLT_MAX, FLT_MAX,
+ "Distance", "Distance between selected tracks", -100.0f, 100.0f);
+}
+
+/********************** 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(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 principal point to center of footage";
+ ot->idname= "CLIP_OT_set_center_principal";
+
+ /* api callbacks */
+ ot->exec= set_center_principal_exec;
+ ot->poll= ED_space_clip_poll;
+
+ /* flags */
+ ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
+}
+
+/********************** hide tracks operator *********************/
+
+static int hide_tracks_exec(bContext *C, wmOperator *op)
+{
+ SpaceClip *sc= CTX_wm_space_clip(C);
+ MovieClip *clip= ED_space_clip(sc);
+ MovieTrackingTrack *track;
+ int unselected;
+
+ unselected= RNA_boolean_get(op->ptr, "unselected");
+
+ track= clip->tracking.tracks.first;
+ while(track) {
+ 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;
+ }
+
+ track= track->next;
+ }
+
+ if(clip->tracking.act_track && clip->tracking.act_track->flag&TRACK_HIDDEN)
+ clip->tracking.act_track= NULL;
+
+ if(unselected==0) {
+ /* no selection on screen now, unlock view so it can be scrolled nice again */
+ sc->flag&= ~SC_LOCK_SELECTION;
+ }
+
+ 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_poll;
+
+ /* flags */
+ ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
+
+ /* properties */
+ RNA_def_boolean(ot->srna, "unselected", 0, "Unselected", "Hide unselected tracks");
+}
+
+/********************** 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(sc);
+ MovieTrackingTrack *track;
+
+ track= clip->tracking.tracks.first;
+ while(track) {
+ track->flag&= ~TRACK_HIDDEN;
+
+ track= track->next;
+ }
+
+ 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_poll;
+
+ /* flags */
+ ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
+}
+
+/********************** detect features operator *********************/
+
+static bGPDlayer *detect_get_layer(MovieClip *clip)
+{
+ bGPDlayer *layer;
+
+ if(!clip->gpd)
+ return NULL;
+
+ layer= clip->gpd->layers.first;
+ while(layer) {
+ if(layer->flag&GP_LAYER_ACTIVE)
+ return layer;
+
+ layer= layer->next;
+ }
+
+ return NULL;
+}
+
+static int detect_features_exec(bContext *C, wmOperator *op)
+{
+ SpaceClip *sc= CTX_wm_space_clip(C);
+ MovieClip *clip= ED_space_clip(sc);
+ ImBuf *ibuf= BKE_movieclip_get_ibuf_flag(clip, &sc->user, 0);
+ MovieTrackingTrack *track= clip->tracking.tracks.first;
+ int placement= RNA_enum_get(op->ptr, "placement");
+ int margin= RNA_int_get(op->ptr, "margin");
+ int min_trackness= RNA_int_get(op->ptr, "min_trackness");
+ int min_distance= RNA_int_get(op->ptr, "min_distance");
+ int place_outside_layer= 0;
+ bGPDlayer *layer= NULL;
+
+ if(placement!=0) {
+ layer= detect_get_layer(clip);
+ place_outside_layer= placement==2;
+ }
+
+ /* deselect existing tracks */
+ while(track) {
+ track->flag&= ~SELECT;
+ track->pat_flag&= ~SELECT;
+ track->search_flag&= ~SELECT;
+
+ track= track->next;
+ }
+
+ BKE_tracking_detect_fast(&clip->tracking, ibuf, sc->user.framenr, margin, min_trackness, min_distance, layer, place_outside_layer);
+
+ IMB_freeImBuf(ibuf);
+
+ WM_event_add_notifier(C, NC_MOVIECLIP|NA_EDITED, NULL);
+
+ return OPERATOR_FINISHED;
+}
+
+void CLIP_OT_detect_features(wmOperatorType *ot)
+{
+ static EnumPropertyItem placement_items[] = {
+ {0, "FRAME", 0, "Whole Frame", "Place markers across the whole frame"},
+ {1, "INSIDE_GPENCIL", 0, "Inside grease pencil", "Place markers only inside areas oulined with grease pencil"},
+ {2, "OUTSIDE_GPENCIL", 0, "Outside grease pencil", "Place markers only outside areas oulined with grease pencil"},
+ {0, NULL, 0, NULL, NULL}
+ };
+
+ /* identifiers */
+ ot->name= "Detect Features";
+ ot->description= "Automatically detect features to track";
+ ot->idname= "CLIP_OT_detect_features";
+
+ /* api callbacks */
+ ot->exec= detect_features_exec;
+ ot->poll= space_clip_frame_poll;
+
+ /* flags */
+ ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
+
+ /* properties */
+ RNA_def_enum(ot->srna, "placement", placement_items, 0, "Placement", "Placement for detected features");
+ RNA_def_int(ot->srna, "margin", 16, 0, INT_MAX, "Margin", "Only corners further than margin pixels from the image edges are considered", 0, 300);
+ RNA_def_int(ot->srna, "min_trackness", 16, 0, INT_MAX, "Trackness", "Minimum score to add a corner", 0, 300);
+ RNA_def_int(ot->srna, "min_distance", 120, 0, INT_MAX, "Distance", "Minimal distance accepted between two corners", 0, 300);
+}
+
+/********************** frame jump operator *********************/
+
+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(sc);
+ MovieTrackingTrack *track;
+ int pos= RNA_enum_get(op->ptr, "position");
+ int delta;
+
+ if(pos<=1) { /* jump to path */
+ track= clip->tracking.act_track;
+
+ if(!track)
+ return OPERATOR_CANCELLED;
+
+ delta= pos == 1 ? 1 : -1;
+
+ while(sc->user.framenr+delta >= SFRA && sc->user.framenr+delta <= EFRA) {
+ MovieTrackingMarker *marker= BKE_tracking_exact_marker(track, sc->user.framenr+delta);
+
+ if(!marker || marker->flag&MARKER_DISABLED)
+ break;
+
+ sc->user.framenr+= delta;
+ }
+ }
+ else { /* to to failed frame */
+ if(clip->tracking.reconstruction.flag&TRACKING_RECONSTRUCTED) {
+ int a= sc->user.framenr;
+ MovieTracking *tracking= &clip->tracking;
+
+ delta= pos == 3 ? 1 : -1;
+
+ a+= delta;
+
+ while(a+delta >= SFRA && a+delta <= EFRA) {
+ MovieReconstructedCamera *cam= BKE_tracking_get_reconstructed_camera(tracking, a);
+
+ if(!cam) {
+ sc->user.framenr= a;
+
+ break;
+ }
+
+ a+= delta;
+ }
+ }
+ }
+
+ if(CFRA!=sc->user.framenr) {
+ CFRA= sc->user.framenr;
+ sound_seek_scene(CTX_data_main(C), CTX_data_scene(C));
+
+ 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 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, "Previons 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= space_clip_frame_poll;
+
+ /* flags */
+ ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
+
+ /* properties */
+ RNA_def_enum(ot->srna, "position", position_items, 0, "Position", "Position to jumo to");
+}
+
+/********************** join tracks operator *********************/
+
+static int join_tracks_exec(bContext *C, wmOperator *op)
+{
+ SpaceClip *sc= CTX_wm_space_clip(C);
+ MovieClip *clip= ED_space_clip(sc);
+ MovieTrackingTrack *act_track, *track, *next;
+
+ act_track= clip->tracking.act_track;
+
+ if(!act_track) {
+ BKE_report(op->reports, RPT_ERROR, "No active track to join to");
+ return OPERATOR_CANCELLED;
+ }
+
+ track= clip->tracking.tracks.first;
+ while(track) {
+ if(TRACK_VIEW_SELECTED(sc, track) && track!=act_track) {
+ if(!BKE_tracking_test_join_tracks(act_track, track)) {
+ BKE_report(op->reports, RPT_ERROR, "Some selected tracks have got keyframed markers to the same frame");
+ return OPERATOR_CANCELLED;
+ }
+ }
+
+ track= track->next;
+ }
+
+ track= clip->tracking.tracks.first;
+ while(track) {
+ next= track->next;
+
+ if(TRACK_VIEW_SELECTED(sc, track) && track!=act_track) {
+ BKE_tracking_join_tracks(act_track, track);
+
+ BKE_tracking_free_track(track);
+ BLI_freelinkN(&clip->tracking.tracks, track);
+ }
+
+ track= next;
+ }
+
+ 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= "Joint Selected Tracks";
+ ot->idname= "CLIP_OT_join_tracks";
+
+ /* api callbacks */
+ ot->exec= join_tracks_exec;
+ ot->poll= space_clip_frame_poll;
+
+ /* flags */
+ ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
+}
+
+/********************** lock tracks operator *********************/
+
+static int lock_tracks_exec(bContext *C, wmOperator *op)
+{
+ SpaceClip *sc= CTX_wm_space_clip(C);
+ MovieClip *clip= ED_space_clip(sc);
+ MovieTracking *tracking= &clip->tracking;
+ MovieTrackingTrack *track= tracking->tracks.first;
+ int action= RNA_enum_get(op->ptr, "action");
+
+ while(track) {
+ if(TRACK_VIEW_SELECTED(sc, track)) {
+ if(action==0) track->flag|= TRACK_LOCKED;
+ else if(action==1) track->flag&= ~TRACK_LOCKED;
+ else track->flag^= TRACK_LOCKED;
+ }
+
+ track= track->next;
+ }
+
+ WM_event_add_notifier(C, NC_MOVIECLIP|NA_EVALUATED, clip);
+
+ return OPERATOR_FINISHED;
+}
+
+void CLIP_OT_lock_tracks(wmOperatorType *ot)
+{
+ static EnumPropertyItem actions_items[] = {
+ {0, "LOCK", 0, "Lock", "Lock selected tracks"},
+ {1, "UNLOCK", 0, "Unlock", "Unlock selected tracks"},
+ {2, "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_poll;
+
+ /* flags */
+ ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
+
+ /* properties */
+ RNA_def_enum(ot->srna, "action", actions_items, 0, "Action", "Lock action to execute");
+}
+
+/********************** 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(sc);
+ MovieTrackingTrack *track, *act_track= clip->tracking.act_track;
+
+ if(!act_track)
+ return OPERATOR_CANCELLED;
+
+ track= clip->tracking.tracks.first;
+ while(track) {
+ 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;
+ }
+ }
+
+ track= track->next;
+ }
+
+ 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_poll;
+
+ /* flags */
+ ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
+}
+
+/********************** add 2d stabilization tracks operator *********************/
+
+static int stabilize_2d_add_exec(bContext *C, wmOperator *UNUSED(op))
+{
+ SpaceClip *sc= CTX_wm_space_clip(C);
+ MovieClip *clip= ED_space_clip(sc);
+ MovieTracking *tracking= &clip->tracking;
+ MovieTrackingTrack *track;
+ MovieTrackingStabilization *stab= &tracking->stabilization;
+ int update= 0;
+
+ track= tracking->tracks.first;
+ while(track) {
+ if(TRACK_VIEW_SELECTED(sc, track) && (track->flag&TRACK_USE_2D_STAB)==0) {
+ track->flag|= TRACK_USE_2D_STAB;
+ stab->tot_track++;
+
+ update= 1;
+ }
+
+ track= track->next;
+ }
+
+ if(update) {
+ stab->ok= 0;
+
+ DAG_id_tag_update(&clip->id, 0);
+ WM_event_add_notifier(C, NC_MOVIECLIP|ND_DISPLAY, clip);
+ }
+
+ return OPERATOR_FINISHED;
+}
+
+void CLIP_OT_stabilize_2d_add(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name= "Add Stabilization Tracks";
+ ot->description= "Add selected tracks to 2D stabilization tool";
+ ot->idname= "CLIP_OT_stabilize_2d_add";
+
+ /* api callbacks */
+ ot->exec= stabilize_2d_add_exec;
+ ot->poll= ED_space_clip_poll;
+
+ /* flags */
+ ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
+}
+
+/********************** remove 2d stabilization tracks operator *********************/
+
+static int stabilize_2d_remove_exec(bContext *C, wmOperator *UNUSED(op))
+{
+ SpaceClip *sc= CTX_wm_space_clip(C);
+ MovieClip *clip= ED_space_clip(sc);
+ MovieTracking *tracking= &clip->tracking;
+ MovieTrackingStabilization *stab= &tracking->stabilization;
+ MovieTrackingTrack *track;
+ int a= 0, update= 0;
+
+ track= tracking->tracks.first;
+ while(track) {
+ if(track->flag&TRACK_USE_2D_STAB) {
+ if(a==stab->act_track) {
+ track->flag&= ~TRACK_USE_2D_STAB;
+
+ stab->act_track--;
+ stab->tot_track--;
+
+ if(stab->act_track<0)
+ stab->act_track= 0;
+
+ update= 1;
+
+ break;
+ }
+
+ a++;
+ }
+
+ track= track->next;
+ }
+
+ if(update) {
+ stab->ok= 0;
+
+ DAG_id_tag_update(&clip->id, 0);
+ WM_event_add_notifier(C, NC_MOVIECLIP|ND_DISPLAY, clip);
+ }
+
+ return OPERATOR_FINISHED;
+}
+
+void CLIP_OT_stabilize_2d_remove(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name= "Remove Stabilization Track";
+ ot->description= "Remove selected track from stabilization";
+ ot->idname= "CLIP_OT_stabilize_2d_remove";
+
+ /* api callbacks */
+ ot->exec= stabilize_2d_remove_exec;
+ ot->poll= ED_space_clip_poll;
+
+ /* flags */
+ ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
+}
+
+/********************** select 2d stabilization tracks operator *********************/
+
+static int stabilize_2d_select_exec(bContext *C, wmOperator *UNUSED(op))
+{
+ SpaceClip *sc= CTX_wm_space_clip(C);
+ MovieClip *clip= ED_space_clip(sc);
+ MovieTracking *tracking= &clip->tracking;
+ MovieTrackingTrack *track;
+ int update= 0;
+
+ track= tracking->tracks.first;
+ while(track) {
+ if(track->flag&TRACK_USE_2D_STAB) {
+ BKE_tracking_track_flag(track, TRACK_AREA_ALL, SELECT, 0);
+
+ update= 1;
+ }
+
+ track= track->next;
+ }
+
+ if(update)
+ WM_event_add_notifier(C, NC_MOVIECLIP|ND_SELECT, clip);
+
+ return OPERATOR_FINISHED;
+}
+
+void CLIP_OT_stabilize_2d_select(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name= "Select Stabilization Tracks";
+ ot->description= "Select track which are used for stabilization";
+ ot->idname= "CLIP_OT_stabilize_2d_select";
+
+ /* api callbacks */
+ ot->exec= stabilize_2d_select_exec;
+ ot->poll= ED_space_clip_poll;
+
+ /* flags */
+ ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
+}
+
+/********************** set 2d stabilization rotation track operator *********************/
+
+static int stabilize_2d_set_rotation_exec(bContext *C, wmOperator *UNUSED(op))
+{
+ SpaceClip *sc= CTX_wm_space_clip(C);
+ MovieClip *clip= ED_space_clip(sc);
+ MovieTracking *tracking= &clip->tracking;
+
+ if(tracking->act_track) {
+ MovieTrackingStabilization *stab= &tracking->stabilization;
+
+ stab->rot_track= tracking->act_track;
+ stab->ok= 0;
+
+ DAG_id_tag_update(&clip->id, 0);
+ WM_event_add_notifier(C, NC_MOVIECLIP|ND_DISPLAY, clip);
+ }
+
+ return OPERATOR_FINISHED;
+}
+
+void CLIP_OT_stabilize_2d_set_rotation(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name= "Set Rotation Track";
+ ot->description= "Use active track to compensate rotaiton when doing 2D stabilization";
+ ot->idname= "CLIP_OT_stabilize_2d_set_rotation";
+
+ /* api callbacks */
+ ot->exec= stabilize_2d_set_rotation_exec;
+ ot->poll= ED_space_clip_poll;
+
+ /* flags */
+ ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
+}
+
+/********************** clean tracks operator *********************/
+
+static int is_track_clean(MovieTrackingTrack *track, int frames, int del)
+{
+ int ok= 1, a, 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(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) {
+ 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(sc);
+ MovieTracking *tracking= &clip->tracking;
+ MovieTrackingTrack *track, *next, *act_track= clip->tracking.act_track;
+ 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;
+
+ track= tracking->tracks.first;
+ while(track) {
+ next= track->next;
+
+ if((track->flag&TRACK_HIDDEN)==0 && (track->flag&TRACK_LOCKED)==0) {
+ int ok= 1;
+
+ 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(track, TRACK_AREA_ALL, SELECT, 0);
+ }
+ else if(action==TRACKING_CLEAN_DELETE_TRACK) {
+ if(track==act_track)
+ clip->tracking.act_track= NULL;
+
+ BKE_tracking_free_track(track);
+ BLI_freelinkN(&clip->tracking.tracks, track);
+ }
+
+ /* happens when all tracking segments are not long enough */
+ if(track->markersnr==0) {
+ if(track==act_track)
+ clip->tracking.act_track= NULL;
+
+ BKE_tracking_free_track(track);
+ BLI_freelinkN(&clip->tracking.tracks, track);
+ }
+ }
+ }
+
+ track= next;
+ }
+
+ WM_event_add_notifier(C, NC_MOVIECLIP|ND_SELECT, clip);
+
+ return OPERATOR_FINISHED;
+}
+
+static int clean_tracks_invoke(bContext *C, wmOperator *op, wmEvent *UNUSED(event))
+{
+ SpaceClip *sc= CTX_wm_space_clip(C);
+ MovieClip *clip= ED_space_clip(sc);
+ int frames= RNA_int_get(op->ptr, "frames");
+ float error= RNA_float_get(op->ptr, "error");
+ int action= RNA_enum_get(op->ptr, "action");
+
+ if(frames==0 && error==0 && action==0) {
+ RNA_int_set(op->ptr, "frames", clip->tracking.settings.clean_frames);
+ RNA_float_set(op->ptr, "error", clip->tracking.settings.clean_error);
+ 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 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";
+ ot->idname= "CLIP_OT_clean_tracks";
+
+ /* api callbacks */
+ ot->exec= clean_tracks_exec;
+ ot->invoke= clean_tracks_invoke;
+ ot->poll= ED_space_clip_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 with have got larger reprojection error", 0.0f, 100.0f);
+ RNA_def_enum(ot->srna, "action", actions_items, 0, "Action", "Cleanup action to execute");
+}