Blender V4.3
transform_mode_shear.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
9#include <cstdlib>
10
12
13#include "BLI_math_matrix.h"
14#include "BLI_math_vector.h"
15#include "BLI_string.h"
16#include "BLI_task.h"
17
18#include "BKE_unit.hh"
19
20#include "ED_screen.hh"
21
22#include "WM_types.hh"
23
24#include "UI_interface.hh"
25
26#include "BLT_translation.hh"
27
28#include "transform.hh"
29#include "transform_convert.hh"
30#include "transform_snap.hh"
31
32#include "transform_mode.hh"
33
34/* -------------------------------------------------------------------- */
47
48static void transdata_elem_shear(const TransInfo *t,
49 const TransDataContainer *tc,
50 TransData *td,
51 const float mat_final[3][3],
52 const bool is_local_center)
53{
54 float tmat[3][3];
55 const float *center;
56 if (t->flag & T_EDIT) {
57 mul_m3_series(tmat, td->smtx, mat_final, td->mtx);
58 }
59 else {
60 copy_m3_m3(tmat, mat_final);
61 }
62
63 if (is_local_center) {
64 center = td->center;
65 }
66 else {
67 center = tc->center_local;
68 }
69
70 float vec[3];
71 sub_v3_v3v3(vec, td->iloc, center);
72 mul_m3_v3(tmat, vec);
73 add_v3_v3(vec, center);
74 sub_v3_v3(vec, td->iloc);
75
77 /* Grease pencil multi-frame falloff. */
78 bGPDstroke *gps = (bGPDstroke *)td->extra;
79 if (gps != nullptr) {
81 }
82 else {
83 mul_v3_fl(vec, td->factor);
84 }
85 }
86 else {
87 mul_v3_fl(vec, td->factor);
88 }
89
90 add_v3_v3v3(td->loc, td->iloc, vec);
91}
92
93static void transdata_elem_shear_fn(void *__restrict iter_data_v,
94 const int iter,
95 const TaskParallelTLS *__restrict /*tls*/)
96{
97 TransDataArgs_Shear *data = static_cast<TransDataArgs_Shear *>(iter_data_v);
98 TransData *td = &data->tc->data[iter];
99 if (td->flag & TD_SKIP) {
100 return;
101 }
102 transdata_elem_shear(data->t, data->tc, td, data->mat_final, data->is_local_center);
103}
104
107/* -------------------------------------------------------------------- */
112{
113 float dir[3];
114 bool dir_flip = false;
116
117 /* Needed for axis aligned view gizmo. */
118 if (t->orient[t->orient_curr].type == V3D_ORIENT_VIEW) {
119 if (t->orient_axis_ortho == 0) {
120 if (t->center2d[1] > t->mouse.imval[1]) {
121 dir_flip = !dir_flip;
122 }
123 }
124 else if (t->orient_axis_ortho == 1) {
125 if (t->center2d[0] > t->mouse.imval[0]) {
126 dir_flip = !dir_flip;
127 }
128 }
129 }
130
131 /* Without this, half the gizmo handles move in the opposite direction. */
132 if ((t->orient_axis_ortho + 1) % 3 != t->orient_axis) {
133 dir_flip = !dir_flip;
134 }
135
136 if (dir_flip) {
137 negate_v3(dir);
138 }
139
140 mul_mat3_m4_v3(t->viewmat, dir);
141 if (normalize_v2(dir) == 0.0f) {
142 dir[0] = 1.0f;
143 }
145
147}
148
150{
152
153 if (event->type == MIDDLEMOUSE && event->val == KM_PRESS) {
154 /* Use custom.mode.data pointer to signal Shear direction. */
155 do {
156 t->orient_axis_ortho = (t->orient_axis_ortho + 1) % 3;
157 } while (t->orient_axis_ortho == t->orient_axis);
158
160
161 status = TREDRAW_HARD;
162 }
163 else if (event->type == EVT_XKEY && event->val == KM_PRESS) {
164 t->orient_axis_ortho = (t->orient_axis + 1) % 3;
166
167 status = TREDRAW_HARD;
168 }
169 else if (event->type == EVT_YKEY && event->val == KM_PRESS) {
170 t->orient_axis_ortho = (t->orient_axis + 2) % 3;
172
173 status = TREDRAW_HARD;
174 }
175
176 return status;
177}
178
179static void apply_shear_value(TransInfo *t, const float value)
180{
181 float smat[3][3];
182 unit_m3(smat);
183 smat[1][0] = value;
184
185 float axismat_inv[3][3];
186 copy_v3_v3(axismat_inv[0], t->spacemtx[t->orient_axis_ortho]);
187 copy_v3_v3(axismat_inv[2], t->spacemtx[t->orient_axis]);
188 cross_v3_v3v3(axismat_inv[1], axismat_inv[0], axismat_inv[2]);
189 float axismat[3][3];
190 invert_m3_m3(axismat, axismat_inv);
191
192 float mat_final[3][3];
193 mul_m3_series(mat_final, axismat_inv, smat, axismat);
194
195 const bool is_local_center = transdata_check_local_center(t, t->around);
196
198 if (tc->data_len < TRANSDATA_THREAD_LIMIT) {
199 TransData *td = tc->data;
200 for (int i = 0; i < tc->data_len; i++, td++) {
201 if (td->flag & TD_SKIP) {
202 continue;
203 }
204 transdata_elem_shear(t, tc, td, mat_final, is_local_center);
205 }
206 }
207 else {
208 TransDataArgs_Shear data{};
209 data.t = t;
210 data.tc = tc;
211 data.is_local_center = is_local_center;
212 copy_m3_m3(data.mat_final, mat_final);
213
214 TaskParallelSettings settings;
216 BLI_task_parallel_range(0, tc->data_len, &data, transdata_elem_shear_fn, &settings);
217 }
218 }
219}
220
221static bool uv_shear_in_clip_bounds_test(const TransInfo *t, const float value)
222{
223 const int axis = t->orient_axis_ortho;
224 if (axis < 0 || 1 < axis) {
225 return true; /* Non standard axis, nothing to do. */
226 }
227 const float *center = t->center_global;
229 TransData *td = tc->data;
230 for (int i = 0; i < tc->data_len; i++, td++) {
231 if (td->flag & TD_SKIP) {
232 continue;
233 }
234 if (td->factor < 1.0f) {
235 continue; /* Proportional edit, will get picked up in next phase. */
236 }
237
238 float uv[2];
239 sub_v2_v2v2(uv, td->iloc, center);
240 uv[axis] = uv[axis] + value * uv[1 - axis] * (2 * axis - 1);
241 add_v2_v2(uv, center);
242 /* TODO: UDIM support. */
243 if (uv[axis] < 0.0f || 1.0f < uv[axis]) {
244 return false;
245 }
246 }
247 }
248 return true;
249}
250
251static bool clip_uv_transform_shear(const TransInfo *t, float *vec, float *vec_inside_bounds)
252{
253 float value = vec[0];
254 if (uv_shear_in_clip_bounds_test(t, value)) {
255 vec_inside_bounds[0] = value; /* Store for next iteration. */
256 return false; /* Nothing to do. */
257 }
258 float value_inside_bounds = vec_inside_bounds[0];
259 if (!uv_shear_in_clip_bounds_test(t, value_inside_bounds)) {
260 return false; /* No known way to fix, may as well shear anyway. */
261 }
262 const int max_i = 32; /* Limit iteration, mainly for debugging. */
263 for (int i = 0; i < max_i; i++) {
264 /* Binary search. */
265 const float value_mid = (value_inside_bounds + value) / 2.0f;
266 if (ELEM(value_mid, value_inside_bounds, value)) {
267 break; /* Float precision reached. */
268 }
269 if (uv_shear_in_clip_bounds_test(t, value_mid)) {
270 value_inside_bounds = value_mid;
271 }
272 else {
273 value = value_mid;
274 }
275 }
276
277 vec_inside_bounds[0] = value_inside_bounds; /* Store for next iteration. */
278 vec[0] = value_inside_bounds; /* Update shear value. */
279 return true;
280}
281
282static void apply_shear(TransInfo *t)
283{
284 float value = t->values[0] + t->values_modal_offset[0];
285 transform_snap_increment(t, &value);
286 applyNumInput(&t->num, &value);
287 t->values_final[0] = value;
288
289 apply_shear_value(t, value);
290
291 if (t->flag & T_CLIP_UV) {
294 }
295
296 /* Not ideal, see #clipUVData code-comment. */
297 if (t->flag & T_PROP_EDIT) {
298 clipUVData(t);
299 }
300 }
301
302 recalc_data(t);
303
304 char str[UI_MAX_DRAW_STR];
305 /* Header print for NumInput. */
306 if (hasNumInput(&t->num)) {
307 char c[NUM_STR_REP_LEN];
308 outputNumInput(&(t->num), c, &t->scene->unit);
309 SNPRINTF(str, IFACE_("Shear: %s %s"), c, t->proptext);
310 }
311 else {
312 /* Default header print. */
313 SNPRINTF(str, IFACE_("Shear: %.3f %s"), value, t->proptext);
314 }
315
317
318 WorkspaceStatus status(t->context);
319 status.item(IFACE_("Confirm"), ICON_MOUSE_LMB);
320 status.item(IFACE_("Cancel"), ICON_MOUSE_RMB);
321 status.item_bool({}, t->orient_axis_ortho == (t->orient_axis + 1) % 3, ICON_EVENT_X);
322 status.item_bool({}, t->orient_axis_ortho == (t->orient_axis + 2) % 3, ICON_EVENT_Y);
323 status.item(IFACE_("Shear Axis"), ICON_NONE);
324 status.item(IFACE_("Swap Axes"), ICON_MOUSE_MMB);
325}
326
327static void initShear(TransInfo *t, wmOperator * /*op*/)
328{
329 t->mode = TFM_SHEAR;
330
331 if (t->orient_axis == t->orient_axis_ortho) {
332 t->orient_axis = 2;
333 t->orient_axis_ortho = 1;
334 }
335
337
338 t->idx_max = 0;
339 t->num.idx_max = 0;
340 t->snap[0] = 0.1f;
341 t->snap[1] = t->snap[0] * 0.1f;
342
343 copy_v3_fl(t->num.val_inc, t->snap[0]);
344 t->num.unit_sys = t->scene->unit.system;
345 t->num.unit_type[0] = B_UNIT_NONE; /* Don't think we have any unit here? */
346
348}
349
353 /*flags*/ T_NO_CONSTRAINT,
354 /*init_fn*/ initShear,
355 /*transform_fn*/ apply_shear,
356 /*transform_matrix_fn*/ nullptr,
357 /*handle_event_fn*/ handleEventShear,
358 /*snap_distance_fn*/ nullptr,
359 /*snap_apply_fn*/ nullptr,
360 /*draw_fn*/ nullptr,
361};
@ B_UNIT_NONE
Definition BKE_unit.hh:106
void mul_m3_v3(const float M[3][3], float r[3])
void copy_m3_m3(float m1[3][3], const float m2[3][3])
void unit_m3(float m[3][3])
bool invert_m3_m3(float inverse[3][3], const float mat[3][3])
#define mul_m3_series(...)
void mul_mat3_m4_v3(const float mat[4][4], float r[3])
MINLINE void sub_v3_v3(float r[3], const float a[3])
MINLINE void sub_v3_v3v3(float r[3], const float a[3], const float b[3])
MINLINE void mul_v3_fl(float r[3], float f)
MINLINE void copy_v3_v3(float r[3], const float a[3])
MINLINE void add_v2_v2(float r[2], const float a[2])
MINLINE void add_v3_v3v3(float r[3], const float a[3], const float b[3])
MINLINE void cross_v3_v3v3(float r[3], const float a[3], const float b[3])
MINLINE void negate_v3(float r[3])
MINLINE void sub_v2_v2v2(float r[2], const float a[2], const float b[2])
MINLINE float normalize_v2(float n[2])
MINLINE void copy_v3_fl(float r[3], float f)
MINLINE void add_v3_v3(float r[3], const float a[3])
#define SNPRINTF(dst, format,...)
Definition BLI_string.h:597
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 ELEM(...)
#define IFACE_(msgid)
int max_i(int a, int b)
Definition Basic.c:11
@ V3D_ORIENT_VIEW
#define NUM_STR_REP_LEN
void outputNumInput(NumInput *n, char *str, const UnitSettings *unit_settings)
Definition numinput.cc:88
bool applyNumInput(NumInput *n, float *vec)
Definition numinput.cc:190
bool hasNumInput(const NumInput *n)
Definition numinput.cc:171
void ED_area_status_text(ScrArea *area, const char *str)
Definition area.cc:803
@ TFM_SHEAR
#define UI_MAX_DRAW_STR
@ KM_PRESS
Definition WM_types.hh:284
void item_bool(std::string text, bool inverted, int icon1, int icon2=0)
Definition area.cc:924
void item(std::string text, int icon1, int icon2=0)
Definition area.cc:908
#define str(s)
blender::float2 imval
Definition transform.hh:383
short idx_max
float val_inc[NUM_MAX_ELEMENTS]
int unit_type[NUM_MAX_ELEMENTS]
struct UnitSettings unit
const TransDataContainer * tc
float center_local[3]
Definition transform.hh:475
float smtx[3][3]
float mtx[3][3]
struct TransInfo::@565 orient[3]
eTfmMode mode
Definition transform.hh:517
short around
Definition transform.hh:580
int orient_axis
Definition transform.hh:640
float snap[2]
Definition transform.hh:561
float center2d[2]
Definition transform.hh:557
float values[4]
Definition transform.hh:624
char proptext[20]
Definition transform.hh:548
short idx_max
Definition transform.hh:559
float values_modal_offset[4]
Definition transform.hh:627
NumInput num
Definition transform.hh:540
Scene * scene
Definition transform.hh:654
eTOType orient_curr
Definition transform.hh:613
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
bContext * context
Definition transform.hh:649
float center_global[3]
Definition transform.hh:555
float spacemtx[3][3]
Definition transform.hh:592
int orient_axis_ortho
Definition transform.hh:642
float values_inside_constraints[4]
Definition transform.hh:635
eTContext options
Definition transform.hh:521
ScrArea * area
Definition transform.hh:651
short type
Definition transform.hh:609
bGPDstroke_Runtime runtime
short val
Definition WM_types.hh:724
short type
Definition WM_types.hh:722
@ INPUT_CUSTOM_RATIO
Definition transform.hh:753
@ CTX_GPENCIL_STROKES
Definition transform.hh:68
void setCustomPointsFromDirection(TransInfo *t, MouseInput *mi, const blender::float2 &dir)
eRedrawFlag
Definition transform.hh:214
@ TREDRAW_NOTHING
Definition transform.hh:215
@ TREDRAW_HARD
Definition transform.hh:217
@ T_PROP_EDIT
Definition transform.hh:98
@ T_NO_CONSTRAINT
Definition transform.hh:95
@ T_CLIP_UV
Definition transform.hh:105
@ T_EDIT
Definition transform.hh:91
void initMouseInputMode(TransInfo *t, MouseInput *mi, MouseInputMode mode)
#define FOREACH_TRANS_DATA_CONTAINER(t, th)
Definition transform.hh:854
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
bool transdata_check_local_center(const TransInfo *t, short around)
void transform_mode_default_modal_orientation_set(TransInfo *t, int type)
transform modes used by different operators.
static bool uv_shear_in_clip_bounds_test(const TransInfo *t, const float value)
static eRedrawFlag handleEventShear(TransInfo *t, const wmEvent *event)
static void apply_shear(TransInfo *t)
static void initShear_mouseInputMode(TransInfo *t)
static void apply_shear_value(TransInfo *t, const float value)
static bool clip_uv_transform_shear(const TransInfo *t, float *vec, float *vec_inside_bounds)
static void initShear(TransInfo *t, wmOperator *)
static void transdata_elem_shear_fn(void *__restrict iter_data_v, const int iter, const TaskParallelTLS *__restrict)
TransModeInfo TransMode_shear
static void transdata_elem_shear(const TransInfo *t, const TransDataContainer *tc, TransData *td, const float mat_final[3][3], const bool is_local_center)
bool transform_snap_increment(const TransInfo *t, float *r_val)
@ EVT_YKEY
@ EVT_XKEY
@ MIDDLEMOUSE