Blender  V3.3
view2d_edge_pan.cc
Go to the documentation of this file.
1 /* SPDX-License-Identifier: GPL-2.0-or-later
2  * Copyright 2021 Blender Foundation. All rights reserved. */
3 
8 #include "BKE_context.h"
9 
10 #include "BLI_math.h"
11 #include "BLI_rect.h"
12 
13 #include "ED_screen.h"
14 
15 #include "MEM_guardedalloc.h"
16 
17 #include "PIL_time.h"
18 
19 #include "RNA_access.h"
20 #include "RNA_define.h"
21 
22 #include "UI_interface.h"
23 #include "UI_view2d.h"
24 
25 #include "WM_api.h"
26 #include "WM_types.h"
27 
28 /* -------------------------------------------------------------------- */
33 {
34  ARegion *region = CTX_wm_region(C);
35 
36  /* Check if there's a region in context to work with. */
37  if (region == nullptr) {
38  return false;
39  }
40 
41  View2D *v2d = &region->v2d;
42 
43  /* Check that 2d-view can pan. */
44  if ((v2d->keepofs & V2D_LOCKOFS_X) && (v2d->keepofs & V2D_LOCKOFS_Y)) {
45  return false;
46  }
47 
48  /* View can pan. */
49  return true;
50 }
51 
53  View2DEdgePanData *vpd,
54  float inside_pad,
55  float outside_pad,
56  float speed_ramp,
57  float max_speed,
58  float delay,
59  float zoom_influence)
60 {
61  if (!UI_view2d_edge_pan_poll(C)) {
62  return;
63  }
64 
65  /* Set pointers to owners. */
66  vpd->screen = CTX_wm_screen(C);
67  vpd->area = CTX_wm_area(C);
68  vpd->region = CTX_wm_region(C);
69  vpd->v2d = &vpd->region->v2d;
70  BLI_rctf_init(&vpd->limit, -FLT_MAX, FLT_MAX, -FLT_MAX, FLT_MAX);
71 
72  BLI_assert(speed_ramp > 0.0f);
73  vpd->inside_pad = inside_pad;
74  vpd->outside_pad = outside_pad;
75  vpd->speed_ramp = speed_ramp;
76  vpd->max_speed = max_speed;
77  vpd->delay = delay;
78  vpd->zoom_influence = zoom_influence;
79 
80  vpd->enabled = false;
81 
82  /* Calculate translation factor, based on size of view. */
83  const float winx = (float)(BLI_rcti_size_x(&vpd->region->winrct) + 1);
84  const float winy = (float)(BLI_rcti_size_y(&vpd->region->winrct) + 1);
85  vpd->facx = (BLI_rctf_size_x(&vpd->v2d->cur)) / winx;
86  vpd->facy = (BLI_rctf_size_y(&vpd->v2d->cur)) / winy;
87 
89 }
90 
92  View2DEdgePanData *vpd, float xmin, float xmax, float ymin, float ymax)
93 {
94  BLI_rctf_init(&vpd->limit, xmin, xmax, ymin, ymax);
95 }
96 
98 {
99  vpd->edge_pan_start_time_x = 0.0;
100  vpd->edge_pan_start_time_y = 0.0;
102  vpd->initial_rect = vpd->region->v2d.cur;
103 }
104 
110  int pan_dir_x,
111  int pan_dir_y,
112  const double current_time)
113 {
114  if (pan_dir_x == 0) {
115  vpd->edge_pan_start_time_x = 0.0;
116  }
117  else if (vpd->edge_pan_start_time_x == 0.0) {
118  vpd->edge_pan_start_time_x = current_time;
119  }
120  if (pan_dir_y == 0) {
121  vpd->edge_pan_start_time_y = 0.0;
122  }
123  else if (vpd->edge_pan_start_time_y == 0.0) {
124  vpd->edge_pan_start_time_y = current_time;
125  }
126 }
127 
134 static float smootherstep(const float domain_max, float x)
135 {
136  x = clamp_f(x / domain_max, 0.0, 1.0);
137  return x * x * x * (x * (x * 6.0 - 15.0) + 10.0);
138 }
139 
141  int event_loc,
142  bool x_dir,
143  const double current_time)
144 {
145  ARegion *region = vpd->region;
146 
147  /* Find the distance from the start of the drag zone. */
148  const int pad = vpd->inside_pad * U.widget_unit;
149  const int min = (x_dir ? region->winrct.xmin : region->winrct.ymin) + pad;
150  const int max = (x_dir ? region->winrct.xmax : region->winrct.ymax) - pad;
151  int distance = 0.0;
152  if (event_loc > max) {
153  distance = event_loc - max;
154  }
155  else if (event_loc < min) {
156  distance = min - event_loc;
157  }
158  else {
159  BLI_assert_msg(0, "Calculating speed outside of pan zones");
160  return 0.0f;
161  }
162  float distance_factor = distance / (vpd->speed_ramp * U.widget_unit);
163  CLAMP(distance_factor, 0.0f, 1.0f);
164 
165  /* Apply a fade in to the speed based on a start time delay. */
166  const double start_time = x_dir ? vpd->edge_pan_start_time_x : vpd->edge_pan_start_time_y;
167  const float delay_factor = vpd->delay > 0.01f ?
168  smootherstep(vpd->delay, (float)(current_time - start_time)) :
169  1.0f;
170 
171  /* Zoom factor increases speed when zooming in and decreases speed when zooming out. */
172  const float zoomx = (float)(BLI_rcti_size_x(&region->winrct) + 1) /
173  BLI_rctf_size_x(&region->v2d.cur);
174  const float zoom_factor = 1.0f + CLAMPIS(vpd->zoom_influence, 0.0f, 1.0f) * (zoomx - 1.0f);
175 
176  return distance_factor * delay_factor * zoom_factor * vpd->max_speed * U.widget_unit *
177  (float)U.dpi_fac;
178 }
179 
180 static void edge_pan_apply_delta(bContext *C, View2DEdgePanData *vpd, float dx, float dy)
181 {
182  View2D *v2d = vpd->v2d;
183  if (!v2d) {
184  return;
185  }
186 
187  /* Calculate amount to move view by. */
188  dx *= vpd->facx;
189  dy *= vpd->facy;
190 
191  /* Only move view on an axis if change is allowed. */
192  if ((v2d->keepofs & V2D_LOCKOFS_X) == 0) {
193  v2d->cur.xmin += dx;
194  v2d->cur.xmax += dx;
195  }
196  if ((v2d->keepofs & V2D_LOCKOFS_Y) == 0) {
197  v2d->cur.ymin += dy;
198  v2d->cur.ymax += dy;
199  }
200 
201  if (dx != 0.0f || dy != 0.0f) {
202  /* Inform v2d about changes after this operation. */
204 
205  /* Don't rebuild full tree in outliner, since we're just changing our view. */
207 
208  /* Request updates to be done. */
210 
211  UI_view2d_sync(vpd->screen, vpd->area, v2d, V2D_LOCK_COPY);
212  }
213 }
214 
216 {
217  ARegion *region = vpd->region;
218 
219  rcti inside_rect, outside_rect;
220  inside_rect = region->winrct;
221  outside_rect = region->winrct;
222  BLI_rcti_pad(&inside_rect, -vpd->inside_pad * U.widget_unit, -vpd->inside_pad * U.widget_unit);
223  BLI_rcti_pad(&outside_rect, vpd->outside_pad * U.widget_unit, vpd->outside_pad * U.widget_unit);
224 
225  /* Check if we can actually start the edge pan (e.g. adding nodes outside the view will start
226  * disabled). */
227  if (BLI_rcti_isect_pt_v(&inside_rect, xy)) {
228  /* We are inside once, can start. */
229  vpd->enabled = true;
230  }
231 
232  rctf *cur = &vpd->v2d->cur;
233  rctf *limit = &vpd->limit;
234 
235  int pan_dir_x = 0;
236  int pan_dir_y = 0;
237  if (vpd->enabled && ((vpd->outside_pad == 0) || BLI_rcti_isect_pt_v(&outside_rect, xy))) {
238  /* Find whether the mouse is beyond X and Y edges. */
239  if (xy[0] > inside_rect.xmax && cur->xmax < limit->xmax) {
240  pan_dir_x = 1;
241  }
242  else if (xy[0] < inside_rect.xmin && cur->xmin > limit->xmin) {
243  pan_dir_x = -1;
244  }
245  if (xy[1] > inside_rect.ymax && cur->ymax < limit->ymax) {
246  pan_dir_y = 1;
247  }
248  else if (xy[1] < inside_rect.ymin && cur->ymin > limit->ymin) {
249  pan_dir_y = -1;
250  }
251  }
252 
253  const double current_time = PIL_check_seconds_timer();
254  edge_pan_manage_delay_timers(vpd, pan_dir_x, pan_dir_y, current_time);
255 
256  /* Calculate the delta since the last time the operator was called. */
257  const float dtime = (float)(current_time - vpd->edge_pan_last_time);
258  float dx = 0.0f, dy = 0.0f;
259  if (pan_dir_x != 0) {
260  const float speed = edge_pan_speed(vpd, xy[0], true, current_time);
261  dx = dtime * speed * (float)pan_dir_x;
262  }
263  if (pan_dir_y != 0) {
264  const float speed = edge_pan_speed(vpd, xy[1], false, current_time);
265  dy = dtime * speed * (float)pan_dir_y;
266  }
267  vpd->edge_pan_last_time = current_time;
268 
269  /* Pan, clamping inside the regions total bounds. */
270  edge_pan_apply_delta(C, vpd, dx, dy);
271 }
272 
274 {
275  /* Only mouse-move events matter here, ignore others. */
276  if (event->type != MOUSEMOVE) {
277  return;
278  }
279 
280  UI_view2d_edge_pan_apply(C, vpd, event->xy);
281 }
282 
284 {
285  View2D *v2d = vpd->v2d;
286  if (!v2d) {
287  return;
288  }
289 
290  v2d->cur = vpd->initial_rect;
291 
292  /* Inform v2d about changes after this operation. */
294 
295  /* Don't rebuild full tree in outliner, since we're just changing our view. */
297 
298  /* Request updates to be done. */
300 
301  UI_view2d_sync(vpd->screen, vpd->area, v2d, V2D_LOCK_COPY);
302 }
303 
305 {
306  /* Default values for edge panning operators. */
308  /*inside_pad*/ 1.0f,
309  /*outside_pad*/ 0.0f,
310  /*speed_ramp*/ 1.0f,
311  /*max_speed*/ 500.0f,
312  /*delay*/ 1.0f,
313  /*zoom_influence*/ 0.0f);
314 }
315 
317  float inside_pad,
318  float outside_pad,
319  float speed_ramp,
320  float max_speed,
321  float delay,
322  float zoom_influence)
323 {
325  ot->srna,
326  "inside_padding",
327  inside_pad,
328  0.0f,
329  100.0f,
330  "Inside Padding",
331  "Inside distance in UI units from the edge of the region within which to start panning",
332  0.0f,
333  100.0f);
335  ot->srna,
336  "outside_padding",
337  outside_pad,
338  0.0f,
339  100.0f,
340  "Outside Padding",
341  "Outside distance in UI units from the edge of the region at which to stop panning",
342  0.0f,
343  100.0f);
345  "speed_ramp",
346  speed_ramp,
347  0.0f,
348  100.0f,
349  "Speed Ramp",
350  "Width of the zone in UI units where speed increases with distance from the edge",
351  0.0f,
352  100.0f);
354  "max_speed",
355  max_speed,
356  0.0f,
357  10000.0f,
358  "Max Speed",
359  "Maximum speed in UI units per second",
360  0.0f,
361  10000.0f);
363  "delay",
364  delay,
365  0.0f,
366  10.0f,
367  "Delay",
368  "Delay in seconds before maximum speed is reached",
369  0.0f,
370  10.0f);
372  "zoom_influence",
373  zoom_influence,
374  0.0f,
375  1.0f,
376  "Zoom Influence",
377  "Influence of the zoom factor on scroll speed",
378  0.0f,
379  1.0f);
380 }
381 
383 {
385  vpd,
386  RNA_float_get(op->ptr, "inside_padding"),
387  RNA_float_get(op->ptr, "outside_padding"),
388  RNA_float_get(op->ptr, "speed_ramp"),
389  RNA_float_get(op->ptr, "max_speed"),
390  RNA_float_get(op->ptr, "delay"),
391  RNA_float_get(op->ptr, "zoom_influence"));
392 }
393 
typedef float(TangentPoint)[2]
struct ScrArea * CTX_wm_area(const bContext *C)
Definition: context.c:738
struct bScreen * CTX_wm_screen(const bContext *C)
Definition: context.c:733
struct ARegion * CTX_wm_region(const bContext *C)
Definition: context.c:749
struct wmWindow * CTX_wm_window(const bContext *C)
Definition: context.c:723
#define BLI_assert(a)
Definition: BLI_assert.h:46
#define BLI_assert_msg(a, msg)
Definition: BLI_assert.h:53
MINLINE float clamp_f(float value, float min, float max)
bool BLI_rcti_isect_pt_v(const struct rcti *rect, const int xy[2])
BLI_INLINE int BLI_rcti_size_y(const struct rcti *rct)
Definition: BLI_rect.h:190
void BLI_rcti_pad(struct rcti *rect, int pad_x, int pad_y)
Definition: rct.c:607
void BLI_rctf_init(struct rctf *rect, float xmin, float xmax, float ymin, float ymax)
Definition: rct.c:407
BLI_INLINE int BLI_rcti_size_x(const struct rcti *rct)
Definition: BLI_rect.h:186
BLI_INLINE float BLI_rctf_size_x(const struct rctf *rct)
Definition: BLI_rect.h:194
BLI_INLINE float BLI_rctf_size_y(const struct rctf *rct)
Definition: BLI_rect.h:198
#define CLAMPIS(a, b, c)
@ V2D_LOCKOFS_X
@ V2D_LOCKOFS_Y
void ED_region_tag_redraw_no_rebuild(struct ARegion *region)
Definition: area.c:674
Read Guarded memory(de)allocation.
Group Output data from inside of a node group A color picker Mix two input colors RGB to Convert a color s luminance to a grayscale value Generate a normal vector and a dot product Bright Control the brightness and contrast of the input color Vector Map an input vectors to used to fine tune the interpolation of the input Camera Retrieve information about the camera and how it relates to the current shading point s position CLAMP
Platform independent time functions.
#define C
Definition: RandGen.cpp:25
#define V2D_LOCK_COPY
Definition: UI_view2d.h:82
void UI_view2d_sync(struct bScreen *screen, struct ScrArea *area, struct View2D *v2dcur, int flag)
Definition: view2d.cc:851
void UI_view2d_curRect_changed(const struct bContext *C, struct View2D *v2d)
int pad[32 - sizeof(int)]
unsigned int U
Definition: btGjkEpa3.h:78
T distance(const T &a, const T &b)
float RNA_float_get(PointerRNA *ptr, const char *name)
Definition: rna_access.c:4957
PropertyRNA * RNA_def_float(StructOrFunctionRNA *cont_, const char *identifier, float default_value, float hardmin, float hardmax, const char *ui_name, const char *ui_description, float softmin, float softmax)
Definition: rna_define.c:3836
#define min(a, b)
Definition: sort.c:35
double edge_pan_last_time
Definition: UI_view2d.h:507
double edge_pan_start_time_x
Definition: UI_view2d.h:508
struct rctf limit
Definition: UI_view2d.h:476
struct bScreen * screen
Definition: UI_view2d.h:468
struct View2D * v2d
Definition: UI_view2d.h:474
float zoom_influence
Definition: UI_view2d.h:498
struct ScrArea * area
Definition: UI_view2d.h:470
struct ARegion * region
Definition: UI_view2d.h:472
double edge_pan_start_time_y
Definition: UI_view2d.h:508
short keepofs
float xmax
Definition: DNA_vec_types.h:69
float xmin
Definition: DNA_vec_types.h:69
float ymax
Definition: DNA_vec_types.h:70
float ymin
Definition: DNA_vec_types.h:70
int ymin
Definition: DNA_vec_types.h:64
int ymax
Definition: DNA_vec_types.h:64
int xmin
Definition: DNA_vec_types.h:63
int xmax
Definition: DNA_vec_types.h:63
int xy[2]
Definition: WM_types.h:682
short type
Definition: WM_types.h:678
struct StructRNA * srna
Definition: WM_types.h:969
struct PointerRNA * ptr
double PIL_check_seconds_timer(void)
Definition: time.c:64
float max
static void edge_pan_apply_delta(bContext *C, View2DEdgePanData *vpd, float dx, float dy)
static void edge_pan_manage_delay_timers(View2DEdgePanData *vpd, int pan_dir_x, int pan_dir_y, const double current_time)
void UI_view2d_edge_pan_operator_init(bContext *C, View2DEdgePanData *vpd, wmOperator *op)
void UI_view2d_edge_pan_set_limits(View2DEdgePanData *vpd, float xmin, float xmax, float ymin, float ymax)
void UI_view2d_edge_pan_cancel(bContext *C, View2DEdgePanData *vpd)
void UI_view2d_edge_pan_init(bContext *C, View2DEdgePanData *vpd, float inside_pad, float outside_pad, float speed_ramp, float max_speed, float delay, float zoom_influence)
static float edge_pan_speed(View2DEdgePanData *vpd, int event_loc, bool x_dir, const double current_time)
void UI_view2d_edge_pan_apply(bContext *C, View2DEdgePanData *vpd, const int xy[2])
void UI_view2d_edge_pan_operator_properties(wmOperatorType *ot)
static float smootherstep(const float domain_max, float x)
void UI_view2d_edge_pan_reset(View2DEdgePanData *vpd)
bool UI_view2d_edge_pan_poll(bContext *C)
void UI_view2d_edge_pan_operator_properties_ex(struct wmOperatorType *ot, float inside_pad, float outside_pad, float speed_ramp, float max_speed, float delay, float zoom_influence)
void UI_view2d_edge_pan_apply_event(bContext *C, View2DEdgePanData *vpd, const wmEvent *event)
int xy[2]
Definition: wm_draw.c:135
void WM_event_add_mousemove(wmWindow *win)
@ MOUSEMOVE
wmOperatorType * ot
Definition: wm_files.c:3479