Fix T44422: Zoom to mouse fails in camera view
authorCampbell Barton <ideasman42@gmail.com>
Thu, 23 Apr 2015 14:12:16 +0000 (00:12 +1000)
committerCampbell Barton <ideasman42@gmail.com>
Thu, 23 Apr 2015 14:18:42 +0000 (00:18 +1000)
Camera now supports all zoom styles too.

source/blender/editors/space_view3d/view3d_edit.c
source/blender/makesdna/DNA_view3d_types.h

index 236fefc70883d57d7cfedec92f7507fd1c6590bb..3f6f17da334318d614a26f97739ba9896d1c11ad 100644 (file)
@@ -517,6 +517,7 @@ void ED_view3d_quadview_update(ScrArea *sa, ARegion *ar, bool do_clip)
 
 typedef struct ViewOpsData {
        /* context pointers (assigned by viewops_data_alloc) */
+       Scene *scene;
        ScrArea *sa;
        ARegion *ar;
        View3D *v3d;
@@ -589,6 +590,7 @@ static void viewops_data_alloc(bContext *C, wmOperator *op)
 
        /* store data */
        op->customdata = vod;
+       vod->scene = CTX_data_scene(C);
        vod->sa = CTX_wm_area(C);
        vod->ar = CTX_wm_region(C);
        vod->v3d = vod->sa->spacedata.first;
@@ -2093,16 +2095,63 @@ void viewzoom_modal_keymap(wmKeyConfig *keyconf)
        WM_modalkeymap_assign(keymap, "VIEW3D_OT_zoom");
 }
 
-static void view_zoom_mouseloc(ARegion *ar, float dfac, int mx, int my)
+static void view_zoom_mouseloc_camera(
+        Scene *scene, View3D *v3d,
+        ARegion *ar, float dfac, int mx, int my)
 {
        RegionView3D *rv3d = ar->regiondata;
+       const float zoomfac = BKE_screen_view3d_zoom_to_fac(rv3d->camzoom);
+       const float zoomfac_new = CLAMPIS(zoomfac * (1.0f / dfac), RV3D_CAMZOOM_MIN_FACTOR, RV3D_CAMZOOM_MAX_FACTOR);
+       const float camzoom_new = BKE_screen_view3d_zoom_from_fac(zoomfac_new);
+
+
+       if (U.uiflag & USER_ZOOM_TO_MOUSEPOS) {
+               float zoomfac_px;
+               rctf camera_frame_old;
+               rctf camera_frame_new;
+
+               const float pt_src[2] = {mx, my};
+               float pt_dst[2];
+               float delta_px[2];
+
+               ED_view3d_calc_camera_border(scene, ar, v3d, rv3d, &camera_frame_old, false);
+               BLI_rctf_translate(&camera_frame_old, ar->winrct.xmin, ar->winrct.ymin);
+
+               rv3d->camzoom = camzoom_new;
+               CLAMP(rv3d->camzoom, RV3D_CAMZOOM_MIN, RV3D_CAMZOOM_MAX);
+
+               ED_view3d_calc_camera_border(scene, ar, v3d, rv3d, &camera_frame_new, false);
+               BLI_rctf_translate(&camera_frame_new, ar->winrct.xmin, ar->winrct.ymin);
+
+               BLI_rctf_transform_pt_v(&camera_frame_new, &camera_frame_old, pt_dst, pt_src);
+               sub_v2_v2v2(delta_px, pt_dst, pt_src);
+
+               /* translate the camera offset using pixel space delta
+                * mapped back to the camera (same logic as panning in camera view) */
+               zoomfac_px = BKE_screen_view3d_zoom_to_fac(rv3d->camzoom) * 2.0f;
+
+               rv3d->camdx += delta_px[0] / (ar->winx * zoomfac_px);
+               rv3d->camdy += delta_px[1] / (ar->winy * zoomfac_px);
+               CLAMP(rv3d->camdx, -1.0f, 1.0f);
+               CLAMP(rv3d->camdy, -1.0f, 1.0f);
+       }
+       else {
+               rv3d->camzoom = camzoom_new;
+               CLAMP(rv3d->camzoom, RV3D_CAMZOOM_MIN, RV3D_CAMZOOM_MAX);
+       }
+}
+
+static void view_zoom_mouseloc_3d(ARegion *ar, float dfac, int mx, int my)
+{
+       RegionView3D *rv3d = ar->regiondata;
+       const float dist_new = rv3d->dist * dfac;
 
        if (U.uiflag & USER_ZOOM_TO_MOUSEPOS) {
                float dvec[3];
                float tvec[3];
                float tpos[3];
                float mval_f[2];
-               float new_dist;
+
                float zfac;
 
                negate_v3_v3(tpos, rv3d->ofs);
@@ -2119,105 +2168,133 @@ static void view_zoom_mouseloc(ARegion *ar, float dfac, int mx, int my)
                negate_v3(tvec);
 
                /* Offset to target position and dolly */
-               new_dist = rv3d->dist * dfac;
-
                copy_v3_v3(rv3d->ofs, tvec);
-               rv3d->dist = new_dist;
+               rv3d->dist = dist_new;
 
                /* Calculate final offset */
                madd_v3_v3v3fl(rv3d->ofs, tvec, dvec, dfac);
        }
        else {
-               rv3d->dist *= dfac;
+               rv3d->dist = dist_new;
        }
 }
 
