Blender V5.0
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_utf8.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
89
91{
92 float dir[3];
93 bool dir_flip = false;
95
96 /* Needed for axis aligned view gizmo. */
97 if (t->orient[t->orient_curr].type == V3D_ORIENT_VIEW) {
98 if (t->orient_axis_ortho == 0) {
99 if (t->center2d[1] > t->mouse.imval[1]) {
100 dir_flip = !dir_flip;
101 }
102 }
103 else if (t->orient_axis_ortho == 1) {
104 if (t->center2d[0] > t->mouse.imval[0]) {
105 dir_flip = !dir_flip;
106 }
107 }
108 }
109
110 /* Without this, half the gizmo handles move in the opposite direction. */
111 if ((t->orient_axis_ortho + 1) % 3 != t->orient_axis) {
112 dir_flip = !dir_flip;
113 }
114
115 if (dir_flip) {
116 negate_v3(dir);
117 }
118
119 mul_mat3_m4_v3(t->viewmat, dir);
120 if (normalize_v2(dir) == 0.0f) {
121 dir[0] = 1.0f;
122 }
124
126}
127
129{
131
132 if (event->type == MIDDLEMOUSE && event->val == KM_PRESS) {
133 /* Use custom.mode.data pointer to signal Shear direction. */
134 do {
135 t->orient_axis_ortho = (t->orient_axis_ortho + 1) % 3;
136 } while (t->orient_axis_ortho == t->orient_axis);
137
139
141 }
142 else if (event->type == EVT_XKEY && event->val == KM_PRESS) {
143 t->orient_axis_ortho = (t->orient_axis + 1) % 3;
145
147 }
148 else if (event->type == EVT_YKEY && event->val == KM_PRESS) {
149 t->orient_axis_ortho = (t->orient_axis + 2) % 3;
151
153 }
154
155 ShearCustomData *custom_data = static_cast<ShearCustomData *>(t->custom.mode.data);
156 bool is_event_handled = (event->type != MOUSEMOVE) && (status || t->redraw);
157 custom_data->update_status_bar = custom_data->update_status_bar || is_event_handled;
158
159 return status;
160}
161
162static void apply_shear_value(TransInfo *t, const float value)
163{
164 float smat[3][3];
165 unit_m3(smat);
166 smat[1][0] = value;
167
168 float axismat_inv[3][3];
169 copy_v3_v3(axismat_inv[0], t->spacemtx[t->orient_axis_ortho]);
170 copy_v3_v3(axismat_inv[2], t->spacemtx[t->orient_axis]);
171 cross_v3_v3v3(axismat_inv[1], axismat_inv[0], axismat_inv[2]);
172 float axismat[3][3];
173 invert_m3_m3(axismat, axismat_inv);
174
175 float mat_final[3][3];
176 mul_m3_series(mat_final, axismat_inv, smat, axismat);
177
178 const bool is_local_center = transdata_check_local_center(t, t->around);
179
181 threading::parallel_for(IndexRange(tc->data_len), 1024, [&](const IndexRange range) {
182 for (const int i : range) {
183 TransData *td = &tc->data[i];
184 if (td->flag & TD_SKIP) {
185 continue;
186 }
187 transdata_elem_shear(t, tc, td, mat_final, is_local_center);
188 }
189 });
190 }
191}
192
193static bool uv_shear_in_clip_bounds_test(const TransInfo *t, const float value)
194{
195 const int axis = t->orient_axis_ortho;
196 if (axis < 0 || 1 < axis) {
197 return true; /* Non standard axis, nothing to do. */
198 }
199 const float *center = t->center_global;
201 TransData *td = tc->data;
202 for (int i = 0; i < tc->data_len; i++, td++) {
203 if (td->flag & TD_SKIP) {
204 continue;
205 }
206 if (td->factor < 1.0f) {
207 continue; /* Proportional edit, will get picked up in next phase. */
208 }
209
210 float uv[2];
211 sub_v2_v2v2(uv, td->iloc, center);
212 uv[axis] = uv[axis] + value * uv[1 - axis] * (2 * axis - 1);
213 add_v2_v2(uv, center);
214 /* TODO: UDIM support. */
215 if (uv[axis] < 0.0f || 1.0f < uv[axis]) {
216 return false;
217 }
218 }
219 }
220 return true;
221}
222
223static bool clip_uv_transform_shear(const TransInfo *t, float *vec, float *vec_inside_bounds)
224{
225 float value = vec[0];
226 if (uv_shear_in_clip_bounds_test(t, value)) {
227 vec_inside_bounds[0] = value; /* Store for next iteration. */
228 return false; /* Nothing to do. */
229 }
230 float value_inside_bounds = vec_inside_bounds[0];
231 if (!uv_shear_in_clip_bounds_test(t, value_inside_bounds)) {
232 return false; /* No known way to fix, may as well shear anyway. */
233 }
234 const int max_i = 32; /* Limit iteration, mainly for debugging. */
235 for (int i = 0; i < max_i; i++) {
236 /* Binary search. */
237 const float value_mid = (value_inside_bounds + value) / 2.0f;
238 if (ELEM(value_mid, value_inside_bounds, value)) {
239 break; /* Float precision reached. */
240 }
241 if (uv_shear_in_clip_bounds_test(t, value_mid)) {
242 value_inside_bounds = value_mid;
243 }
244 else {
245 value = value_mid;
246 }
247 }
248
249 vec_inside_bounds[0] = value_inside_bounds; /* Store for next iteration. */
250 vec[0] = value_inside_bounds; /* Update shear value. */
251 return true;
252}
253
254static void apply_shear(TransInfo *t)
255{
256 float value = t->values[0] + t->values_modal_offset[0];
257 transform_snap_increment(t, &value);
258 applyNumInput(&t->num, &value);
259 t->values_final[0] = value;
260
261 apply_shear_value(t, value);
262
263 if (t->flag & T_CLIP_UV) {
266 }
267
268 /* Not ideal, see #clipUVData code-comment. */
269 if (t->flag & T_PROP_EDIT) {
270 clipUVData(t);
271 }
272 }
273
274 recalc_data(t);
275
276 char str[UI_MAX_DRAW_STR];
277 /* Header print for NumInput. */
278 if (hasNumInput(&t->num)) {
279 char c[NUM_STR_REP_LEN];
280 outputNumInput(&(t->num), c, t->scene->unit);
281 SNPRINTF_UTF8(str, IFACE_("Shear: %s %s"), c, t->proptext);
282 }
283 else {
284 /* Default header print. */
285 SNPRINTF_UTF8(str, IFACE_("Shear: %.3f %s"), value, t->proptext);
286 }
287
289
290 ShearCustomData *custom_data = static_cast<ShearCustomData *>(t->custom.mode.data);
291 if (custom_data->op && custom_data->update_status_bar) {
292 custom_data->update_status_bar = false;
293
295
296 status.opmodal(IFACE_("Confirm"), custom_data->op->type, TFM_MODAL_CONFIRM);
297 status.opmodal(IFACE_("Cancel"), custom_data->op->type, TFM_MODAL_CANCEL);
298
299 status.item_bool({}, t->orient_axis_ortho == (t->orient_axis + 1) % 3, ICON_EVENT_X);
300 status.item_bool({}, t->orient_axis_ortho == (t->orient_axis + 2) % 3, ICON_EVENT_Y);
301 status.item(IFACE_("Shear Axis"), ICON_NONE);
302 status.item(IFACE_("Swap Axes"), ICON_MOUSE_MMB);
303
304 status.opmodal(
305 IFACE_("Snap"), custom_data->op->type, TFM_MODAL_SNAP_TOGGLE, t->modifiers & MOD_SNAP);
306 status.opmodal(IFACE_("Snap Invert"),
307 custom_data->op->type,
310 status.opmodal(IFACE_("Precision"),
311 custom_data->op->type,
314
315 if (t->proptext[0]) {
316 status.opmodal({}, custom_data->op->type, TFM_MODAL_PROPSIZE_UP);
317 status.opmodal(IFACE_("Proportional Size"), custom_data->op->type, TFM_MODAL_PROPSIZE_DOWN);
318 }
319 }
320}
321
322static void initShear(TransInfo *t, wmOperator *op)
323{
324 t->mode = TFM_SHEAR;
325
326 if (t->orient_axis == t->orient_axis_ortho) {
327 t->orient_axis = 2;
328 t->orient_axis_ortho = 1;
329 }
330
332
333 t->idx_max = 0;
334 t->num.idx_max = 0;
335 t->increment[0] = 0.1f;
336 t->increment_precision = 0.1f;
337
338 copy_v3_fl(t->num.val_inc, t->increment[0]);
339 t->num.unit_sys = t->scene->unit.system;
340 t->num.unit_type[0] = B_UNIT_NONE; /* Don't think we have any unit here? */
341
342 ShearCustomData *custom_data = static_cast<ShearCustomData *>(
343 MEM_callocN(sizeof(*custom_data), __func__));
344 t->custom.mode.data = custom_data;
345 t->custom.mode.free_cb = [](TransInfo *, TransDataContainer *, TransCustomData *custom_data) {
346 MEM_freeN(custom_data->data);
347 custom_data->data = nullptr;
348 };
349
350 custom_data->update_status_bar = true;
351 custom_data->op = op;
352
354}
355
357
359 /*flags*/ T_NO_CONSTRAINT,
360 /*init_fn*/ initShear,
361 /*transform_fn*/ apply_shear,
362 /*transform_matrix_fn*/ nullptr,
363 /*handle_event_fn*/ handleEventShear,
364 /*snap_distance_fn*/ nullptr,
365 /*snap_apply_fn*/ nullptr,
366 /*draw_fn*/ nullptr,
367};
368
369} // namespace blender::ed::transform
@ B_UNIT_NONE
Definition BKE_unit.hh:136
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_UTF8(dst, format,...)
#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:190
void outputNumInput(NumInput *n, char *str, const UnitSettings &unit_settings)
Definition numinput.cc:88
bool hasNumInput(const NumInput *n)
Definition numinput.cc:171
void ED_area_status_text(ScrArea *area, const char *str)
Definition area.cc:851
#define UI_MAX_DRAW_STR
@ KM_PRESS
Definition WM_types.hh:311
#define str(s)
void * MEM_callocN(size_t len, const char *str)
Definition mallocn.cc:118
void MEM_freeN(void *vmemh)
Definition mallocn.cc:113
void initMouseInputMode(TransInfo *t, MouseInput *mi, MouseInputMode mode)
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)
static void initShear(TransInfo *t, wmOperator *op)
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
const int status
short idx_max
float val_inc[NUM_MAX_ELEMENTS]
int unit_type[NUM_MAX_ELEMENTS]
struct UnitSettings unit
void(* free_cb)(TransInfo *, TransDataContainer *tc, TransCustomData *custom_data)
Definition transform.hh:633
TransCustomDataContainer custom
Definition transform.hh:974
struct blender::ed::transform::TransInfo::@342160341045112220003361360177273117120157367076 orient[3]
wmEventType type
Definition WM_types.hh:757
short val
Definition WM_types.hh:759
struct wmOperatorType * type
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