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_view3d/view3d_navigate_zoom.c')
-rw-r--r--source/blender/editors/space_view3d/view3d_navigate_zoom.c598
1 files changed, 598 insertions, 0 deletions
diff --git a/source/blender/editors/space_view3d/view3d_navigate_zoom.c b/source/blender/editors/space_view3d/view3d_navigate_zoom.c
new file mode 100644
index 00000000000..a6c7d06c079
--- /dev/null
+++ b/source/blender/editors/space_view3d/view3d_navigate_zoom.c
@@ -0,0 +1,598 @@
+/*
+ * 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.
+ */
+
+/** \file
+ * \ingroup spview3d
+ */
+
+#include "BLI_math.h"
+#include "BLI_rect.h"
+
+#include "BKE_context.h"
+#include "BKE_screen.h"
+
+#include "DEG_depsgraph_query.h"
+
+#include "WM_api.h"
+
+#include "RNA_access.h"
+
+#include "ED_screen.h"
+
+#include "PIL_time.h"
+
+#include "view3d_intern.h"
+#include "view3d_navigate.h" /* own include */
+
+/* -------------------------------------------------------------------- */
+/** \name View Zoom Operator
+ * \{ */
+
+/* #viewdolly_modal_keymap has an exact copy of this, apply fixes to both. */
+void viewzoom_modal_keymap(wmKeyConfig *keyconf)
+{
+ static const EnumPropertyItem modal_items[] = {
+ {VIEW_MODAL_CONFIRM, "CONFIRM", 0, "Confirm", ""},
+
+ {VIEWROT_MODAL_SWITCH_ROTATE, "SWITCH_TO_ROTATE", 0, "Switch to Rotate"},
+ {VIEWROT_MODAL_SWITCH_MOVE, "SWITCH_TO_MOVE", 0, "Switch to Move"},
+
+ {0, NULL, 0, NULL, NULL},
+ };
+
+ wmKeyMap *keymap = WM_modalkeymap_find(keyconf, "View3D Zoom Modal");
+
+ /* this function is called for each spacetype, only needs to add map once */
+ if (keymap && keymap->modal_items) {
+ return;
+ }
+
+ keymap = WM_modalkeymap_ensure(keyconf, "View3D Zoom Modal", modal_items);
+
+ /* disabled mode switching for now, can re-implement better, later on */
+#if 0
+ WM_modalkeymap_add_item(keymap, LEFTMOUSE, KM_RELEASE, KM_ANY, 0, VIEWROT_MODAL_SWITCH_ROTATE);
+ WM_modalkeymap_add_item(keymap, LEFTCTRLKEY, KM_RELEASE, KM_ANY, 0, VIEWROT_MODAL_SWITCH_ROTATE);
+ WM_modalkeymap_add_item(keymap, LEFTSHIFTKEY, KM_PRESS, KM_ANY, 0, VIEWROT_MODAL_SWITCH_MOVE);
+#endif
+
+ /* assign map to operators */
+ WM_modalkeymap_assign(keymap, "VIEW3D_OT_zoom");
+}
+
+/**
+ * \param zoom_xy: Optionally zoom to window location
+ * (coords compatible w/ #wmEvent.xy). Use when not NULL.
+ */
+static void view_zoom_to_window_xy_camera(Scene *scene,
+ Depsgraph *depsgraph,
+ View3D *v3d,
+ ARegion *region,
+ float dfac,
+ const int zoom_xy[2])
+{
+ RegionView3D *rv3d = region->regiondata;
+ const float zoomfac = BKE_screen_view3d_zoom_to_fac(rv3d->camzoom);
+ const float zoomfac_new = clamp_f(
+ 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 (zoom_xy != NULL) {
+ float zoomfac_px;
+ rctf camera_frame_old;
+ rctf camera_frame_new;
+
+ const float pt_src[2] = {zoom_xy[0], zoom_xy[1]};
+ float pt_dst[2];
+ float delta_px[2];
+
+ ED_view3d_calc_camera_border(scene, depsgraph, region, v3d, rv3d, &camera_frame_old, false);
+ BLI_rctf_translate(&camera_frame_old, region->winrct.xmin, region->winrct.ymin);
+
+ rv3d->camzoom = camzoom_new;
+ CLAMP(rv3d->camzoom, RV3D_CAMZOOM_MIN, RV3D_CAMZOOM_MAX);
+
+ ED_view3d_calc_camera_border(scene, depsgraph, region, v3d, rv3d, &camera_frame_new, false);
+ BLI_rctf_translate(&camera_frame_new, region->winrct.xmin, region->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] / (region->winx * zoomfac_px);
+ rv3d->camdy += delta_px[1] / (region->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);
+ }
+}
+
+/**
+ * \param zoom_xy: Optionally zoom to window location
+ * (coords compatible w/ #wmEvent.xy). Use when not NULL.
+ */
+static void view_zoom_to_window_xy_3d(ARegion *region, float dfac, const int zoom_xy[2])
+{
+ RegionView3D *rv3d = region->regiondata;
+ const float dist_new = rv3d->dist * dfac;
+
+ if (zoom_xy != NULL) {
+ float dvec[3];
+ float tvec[3];
+ float tpos[3];
+ float mval_f[2];
+
+ float zfac;
+
+ negate_v3_v3(tpos, rv3d->ofs);
+
+ mval_f[0] = (float)(((zoom_xy[0] - region->winrct.xmin) * 2) - region->winx) / 2.0f;
+ mval_f[1] = (float)(((zoom_xy[1] - region->winrct.ymin) * 2) - region->winy) / 2.0f;
+
+ /* Project cursor position into 3D space */
+ zfac = ED_view3d_calc_zfac(rv3d, tpos, NULL);
+ ED_view3d_win_to_delta(region, mval_f, dvec, zfac);
+
+ /* Calculate view target position for dolly */
+ add_v3_v3v3(tvec, tpos, dvec);
+ negate_v3(tvec);
+
+ /* Offset to target position and dolly */
+ copy_v3_v3(rv3d->ofs, tvec);
+ rv3d->dist = dist_new;
+
+ /* Calculate final offset */
+ madd_v3_v3v3fl(rv3d->ofs, tvec, dvec, dfac);
+ }
+ else {
+ rv3d->dist = dist_new;
+ }
+}
+
+static float viewzoom_scale_value(const rcti *winrct,
+ const eViewZoom_Style viewzoom,
+ const bool zoom_invert,
+ const bool zoom_invert_force,
+ const int xy_curr[2],
+ const int xy_init[2],
+ const float val,
+ const float val_orig,
+ double *r_timer_lastdraw)
+{
+ float zfac;
+
+ if (viewzoom == USER_ZOOM_CONTINUE) {
+ double time = PIL_check_seconds_timer();
+ float time_step = (float)(time - *r_timer_lastdraw);
+ float fac;
+
+ if (U.uiflag & USER_ZOOM_HORIZ) {
+ fac = (float)(xy_init[0] - xy_curr[0]);
+ }
+ else {
+ fac = (float)(xy_init[1] - xy_curr[1]);
+ }
+
+ fac /= U.dpi_fac;
+
+ if (zoom_invert != zoom_invert_force) {
+ fac = -fac;
+ }
+
+ zfac = 1.0f + ((fac / 20.0f) * time_step);
+ *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(winrct),
+ BLI_rcti_cent_y(winrct),
+ };
+ float len_new = (5 * U.dpi_fac) + ((float)len_v2v2_int(ctr, xy_curr) / U.dpi_fac);
+ float len_old = (5 * U.dpi_fac) + ((float)len_v2v2_int(ctr, xy_init) / U.dpi_fac);
+
+ /* 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 len_new = 5 * U.dpi_fac;
+ float len_old = 5 * U.dpi_fac;
+
+ if (U.uiflag & USER_ZOOM_HORIZ) {
+ len_new += (winrct->xmax - (xy_curr[0])) / U.dpi_fac;
+ len_old += (winrct->xmax - (xy_init[0])) / U.dpi_fac;
+ }
+ else {
+ len_new += (winrct->ymax - (xy_curr[1])) / U.dpi_fac;
+ len_old += (winrct->ymax - (xy_init[1])) / U.dpi_fac;
+ }
+
+ 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;
+ }
+
+ return zfac;
+}
+
+static float viewzoom_scale_value_offset(const rcti *winrct,
+ const eViewZoom_Style viewzoom,
+ const bool zoom_invert,
+ const bool zoom_invert_force,
+ const int xy_curr[2],
+ const int xy_init[2],
+ const int xy_offset[2],
+ const float val,
+ const float val_orig,
+ double *r_timer_lastdraw)
+{
+ const int xy_curr_offset[2] = {
+ xy_curr[0] + xy_offset[0],
+ xy_curr[1] + xy_offset[1],
+ };
+ const int xy_init_offset[2] = {
+ xy_init[0] + xy_offset[0],
+ xy_init[1] + xy_offset[1],
+ };
+ return viewzoom_scale_value(winrct,
+ viewzoom,
+ zoom_invert,
+ zoom_invert_force,
+ xy_curr_offset,
+ xy_init_offset,
+ val,
+ val_orig,
+ r_timer_lastdraw);
+}
+
+static void viewzoom_apply_camera(ViewOpsData *vod,
+ const int xy[2],
+ const eViewZoom_Style viewzoom,
+ const bool zoom_invert,
+ const bool zoom_to_pos)
+{
+ float zfac;
+ float zoomfac_prev = BKE_screen_view3d_zoom_to_fac(vod->init.camzoom) * 2.0f;
+ float zoomfac = BKE_screen_view3d_zoom_to_fac(vod->rv3d->camzoom) * 2.0f;
+
+ zfac = viewzoom_scale_value_offset(&vod->region->winrct,
+ viewzoom,
+ zoom_invert,
+ true,
+ xy,
+ vod->init.event_xy,
+ vod->init.event_xy_offset,
+ zoomfac,
+ zoomfac_prev,
+ &vod->prev.time);
+
+ if (!ELEM(zfac, 1.0f, 0.0f)) {
+ /* calculate inverted, then invert again (needed because of camera zoom scaling) */
+ zfac = 1.0f / zfac;
+ view_zoom_to_window_xy_camera(vod->scene,
+ vod->depsgraph,
+ vod->v3d,
+ vod->region,
+ zfac,
+ zoom_to_pos ? vod->prev.event_xy : NULL);
+ }
+
+ ED_region_tag_redraw(vod->region);
+}
+
+static void viewzoom_apply_3d(ViewOpsData *vod,
+ const int xy[2],
+ const eViewZoom_Style viewzoom,
+ const bool zoom_invert,
+ const bool zoom_to_pos)
+{
+ float zfac;
+ float dist_range[2];
+
+ ED_view3d_dist_range_get(vod->v3d, dist_range);
+
+ zfac = viewzoom_scale_value_offset(&vod->region->winrct,
+ viewzoom,
+ zoom_invert,
+ false,
+ xy,
+ vod->init.event_xy,
+ vod->init.event_xy_offset,
+ vod->rv3d->dist,
+ vod->init.dist,
+ &vod->prev.time);
+
+ 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_to_window_xy_3d(vod->region, zfac, zoom_to_pos ? vod->prev.event_xy : NULL);
+ }
+
+ /* these limits were in old code too */
+ CLAMP(vod->rv3d->dist, dist_range[0], dist_range[1]);
+
+ if (RV3D_LOCK_FLAGS(vod->rv3d) & RV3D_BOXVIEW) {
+ view3d_boxview_sync(vod->area, vod->region);
+ }
+
+ ED_view3d_camera_lock_sync(vod->depsgraph, vod->v3d, vod->rv3d);
+
+ ED_region_tag_redraw(vod->region);
+}
+
+static void viewzoom_apply(ViewOpsData *vod,
+ const int xy[2],
+ const eViewZoom_Style viewzoom,
+ const bool zoom_invert,
+ const bool zoom_to_pos)
+{
+ 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, zoom_to_pos);
+ }
+ else {
+ viewzoom_apply_3d(vod, xy, viewzoom, zoom_invert, zoom_to_pos);
+ }
+}
+
+static int viewzoom_modal(bContext *C, wmOperator *op, const wmEvent *event)
+{
+ ViewOpsData *vod = op->customdata;
+ short event_code = VIEW_PASS;
+ bool use_autokey = false;
+ int ret = OPERATOR_RUNNING_MODAL;
+
+ /* execute the events */
+ if (event->type == TIMER && event->customdata == vod->timer) {
+ /* continuous zoom */
+ event_code = VIEW_APPLY;
+ }
+ else if (event->type == MOUSEMOVE) {
+ event_code = VIEW_APPLY;
+ }
+ else if (event->type == EVT_MODAL_MAP) {
+ switch (event->val) {
+ case VIEW_MODAL_CONFIRM:
+ event_code = VIEW_CONFIRM;
+ break;
+ case VIEWROT_MODAL_SWITCH_MOVE:
+ WM_operator_name_call(C, "VIEW3D_OT_move", WM_OP_INVOKE_DEFAULT, NULL);
+ event_code = VIEW_CONFIRM;
+ break;
+ case VIEWROT_MODAL_SWITCH_ROTATE:
+ WM_operator_name_call(C, "VIEW3D_OT_rotate", WM_OP_INVOKE_DEFAULT, NULL);
+ event_code = VIEW_CONFIRM;
+ break;
+ }
+ }
+ else if (event->type == vod->init.event_type && event->val == KM_RELEASE) {
+ event_code = VIEW_CONFIRM;
+ }
+
+ if (event_code == VIEW_APPLY) {
+ const bool use_cursor_init = RNA_boolean_get(op->ptr, "use_cursor_init");
+ viewzoom_apply(vod,
+ event->xy,
+ (eViewZoom_Style)U.viewzoom,
+ (U.uiflag & USER_ZOOM_INVERT) != 0,
+ (use_cursor_init && (U.uiflag & USER_ZOOM_TO_MOUSEPOS)));
+ if (ED_screen_animation_playing(CTX_wm_manager(C))) {
+ use_autokey = true;
+ }
+ }
+ else if (event_code == VIEW_CONFIRM) {
+ use_autokey = true;
+ ret = OPERATOR_FINISHED;
+ }
+
+ if (use_autokey) {
+ ED_view3d_camera_lock_autokey(vod->v3d, vod->rv3d, C, false, true);
+ }
+
+ if (ret & OPERATOR_FINISHED) {
+ viewops_data_free(C, op->customdata);
+ op->customdata = NULL;
+ }
+
+ return ret;
+}
+
+static int viewzoom_exec(bContext *C, wmOperator *op)
+{
+ Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
+ Scene *scene = CTX_data_scene(C);
+ View3D *v3d;
+ RegionView3D *rv3d;
+ ScrArea *area;
+ ARegion *region;
+ bool use_cam_zoom;
+ float dist_range[2];
+
+ const int delta = RNA_int_get(op->ptr, "delta");
+ const bool use_cursor_init = RNA_boolean_get(op->ptr, "use_cursor_init");
+
+ if (op->customdata) {
+ ViewOpsData *vod = op->customdata;
+
+ area = vod->area;
+ region = vod->region;
+ }
+ else {
+ area = CTX_wm_area(C);
+ region = CTX_wm_region(C);
+ }
+
+ v3d = area->spacedata.first;
+ rv3d = region->regiondata;
+
+ use_cam_zoom = (rv3d->persp == RV3D_CAMOB) &&
+ !(rv3d->is_persp && ED_view3d_camera_lock_check(v3d, rv3d));
+
+ int zoom_xy_buf[2];
+ const int *zoom_xy = NULL;
+ if (use_cursor_init && (U.uiflag & USER_ZOOM_TO_MOUSEPOS)) {
+ zoom_xy_buf[0] = RNA_struct_property_is_set(op->ptr, "mx") ? RNA_int_get(op->ptr, "mx") :
+ region->winx / 2;
+ zoom_xy_buf[1] = RNA_struct_property_is_set(op->ptr, "my") ? RNA_int_get(op->ptr, "my") :
+ region->winy / 2;
+ zoom_xy = zoom_xy_buf;
+ }
+
+ ED_view3d_dist_range_get(v3d, dist_range);
+
+ if (delta < 0) {
+ const float step = 1.2f;
+ if (use_cam_zoom) {
+ view_zoom_to_window_xy_camera(scene, depsgraph, v3d, region, step, zoom_xy);
+ }
+ else {
+ if (rv3d->dist < dist_range[1]) {
+ view_zoom_to_window_xy_3d(region, step, zoom_xy);
+ }
+ }
+ }
+ else {
+ const float step = 1.0f / 1.2f;
+ if (use_cam_zoom) {
+ view_zoom_to_window_xy_camera(scene, depsgraph, v3d, region, step, zoom_xy);
+ }
+ else {
+ if (rv3d->dist > dist_range[0]) {
+ view_zoom_to_window_xy_3d(region, step, zoom_xy);
+ }
+ }
+ }
+
+ if (RV3D_LOCK_FLAGS(rv3d) & RV3D_BOXVIEW) {
+ view3d_boxview_sync(area, region);
+ }
+
+ ED_view3d_camera_lock_sync(depsgraph, v3d, rv3d);
+ ED_view3d_camera_lock_autokey(v3d, rv3d, C, false, true);
+
+ ED_region_tag_redraw(region);
+
+ viewops_data_free(C, op->customdata);
+ op->customdata = NULL;
+
+ return OPERATOR_FINISHED;
+}
+
+/* viewdolly_invoke() copied this function, changes here may apply there */
+static int viewzoom_invoke(bContext *C, wmOperator *op, const wmEvent *event)
+{
+ ViewOpsData *vod;
+
+ const bool use_cursor_init = RNA_boolean_get(op->ptr, "use_cursor_init");
+
+ vod = op->customdata = viewops_data_create(
+ C,
+ event,
+ (viewops_flag_from_prefs() & ~VIEWOPS_FLAG_ORBIT_SELECT) |
+ (use_cursor_init ? VIEWOPS_FLAG_USE_MOUSE_INIT : 0));
+
+ ED_view3d_smooth_view_force_finish(C, vod->v3d, vod->region);
+
+ /* if one or the other zoom position aren't set, set from event */
+ if (!RNA_struct_property_is_set(op->ptr, "mx") || !RNA_struct_property_is_set(op->ptr, "my")) {
+ RNA_int_set(op->ptr, "mx", event->xy[0]);
+ RNA_int_set(op->ptr, "my", event->xy[1]);
+ }
+
+ if (RNA_struct_property_is_set(op->ptr, "delta")) {
+ viewzoom_exec(C, op);
+ }
+ else {
+ if (ELEM(event->type, MOUSEZOOM, MOUSEPAN)) {
+
+ if (U.uiflag & USER_ZOOM_HORIZ) {
+ vod->init.event_xy[0] = vod->prev.event_xy[0] = event->xy[0];
+ }
+ else {
+ /* Set y move = x move as MOUSEZOOM uses only x axis to pass magnification value */
+ vod->init.event_xy[1] = vod->prev.event_xy[1] = vod->init.event_xy[1] + event->xy[0] -
+ event->prev_xy[0];
+ }
+ viewzoom_apply(vod,
+ event->prev_xy,
+ USER_ZOOM_DOLLY,
+ (U.uiflag & USER_ZOOM_INVERT) != 0,
+ (use_cursor_init && (U.uiflag & USER_ZOOM_TO_MOUSEPOS)));
+ ED_view3d_camera_lock_autokey(vod->v3d, vod->rv3d, C, false, true);
+
+ viewops_data_free(C, op->customdata);
+ op->customdata = NULL;
+ return OPERATOR_FINISHED;
+ }
+
+ if (U.viewzoom == USER_ZOOM_CONTINUE) {
+ /* needs a timer to continue redrawing */
+ vod->timer = WM_event_add_timer(CTX_wm_manager(C), CTX_wm_window(C), TIMER, 0.01f);
+ vod->prev.time = PIL_check_seconds_timer();
+ }
+
+ /* add temp handler */
+ WM_event_add_modal_handler(C, op);
+
+ return OPERATOR_RUNNING_MODAL;
+ }
+ return OPERATOR_FINISHED;
+}
+
+static void viewzoom_cancel(bContext *C, wmOperator *op)
+{
+ viewops_data_free(C, op->customdata);
+ op->customdata = NULL;
+}
+
+void VIEW3D_OT_zoom(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "Zoom View";
+ ot->description = "Zoom in/out in the view";
+ ot->idname = "VIEW3D_OT_zoom";
+
+ /* api callbacks */
+ ot->invoke = viewzoom_invoke;
+ ot->exec = viewzoom_exec;
+ ot->modal = viewzoom_modal;
+ ot->poll = view3d_zoom_or_dolly_poll;
+ ot->cancel = viewzoom_cancel;
+
+ /* flags */
+ ot->flag = OPTYPE_BLOCKING | OPTYPE_GRAB_CURSOR_XY;
+
+ /* properties */
+ view3d_operator_properties_common(
+ ot, V3D_OP_PROP_DELTA | V3D_OP_PROP_MOUSE_CO | V3D_OP_PROP_USE_MOUSE_INIT);
+}
+
+/** \} */