-
-static void viewzoom_apply(ViewOpsData *vod, const int xy[2], const short viewzoom, const short zoom_invert)
+static float viewzoom_scale_value(
+        const rcti *winrct,
+        const short viewzoom,
+        const bool zoom_invert, const bool zoom_invert_force,
+        const int xy[2], const int xy_orig[2],
+        const float val, const float val_orig,
+        double *r_timer_lastdraw)
 {
-       float zfac = 1.0;
-       bool use_cam_zoom;
-       float dist_range[2];
-
-       use_cam_zoom = (vod->rv3d->persp == RV3D_CAMOB) && !(vod->rv3d->is_persp && ED_view3d_camera_lock_check(vod->v3d, vod->rv3d));
-
-       ED_view3d_dist_range_get(vod->v3d, dist_range);
-
-       if (use_cam_zoom) {
-               float delta;
-               delta = (xy[0] - vod->origx + xy[1] - vod->origy) / 10.0f;
-               vod->rv3d->camzoom = vod->camzoom_prev + (zoom_invert ? -delta : delta);
-
-               CLAMP(vod->rv3d->camzoom, RV3D_CAMZOOM_MIN, RV3D_CAMZOOM_MAX);
-       }
+       float zfac;
 
        if (viewzoom == USER_ZOOM_CONT) {
                double time = PIL_check_seconds_timer();
-               float time_step = (float)(time - vod->timer_lastdraw);
+               float time_step = (float)(time - *r_timer_lastdraw);
                float fac;
 
                if (U.uiflag & USER_ZOOM_HORIZ) {
-                       fac = (float)(vod->origx - xy[0]);
+                       fac = (float)(xy_orig[0] - xy[0]);
                }
                else {
-                       fac = (float)(vod->origy - xy[1]);
+                       fac = (float)(xy_orig[1] - xy[1]);
                }
 
-               if (zoom_invert) {
+               if (zoom_invert != zoom_invert_force) {
                        fac = -fac;
                }
 
                /* oldstyle zoom */
                zfac = 1.0f + ((fac / 20.0f) * time_step);
-               vod->timer_lastdraw = time;
+               *r_timer_lastdraw = time;
        }
        else if (viewzoom == USER_ZOOM_SCALE) {
                /* method which zooms based on how far you move the mouse */
 
                const int ctr[2] = {
-                   BLI_rcti_cent_x(&vod->ar->winrct),
-                   BLI_rcti_cent_y(&vod->ar->winrct),
+                   BLI_rcti_cent_x(winrct),
+                   BLI_rcti_cent_y(winrct),
                };
-               const float len_new = 5 + len_v2v2_int(ctr, xy);
-               const float len_old = 5 + len_v2v2_int(ctr, &vod->origx);
-               zfac = vod->dist_prev * ((len_old + 5) / (len_new + 5)) / vod->rv3d->dist;
+               float len_new = 5 + len_v2v2_int(ctr, xy);
+               float len_old = 5 + len_v2v2_int(ctr, xy_orig);
+
+               /* intentionally ignore 'zoom_invert' for scale */
+               if (zoom_invert_force) {
+                       SWAP(float, len_new, len_old);
+               }
+
+               zfac = val_orig * (len_old / max_ff(len_new, 1.0f)) / val;
        }
        else {  /* USER_ZOOM_DOLLY */
-               float len1, len2;
-               
+               float len_new = 5;
+               float len_old = 5;
+
                if (U.uiflag & USER_ZOOM_HORIZ) {
-                       len1 = (vod->ar->winrct.xmax - xy[0]) + 5;
-                       len2 = (vod->ar->winrct.xmax - vod->origx) + 5;
+                       len_new += (winrct->xmax - xy[0]);
+                       len_old += (winrct->xmax - xy_orig[0]);
                }
                else {
-                       len1 = (vod->ar->winrct.ymax - xy[1]) + 5;
-                       len2 = (vod->ar->winrct.ymax - vod->origy) + 5;
-               }
-               if (zoom_invert) {
-                       SWAP(float, len1, len2);
+                       len_new += (winrct->ymax - xy[1]);
+                       len_old += (winrct->ymax - xy_orig[1]);
                }
-               
-               if (use_cam_zoom) {
-                       /* zfac is ignored in this case, see below */
-#if 0
-                       zfac = vod->camzoom_prev * (2.0f * ((len1 / len2) - 1.0f) + 1.0f) / vod->rv3d->camzoom;
-#endif
-               }
-               else {
-                       zfac = vod->dist_prev * (2.0f * ((len1 / len2) - 1.0f) + 1.0f) / vod->rv3d->dist;
+
+               if (zoom_invert != zoom_invert_force) {
+                       SWAP(float, len_new, len_old);
                }
+
+               zfac = val_orig * (2.0f * ((len_new / max_ff(len_old, 1.0f)) - 1.0f) + 1.0f) / val;
        }
 
-       if (!use_cam_zoom) {
-               if (zfac != 1.0f) {
-                       const float zfac_min = dist_range[0] / vod->rv3d->dist;
-                       const float zfac_max = dist_range[1] / vod->rv3d->dist;
-                       CLAMP(zfac, zfac_min, zfac_max);
 
-                       if (zfac != 1.0f) {
-                               view_zoom_mouseloc(vod->ar, zfac, vod->oldx, vod->oldy);
-                       }
-               }
+       return zfac;
+}
+
+static void viewzoom_apply_camera(
+        ViewOpsData *vod, const int xy[2],
+        const short viewzoom, const bool zoom_invert)
+{
+       float zfac;
+       float zoomfac_prev = BKE_screen_view3d_zoom_to_fac(vod->camzoom_prev) * 2.0f;
+       float zoomfac =      BKE_screen_view3d_zoom_to_fac(vod->rv3d->camzoom) * 2.0f;
+
+       zfac = viewzoom_scale_value(
+              &vod->ar->winrct, viewzoom, zoom_invert, true, xy, &vod->origx,
+              zoomfac, zoomfac_prev,
+              &vod->timer_lastdraw);
+
+       if (zfac != 1.0f && zfac != 0.0f) {
+               /* calculate inverted, then invert again (needed because of camera zoom scaling) */
+               zfac = 1.0f / zfac;
+               view_zoom_mouseloc_camera(
+                       vod->scene, vod->v3d,
+                       vod->ar, zfac, vod->oldx, vod->oldy);
+       }
+
+       ED_region_tag_redraw(vod->ar);
+}
+
+static void viewzoom_apply_3d(
+        ViewOpsData *vod, const int xy[2],
+        const short viewzoom, const bool zoom_invert)
+{
+       float zfac;
+       float dist_range[2];
+
+       ED_view3d_dist_range_get(vod->v3d, dist_range);
+
+       zfac = viewzoom_scale_value(
+              &vod->ar->winrct, viewzoom, zoom_invert, false, xy, &vod->origx,
+              vod->rv3d->dist, vod->dist_prev,
+              &vod->timer_lastdraw);
+
+       if (zfac != 1.0f) {
+               const float zfac_min = dist_range[0] / vod->rv3d->dist;
+               const float zfac_max = dist_range[1] / vod->rv3d->dist;
+               CLAMP(zfac, zfac_min, zfac_max);
+
+               view_zoom_mouseloc_3d(
+                       vod->ar, zfac, vod->oldx, vod->oldy);
        }
 
        /* these limits were in old code too */
@@ -2231,6 +2308,19 @@ static void viewzoom_apply(ViewOpsData *vod, const int xy[2], const short viewzo
        ED_region_tag_redraw(vod->ar);
 }
 
+static void viewzoom_apply(
+        ViewOpsData *vod, const int xy[2],
+        const short viewzoom, const bool zoom_invert)
+{
+       if ((vod->rv3d->persp == RV3D_CAMOB) &&
+           (vod->rv3d->is_persp && ED_view3d_camera_lock_check(vod->v3d, vod->rv3d)) == 0)
+       {
+               viewzoom_apply_camera(vod, xy, viewzoom, zoom_invert);
+       }
+       else {
+               viewzoom_apply_3d(vod, xy, viewzoom, zoom_invert);
+       }
+}
 
 static int viewzoom_modal(bContext *C, wmOperator *op, const wmEvent *event)
 {
@@ -2291,6 +2381,7 @@ static int viewzoom_modal(bContext *C, wmOperator *op, const wmEvent *event)
 
 static int viewzoom_exec(bContext *C, wmOperator *op)
 {
+       Scene *scene = CTX_data_scene(C);
        View3D *v3d;
        RegionView3D *rv3d;
        ScrArea *sa;
@@ -2323,22 +2414,26 @@ static int viewzoom_exec(bContext *C, wmOperator *op)
        ED_view3d_dist_range_get(v3d, dist_range);
 
        if (delta < 0) {
+               const float step = 1.2f;
                /* this min and max is also in viewmove() */
                if (use_cam_zoom) {
-                       rv3d->camzoom -= 10.0f;
-                       if (rv3d->camzoom < RV3D_CAMZOOM_MIN) rv3d->camzoom = RV3D_CAMZOOM_MIN;
+                       view_zoom_mouseloc_camera(scene, v3d, ar, step, mx, my);
                }
-               else if (rv3d->dist < dist_range[1]) {
-                       view_zoom_mouseloc(ar, 1.2f, mx, my);
+               else {
+                       if (rv3d->dist < dist_range[1]) {
+                               view_zoom_mouseloc_3d(ar, step, mx, my);
+                       }
                }
        }
        else {
+               const float step = 1.0f / 1.2f;
                if (use_cam_zoom) {
-                       rv3d->camzoom += 10.0f;
-                       if (rv3d->camzoom > RV3D_CAMZOOM_MAX) rv3d->camzoom = RV3D_CAMZOOM_MAX;
+                       view_zoom_mouseloc_camera(scene, v3d, ar, step, mx, my);
                }
-               else if (rv3d->dist > dist_range[0]) {
-                       view_zoom_mouseloc(ar, 0.83333f, mx, my);
+               else {
+                       if (rv3d->dist > dist_range[0]) {
+                               view_zoom_mouseloc_3d(ar, step, mx, my);
+                       }
                }
        }
 
index 66ee1fe4242e6ca75dca99f4bcdac0910002f9f1..a0516590d435cb7ef3428b73b58960d507f90267 100644 (file)
@@ -379,6 +379,9 @@ enum {
 #define RV3D_CAMZOOM_MIN -30
 #define RV3D_CAMZOOM_MAX 600
 
-#endif
+/* #BKE_screen_view3d_zoom_to_fac() values above */
+#define RV3D_CAMZOOM_MIN_FACTOR  0.1657359312880714853f
+#define RV3D_CAMZOOM_MAX_FACTOR 44.9852813742385702928f
 
+#endif