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

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSergey Sharybin <sergey@blender.org>2021-01-19 16:28:44 +0300
committerSergey Sharybin <sergey@blender.org>2021-01-26 18:23:09 +0300
commit66f8835f9c03e7736297b5995a68e1455ed14e2a (patch)
treed44f2a93d14c55944a5ae55282a1527d7db90664 /source/blender
parent3d3b6d94e6e646828bd03c5656673a5d00a806aa (diff)
Fix T84850: "Lock to Selection" causes unwanted jumps
Adding new tracks, mask points, mask primitives, changing selection was causing an unwanted jumps in the view. This change makes it so those operations are preserving view offset. Differential Revision: https://developer.blender.org/D10146
Diffstat (limited to 'source/blender')
-rw-r--r--source/blender/editors/include/ED_clip.h24
-rw-r--r--source/blender/editors/include/ED_mask.h5
-rw-r--r--source/blender/editors/mask/mask_add.c10
-rw-r--r--source/blender/editors/mask/mask_edit.c36
-rw-r--r--source/blender/editors/mask/mask_intern.h15
-rw-r--r--source/blender/editors/mask/mask_ops.c39
-rw-r--r--source/blender/editors/mask/mask_query.c13
-rw-r--r--source/blender/editors/mask/mask_select.c13
-rw-r--r--source/blender/editors/space_clip/clip_editor.c160
-rw-r--r--source/blender/editors/space_clip/clip_intern.h5
-rw-r--r--source/blender/editors/space_clip/clip_ops.c8
-rw-r--r--source/blender/editors/space_clip/clip_utils.c147
-rw-r--r--source/blender/editors/space_clip/tracking_ops.c9
-rw-r--r--source/blender/editors/space_clip/tracking_select.c17
-rw-r--r--source/blender/editors/space_image/image_ops.c2
15 files changed, 374 insertions, 129 deletions
diff --git a/source/blender/editors/include/ED_clip.h b/source/blender/editors/include/ED_clip.h
index 0a66c439f79..56494f87bfe 100644
--- a/source/blender/editors/include/ED_clip.h
+++ b/source/blender/editors/include/ED_clip.h
@@ -99,6 +99,30 @@ void ED_space_clip_set_clip(struct bContext *C,
struct Mask *ED_space_clip_get_mask(struct SpaceClip *sc);
void ED_space_clip_set_mask(struct bContext *C, struct SpaceClip *sc, struct Mask *mask);
+/* Locked state is used to preserve current clip editor viewport upon changes. Example usage:
+ *
+ * ...
+ *
+ * ClipViewLockState lock_state;
+ * ED_clip_view_lock_state_store(C, &lock_state);
+ *
+ * <change selection>
+ *
+ * ED_clip_view_lock_state_restore_no_jump(C, &lock_state);
+ *
+ * These function are to be used from space clip editor context only. Otherwise debug builds will
+ * assert, release builds will crash. */
+
+typedef struct ClipViewLockState {
+ float offset_x, offset_y;
+ float lock_offset_x, lock_offset_y;
+ float zoom;
+} ClipViewLockState;
+
+void ED_clip_view_lock_state_store(const struct bContext *C, ClipViewLockState *state);
+void ED_clip_view_lock_state_restore_no_jump(const struct bContext *C,
+ const ClipViewLockState *state);
+
/* ** clip_ops.c ** */
void ED_operatormacros_clip(void);
diff --git a/source/blender/editors/include/ED_mask.h b/source/blender/editors/include/ED_mask.h
index bcf52da3f69..b20dae694fc 100644
--- a/source/blender/editors/include/ED_mask.h
+++ b/source/blender/editors/include/ED_mask.h
@@ -63,7 +63,10 @@ void ED_mask_point_pos__reverse(
struct ScrArea *area, struct ARegion *region, float x, float y, float *xr, float *yr);
void ED_mask_cursor_location_get(struct ScrArea *area, float cursor[2]);
-bool ED_mask_selected_minmax(const struct bContext *C, float min[2], float max[2]);
+bool ED_mask_selected_minmax(const struct bContext *C,
+ float min[2],
+ float max[2],
+ bool include_handles);
/* mask_draw.c */
void ED_mask_draw(const struct bContext *C, const char draw_flag, const char draw_type);
diff --git a/source/blender/editors/mask/mask_add.c b/source/blender/editors/mask/mask_add.c
index d7b3d74bc7e..1226cc57359 100644
--- a/source/blender/editors/mask/mask_add.c
+++ b/source/blender/editors/mask/mask_add.c
@@ -507,6 +507,9 @@ static int add_vertex_handle_cyclic(
static int add_vertex_exec(bContext *C, wmOperator *op)
{
+ MaskViewLockState lock_state;
+ ED_mask_view_lock_state_store(C, &lock_state);
+
Mask *mask = CTX_data_edit_mask(C);
if (mask == NULL) {
/* if there's no active mask, create one */
@@ -548,6 +551,8 @@ static int add_vertex_exec(bContext *C, wmOperator *op)
DEG_id_tag_update(&mask->id, ID_RECALC_GEOMETRY);
+ ED_mask_view_lock_state_restore_no_jump(C, &lock_state);
+
return OPERATOR_FINISHED;
}
@@ -690,6 +695,9 @@ void MASK_OT_add_feather_vertex(wmOperatorType *ot)
static int create_primitive_from_points(
bContext *C, wmOperator *op, const float (*points)[2], int num_points, char handle_type)
{
+ MaskViewLockState lock_state;
+ ED_mask_view_lock_state_store(C, &lock_state);
+
ScrArea *area = CTX_wm_area(C);
int size = RNA_float_get(op->ptr, "size");
@@ -752,6 +760,8 @@ static int create_primitive_from_points(
DEG_id_tag_update(&mask->id, ID_RECALC_GEOMETRY);
+ ED_mask_view_lock_state_restore_no_jump(C, &lock_state);
+
return OPERATOR_FINISHED;
}
diff --git a/source/blender/editors/mask/mask_edit.c b/source/blender/editors/mask/mask_edit.c
index 663ae0097ad..e6c6424e5f0 100644
--- a/source/blender/editors/mask/mask_edit.c
+++ b/source/blender/editors/mask/mask_edit.c
@@ -184,3 +184,39 @@ void ED_operatormacros_mask(void)
}
/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Lock-to-selection viewport preservation
+ * \{ */
+
+void ED_mask_view_lock_state_store(const bContext *C, MaskViewLockState *state)
+{
+ SpaceClip *space_clip = CTX_wm_space_clip(C);
+ if (space_clip != NULL) {
+ ED_clip_view_lock_state_store(C, &state->space_clip_state);
+ }
+}
+
+void ED_mask_view_lock_state_restore_no_jump(const bContext *C, const MaskViewLockState *state)
+{
+ SpaceClip *space_clip = CTX_wm_space_clip(C);
+ if (space_clip != NULL) {
+ if ((space_clip->flag & SC_LOCK_SELECTION) == 0) {
+ /* Early output if the editor is not locked to selection.
+ * Avoids forced dependency graph evaluation here. */
+ return;
+ }
+
+ /* Mask's lock-to-selection requres deformed splines to be evaluated to calculate bounds of
+ * points after animation has been evaluated. The restore-no-jump type of function does
+ * calculation of new offset for the view for an updated state of mask to cancel the offset out
+ * by modifying locked offset. In order to do such calculation mask needs to be evaluated after
+ * modification by an operator. */
+ struct Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
+ (void)depsgraph;
+
+ ED_clip_view_lock_state_restore_no_jump(C, &state->space_clip_state);
+ }
+}
+
+/** \} */
diff --git a/source/blender/editors/mask/mask_intern.h b/source/blender/editors/mask/mask_intern.h
index f6990583383..ee1784011ea 100644
--- a/source/blender/editors/mask/mask_intern.h
+++ b/source/blender/editors/mask/mask_intern.h
@@ -23,6 +23,8 @@
#pragma once
+#include "ED_clip.h"
+
struct Mask;
struct bContext;
struct wmOperatorType;
@@ -92,6 +94,19 @@ void ED_mask_select_flush_all(struct Mask *mask);
bool ED_maskedit_poll(struct bContext *C);
bool ED_maskedit_mask_poll(struct bContext *C);
+/* Generalized solution for preserving editor viewport when making changes while lock-to-selection
+ * is enabled.
+ * Any mask operator can use this API, without worrying that some editors do not have an idea of
+ * lock-to-selection. */
+
+typedef struct MaskViewLockState {
+ ClipViewLockState space_clip_state;
+} MaskViewLockState;
+
+void ED_mask_view_lock_state_store(const struct bContext *C, MaskViewLockState *state);
+void ED_mask_view_lock_state_restore_no_jump(const struct bContext *C,
+ const MaskViewLockState *state);
+
/* mask_query.c */
bool ED_mask_find_nearest_diff_point(const struct bContext *C,
struct Mask *mask,
diff --git a/source/blender/editors/mask/mask_ops.c b/source/blender/editors/mask/mask_ops.c
index 25cc39bf9a0..00a1dfb7d87 100644
--- a/source/blender/editors/mask/mask_ops.c
+++ b/source/blender/editors/mask/mask_ops.c
@@ -226,6 +226,12 @@ typedef struct SlidePointData {
int width, height;
float prev_mouse_coord[2];
+
+ /* Previous clip coordinate which was resolved from mouse position (0, 0).
+ * Is used to compansate for view offste moving in-between of mouse events when
+ * lock-to-selection is enabled. */
+ float prev_zero_coord[2];
+
float no[2];
bool is_curvature_only, is_accurate, is_initial_feather, is_overall_feather;
@@ -431,6 +437,9 @@ static void *slide_point_customdata(bContext *C, wmOperator *op, const wmEvent *
const float threshold = 19;
eMaskWhichHandle which_handle;
+ MaskViewLockState lock_state;
+ ED_mask_view_lock_state_store(C, &lock_state);
+
ED_mask_mouse_pos(area, region, event->mval, co);
ED_mask_get_size(area, &width, &height);
@@ -530,7 +539,15 @@ static void *slide_point_customdata(bContext *C, wmOperator *op, const wmEvent *
}
customdata->which_handle = which_handle;
+ {
+ WM_event_add_notifier(C, NC_MASK | NA_EDITED, mask);
+ DEG_id_tag_update(&mask->id, 0);
+
+ ED_mask_view_lock_state_restore_no_jump(C, &lock_state);
+ }
+
ED_mask_mouse_pos(area, region, event->mval, customdata->prev_mouse_coord);
+ ED_mask_mouse_pos(area, region, (int[2]){0, 0}, customdata->prev_zero_coord);
}
return customdata;
@@ -655,10 +672,24 @@ static int slide_point_modal(bContext *C, wmOperator *op, const wmEvent *event)
ED_mask_mouse_pos(area, region, event->mval, co);
sub_v2_v2v2(delta, co, data->prev_mouse_coord);
+ copy_v2_v2(data->prev_mouse_coord, co);
+
+ /* Compensate for possibly moved view offset since the last event.
+ * The idea is to see how mapping of a fixed and known position did change. */
+ {
+ float zero_coord[2];
+ ED_mask_mouse_pos(area, region, (int[2]){0, 0}, zero_coord);
+
+ float zero_delta[2];
+ sub_v2_v2v2(zero_delta, zero_coord, data->prev_zero_coord);
+ sub_v2_v2(delta, zero_delta);
+
+ copy_v2_v2(data->prev_zero_coord, zero_coord);
+ }
+
if (data->is_accurate) {
mul_v2_fl(delta, 0.2f);
}
- copy_v2_v2(data->prev_mouse_coord, co);
if (data->action == SLIDE_ACTION_HANDLE) {
float new_handle[2];
@@ -966,6 +997,9 @@ static SlideSplineCurvatureData *slide_spline_curvature_customdata(bContext *C,
float u, co[2];
BezTriple *next_bezt;
+ MaskViewLockState lock_state;
+ ED_mask_view_lock_state_store(C, &lock_state);
+
ED_mask_mouse_pos(CTX_wm_area(C), CTX_wm_region(C), event->mval, co);
if (!ED_mask_find_nearest_diff_point(C,
@@ -1047,6 +1081,9 @@ static SlideSplineCurvatureData *slide_spline_curvature_customdata(bContext *C,
mask_layer->act_point = point;
ED_mask_select_flush_all(mask);
+ DEG_id_tag_update(&mask->id, 0);
+ ED_mask_view_lock_state_restore_no_jump(C, &lock_state);
+
return slide_data;
}
diff --git a/source/blender/editors/mask/mask_query.c b/source/blender/editors/mask/mask_query.c
index cfd57ca3477..8f4f14a1ba8 100644
--- a/source/blender/editors/mask/mask_query.c
+++ b/source/blender/editors/mask/mask_query.c
@@ -604,7 +604,7 @@ void ED_mask_point_pos__reverse(
*yr = co[1];
}
-bool ED_mask_selected_minmax(const bContext *C, float min[2], float max[2])
+bool ED_mask_selected_minmax(const bContext *C, float min[2], float max[2], bool include_handles)
{
Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C);
Mask *mask = CTX_data_edit_mask(C);
@@ -638,22 +638,29 @@ bool ED_mask_selected_minmax(const bContext *C, float min[2], float max[2])
}
if (bezt->f2 & SELECT) {
minmax_v2v2_v2(min, max, deform_point->bezt.vec[1]);
+ ok = true;
}
- if (BKE_mask_point_handles_mode_get(point) == MASK_HANDLE_MODE_STICK) {
+
+ if (!include_handles) {
+ /* Ignore handles. */
+ }
+ else if (BKE_mask_point_handles_mode_get(point) == MASK_HANDLE_MODE_STICK) {
BKE_mask_point_handle(deform_point, MASK_WHICH_HANDLE_STICK, handle);
minmax_v2v2_v2(min, max, handle);
+ ok = true;
}
else {
if ((bezt->f1 & SELECT) && (bezt->h1 != HD_VECT)) {
BKE_mask_point_handle(deform_point, MASK_WHICH_HANDLE_LEFT, handle);
minmax_v2v2_v2(min, max, handle);
+ ok = true;
}
if ((bezt->f3 & SELECT) && (bezt->h2 != HD_VECT)) {
BKE_mask_point_handle(deform_point, MASK_WHICH_HANDLE_RIGHT, handle);
minmax_v2v2_v2(min, max, handle);
+ ok = true;
}
}
- ok = true;
}
}
}
diff --git a/source/blender/editors/mask/mask_select.c b/source/blender/editors/mask/mask_select.c
index cdc6ece1e84..5c369afc4cd 100644
--- a/source/blender/editors/mask/mask_select.c
+++ b/source/blender/editors/mask/mask_select.c
@@ -214,12 +214,17 @@ static int select_all_exec(bContext *C, wmOperator *op)
Mask *mask = CTX_data_edit_mask(C);
int action = RNA_enum_get(op->ptr, "action");
+ MaskViewLockState lock_state;
+ ED_mask_view_lock_state_store(C, &lock_state);
+
ED_mask_select_toggle_all(mask, action);
ED_mask_select_flush_all(mask);
DEG_id_tag_update(&mask->id, ID_RECALC_SELECT);
WM_event_add_notifier(C, NC_MASK | ND_SELECT, mask);
+ ED_mask_view_lock_state_restore_no_jump(C, &lock_state);
+
return OPERATOR_FINISHED;
}
@@ -261,6 +266,9 @@ static int select_exec(bContext *C, wmOperator *op)
eMaskWhichHandle which_handle;
const float threshold = 19;
+ MaskViewLockState lock_state;
+ ED_mask_view_lock_state_store(C, &lock_state);
+
RNA_float_get_array(op->ptr, "location", co);
point = ED_mask_point_find_nearest(
@@ -324,6 +332,8 @@ static int select_exec(bContext *C, wmOperator *op)
DEG_id_tag_update(&mask->id, ID_RECALC_SELECT);
WM_event_add_notifier(C, NC_MASK | ND_SELECT, mask);
+ ED_mask_view_lock_state_restore_no_jump(C, &lock_state);
+
return OPERATOR_FINISHED;
}
@@ -364,12 +374,15 @@ static int select_exec(bContext *C, wmOperator *op)
DEG_id_tag_update(&mask->id, ID_RECALC_SELECT);
WM_event_add_notifier(C, NC_MASK | ND_SELECT, mask);
+ ED_mask_view_lock_state_restore_no_jump(C, &lock_state);
+
return OPERATOR_FINISHED;
}
if (deselect_all) {
/* For clip editor tracks, leave deselect all to clip editor. */
if (!ED_clip_can_select(C)) {
ED_mask_deselect_all(C);
+ ED_mask_view_lock_state_restore_no_jump(C, &lock_state);
return OPERATOR_FINISHED;
}
}
diff --git a/source/blender/editors/space_clip/clip_editor.c b/source/blender/editors/space_clip/clip_editor.c
index bd11a746e11..af1d082d317 100644
--- a/source/blender/editors/space_clip/clip_editor.c
+++ b/source/blender/editors/space_clip/clip_editor.c
@@ -327,118 +327,18 @@ void ED_clip_update_frame(const Main *mainp, int cfra)
}
}
-static bool selected_tracking_boundbox(SpaceClip *sc, float min[2], float max[2])
+bool ED_clip_view_selection(const bContext *C, ARegion *UNUSED(region), bool fit)
{
- MovieClip *clip = ED_space_clip_get_clip(sc);
- MovieTrackingTrack *track;
- int width, height;
- bool ok = false;
- ListBase *tracksbase = BKE_tracking_get_active_tracks(&clip->tracking);
- int framenr = ED_space_clip_get_clip_frame_number(sc);
-
- INIT_MINMAX2(min, max);
-
- ED_space_clip_get_size(sc, &width, &height);
-
- track = tracksbase->first;
- while (track) {
- if (TRACK_VIEW_SELECTED(sc, track)) {
- MovieTrackingMarker *marker = BKE_tracking_marker_get(track, 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);
-
- minmax_v2v2_v2(min, max, pos);
-
- ok = true;
- }
- }
-
- track = track->next;
- }
-
- return ok;
-}
-
-static bool selected_boundbox(const bContext *C, float min[2], float max[2])
-{
- SpaceClip *sc = CTX_wm_space_clip(C);
- if (sc->mode == SC_MODE_TRACKING) {
- return selected_tracking_boundbox(sc, min, max);
- }
-
- if (ED_mask_selected_minmax(C, min, max)) {
- MovieClip *clip = ED_space_clip_get_clip(sc);
- int width, height;
- ED_space_clip_get_size(sc, &width, &height);
- BKE_mask_coord_to_movieclip(clip, &sc->user, min, min);
- BKE_mask_coord_to_movieclip(clip, &sc->user, max, max);
- min[0] *= width;
- min[1] *= height;
- max[0] *= width;
- max[1] *= height;
- return true;
- }
- return false;
-}
-
-bool ED_clip_view_selection(const bContext *C, ARegion *region, bool fit)
-{
- SpaceClip *sc = CTX_wm_space_clip(C);
- int w, h, frame_width, frame_height;
- float min[2], max[2];
-
- ED_space_clip_get_size(sc, &frame_width, &frame_height);
-
- if ((frame_width == 0) || (frame_height == 0) || (sc->clip == NULL)) {
+ float offset_x, offset_y;
+ float zoom;
+ if (!clip_view_calculate_view_selection(C, fit, &offset_x, &offset_y, &zoom)) {
return false;
}
- if (!selected_boundbox(C, min, max)) {
- return false;
- }
-
- /* 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_get_aspect(sc, &aspx, &aspy);
-
- width = BLI_rcti_size_x(&region->winrct) + 1;
- height = BLI_rcti_size_y(&region->winrct) + 1;
-
- zoomx = (float)width / w / aspx;
- zoomy = (float)height / h / aspy;
-
- newzoom = 1.0f / power_of_2(1.0f / min_ff(zoomx, zoomy));
-
- if (fit || sc->zoom > newzoom) {
- sc->zoom = newzoom;
- }
- }
+ SpaceClip *sc = CTX_wm_space_clip(C);
+ sc->xof = offset_x;
+ sc->yof = offset_y;
+ sc->zoom = zoom;
return true;
}
@@ -1177,3 +1077,47 @@ void clip_start_prefetch_job(const bContext *C)
/* and finally start the job */
WM_jobs_start(CTX_wm_manager(C), wm_job);
}
+
+void ED_clip_view_lock_state_store(const bContext *C, ClipViewLockState *state)
+{
+ SpaceClip *space_clip = CTX_wm_space_clip(C);
+ BLI_assert(space_clip != NULL);
+
+ state->offset_x = space_clip->xof;
+ state->offset_y = space_clip->yof;
+ state->zoom = space_clip->zoom;
+
+ state->lock_offset_x = 0.0f;
+ state->lock_offset_y = 0.0f;
+
+ if ((space_clip->flag & SC_LOCK_SELECTION) == 0) {
+ return;
+ }
+
+ if (!clip_view_calculate_view_selection(
+ C, false, &state->offset_x, &state->offset_y, &state->zoom)) {
+ return;
+ }
+
+ state->lock_offset_x = space_clip->xlockof;
+ state->lock_offset_y = space_clip->ylockof;
+}
+
+void ED_clip_view_lock_state_restore_no_jump(const bContext *C, const ClipViewLockState *state)
+{
+ SpaceClip *space_clip = CTX_wm_space_clip(C);
+ BLI_assert(space_clip != NULL);
+
+ if ((space_clip->flag & SC_LOCK_SELECTION) == 0) {
+ return;
+ }
+
+ float offset_x, offset_y;
+ float zoom;
+ if (!clip_view_calculate_view_selection(C, false, &offset_x, &offset_y, &zoom)) {
+ return;
+ }
+
+ space_clip->xlockof = state->offset_x + state->lock_offset_x - offset_x;
+ space_clip->ylockof = state->offset_y + state->lock_offset_y - offset_y;
+}
diff --git a/source/blender/editors/space_clip/clip_intern.h b/source/blender/editors/space_clip/clip_intern.h
index 81df8cc8ecd..b9a69204281 100644
--- a/source/blender/editors/space_clip/clip_intern.h
+++ b/source/blender/editors/space_clip/clip_intern.h
@@ -171,8 +171,13 @@ void clip_delete_plane_track(struct bContext *C,
struct MovieClip *clip,
struct MovieTrackingPlaneTrack *plane_track);
+void clip_view_offset_for_center_to_point(
+ SpaceClip *sc, const float x, const float y, float *r_offset_x, float *r_offset_y);
void clip_view_center_to_point(SpaceClip *sc, float x, float y);
+bool clip_view_calculate_view_selection(
+ const struct bContext *C, bool fit, float *r_offset_x, float *r_offset_y, float *r_zoom);
+
void clip_draw_sfra_efra(struct View2D *v2d, struct Scene *scene);
/* tracking_ops.c */
diff --git a/source/blender/editors/space_clip/clip_ops.c b/source/blender/editors/space_clip/clip_ops.c
index cd4a1ffb526..cb84ea6571c 100644
--- a/source/blender/editors/space_clip/clip_ops.c
+++ b/source/blender/editors/space_clip/clip_ops.c
@@ -1840,8 +1840,16 @@ void CLIP_OT_cursor_set(wmOperatorType *ot)
static int lock_selection_togglee_exec(bContext *C, wmOperator *UNUSED(op))
{
SpaceClip *space_clip = CTX_wm_space_clip(C);
+
+ ClipViewLockState lock_state;
+ ED_clip_view_lock_state_store(C, &lock_state);
+
space_clip->flag ^= SC_LOCK_SELECTION;
+
+ ED_clip_view_lock_state_restore_no_jump(C, &lock_state);
+
WM_event_add_notifier(C, NC_SPACE | ND_SPACE_CLIP, NULL);
+
return OPERATOR_FINISHED;
}
diff --git a/source/blender/editors/space_clip/clip_utils.c b/source/blender/editors/space_clip/clip_utils.c
index c7f2a027ba8..faa3f18e8c1 100644
--- a/source/blender/editors/space_clip/clip_utils.c
+++ b/source/blender/editors/space_clip/clip_utils.c
@@ -27,10 +27,12 @@
#include "BLI_listbase.h"
#include "BLI_math.h"
+#include "BLI_rect.h"
#include "BLI_utildefines.h"
#include "BKE_animsys.h"
#include "BKE_context.h"
+#include "BKE_mask.h"
#include "BKE_movieclip.h"
#include "BKE_tracking.h"
@@ -44,6 +46,7 @@
#include "WM_types.h"
#include "ED_clip.h"
+#include "ED_mask.h"
#include "ED_screen.h"
#include "UI_interface.h"
@@ -395,16 +398,152 @@ void clip_delete_plane_track(bContext *C, MovieClip *clip, MovieTrackingPlaneTra
DEG_id_tag_update(&clip->id, 0);
}
-void clip_view_center_to_point(SpaceClip *sc, float x, float y)
+/* Calculate space clip offset to be centered at the given point. */
+void clip_view_offset_for_center_to_point(
+ SpaceClip *sc, const float x, const float y, float *r_offset_x, float *r_offset_y)
{
int width, height;
+ ED_space_clip_get_size(sc, &width, &height);
+
float aspx, aspy;
+ ED_space_clip_get_aspect(sc, &aspx, &aspy);
+
+ *r_offset_x = (x - 0.5f) * width * aspx;
+ *r_offset_y = (y - 0.5f) * height * aspy;
+}
+
+void clip_view_center_to_point(SpaceClip *sc, float x, float y)
+{
+ clip_view_offset_for_center_to_point(sc, x, y, &sc->xof, &sc->yof);
+}
+
+static bool selected_tracking_boundbox(SpaceClip *sc, float min[2], float max[2])
+{
+ MovieClip *clip = ED_space_clip_get_clip(sc);
+ MovieTrackingTrack *track;
+ int width, height;
+ bool ok = false;
+ ListBase *tracksbase = BKE_tracking_get_active_tracks(&clip->tracking);
+ int framenr = ED_space_clip_get_clip_frame_number(sc);
+
+ INIT_MINMAX2(min, max);
ED_space_clip_get_size(sc, &width, &height);
- ED_space_clip_get_aspect(sc, &aspx, &aspy);
- sc->xof = (x - 0.5f) * width * aspx;
- sc->yof = (y - 0.5f) * height * aspy;
+ track = tracksbase->first;
+ while (track) {
+ if (TRACK_VIEW_SELECTED(sc, track)) {
+ MovieTrackingMarker *marker = BKE_tracking_marker_get(track, 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);
+
+ minmax_v2v2_v2(min, max, pos);
+
+ ok = true;
+ }
+ }
+
+ track = track->next;
+ }
+
+ return ok;
+}
+
+static bool selected_boundbox(const bContext *C, float min[2], float max[2], bool include_handles)
+{
+ SpaceClip *sc = CTX_wm_space_clip(C);
+ if (sc->mode == SC_MODE_TRACKING) {
+ return selected_tracking_boundbox(sc, min, max);
+ }
+
+ if (ED_mask_selected_minmax(C, min, max, include_handles)) {
+ MovieClip *clip = ED_space_clip_get_clip(sc);
+ int width, height;
+ ED_space_clip_get_size(sc, &width, &height);
+ BKE_mask_coord_to_movieclip(clip, &sc->user, min, min);
+ BKE_mask_coord_to_movieclip(clip, &sc->user, max, max);
+ min[0] *= width;
+ min[1] *= height;
+ max[0] *= width;
+ max[1] *= height;
+ return true;
+ }
+ return false;
+}
+
+bool clip_view_calculate_view_selection(
+ const bContext *C, bool fit, float *r_offset_x, float *r_offset_y, float *r_zoom)
+{
+ SpaceClip *sc = CTX_wm_space_clip(C);
+
+ int frame_width, frame_height;
+ ED_space_clip_get_size(sc, &frame_width, &frame_height);
+
+ if ((frame_width == 0) || (frame_height == 0) || (sc->clip == NULL)) {
+ return false;
+ }
+
+ /* NOTE: The `fit` argment is set to truth when doing "View to Selected" operator, and it set to
+ * false when this function is used for Lock-to-Selection functionality. When locking to
+ * selection the handles are to be ignored. So we can deriver the `include_handles` from `fit`.
+ *
+ * TODO(sergey): Make such decision more explicit. Maybe pass usecase for the calculation to tell
+ * operator from lock-to-selection apart. */
+ float min[2], max[2];
+ if (!selected_boundbox(C, min, max, fit)) {
+ return false;
+ }
+
+ /* center view */
+ clip_view_offset_for_center_to_point(sc,
+ (max[0] + min[0]) / (2 * frame_width),
+ (max[1] + min[1]) / (2 * frame_height),
+ r_offset_x,
+ r_offset_y);
+
+ const int w = max[0] - min[0];
+ const int h = max[1] - min[1];
+
+ /* set zoom to see all selection */
+ *r_zoom = sc->zoom;
+ if (w > 0 && h > 0) {
+ ARegion *region = CTX_wm_region(C);
+
+ int width, height;
+ float zoomx, zoomy, newzoom, aspx, aspy;
+
+ ED_space_clip_get_aspect(sc, &aspx, &aspy);
+
+ width = BLI_rcti_size_x(&region->winrct) + 1;
+ height = BLI_rcti_size_y(&region->winrct) + 1;
+
+ zoomx = (float)width / w / aspx;
+ zoomy = (float)height / h / aspy;
+
+ newzoom = 1.0f / power_of_2(1.0f / min_ff(zoomx, zoomy));
+
+ if (fit) {
+ *r_zoom = newzoom;
+ }
+ }
+
+ return true;
}
void clip_draw_sfra_efra(View2D *v2d, Scene *scene)
diff --git a/source/blender/editors/space_clip/tracking_ops.c b/source/blender/editors/space_clip/tracking_ops.c
index 10574063ebd..0f4fc2a2160 100644
--- a/source/blender/editors/space_clip/tracking_ops.c
+++ b/source/blender/editors/space_clip/tracking_ops.c
@@ -88,17 +88,16 @@ static int add_marker_exec(bContext *C, wmOperator *op)
MovieClip *clip = ED_space_clip_get_clip(sc);
float pos[2];
+ ClipViewLockState lock_state;
+ ED_clip_view_lock_state_store(C, &lock_state);
+
RNA_float_get_array(op->ptr, "location", pos);
if (!add_marker(C, pos[0], pos[1])) {
return OPERATOR_CANCELLED;
}
- /* Reset offset from locked position, so frame jumping wouldn't be so
- * confusing.
- */
- sc->xlockof = 0;
- sc->ylockof = 0;
+ ED_clip_view_lock_state_restore_no_jump(C, &lock_state);
WM_event_add_notifier(C, NC_MOVIECLIP | NA_EDITED, clip);
diff --git a/source/blender/editors/space_clip/tracking_select.c b/source/blender/editors/space_clip/tracking_select.c
index 063ea9592aa..ecd73f82e22 100644
--- a/source/blender/editors/space_clip/tracking_select.c
+++ b/source/blender/editors/space_clip/tracking_select.c
@@ -304,6 +304,9 @@ static int mouse_select(bContext *C, const float co[2], const bool extend, const
track = find_nearest_track(sc, tracksbase, co, &distance_to_track);
plane_track = find_nearest_plane_track(sc, plane_tracks_base, co, &distance_to_plane_track);
+ ClipViewLockState lock_state;
+ ED_clip_view_lock_state_store(C, &lock_state);
+
/* Do not select beyond some reasonable distance, that is useless and
* prevents the 'deselect on nothing' behavior. */
if (distance_to_track > 0.05f) {
@@ -377,10 +380,7 @@ static int mouse_select(bContext *C, const float co[2], const bool extend, const
ED_mask_deselect_all(C);
}
- if (!extend) {
- sc->xlockof = 0.0f;
- sc->ylockof = 0.0f;
- }
+ ED_clip_view_lock_state_restore_no_jump(C, &lock_state);
BKE_tracking_dopesheet_tag_update(tracking);
@@ -867,15 +867,20 @@ static int select_all_exec(bContext *C, wmOperator *op)
MovieClip *clip = ED_space_clip_get_clip(sc);
MovieTracking *tracking = &clip->tracking;
- int action = RNA_enum_get(op->ptr, "action");
+ const int action = RNA_enum_get(op->ptr, "action");
- bool has_selection = false;
+ ClipViewLockState lock_state;
+ ED_clip_view_lock_state_store(C, &lock_state);
+ bool has_selection = false;
ED_clip_select_all(sc, action, &has_selection);
if (!has_selection) {
sc->flag &= ~SC_LOCK_SELECTION;
}
+ else {
+ ED_clip_view_lock_state_restore_no_jump(C, &lock_state);
+ }
BKE_tracking_dopesheet_tag_update(tracking);
diff --git a/source/blender/editors/space_image/image_ops.c b/source/blender/editors/space_image/image_ops.c
index 86e52814d6f..097e304c893 100644
--- a/source/blender/editors/space_image/image_ops.c
+++ b/source/blender/editors/space_image/image_ops.c
@@ -900,7 +900,7 @@ static int image_view_selected_exec(bContext *C, wmOperator *UNUSED(op))
}
}
else if (ED_space_image_check_show_maskedit(sima, obedit)) {
- if (!ED_mask_selected_minmax(C, min, max)) {
+ if (!ED_mask_selected_minmax(C, min, max, true)) {
return OPERATOR_CANCELLED;
}
}