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:
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.c310
-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.c2941
13 files changed, 8248 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..9eb96a9a4c5
--- /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) && (sc->flag&SC_SHOW_PYRAMID_LEVELS) && (track->tracker==TRACKER_KLT) && (marker->flag&MARKER_DISABLED)==0) {
+ 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..c5036145792
--- /dev/null
+++ b/source/blender/editors/space_clip/clip_editor.c
@@ -0,0 +1,310 @@
+/*
+ * ***** 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];
+ pos[1]= marker->pos[1]+track->offset[1];
+ pos[2]= 0.0f;
+
+ /* undistortion happens for normalized coords */
+ if(sc->user.render_flag&MCLIP_PROXY_RENDER_UNDISTORT)
+ /* undistortion happens for normalized coords */
+ ED_clip_point_undistorted_pos(sc, pos, pos);
+
+ pos[0]*= width;
+ pos[1]*= height;
+
+ 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..e5bf053aa1c
--- /dev/null
+++ b/source/blender/editors/space_clip/tracking_ops.c
@@ -0,0 +1,2941 @@
+/*
+ * ***** 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);
+ track= NULL;
+ }
+
+ /* happens when all tracking segments are not long enough */
+ if(track && track->markersnr==0) {
+ if(track==act_track)
+ clip->tracking.act_track= NULL;
+
+ BKE_tracking_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");
+}