Blender  V3.3
transform_mode_resize.c
Go to the documentation of this file.
1 /* SPDX-License-Identifier: GPL-2.0-or-later
2  * Copyright 2001-2002 NaN Holding BV. All rights reserved. */
3 
8 #include <stdlib.h>
9 
10 #include "BLI_math.h"
11 #include "BLI_task.h"
12 
13 #include "BKE_context.h"
14 #include "BKE_image.h"
15 #include "BKE_unit.h"
16 
17 #include "ED_screen.h"
18 
19 #include "UI_interface.h"
20 
21 #include "transform.h"
22 #include "transform_constraints.h"
23 #include "transform_convert.h"
24 #include "transform_mode.h"
25 #include "transform_snap.h"
26 
27 /* -------------------------------------------------------------------- */
32  const TransInfo *t;
34  float mat[3][3];
35 };
36 
37 static void element_resize_fn(void *__restrict iter_data_v,
38  const int iter,
39  const TaskParallelTLS *__restrict UNUSED(tls))
40 {
41  struct ElemResizeData *data = iter_data_v;
42  TransData *td = &data->tc->data[iter];
43  if (td->flag & TD_SKIP) {
44  return;
45  }
46  ElementResize(data->t, data->tc, td, data->mat);
47 }
48 
51 /* -------------------------------------------------------------------- */
55 static float ResizeBetween(TransInfo *t, const float p1[3], const float p2[3])
56 {
57  float d1[3], d2[3], len_d1;
58 
59  sub_v3_v3v3(d1, p1, t->center_global);
60  sub_v3_v3v3(d2, p2, t->center_global);
61 
62  if (t->con.applyRot != NULL && (t->con.mode & CON_APPLY)) {
63  mul_m3_v3(t->con.pmtx, d1);
64  mul_m3_v3(t->con.pmtx, d2);
65  }
66 
67  project_v3_v3v3(d1, d1, d2);
68 
69  len_d1 = len_v3(d1);
70 
71  /* Use 'invalid' dist when `center == p1` (after projecting),
72  * in this case scale will _never_ move the point in relation to the center,
73  * so it makes no sense to take it into account when scaling. see: T46503 */
74  return len_d1 != 0.0f ? len_v3(d2) / len_d1 : TRANSFORM_DIST_INVALID;
75 }
76 
77 static void ApplySnapResize(TransInfo *t, float vec[3])
78 {
79  float point[3];
81 
82  float dist = ResizeBetween(t, t->tsnap.snapTarget, point);
83  if (dist != TRANSFORM_DIST_INVALID) {
84  copy_v3_fl(vec, dist);
85  }
86 }
87 
94 static void constrain_scale_to_boundary(const float numerator,
95  const float denominator,
96  float *scale)
97 {
98  if (denominator == 0.0f) {
99  /* The origin of the scale is on the edge of the boundary. */
100  if (numerator < 0.0f) {
101  /* Negative scale will wrap around and put us outside the boundary. */
102  *scale = 0.0f; /* Hold at the boundary instead. */
103  }
104  return; /* Nothing else we can do without more info. */
105  }
106 
107  const float correction = numerator / denominator;
108  if (correction < 0.0f || !isfinite(correction)) {
109  /* TODO: Correction is negative or invalid, but we lack context to fix `*scale`. */
110  return;
111  }
112 
113  if (denominator < 0.0f) {
114  /* Scale origin is outside boundary, only make scale bigger. */
115  if (*scale < correction) {
116  *scale = correction;
117  }
118  return;
119  }
120 
121  /* Scale origin is inside boundary, the "regular" case, limit maximum scale. */
122  if (*scale > correction) {
123  *scale = correction;
124  }
125 }
126 
127 static bool clip_uv_transform_resize(TransInfo *t, float vec[2])
128 {
129  /* Check if the current image in UV editor is a tiled image or not. */
130  const SpaceImage *sima = t->area->spacedata.first;
131  const Image *image = sima->image;
132  const bool is_tiled_image = image && (image->source == IMA_SRC_TILED);
133 
134  /* Stores the coordinates of the closest UDIM tile.
135  * Also acts as an offset to the tile from the origin of UV space. */
136  float base_offset[2] = {0.0f, 0.0f};
137 
138  /* If tiled image then constrain to correct/closest UDIM tile, else 0-1 UV space. */
139  if (is_tiled_image) {
140  BKE_image_find_nearest_tile_with_offset(image, t->center_global, base_offset);
141  }
142 
143  /* Assume no change is required. */
144  float scale = 1.0f;
145 
146  /* Are we scaling U and V together, or just one axis? */
147  const bool adjust_u = !(t->con.mode & CON_AXIS1);
148  const bool adjust_v = !(t->con.mode & CON_AXIS0);
149  const bool use_local_center = transdata_check_local_center(t, t->around);
151  for (TransData *td = tc->data; td < tc->data + tc->data_len; td++) {
152 
153  /* Get scale origin. */
154  const float *scale_origin = use_local_center ? td->center : t->center_global;
155 
156  /* Alias td->loc as min and max just in case we need to optimize later. */
157  const float *min = td->loc;
158  const float *max = td->loc;
159 
160  if (adjust_u) {
161  /* Update U against the left border. */
163  scale_origin[0] - base_offset[0], scale_origin[0] - min[0], &scale);
164 
165  /* Now the right border, negated, because `-1.0 / -1.0 = 1.0` */
167  base_offset[0] + t->aspect[0] - scale_origin[0], max[0] - scale_origin[0], &scale);
168  }
169 
170  /* Do the same for the V co-ordinate. */
171  if (adjust_v) {
173  scale_origin[1] - base_offset[1], scale_origin[1] - min[1], &scale);
174 
176  base_offset[1] + t->aspect[1] - scale_origin[1], max[1] - scale_origin[1], &scale);
177  }
178  }
179  }
180  vec[0] *= scale;
181  vec[1] *= scale;
182  return scale != 1.0f;
183 }
184 
185 static void applyResize(TransInfo *t, const int UNUSED(mval[2]))
186 {
187  float mat[3][3];
188  int i;
189  char str[UI_MAX_DRAW_STR];
190 
191  if (t->flag & T_INPUT_IS_VALUES_FINAL) {
192  copy_v3_v3(t->values_final, t->values);
193  }
194  else {
195  float ratio = t->values[0];
196 
197  copy_v3_fl(t->values_final, ratio);
198  add_v3_v3(t->values_final, t->values_modal_offset);
199 
200  transform_snap_increment(t, t->values_final);
201 
202  if (applyNumInput(&t->num, t->values_final)) {
203  constraintNumInput(t, t->values_final);
204  }
205 
206  applySnappingAsGroup(t, t->values_final);
207  }
208 
209  size_to_mat3(mat, t->values_final);
210  if (t->con.mode & CON_APPLY) {
211  t->con.applySize(t, NULL, NULL, mat);
212 
213  /* Only so we have re-usable value with redo. */
214  float pvec[3] = {0.0f, 0.0f, 0.0f};
215  int j = 0;
216  for (i = 0; i < 3; i++) {
217  if (!(t->con.mode & (CON_AXIS0 << i))) {
218  t->values_final[i] = 1.0f;
219  }
220  else {
221  pvec[j++] = t->values_final[i];
222  }
223  }
224  headerResize(t, pvec, str, sizeof(str));
225  }
226  else {
227  headerResize(t, t->values_final, str, sizeof(str));
228  }
229 
230  copy_m3_m3(t->mat, mat); /* used in gizmo */
231 
233 
235  TransData *td = tc->data;
236  for (i = 0; i < tc->data_len; i++, td++) {
237  if (td->flag & TD_SKIP) {
238  continue;
239  }
240 
241  ElementResize(t, tc, td, mat);
242  }
243  }
244  else {
245  struct ElemResizeData data = {
246  .t = t,
247  .tc = tc,
248  };
249  copy_m3_m3(data.mat, mat);
250 
251  TaskParallelSettings settings;
254  }
255  }
256 
257  /* Evil hack - redo resize if clipping needed. */
258  if (t->flag & T_CLIP_UV && clip_uv_transform_resize(t, t->values_final)) {
259  size_to_mat3(mat, t->values_final);
260 
261  if (t->con.mode & CON_APPLY) {
262  t->con.applySize(t, NULL, NULL, mat);
263  }
264 
266  TransData *td = tc->data;
267  for (i = 0; i < tc->data_len; i++, td++) {
268  ElementResize(t, tc, td, mat);
269  }
270 
271  /* In proportional edit it can happen that */
272  /* vertices in the radius of the brush end */
273  /* outside the clipping area */
274  /* XXX HACK - dg */
275  if (t->flag & T_PROP_EDIT) {
276  clipUVData(t);
277  }
278  }
279  }
280 
281  recalcData(t);
282 
283  ED_area_status_text(t->area, str);
284 }
285 
286 void initResize(TransInfo *t, float mouse_dir_constraint[3])
287 {
288  t->mode = TFM_RESIZE;
289  t->transform = applyResize;
290  t->tsnap.applySnap = ApplySnapResize;
291  t->tsnap.distance = ResizeBetween;
292 
293  if (is_zero_v3(mouse_dir_constraint)) {
295  }
296  else {
297  int mval_start[2], mval_end[2];
298  float mval_dir[3], t_mval[2];
299  float viewmat[3][3];
300 
301  copy_m3_m4(viewmat, t->viewmat);
302  mul_v3_m3v3(mval_dir, viewmat, mouse_dir_constraint);
303  normalize_v2(mval_dir);
304  if (is_zero_v2(mval_dir)) {
305  /* The screen space direction is orthogonal to the view.
306  * Fall back to constraining on the Y axis. */
307  mval_dir[0] = 0;
308  mval_dir[1] = 1;
309  }
310 
311  mval_start[0] = t->center2d[0];
312  mval_start[1] = t->center2d[1];
313 
314  t_mval[0] = t->mval[0] - mval_start[0];
315  t_mval[1] = t->mval[1] - mval_start[1];
316  project_v2_v2v2(mval_dir, t_mval, mval_dir);
317 
318  mval_end[0] = t->center2d[0] + mval_dir[0];
319  mval_end[1] = t->center2d[1] + mval_dir[1];
320 
321  setCustomPoints(t, &t->mouse, mval_end, mval_start);
322 
324  }
325 
326  t->flag |= T_NULL_ONE;
327  t->num.val_flag[0] |= NUM_NULL_ONE;
328  t->num.val_flag[1] |= NUM_NULL_ONE;
329  t->num.val_flag[2] |= NUM_NULL_ONE;
330  t->num.flag |= NUM_AFFECT_ALL;
331  if ((t->flag & T_EDIT) == 0) {
332 #ifdef USE_NUM_NO_ZERO
333  t->num.val_flag[0] |= NUM_NO_ZERO;
334  t->num.val_flag[1] |= NUM_NO_ZERO;
335  t->num.val_flag[2] |= NUM_NO_ZERO;
336 #endif
337  }
338 
339  t->idx_max = 2;
340  t->num.idx_max = 2;
341  t->snap[0] = 0.1f;
342  t->snap[1] = t->snap[0] * 0.1f;
343 
344  copy_v3_fl(t->num.val_inc, t->snap[0]);
345  t->num.unit_sys = t->scene->unit.system;
346  t->num.unit_type[0] = B_UNIT_NONE;
347  t->num.unit_type[1] = B_UNIT_NONE;
348  t->num.unit_type[2] = B_UNIT_NONE;
349 
351 }
352 
int BKE_image_find_nearest_tile_with_offset(const struct Image *image, const float co[2], float r_uv_offset[2]) ATTR_NONNULL(1
@ B_UNIT_NONE
Definition: BKE_unit.h:100
void mul_m3_v3(const float M[3][3], float r[3])
Definition: math_matrix.c:926
void size_to_mat3(float R[3][3], const float size[3])
Definition: math_matrix.c:2098
void copy_m3_m3(float m1[3][3], const float m2[3][3])
Definition: math_matrix.c:71
void copy_m3_m4(float m1[3][3], const float m2[4][4])
Definition: math_matrix.c:87
void mul_v3_m3v3(float r[3], const float M[3][3], const float a[3])
Definition: math_matrix.c:897
MINLINE void sub_v3_v3v3(float r[3], const float a[3], const float b[3])
MINLINE void copy_v3_v3(float r[3], const float a[3])
MINLINE bool is_zero_v3(const float a[3]) ATTR_WARN_UNUSED_RESULT
void project_v3_v3v3(float out[3], const float p[3], const float v_proj[3])
Definition: math_vector.c:600
MINLINE bool is_zero_v2(const float a[2]) ATTR_WARN_UNUSED_RESULT
MINLINE void copy_v3_fl(float r[3], float f)
MINLINE float normalize_v2(float r[2])
MINLINE void add_v3_v3(float r[3], const float a[3])
void project_v2_v2v2(float out[2], const float p[2], const float v_proj[2])
Definition: math_vector.c:589
MINLINE float len_v3(const float a[3]) ATTR_WARN_UNUSED_RESULT
void BLI_task_parallel_range(int start, int stop, void *userdata, TaskParallelRangeFunc func, const TaskParallelSettings *settings)
Definition: task_range.cc:94
BLI_INLINE void BLI_parallel_range_settings_defaults(TaskParallelSettings *settings)
Definition: BLI_task.h:293
#define UNUSED(x)
@ IMA_SRC_TILED
@ V3D_ORIENT_GLOBAL
@ NUM_NULL_ONE
Definition: ED_numinput.h:55
@ NUM_NO_ZERO
Definition: ED_numinput.h:57
@ NUM_AFFECT_ALL
Definition: ED_numinput.h:48
bool applyNumInput(NumInput *n, float *vec)
Definition: numinput.c:189
void ED_area_status_text(ScrArea *area, const char *str)
Definition: area.c:792
@ TFM_RESIZE
Definition: ED_transform.h:32
_GL_VOID GLfloat value _GL_VOID_RET _GL_VOID const GLuint GLboolean *residences _GL_BOOL_RET _GL_VOID GLsizei GLfloat GLfloat GLfloat GLfloat const GLubyte *bitmap _GL_VOID_RET _GL_VOID GLenum const void *lists _GL_VOID_RET _GL_VOID const GLdouble *equation _GL_VOID_RET _GL_VOID GLdouble GLdouble blue _GL_VOID_RET _GL_VOID GLfloat GLfloat blue _GL_VOID_RET _GL_VOID GLint GLint blue _GL_VOID_RET _GL_VOID GLshort GLshort blue _GL_VOID_RET _GL_VOID GLubyte GLubyte blue _GL_VOID_RET _GL_VOID GLuint GLuint blue _GL_VOID_RET _GL_VOID GLushort GLushort blue _GL_VOID_RET _GL_VOID GLbyte GLbyte GLbyte alpha _GL_VOID_RET _GL_VOID GLdouble GLdouble GLdouble alpha _GL_VOID_RET _GL_VOID GLfloat GLfloat GLfloat alpha _GL_VOID_RET _GL_VOID GLint GLint GLint alpha _GL_VOID_RET _GL_VOID GLshort GLshort GLshort alpha _GL_VOID_RET _GL_VOID GLubyte GLubyte GLubyte alpha _GL_VOID_RET _GL_VOID GLuint GLuint GLuint alpha _GL_VOID_RET _GL_VOID GLushort GLushort GLushort alpha _GL_VOID_RET _GL_VOID GLenum mode _GL_VOID_RET _GL_VOID GLint GLsizei GLsizei GLenum type _GL_VOID_RET _GL_VOID GLsizei GLenum GLenum const void *pixels _GL_VOID_RET _GL_VOID const void *pointer _GL_VOID_RET _GL_VOID GLdouble v _GL_VOID_RET _GL_VOID GLfloat v _GL_VOID_RET _GL_VOID GLint GLint i2 _GL_VOID_RET _GL_VOID GLint j _GL_VOID_RET _GL_VOID GLfloat param _GL_VOID_RET _GL_VOID GLint param _GL_VOID_RET _GL_VOID GLdouble GLdouble GLdouble GLdouble GLdouble zFar _GL_VOID_RET _GL_UINT GLdouble *equation _GL_VOID_RET _GL_VOID GLenum GLint *params _GL_VOID_RET _GL_VOID GLenum GLfloat *v _GL_VOID_RET _GL_VOID GLenum GLfloat *params _GL_VOID_RET _GL_VOID GLfloat *values _GL_VOID_RET _GL_VOID GLushort *values _GL_VOID_RET _GL_VOID GLenum GLfloat *params _GL_VOID_RET _GL_VOID GLenum GLdouble *params _GL_VOID_RET _GL_VOID GLenum GLint *params _GL_VOID_RET _GL_VOID GLsizei const void *pointer _GL_VOID_RET _GL_VOID GLsizei const void *pointer _GL_VOID_RET _GL_BOOL GLfloat param _GL_VOID_RET _GL_VOID GLint param _GL_VOID_RET _GL_VOID GLenum GLfloat param _GL_VOID_RET _GL_VOID GLenum GLint param _GL_VOID_RET _GL_VOID GLushort pattern _GL_VOID_RET _GL_VOID GLdouble GLdouble GLint GLint const GLdouble *points _GL_VOID_RET _GL_VOID GLdouble GLdouble GLint GLint GLdouble GLdouble GLint GLint const GLdouble *points _GL_VOID_RET _GL_VOID GLdouble GLdouble u2 _GL_VOID_RET _GL_VOID GLdouble GLdouble GLint GLdouble GLdouble v2 _GL_VOID_RET _GL_VOID GLenum GLfloat param _GL_VOID_RET _GL_VOID GLenum GLint param _GL_VOID_RET _GL_VOID GLenum mode _GL_VOID_RET _GL_VOID GLdouble GLdouble nz _GL_VOID_RET _GL_VOID GLfloat GLfloat nz _GL_VOID_RET _GL_VOID GLint GLint nz _GL_VOID_RET _GL_VOID GLshort GLshort nz _GL_VOID_RET _GL_VOID GLsizei const void *pointer _GL_VOID_RET _GL_VOID GLsizei const GLfloat *values _GL_VOID_RET _GL_VOID GLsizei const GLushort *values _GL_VOID_RET _GL_VOID GLint param _GL_VOID_RET _GL_VOID const GLuint const GLclampf *priorities _GL_VOID_RET _GL_VOID GLdouble y _GL_VOID_RET _GL_VOID GLfloat y _GL_VOID_RET _GL_VOID GLint y _GL_VOID_RET _GL_VOID GLshort y _GL_VOID_RET _GL_VOID GLdouble GLdouble z _GL_VOID_RET _GL_VOID GLfloat GLfloat z _GL_VOID_RET _GL_VOID GLint GLint z _GL_VOID_RET _GL_VOID GLshort GLshort z _GL_VOID_RET _GL_VOID GLdouble GLdouble GLdouble w _GL_VOID_RET _GL_VOID GLfloat GLfloat GLfloat w _GL_VOID_RET _GL_VOID GLint GLint GLint w _GL_VOID_RET _GL_VOID GLshort GLshort GLshort w _GL_VOID_RET _GL_VOID GLdouble GLdouble GLdouble y2 _GL_VOID_RET _GL_VOID GLfloat GLfloat GLfloat y2 _GL_VOID_RET _GL_VOID GLint GLint GLint y2 _GL_VOID_RET _GL_VOID GLshort GLshort GLshort y2 _GL_VOID_RET _GL_VOID GLdouble GLdouble GLdouble z _GL_VOID_RET _GL_VOID GLdouble GLdouble z _GL_VOID_RET _GL_VOID GLuint *buffer _GL_VOID_RET _GL_VOID GLdouble t _GL_VOID_RET _GL_VOID GLfloat t _GL_VOID_RET _GL_VOID GLint t _GL_VOID_RET _GL_VOID GLshort t _GL_VOID_RET _GL_VOID GLdouble t
in reality light always falls off quadratically Particle Retrieve the data of the particle that spawned the object for example to give variation to multiple instances of an object Point Retrieve information about points in a point cloud Retrieve the edges of an object as it appears to Cycles topology will always appear triangulated Convert a blackbody temperature to an RGB value Normal Generate a perturbed normal from an RGB normal map image Typically used for faking highly detailed surfaces Generate an OSL shader from a file or text data block Image Sample an image file as a texture Sky Generate a procedural sky texture Noise Generate fractal Perlin noise Wave Generate procedural bands or rings with noise Voronoi Generate Worley noise based on the distance to random points Typically used to generate textures such as or biological cells Brick Generate a procedural texture producing bricks Texture Retrieve multiple types of texture coordinates nTypically used as inputs for texture nodes Vector Convert a point
#define UI_MAX_DRAW_STR
Definition: UI_interface.h:91
depth_tx normal_tx diffuse_light_tx specular_light_tx volume_light_tx environment_tx ambient_occlusion_tx aov_value_tx in_weight_img image(1, GPU_R32F, Qualifier::WRITE, ImageType::FLOAT_2D_ARRAY, "out_weight_img") .image(3
#define str(s)
bool isfinite(uchar)
Definition: scene/image.cpp:31
#define min(a, b)
Definition: sort.c:35
void setCustomPoints(TransInfo *t, MouseInput *mi, const int start[2], const int end[2])
void initMouseInputMode(TransInfo *t, MouseInput *mi, MouseInputMode mode)
#define FOREACH_TRANS_DATA_CONTAINER(t, th)
const TransInfo * t
const TransDataContainer * tc
struct Image * image
void constraintNumInput(TransInfo *t, float vec[3])
void recalcData(TransInfo *t)
void clipUVData(TransInfo *t)
conversion and adaptation of different datablocks to a common struct.
@ TD_SKIP
#define TRANSDATA_THREAD_LIMIT
float max
bool transdata_check_local_center(const TransInfo *t, short around)
void ElementResize(const TransInfo *t, const TransDataContainer *tc, TransData *td, const float mat[3][3])
void transform_mode_default_modal_orientation_set(TransInfo *t, int type)
void headerResize(TransInfo *t, const float vec[3], char *str, const int str_size)
transform modes used by different operators.
static void ApplySnapResize(TransInfo *t, float vec[3])
void initResize(TransInfo *t, float mouse_dir_constraint[3])
static void applyResize(TransInfo *t, const int UNUSED(mval[2]))
static void element_resize_fn(void *__restrict iter_data_v, const int iter, const TaskParallelTLS *__restrict UNUSED(tls))
static bool clip_uv_transform_resize(TransInfo *t, float vec[2])
static float ResizeBetween(TransInfo *t, const float p1[3], const float p2[3])
static void constrain_scale_to_boundary(const float numerator, const float denominator, float *scale)
bool transform_snap_increment(const TransInfo *t, float *r_val)
void applySnappingAsGroup(TransInfo *t, float *vec)
void getSnapPoint(const TransInfo *t, float vec[3])