Blender  V3.3
transform_mode_rotate.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_report.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_convert.h"
23 #include "transform_snap.h"
24 
25 #include "transform_mode.h"
26 
27 /* -------------------------------------------------------------------- */
38  float mat[3][3];
39 };
40 
41 static void rmat_cache_init(struct RotateMatrixCache *rmc, const float angle, const float axis[3])
42 {
44  rmc->do_update_matrix = 0;
45 }
46 
47 static void rmat_cache_reset(struct RotateMatrixCache *rmc)
48 {
49  rmc->do_update_matrix = 2;
50 }
51 
52 static void rmat_cache_update(struct RotateMatrixCache *rmc,
53  const float axis[3],
54  const float angle)
55 {
56  if (rmc->do_update_matrix > 0) {
58  rmc->do_update_matrix--;
59  }
60 }
61 
64 /* -------------------------------------------------------------------- */
72  const TransInfo *t;
74  const float axis[3];
75  float angle;
76  float angle_step;
78 };
79 
81  struct RotateMatrixCache rmc;
82 };
83 
84 static void transdata_elem_rotate(const TransInfo *t,
85  const TransDataContainer *tc,
86  TransData *td,
87  const float axis[3],
88  const float angle,
89  const float angle_step,
90  const bool is_large_rotation,
91  struct RotateMatrixCache *rmc)
92 {
93  float axis_buffer[3];
94  const float *axis_final = axis;
95 
96  float angle_final = angle;
97  if (t->con.applyRot) {
98  copy_v3_v3(axis_buffer, axis);
99  axis_final = axis_buffer;
100  t->con.applyRot(t, tc, td, axis_buffer, NULL);
101  angle_final = angle * td->factor;
102  /* Even though final angle might be identical to orig value,
103  * we have to update the rotation matrix in that case... */
104  rmat_cache_reset(rmc);
105  }
106  else if (t->flag & T_PROP_EDIT) {
107  angle_final = angle * td->factor;
108  }
109 
110  /* Rotation is very likely to be above 180°, we need to do rotation by steps.
111  * Note that this is only needed when doing 'absolute' rotation
112  * (i.e. from initial rotation again, typically when using numinput).
113  * regular incremental rotation (from mouse/widget/...) will be called often enough,
114  * hence steps are small enough to be properly handled without that complicated trick.
115  * Note that we can only do that kind of stepped rotation if we have initial rotation values
116  * (and access to some actual rotation value storage).
117  * Otherwise, just assume it's useless (e.g. in case of mesh/UV/etc. editing).
118  * Also need to be in Euler rotation mode, the others never allow more than one turn anyway.
119  */
120  if (is_large_rotation && td->ext != NULL && td->ext->rotOrder == ROT_MODE_EUL) {
121  copy_v3_v3(td->ext->rot, td->ext->irot);
122  for (float angle_progress = angle_step; fabsf(angle_progress) < fabsf(angle_final);
123  angle_progress += angle_step) {
124  axis_angle_normalized_to_mat3(rmc->mat, axis_final, angle_progress);
125  ElementRotation(t, tc, td, rmc->mat, t->around);
126  }
127  rmat_cache_reset(rmc);
128  }
129  else if (angle_final != angle) {
130  rmat_cache_reset(rmc);
131  }
132 
133  rmat_cache_update(rmc, axis_final, angle_final);
134 
135  ElementRotation(t, tc, td, rmc->mat, t->around);
136 }
137 
138 static void transdata_elem_rotate_fn(void *__restrict iter_data_v,
139  const int iter,
140  const TaskParallelTLS *__restrict tls)
141 {
142  struct TransDataArgs_Rotate *data = iter_data_v;
143  struct TransDataArgs_RotateTLS *tls_data = tls->userdata_chunk;
144 
145  TransData *td = &data->tc->data[iter];
146  if (td->flag & TD_SKIP) {
147  return;
148  }
150  data->tc,
151  td,
152  data->axis,
153  data->angle,
154  data->angle_step,
155  data->is_large_rotation,
156  &tls_data->rmc);
157 }
158 
161 /* -------------------------------------------------------------------- */
165 static float RotationBetween(TransInfo *t, const float p1[3], const float p2[3])
166 {
167  float angle, start[3], end[3];
168 
169  sub_v3_v3v3(start, p1, t->center_global);
170  sub_v3_v3v3(end, p2, t->center_global);
171 
172  /* Angle around a constraint axis (error prone, will need debug). */
173  if (t->con.applyRot != NULL && (t->con.mode & CON_APPLY)) {
174  float axis[3], tmp[3];
175 
176  t->con.applyRot(t, NULL, NULL, axis, NULL);
177 
178  project_v3_v3v3(tmp, end, axis);
179  sub_v3_v3v3(end, end, tmp);
180 
181  project_v3_v3v3(tmp, start, axis);
182  sub_v3_v3v3(start, start, tmp);
183 
184  normalize_v3(end);
185  normalize_v3(start);
186 
187  cross_v3_v3v3(tmp, start, end);
188 
189  if (dot_v3v3(tmp, axis) < 0.0f) {
190  angle = -acosf(dot_v3v3(start, end));
191  }
192  else {
193  angle = acosf(dot_v3v3(start, end));
194  }
195  }
196  else {
197  float mtx[3][3];
198 
199  copy_m3_m4(mtx, t->viewmat);
200 
201  mul_m3_v3(mtx, end);
202  mul_m3_v3(mtx, start);
203 
204  angle = atan2f(start[1], start[0]) - atan2f(end[1], end[0]);
205  }
206 
207  if (angle > (float)M_PI) {
208  angle = angle - 2 * (float)M_PI;
209  }
210  else if (angle < -((float)M_PI)) {
211  angle = 2.0f * (float)M_PI + angle;
212  }
213 
214  return angle;
215 }
216 
217 static void ApplySnapRotation(TransInfo *t, float *value)
218 {
219  float point[3];
220  getSnapPoint(t, point);
221 
222  float dist = RotationBetween(t, t->tsnap.snapTarget, point);
223  *value = dist;
224 }
225 
226 static float large_rotation_limit(float angle)
227 {
228  /* Limit rotation to 1001 turns max
229  * (otherwise iterative handling of 'large' rotations would become too slow). */
230  const float angle_max = (float)(M_PI * 2000.0);
231  if (fabsf(angle) > angle_max) {
232  const float angle_sign = angle < 0.0f ? -1.0f : 1.0f;
233  angle = angle_sign * (fmodf(fabsf(angle), (float)(M_PI * 2.0)) + angle_max);
234  }
235  return angle;
236 }
237 
239  float angle,
240  const float axis[3],
241  const bool is_large_rotation)
242 {
243  const float angle_sign = angle < 0.0f ? -1.0f : 1.0f;
244  /* We cannot use something too close to 180°, or 'continuous' rotation may fail
245  * due to computing error... */
246  const float angle_step = angle_sign * (float)(0.9 * M_PI);
247 
248  if (is_large_rotation) {
249  /* Just in case, calling code should have already done that in practice
250  * (for UI feedback reasons). */
252  }
253 
254  struct RotateMatrixCache rmc = {0};
255  rmat_cache_init(&rmc, angle, axis);
256 
258  if (tc->data_len < TRANSDATA_THREAD_LIMIT) {
259  TransData *td = tc->data;
260  for (int i = 0; i < tc->data_len; i++, td++) {
261  if (td->flag & TD_SKIP) {
262  continue;
263  }
264  transdata_elem_rotate(t, tc, td, axis, angle, angle_step, is_large_rotation, &rmc);
265  }
266  }
267  else {
268  struct TransDataArgs_Rotate data = {
269  .t = t,
270  .tc = tc,
271  .axis = {UNPACK3(axis)},
272  .angle = angle,
273  .angle_step = angle_step,
274  .is_large_rotation = is_large_rotation,
275  };
276  struct TransDataArgs_RotateTLS tls_data = {
277  .rmc = rmc,
278  };
279 
280  TaskParallelSettings settings;
282  settings.userdata_chunk = &tls_data;
283  settings.userdata_chunk_size = sizeof(tls_data);
284  BLI_task_parallel_range(0, tc->data_len, &data, transdata_elem_rotate_fn, &settings);
285  }
286  }
287 }
288 
289 static void applyRotation(TransInfo *t, const int UNUSED(mval[2]))
290 {
291  char str[UI_MAX_DRAW_STR];
292  float axis_final[3];
293  float final = t->values[0] + t->values_modal_offset[0];
294 
295  if ((t->con.mode & CON_APPLY) && t->con.applyRot) {
296  t->con.applyRot(t, NULL, NULL, axis_final, &final);
297  }
298  else {
299  negate_v3_v3(axis_final, t->spacemtx[t->orient_axis]);
300  }
301 
302  if (applyNumInput(&t->num, &final)) {
303  /* We have to limit the amount of turns to a reasonable number here,
304  * to avoid things getting *very* slow, see how applyRotationValue() handles those... */
305  final = large_rotation_limit(final);
306  }
307  else {
308  applySnappingAsGroup(t, &final);
309  if (!(activeSnap(t) && validSnap(t))) {
310  transform_snap_increment(t, &final);
311  }
312  }
313 
314  t->values_final[0] = final;
315 
316  headerRotation(t, str, sizeof(str), final);
317 
318  const bool is_large_rotation = hasNumInput(&t->num);
319  applyRotationValue(t, final, axis_final, is_large_rotation);
320 
321  recalcData(t);
322 
323  ED_area_status_text(t->area, str);
324 }
325 
326 static void applyRotationMatrix(TransInfo *t, float mat_xform[4][4])
327 {
328  float axis_final[3];
329  const float angle_final = t->values_final[0];
330  if ((t->con.mode & CON_APPLY) && t->con.applyRot) {
331  t->con.applyRot(t, NULL, NULL, axis_final, NULL);
332  }
333  else {
334  negate_v3_v3(axis_final, t->spacemtx[t->orient_axis]);
335  }
336 
337  float mat3[3][3];
338  float mat4[4][4];
339  axis_angle_normalized_to_mat3(mat3, axis_final, angle_final);
340  copy_m4_m3(mat4, mat3);
341  transform_pivot_set_m4(mat4, t->center_global);
342  mul_m4_m4m4(mat_xform, mat4, mat_xform);
343 }
344 
346 {
347  if (t->spacetype == SPACE_ACTION) {
348  BKE_report(t->reports, RPT_ERROR, "Rotation is not supported in the Dope Sheet Editor");
349  t->state = TRANS_CANCEL;
350  }
351 
352  t->mode = TFM_ROTATION;
353  t->transform = applyRotation;
354  t->transform_matrix = applyRotationMatrix;
355  t->tsnap.applySnap = ApplySnapRotation;
356  t->tsnap.distance = RotationBetween;
357 
358  initMouseInputMode(t, &t->mouse, INPUT_ANGLE);
359 
360  t->idx_max = 0;
361  t->num.idx_max = 0;
362  t->snap[0] = DEG2RAD(5.0);
363  t->snap[1] = DEG2RAD(1.0);
364 
365  copy_v3_fl(t->num.val_inc, t->snap[1]);
366  t->num.unit_sys = t->scene->unit.system;
367  t->num.unit_use_radians = (t->scene->unit.system_rotation == USER_UNIT_ROT_RADIANS);
368  t->num.unit_type[0] = B_UNIT_ROTATION;
369 
370  if (t->flag & T_2D_EDIT) {
371  t->flag |= T_NO_CONSTRAINT;
372  }
373 
375 }
376 
typedef float(TangentPoint)[2]
void BKE_report(ReportList *reports, eReportType type, const char *message)
Definition: report.c:83
@ B_UNIT_ROTATION
Definition: BKE_unit.h:105
#define M_PI
Definition: BLI_math_base.h:20
void mul_m3_v3(const float M[3][3], float r[3])
Definition: math_matrix.c:926
void mul_m4_m4m4(float R[4][4], const float A[4][4], const float B[4][4])
Definition: math_matrix.c:259
void copy_m3_m4(float m1[3][3], const float m2[4][4])
Definition: math_matrix.c:87
void copy_m4_m3(float m1[4][4], const float m2[3][3])
Definition: math_matrix.c:102
void transform_pivot_set_m4(float mat[4][4], const float pivot[3])
Definition: math_matrix.c:2369
#define DEG2RAD(_deg)
void axis_angle_normalized_to_mat3(float R[3][3], const float axis[3], float angle)
MINLINE float normalize_v3(float r[3])
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 void negate_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])
Definition: math_vector.c:600
MINLINE float dot_v3v3(const float a[3], const float b[3]) ATTR_WARN_UNUSED_RESULT
MINLINE void cross_v3_v3v3(float r[3], const float a[3], const float b[3])
MINLINE void copy_v3_fl(float r[3], float f)
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)
#define UNPACK3(a)
@ ROT_MODE_EUL
#define USER_UNIT_ROT_RADIANS
@ SPACE_ACTION
@ V3D_ORIENT_VIEW
bool applyNumInput(NumInput *n, float *vec)
Definition: numinput.c:189
bool hasNumInput(const NumInput *n)
Definition: numinput.c:170
void ED_area_status_text(ScrArea *area, const char *str)
Definition: area.c:792
@ TFM_ROTATION
Definition: ED_transform.h:31
_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
SIMD_FORCE_INLINE btScalar angle(const btVector3 &v) const
Return the angle between this and another vector.
Definition: btVector3.h:356
#define str(s)
#define atan2f(x, y)
Definition: metal/compat.h:227
#define fmodf(x, y)
Definition: metal/compat.h:230
#define acosf(x)
Definition: metal/compat.h:222
#define fabsf(x)
Definition: metal/compat.h:219
void initMouseInputMode(TransInfo *t, MouseInput *mi, MouseInputMode mode)
#define FOREACH_TRANS_DATA_CONTAINER(t, th)
size_t userdata_chunk_size
Definition: BLI_task.h:169
struct RotateMatrixCache rmc
const TransDataContainer * tc
TransDataExtension * ext
void recalcData(TransInfo *t)
conversion and adaptation of different datablocks to a common struct.
@ TD_SKIP
#define TRANSDATA_THREAD_LIMIT
void transform_mode_default_modal_orientation_set(TransInfo *t, int type)
void ElementRotation(const TransInfo *t, const TransDataContainer *tc, TransData *td, const float mat[3][3], const short around)
void headerRotation(TransInfo *t, char *str, const int str_size, float final)
transform modes used by different operators.
static void transdata_elem_rotate_fn(void *__restrict iter_data_v, const int iter, const TaskParallelTLS *__restrict tls)
static void applyRotationValue(TransInfo *t, float angle, const float axis[3], const bool is_large_rotation)
static float RotationBetween(TransInfo *t, const float p1[3], const float p2[3])
static void applyRotation(TransInfo *t, const int UNUSED(mval[2]))
static void transdata_elem_rotate(const TransInfo *t, const TransDataContainer *tc, TransData *td, const float axis[3], const float angle, const float angle_step, const bool is_large_rotation, struct RotateMatrixCache *rmc)
static float large_rotation_limit(float angle)
static void ApplySnapRotation(TransInfo *t, float *value)
static void rmat_cache_update(struct RotateMatrixCache *rmc, const float axis[3], const float angle)
void initRotation(TransInfo *t)
static void rmat_cache_reset(struct RotateMatrixCache *rmc)
static void rmat_cache_init(struct RotateMatrixCache *rmc, const float angle, const float axis[3])
static void applyRotationMatrix(TransInfo *t, float mat_xform[4][4])
bool validSnap(const TransInfo *t)
bool transform_snap_increment(const TransInfo *t, float *r_val)
void applySnappingAsGroup(TransInfo *t, float *vec)
void getSnapPoint(const TransInfo *t, float vec[3])
bool activeSnap(const TransInfo *t)