Blender V4.5
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
8
9#include "BLI_math_matrix.h"
10#include "BLI_math_vector.h"
11#include "BLI_string.h"
12#include "BLI_task.hh"
13
14#include "BKE_unit.hh"
15
16#include "ED_screen.hh"
17
18#include "UI_interface.hh"
19
20#include "BLT_translation.hh"
21
22#include "transform.hh"
23#include "transform_convert.hh"
24#include "transform_snap.hh"
25
26#include "transform_mode.hh"
27
28namespace blender::ed::transform {
29
30/* -------------------------------------------------------------------- */
33
34static void transdata_elem_shear(const TransInfo *t,
35 const TransDataContainer *tc,
36 TransData *td,
37 const float mat_final[3][3],
38 const bool is_local_center)
39{
40 float tmat[3][3];
41 const float *center;
42 if (t->flag & T_EDIT) {
43 mul_m3_series(tmat, td->smtx, mat_final, td->mtx);
44 }
45 else {
46 copy_m3_m3(tmat, mat_final);
47 }
48
49 if (is_local_center) {
50 center = td->center;
51 }
52 else {
53 center = tc->center_local;
54 }
55
56 float vec[3];
57 sub_v3_v3v3(vec, td->iloc, center);
58 mul_m3_v3(tmat, vec);
59 add_v3_v3(vec, center);
60 sub_v3_v3(vec, td->iloc);
61
63 /* Grease pencil multi-frame falloff. */
64 float *gp_falloff = static_cast<float *>(td->extra);
65 if (gp_falloff != nullptr) {
66 mul_v3_fl(vec, td->factor * *gp_falloff);
67 }
68 else {
69 mul_v3_fl(vec, td->factor);
70 }
71 }
72 else {
73 mul_v3_fl(vec, td->factor);
74 }
75
76 add_v3_v3v3(td->loc, td->iloc, vec);
77}
78
80
81/* -------------------------------------------------------------------- */
84
86{
87 float dir[3];
88 bool dir_flip = false;
90
91 /* Needed for axis aligned view gizmo. */
92 if (t->orient[t->orient_curr].type == V3D_ORIENT_VIEW) {
93 if (t->orient_axis_ortho == 0) {
94 if (t->center2d[1] > t->mouse.imval[1]) {
95 dir_flip = !dir_flip;
96 }
97 }
98 else if (t->orient_axis_ortho == 1) {
99 if (t->center2d[0] > t->mouse.imval[0]) {
100 dir_flip = !dir_flip;
101 }
102 }
103 }
104
105 /* Without this, half the gizmo handles move in the opposite direction. */
106 if ((t->orient_axis_ortho + 1) % 3 != t->orient_axis) {
107 dir_flip = !dir_flip;
108 }
109
110 if (dir_flip) {
111 negate_v3(dir);
112 }
113
114 mul_mat3_m4_v3(t->viewmat, dir);
115 if (normalize_v2(dir) == 0.0f) {
116 dir[0] = 1.0f;
117 }
119
121}
122
124{
126
127 if (event->type == MIDDLEMOUSE && event->val == KM_PRESS) {
128 /* Use custom.mode.data pointer to signal Shear direction. */
129 do {
130 t->orient_axis_ortho = (t->orient_axis_ortho + 1) % 3;
131 } while (t->orient_axis_ortho == t->orient_axis);
132
134
135 status = TREDRAW_HARD;
136 }
137 else if (event->type == EVT_XKEY && event->val == KM_PRESS) {
138 t->orient_axis_ortho = (t->orient_axis + 1) % 3;
140
141 status = TREDRAW_HARD;
142 }
143 else if (event->type == EVT_YKEY && event->val == KM_PRESS) {
144 t->orient_axis_ortho = (t->orient_axis + 2) % 3;
146
147 status = TREDRAW_HARD;
148 }
149
150 bool is_event_handled = (event->type != MOUSEMOVE) && (status || t->redraw);
151 bool update_status_bar = t->custom.mode.data || is_event_handled;
152 t->custom.mode.data = POINTER_FROM_INT(update_status_bar);
153
154 return status;
155}
156
157static void apply_shear_value(TransInfo *t, const float value)
158{
159 float smat[3][3];
160 unit_m3(smat);
161 smat[1][0] = value;
162
163 float axismat_inv[3][3];
164 copy_v3_v3(axismat_inv[0], t->spacemtx[t->orient_axis_ortho]);
165 copy_v3_v3(axismat_inv[2], t->spacemtx[t->orient_axis]);
166 cross_v3_v3v3(axismat_inv[1], axismat_inv[0], axismat_inv[2]);
167 float axismat[3][3];
168 invert_m3_m3(axismat, axismat_inv);
169
170 float mat_final[3][3];
171 mul_m3_series(mat_final, axismat_inv, smat, axismat);
172
173 const bool is_local_center = transdata_check_local_center(t, t->around);
174
176 threading::parallel_for(IndexRange(tc->data_len), 1024, [&](const IndexRange range) {
177 for (const int i : range) {
178 TransData *td = &tc->data[i];
179 if (td->flag & TD_SKIP) {
180 continue;
181 }
182 transdata_elem_shear(t, tc, td, mat_final, is_local_center);
183 }
184 });
185 }
186}
187
188static bool uv_shear_in_clip_bounds_test(const TransInfo *t, const float value)
189{
190 const int axis = t->orient_axis_ortho;
191 if (axis < 0 || 1 < axis) {
192 return true; /* Non standard axis, nothing to do. */
193 }
194 const float *center = t->center_global;
196 TransData *td = tc->data;
197 for (int i = 0; i < tc->data_len; i++, td++) {
198 if (td->flag & TD_SKIP) {
199 continue;
200 }
201 if (td->factor < 1.0f) {
202 continue; /* Proportional edit, will get picked up in next phase. */
203 }
204
205 float uv[2];
206 sub_v2_v2v2(uv, td->iloc, center);
207 uv[axis] = uv[axis] + value * uv[1 - axis] * (2 * axis - 1);
208 add_v2_v2(uv, center);
209 /* TODO: UDIM support. */
210 if (uv[axis] < 0.0f || 1.0f < uv[axis]) {
211 return false;
212 }
213 }
214 }
215 return true;
216}
217
218static bool clip_uv_transform_shear(const TransInfo *t, float *vec, float *vec_inside_bounds)
219{
220 float value = vec[0];
221 if (uv_shear_in_clip_bounds_test(t, value)) {
222 vec_inside_bounds[0] = value; /* Store for next iteration. */
223 return false; /* Nothing to do. */
224 }
225 float value_inside_bounds = vec_inside_bounds[0];
226 if (!uv_shear_in_clip_bounds_test(t, value_inside_bounds)) {
227 return false; /* No known way to fix, may as well shear anyway. */
228 }
229 const int max_i = 32; /* Limit iteration, mainly for debugging. */
230 for (int i = 0; i < max_i; i++) {
231 /* Binary search. */
232 const float value_mid = (value_inside_bounds + value) / 2.0f;
233 if (ELEM(value_mid, value_inside_bounds, value)) {
234 break; /* Float precision reached. */
235 }
236 if (uv_shear_in_clip_bounds_test(t, value_mid)) {
237 value_inside_bounds = value_mid;
238 }
239 else {
240 value = value_mid;
241 }
242 }
243
244 vec_inside_bounds[0] = value_inside_bounds; /* Store for next iteration. */
245 vec[0] = value_inside_bounds; /* Update shear value. */
246 return true;
247}
248
249static void apply_shear(TransInfo *t)
250{
251 float value = t->values[0] + t->values_modal_offset[0];
252 transform_snap_increment(t, &value);
253 applyNumInput(&t->num, &value);
254 t->values_final[0] = value;
255
256 apply_shear_value(t, value);
257
258 if (t->flag & T_CLIP_UV) {
261 }
262
263 /* Not ideal, see #clipUVData code-comment. */
264 if (t->flag & T_PROP_EDIT) {
265 clipUVData(t);
266 }
267 }
268
269 recalc_data(t);
270
271 char str[UI_MAX_DRAW_STR];
272 /* Header print for NumInput. */
273 if (hasNumInput(&t->num)) {
274 char c[NUM_STR_REP_LEN];
275 outputNumInput(&(t->num), c, t->scene->unit);
276 SNPRINTF(str, IFACE_("Shear: %s %s"), c, t->proptext);
277 }
278 else {
279 /* Default header print. */
280 SNPRINTF(str, IFACE_("Shear: %.3f %s"), value, t->proptext);
281 }
282
284
285 bool update_status_bar = POINTER_AS_INT(t->custom.mode.data);
286 if (update_status_bar) {
288
289 WorkspaceStatus status(t->context);
290 status.item(IFACE_("Confirm"), ICON_MOUSE_LMB);
291 status.item(IFACE_("Cancel"), ICON_MOUSE_RMB);
292 status.item_bool({}, t->orient_axis_ortho == (t->orient_axis + 1) % 3, ICON_EVENT_X);
293 status.item_bool({}, t->orient_axis_ortho == (t->orient_axis + 2) % 3, ICON_EVENT_Y);
294 status.item(IFACE_("Shear Axis"), ICON_NONE);
295 status.item(IFACE_("Swap Axes"), ICON_MOUSE_MMB);
296 }
297}
298
299static void initShear(TransInfo *t, wmOperator * /*op*/)
300{
301 t->mode = TFM_SHEAR;
302
303 if (t->orient_axis == t->orient_axis_ortho) {
304 t->orient_axis = 2;
305 t->orient_axis_ortho = 1;
306 }
307
309
310 t->idx_max = 0;
311 t->num.idx_max = 0;
312 t->snap[0] = 0.1f;
313 t->snap[1] = t->snap[0] * 0.1f;
314
315 copy_v3_fl(t->num.val_inc, t->snap[0]);
316 t->num.unit_sys = t->scene->unit.system;
317 t->num.unit_type[0] = B_UNIT_NONE; /* Don't think we have any unit here? */
318
319 bool update_status_bar = true;
320 t->custom.mode.data = POINTER_FROM_INT(update_status_bar);
321
323}
324
326
328 /*flags*/ T_NO_CONSTRAINT,
329 /*init_fn*/ initShear,
330 /*transform_fn*/ apply_shear,
331 /*transform_matrix_fn*/ nullptr,
332 /*handle_event_fn*/ handleEventShear,
333 /*snap_distance_fn*/ nullptr,
334 /*snap_apply_fn*/ nullptr,
335 /*draw_fn*/ nullptr,
336};
337
338} // namespace blender::ed::transform
@ B_UNIT_NONE
Definition BKE_unit.hh:123
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:599
#define POINTER_FROM_INT(i)
#define POINTER_AS_INT(i)
#define ELEM(...)
#define IFACE_(msgid)
int max_i(int a, int b)
Definition Basic.c:11
@ V3D_ORIENT_VIEW
#define NUM_STR_REP_LEN
bool applyNumInput(NumInput *n, float *vec)
Definition numinput.cc:189
void outputNumInput(NumInput *n, char *str, const UnitSettings &unit_settings)
Definition numinput.cc:87
bool hasNumInput(const NumInput *n)
Definition numinput.cc:170
void ED_area_status_text(ScrArea *area, const char *str)
Definition area.cc:872
#define UI_MAX_DRAW_STR
@ KM_PRESS
Definition WM_types.hh:308
void item_bool(std::string text, bool inverted, int icon1, int icon2=0)
Definition area.cc:995
void item(std::string text, int icon1, int icon2=0)
Definition area.cc:979
#define str(s)
void initMouseInputMode(TransInfo *t, MouseInput *mi, MouseInputMode mode)
static void initShear(TransInfo *t, wmOperator *)
void recalc_data(TransInfo *t)
void clipUVData(TransInfo *t)
static eRedrawFlag handleEventShear(TransInfo *t, const wmEvent *event)
bool transform_snap_increment(const TransInfo *t, float *r_val)
void setCustomPointsFromDirection(TransInfo *t, MouseInput *mi, const float2 &dir)
static void initShear_mouseInputMode(TransInfo *t)
static void apply_shear_value(TransInfo *t, const float value)
void transform_mode_default_modal_orientation_set(TransInfo *t, int type)
static void transdata_elem_shear(const TransInfo *t, const TransDataContainer *tc, TransData *td, const float mat_final[3][3], const bool is_local_center)
static bool clip_uv_transform_shear(const TransInfo *t, float *vec, float *vec_inside_bounds)
static bool uv_shear_in_clip_bounds_test(const TransInfo *t, const float value)
static void apply_shear(TransInfo *t)
bool transdata_check_local_center(const TransInfo *t, short around)
void parallel_for(const IndexRange range, const int64_t grain_size, const Function &function, const TaskSizeHints &size_hints=detail::TaskSizeHints_Static(1))
Definition BLI_task.hh:93
short idx_max
float val_inc[NUM_MAX_ELEMENTS]
int unit_type[NUM_MAX_ELEMENTS]
struct UnitSettings unit
TransCustomDataContainer custom
Definition transform.hh:968
struct blender::ed::transform::TransInfo::@040124034302070131153200326237043302276016250327 orient[3]
wmEventType type
Definition WM_types.hh:754
short val
Definition WM_types.hh:756
i
Definition text_draw.cc:230
#define FOREACH_TRANS_DATA_CONTAINER(t, th)
Definition transform.hh:42
conversion and adaptation of different datablocks to a common struct.
transform modes used by different operators.
@ EVT_YKEY
@ EVT_XKEY
@ MOUSEMOVE
@ MIDDLEMOUSE