Blender V4.3
transform_mode_resize.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2001-2002 NaN Holding BV. All rights reserved.
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
8
9#include <cstdlib>
10
12
13#include "BLI_math_matrix.h"
14#include "BLI_math_vector.h"
15#include "BLI_task.h"
16
17#include "BKE_image.hh"
18#include "BKE_unit.hh"
19
20#include "ED_screen.hh"
21
22#include "RNA_access.hh"
23
24#include "UI_interface.hh"
25
26#include "transform.hh"
28#include "transform_convert.hh"
29#include "transform_mode.hh"
30#include "transform_snap.hh"
31
32using namespace blender;
33
34/* -------------------------------------------------------------------- */
37
39 const TransInfo *t;
41 float mat[3][3];
42};
43
44static void element_resize_fn(void *__restrict iter_data_v,
45 const int iter,
46 const TaskParallelTLS *__restrict /*tls*/)
47{
48 ElemResizeData *data = static_cast<ElemResizeData *>(iter_data_v);
49 TransData *td = &data->tc->data[iter];
50 if (td->flag & TD_SKIP) {
51 return;
52 }
53 ElementResize(data->t, data->tc, td, data->mat);
54}
55
57
58/* -------------------------------------------------------------------- */
61
62static float ResizeBetween(TransInfo *t, const float p1[3], const float p2[3])
63{
64 float d1[3], d2[3], len_d1;
65
66 sub_v3_v3v3(d1, p1, t->center_global);
67 sub_v3_v3v3(d2, p2, t->center_global);
68
69 if (t->con.applyRot != nullptr && (t->con.mode & CON_APPLY)) {
70 mul_m3_v3(t->con.pmtx, d1);
71 mul_m3_v3(t->con.pmtx, d2);
72 }
73
74 project_v3_v3v3(d1, d1, d2);
75
76 len_d1 = len_v3(d1);
77
78 /* Use 'invalid' dist when `center == p1` (after projecting),
79 * in this case scale will _never_ move the point in relation to the center,
80 * so it makes no sense to take it into account when scaling. see: #46503 */
81 return len_d1 != 0.0f ? len_v3(d2) / len_d1 : TRANSFORM_DIST_INVALID;
82}
83
84static void ApplySnapResize(TransInfo *t, float vec[3])
85{
86 float point[3];
88
89 float dist = ResizeBetween(t, t->tsnap.snap_source, point);
90 if (dist != TRANSFORM_DIST_INVALID) {
91 copy_v3_fl(vec, dist);
92 }
93}
94
101static void constrain_scale_to_boundary(const float numerator,
102 const float denominator,
103 float *scale)
104{
105 /* It's possible the numerator or denominator can be very close to zero due to so-called
106 * "catastrophic cancellation". See #102923 for an example. We use epsilon tests here to
107 * distinguish between genuine negative coordinates versus coordinates that should be rounded off
108 * to zero. */
109 const float epsilon = 0.25f / 65536.0f; /* A quarter of a texel on a 65536 x 65536 texture. */
110 if (fabsf(denominator) < epsilon) {
111 /* The origin of the scale is very near the edge of the boundary. */
112 if (numerator < -epsilon) {
113 /* Negative scale will wrap around and put us outside the boundary. */
114 *scale = 0.0f; /* Hold at the boundary instead. */
115 }
116 return; /* Nothing else we can do without more info. */
117 }
118
119 const float correction = numerator / denominator;
120 if (correction < 0.0f || !isfinite(correction)) {
121 /* TODO: Correction is negative or invalid, but we lack context to fix `*scale`. */
122 return;
123 }
124
125 if (denominator < 0.0f) {
126 /* Scale origin is outside boundary, only make scale bigger. */
127 if (*scale < correction) {
128 *scale = correction;
129 }
130 return;
131 }
132
133 /* Scale origin is inside boundary, the "regular" case, limit maximum scale. */
134 if (*scale > correction) {
135 *scale = correction;
136 }
137}
138
139static bool clip_uv_transform_resize(TransInfo *t, float vec[2])
140{
141
142 /* Stores the coordinates of the closest UDIM tile.
143 * Also acts as an offset to the tile from the origin of UV space. */
144 float base_offset[2] = {0.0f, 0.0f};
145
146 /* If tiled image then constrain to correct/closest UDIM tile, else 0-1 UV space. */
147 const SpaceImage *sima = static_cast<const SpaceImage *>(t->area->spacedata.first);
149
150 /* Assume no change is required. */
151 float scale = 1.0f;
152
153 /* Are we scaling U and V together, or just one axis? */
154 const bool adjust_u = !(t->con.mode & CON_AXIS1);
155 const bool adjust_v = !(t->con.mode & CON_AXIS0);
156 const bool use_local_center = transdata_check_local_center(t, t->around);
158 for (TransData *td = tc->data; td < tc->data + tc->data_len; td++) {
159
160 /* Get scale origin. */
161 const float *scale_origin = use_local_center ? td->center : t->center_global;
162
163 /* Alias td->loc as min and max just in case we need to optimize later. */
164 const float *min = td->loc;
165 const float *max = td->loc;
166
167 if (adjust_u) {
168 /* Update U against the left border. */
170 scale_origin[0] - base_offset[0], scale_origin[0] - min[0], &scale);
171
172 /* Now the right border, negated, because `-1.0 / -1.0 = 1.0`. */
174 base_offset[0] + t->aspect[0] - scale_origin[0], max[0] - scale_origin[0], &scale);
175 }
176
177 /* Do the same for the V co-ordinate. */
178 if (adjust_v) {
180 scale_origin[1] - base_offset[1], scale_origin[1] - min[1], &scale);
181
183 base_offset[1] + t->aspect[1] - scale_origin[1], max[1] - scale_origin[1], &scale);
184 }
185 }
186 }
187 vec[0] *= scale;
188 vec[1] *= scale;
189 return scale != 1.0f;
190}
191
192static void applyResize(TransInfo *t)
193{
194 float mat[3][3];
195 int i;
196 char str[UI_MAX_DRAW_STR];
197
198 if (t->flag & T_INPUT_IS_VALUES_FINAL) {
200 }
201 else {
202 float ratio = t->values[0];
203
204 copy_v3_fl(t->values_final, ratio);
206
208
209 if (applyNumInput(&t->num, t->values_final)) {
211 }
212
214 }
215
216 size_to_mat3(mat, t->values_final);
217 if (t->con.mode & CON_APPLY) {
218 t->con.applySize(t, nullptr, nullptr, mat);
219
220 /* Only so we have re-usable value with redo. */
221 float pvec[3] = {0.0f, 0.0f, 0.0f};
222 int j = 0;
223 for (i = 0; i < 3; i++) {
224 if (!(t->con.mode & (CON_AXIS0 << i))) {
225 t->values_final[i] = 1.0f;
226 }
227 else {
228 pvec[j++] = t->values_final[i];
229 }
230 }
231 headerResize(t, pvec, str, sizeof(str));
232 }
233 else {
234 headerResize(t, t->values_final, str, sizeof(str));
235 }
236
237 copy_m3_m3(t->mat, mat); /* Used in gizmo. */
238
240
241 if (tc->data_len < TRANSDATA_THREAD_LIMIT) {
242 TransData *td = tc->data;
243 for (i = 0; i < tc->data_len; i++, td++) {
244 if (td->flag & TD_SKIP) {
245 continue;
246 }
247
248 ElementResize(t, tc, td, mat);
249 }
250 }
251 else {
253 data.t = t;
254 data.tc = tc;
255 copy_m3_m3(data.mat, mat);
256
257 TaskParallelSettings settings;
259 BLI_task_parallel_range(0, tc->data_len, &data, element_resize_fn, &settings);
260 }
261 }
262
263 /* Evil hack - redo resize if clipping needed. */
265 size_to_mat3(mat, t->values_final);
266
267 if (t->con.mode & CON_APPLY) {
268 t->con.applySize(t, nullptr, nullptr, mat);
269 }
270
272 TransData *td = tc->data;
273 for (i = 0; i < tc->data_len; i++, td++) {
274 ElementResize(t, tc, td, mat);
275 }
276
277 /* Not ideal, see #clipUVData code-comment. */
278 if (t->flag & T_PROP_EDIT) {
279 clipUVData(t);
280 }
281 }
282 }
283
284 recalc_data(t);
285
287}
288
289static void resize_transform_matrix_fn(TransInfo *t, float mat_xform[4][4])
290{
291 float mat4[4][4];
292 copy_m4_m3(mat4, t->mat);
294 mul_m4_m4m4(mat_xform, mat4, mat_xform);
295}
296
297static void initResize(TransInfo *t, wmOperator *op)
298{
299 float mouse_dir_constraint[3];
300 if (op) {
301 PropertyRNA *prop = RNA_struct_find_property(op->ptr, "mouse_dir_constraint");
302 if (prop) {
303 RNA_property_float_get_array(op->ptr, prop, mouse_dir_constraint);
304 }
305 else {
306 /* Resize is expected to have this property. */
307 BLI_assert(!STREQ(op->idname, "TRANSFORM_OT_resize"));
308 }
309 }
310 else {
311 zero_v3(mouse_dir_constraint);
312 }
313
314 if (is_zero_v3(mouse_dir_constraint)) {
316 }
317 else {
318 int mval_start[2], mval_end[2];
319 float mval_dir[3];
320 float viewmat[3][3];
321
322 copy_m3_m4(viewmat, t->viewmat);
323 mul_v3_m3v3(mval_dir, viewmat, mouse_dir_constraint);
324 normalize_v2(mval_dir);
325 if (is_zero_v2(mval_dir)) {
326 /* The screen space direction is orthogonal to the view.
327 * Fall back to constraining on the Y axis. */
328 mval_dir[0] = 0;
329 mval_dir[1] = 1;
330 }
331
332 mval_start[0] = t->center2d[0];
333 mval_start[1] = t->center2d[1];
334
335 float2 t_mval = t->mval - float2(t->center2d);
336 project_v2_v2v2(mval_dir, t_mval, mval_dir);
337
338 mval_end[0] = t->center2d[0] + mval_dir[0];
339 mval_end[1] = t->center2d[1] + mval_dir[1];
340
341 setCustomPoints(t, &t->mouse, mval_end, mval_start);
342
344 }
345
346 t->num.val_flag[0] |= NUM_NULL_ONE;
347 t->num.val_flag[1] |= NUM_NULL_ONE;
348 t->num.val_flag[2] |= NUM_NULL_ONE;
349 t->num.flag |= NUM_AFFECT_ALL;
350 if ((t->flag & T_EDIT) == 0) {
351#ifdef USE_NUM_NO_ZERO
352 t->num.val_flag[0] |= NUM_NO_ZERO;
353 t->num.val_flag[1] |= NUM_NO_ZERO;
354 t->num.val_flag[2] |= NUM_NO_ZERO;
355#endif
356 }
357
358 t->idx_max = 2;
359 t->num.idx_max = 2;
360 t->snap[0] = 0.1f;
361 t->snap[1] = t->snap[0] * 0.1f;
362
363 copy_v3_fl(t->num.val_inc, t->snap[0]);
364 t->num.unit_sys = t->scene->unit.system;
365 t->num.unit_type[0] = B_UNIT_NONE;
366 t->num.unit_type[1] = B_UNIT_NONE;
367 t->num.unit_type[2] = B_UNIT_NONE;
368
370}
371
373
375 /*flags*/ T_NULL_ONE,
376 /*init_fn*/ initResize,
377 /*transform_fn*/ applyResize,
378 /*transform_matrix_fn*/ resize_transform_matrix_fn,
379 /*handle_event_fn*/ nullptr,
380 /*snap_distance_fn*/ ResizeBetween,
381 /*snap_apply_fn*/ ApplySnapResize,
382 /*draw_fn*/ nullptr,
383};
int BKE_image_find_nearest_tile_with_offset(const Image *image, const float co[2], float r_uv_offset[2]) ATTR_NONNULL(2
@ B_UNIT_NONE
Definition BKE_unit.hh:106
#define BLI_assert(a)
Definition BLI_assert.h:50
void mul_m3_v3(const float M[3][3], float r[3])
void mul_m4_m4m4(float R[4][4], const float A[4][4], const float B[4][4])
void size_to_mat3(float R[3][3], const float size[3])
void copy_m3_m3(float m1[3][3], const float m2[3][3])
void copy_m3_m4(float m1[3][3], const float m2[4][4])
void copy_m4_m3(float m1[4][4], const float m2[3][3])
void transform_pivot_set_m4(float mat[4][4], const float pivot[3])
void mul_v3_m3v3(float r[3], const float M[3][3], const float a[3])
MINLINE void sub_v3_v3v3(float r[3], const float a[3], const float b[3])
MINLINE bool is_zero_v2(const float v[2]) ATTR_WARN_UNUSED_RESULT
MINLINE void copy_v3_v3(float r[3], const float a[3])
void project_v3_v3v3(float out[3], const float p[3], const float v_proj[3])
MINLINE bool is_zero_v3(const float v[3]) ATTR_WARN_UNUSED_RESULT
MINLINE float normalize_v2(float n[2])
MINLINE void copy_v3_fl(float r[3], float f)
MINLINE void zero_v3(float r[3])
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])
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:99
BLI_INLINE void BLI_parallel_range_settings_defaults(TaskParallelSettings *settings)
Definition BLI_task.h:230
#define STREQ(a, b)
@ V3D_ORIENT_GLOBAL
@ NUM_NULL_ONE
@ NUM_NO_ZERO
@ NUM_AFFECT_ALL
bool applyNumInput(NumInput *n, float *vec)
Definition numinput.cc:190
void ED_area_status_text(ScrArea *area, const char *str)
Definition area.cc:803
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 Gabor Generate Gabor noise Gradient Generate interpolated color and intensity values based on the input vector Magic Generate a psychedelic color texture 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
#define fabsf(x)
#define str(s)
VecBase< float, 2 > float2
void RNA_property_float_get_array(PointerRNA *ptr, PropertyRNA *prop, float *values)
PropertyRNA * RNA_struct_find_property(PointerRNA *ptr, const char *identifier)
#define min(a, b)
Definition sort.c:32
const TransInfo * t
const TransDataContainer * tc
void * first
short idx_max
short val_flag[NUM_MAX_ELEMENTS]
float val_inc[NUM_MAX_ELEMENTS]
int unit_type[NUM_MAX_ELEMENTS]
short flag
struct UnitSettings unit
ListBase spacedata
struct Image * image
void(* applySize)(const TransInfo *t, const TransDataContainer *tc, const TransData *td, float r_smat[3][3])
Definition transform.hh:366
void(* applyRot)(const TransInfo *t, const TransDataContainer *tc, const TransData *td, float r_axis[3], float *r_angle)
Definition transform.hh:371
float pmtx[3][3]
Definition transform.hh:349
eTConstraint mode
Definition transform.hh:351
short around
Definition transform.hh:580
float snap[2]
Definition transform.hh:561
float center2d[2]
Definition transform.hh:557
float values[4]
Definition transform.hh:624
TransSnap tsnap
Definition transform.hh:537
short idx_max
Definition transform.hh:559
float values_modal_offset[4]
Definition transform.hh:627
NumInput num
Definition transform.hh:540
float aspect[3]
Definition transform.hh:553
Scene * scene
Definition transform.hh:654
eTFlag flag
Definition transform.hh:523
MouseInput mouse
Definition transform.hh:543
float viewmat[4][4]
Definition transform.hh:573
float values_final[4]
Definition transform.hh:632
float mat[3][3]
Definition transform.hh:589
TransCon con
Definition transform.hh:534
float center_global[3]
Definition transform.hh:555
blender::float2 mval
Definition transform.hh:663
ScrArea * area
Definition transform.hh:651
float snap_source[3]
Definition transform.hh:325
struct PointerRNA * ptr
@ INPUT_CUSTOM_RATIO
Definition transform.hh:753
@ INPUT_SPRING_FLIP
Definition transform.hh:744
@ CON_APPLY
Definition transform.hh:193
@ CON_AXIS1
Definition transform.hh:196
@ CON_AXIS0
Definition transform.hh:195
@ T_PROP_EDIT
Definition transform.hh:98
@ T_INPUT_IS_VALUES_FINAL
Definition transform.hh:115
@ T_NULL_ONE
Definition transform.hh:96
@ T_CLIP_UV
Definition transform.hh:105
@ T_EDIT
Definition transform.hh:91
#define TRANSFORM_DIST_INVALID
Definition transform.hh:845
void setCustomPoints(TransInfo *t, MouseInput *mi, const int mval_start[2], const int mval_end[2])
void initMouseInputMode(TransInfo *t, MouseInput *mi, MouseInputMode mode)
#define FOREACH_TRANS_DATA_CONTAINER(t, th)
Definition transform.hh:854
void constraintNumInput(TransInfo *t, float vec[3])
void clipUVData(TransInfo *t)
void recalc_data(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.
TransModeInfo TransMode_resize
static void ApplySnapResize(TransInfo *t, float vec[3])
static void initResize(TransInfo *t, wmOperator *op)
static void applyResize(TransInfo *t)
static void resize_transform_matrix_fn(TransInfo *t, float mat_xform[4][4])
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 element_resize_fn(void *__restrict iter_data_v, const int iter, const TaskParallelTLS *__restrict)
static void constrain_scale_to_boundary(const float numerator, const float denominator, float *scale)
void transform_snap_mixed_apply(TransInfo *t, float *vec)
bool transform_snap_increment(const TransInfo *t, float *r_val)
void getSnapPoint(const TransInfo *t, float vec[3])