Blender  V2.93
transform_mode_rotate.c
Go to the documentation of this file.
1 /*
2  * This program is free software; you can redistribute it and/or
3  * modify it under the terms of the GNU General Public License
4  * as published by the Free Software Foundation; either version 2
5  * of the License, or (at your option) any later version.
6  *
7  * This program is distributed in the hope that it will be useful,
8  * but WITHOUT ANY WARRANTY; without even the implied warranty of
9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10  * GNU General Public License for more details.
11  *
12  * You should have received a copy of the GNU General Public License
13  * along with this program; if not, write to the Free Software Foundation,
14  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
15  *
16  * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
17  * All rights reserved.
18  */
19 
24 #include <stdlib.h>
25 
26 #include "BLI_math.h"
27 
28 #include "BKE_context.h"
29 #include "BKE_unit.h"
30 
31 #include "ED_screen.h"
32 
33 #include "UI_interface.h"
34 
35 #include "transform.h"
36 #include "transform_mode.h"
37 #include "transform_snap.h"
38 
39 /* -------------------------------------------------------------------- */
43 static float RotationBetween(TransInfo *t, const float p1[3], const float p2[3])
44 {
45  float angle, start[3], end[3];
46 
47  sub_v3_v3v3(start, p1, t->center_global);
48  sub_v3_v3v3(end, p2, t->center_global);
49 
50  /* Angle around a constraint axis (error prone, will need debug). */
51  if (t->con.applyRot != NULL && (t->con.mode & CON_APPLY)) {
52  float axis[3], tmp[3];
53 
54  t->con.applyRot(t, NULL, NULL, axis, NULL);
55 
56  project_v3_v3v3(tmp, end, axis);
57  sub_v3_v3v3(end, end, tmp);
58 
59  project_v3_v3v3(tmp, start, axis);
60  sub_v3_v3v3(start, start, tmp);
61 
62  normalize_v3(end);
63  normalize_v3(start);
64 
65  cross_v3_v3v3(tmp, start, end);
66 
67  if (dot_v3v3(tmp, axis) < 0.0f) {
68  angle = -acosf(dot_v3v3(start, end));
69  }
70  else {
71  angle = acosf(dot_v3v3(start, end));
72  }
73  }
74  else {
75  float mtx[3][3];
76 
77  copy_m3_m4(mtx, t->viewmat);
78 
79  mul_m3_v3(mtx, end);
80  mul_m3_v3(mtx, start);
81 
82  angle = atan2f(start[1], start[0]) - atan2f(end[1], end[0]);
83  }
84 
85  if (angle > (float)M_PI) {
86  angle = angle - 2 * (float)M_PI;
87  }
88  else if (angle < -((float)M_PI)) {
89  angle = 2.0f * (float)M_PI + angle;
90  }
91 
92  return angle;
93 }
94 
95 static void ApplySnapRotation(TransInfo *t, float *value)
96 {
97  float point[3];
98  getSnapPoint(t, point);
99 
100  float dist = RotationBetween(t, t->tsnap.snapTarget, point);
101  *value = dist;
102 }
103 
104 static float large_rotation_limit(float angle)
105 {
106  /* Limit rotation to 1001 turns max
107  * (otherwise iterative handling of 'large' rotations would become too slow). */
108  const float angle_max = (float)(M_PI * 2000.0);
109  if (fabsf(angle) > angle_max) {
110  const float angle_sign = angle < 0.0f ? -1.0f : 1.0f;
111  angle = angle_sign * (fmodf(fabsf(angle), (float)(M_PI * 2.0)) + angle_max);
112  }
113  return angle;
114 }
115 
117  float angle,
118  float axis[3],
119  const bool is_large_rotation)
120 {
121  float mat[3][3];
122  int i;
123 
124  const float angle_sign = angle < 0.0f ? -1.0f : 1.0f;
125  /* We cannot use something too close to 180°, or 'continuous' rotation may fail
126  * due to computing error... */
127  const float angle_step = angle_sign * (float)(0.9 * M_PI);
128 
129  if (is_large_rotation) {
130  /* Just in case, calling code should have already done that in practice
131  * (for UI feedback reasons). */
133  }
134 
136  /* Counter for needed updates (when we need to update to non-default matrix,
137  * we also need another update on next iteration to go back to default matrix,
138  * hence the '2' value used here, instead of a mere boolean). */
139  short do_update_matrix = 0;
140 
142  TransData *td = tc->data;
143  for (i = 0; i < tc->data_len; i++, td++) {
144  if (td->flag & TD_SKIP) {
145  continue;
146  }
147 
148  float angle_final = angle;
149  if (t->con.applyRot) {
150  t->con.applyRot(t, tc, td, axis, NULL);
151  angle_final = angle * td->factor;
152  /* Even though final angle might be identical to orig value,
153  * we have to update the rotation matrix in that case... */
154  do_update_matrix = 2;
155  }
156  else if (t->flag & T_PROP_EDIT) {
157  angle_final = angle * td->factor;
158  }
159 
160  /* Rotation is very likely to be above 180°, we need to do rotation by steps.
161  * Note that this is only needed when doing 'absolute' rotation
162  * (i.e. from initial rotation again, typically when using numinput).
163  * regular incremental rotation (from mouse/widget/...) will be called often enough,
164  * hence steps are small enough to be properly handled without that complicated trick.
165  * Note that we can only do that kind of stepped rotation if we have initial rotation values
166  * (and access to some actual rotation value storage).
167  * Otherwise, just assume it's useless (e.g. in case of mesh/UV/etc. editing).
168  * Also need to be in Euler rotation mode, the others never allow more than one turn anyway.
169  */
170  if (is_large_rotation && td->ext != NULL && td->ext->rotOrder == ROT_MODE_EUL) {
171  copy_v3_v3(td->ext->rot, td->ext->irot);
172  for (float angle_progress = angle_step; fabsf(angle_progress) < fabsf(angle_final);
173  angle_progress += angle_step) {
174  axis_angle_normalized_to_mat3(mat, axis, angle_progress);
175  ElementRotation(t, tc, td, mat, t->around);
176  }
177  do_update_matrix = 2;
178  }
179  else if (angle_final != angle) {
180  do_update_matrix = 2;
181  }
182 
183  if (do_update_matrix > 0) {
184  axis_angle_normalized_to_mat3(mat, axis, angle_final);
185  do_update_matrix--;
186  }
187 
188  ElementRotation(t, tc, td, mat, t->around);
189  }
190  }
191 }
192 
193 static void applyRotation(TransInfo *t, const int UNUSED(mval[2]))
194 {
195  char str[UI_MAX_DRAW_STR];
196  float axis_final[3];
197  float final = t->values[0];
198 
199  if ((t->con.mode & CON_APPLY) && t->con.applyRot) {
200  t->con.applyRot(t, NULL, NULL, axis_final, &final);
201  }
202  else {
203  negate_v3_v3(axis_final, t->spacemtx[t->orient_axis]);
204  }
205 
206  if (applyNumInput(&t->num, &final)) {
207  /* We have to limit the amount of turns to a reasonable number here,
208  * to avoid things getting *very* slow, see how applyRotationValue() handles those... */
209  final = large_rotation_limit(final);
210  }
211  else {
212  applySnapping(t, &final);
213  if (!(activeSnap(t) && validSnap(t))) {
214  transform_snap_increment(t, &final);
215  }
216  }
217 
218  t->values_final[0] = final;
219 
220  headerRotation(t, str, final);
221 
222  const bool is_large_rotation = hasNumInput(&t->num);
223  applyRotationValue(t, final, axis_final, is_large_rotation);
224 
225  recalcData(t);
226 
227  ED_area_status_text(t->area, str);
228 }
229 
231 {
232  t->mode = TFM_ROTATION;
233  t->transform = applyRotation;
234  t->tsnap.applySnap = ApplySnapRotation;
235  t->tsnap.distance = RotationBetween;
236 
237  initMouseInputMode(t, &t->mouse, INPUT_ANGLE);
238 
239  t->idx_max = 0;
240  t->num.idx_max = 0;
241  t->snap[0] = DEG2RAD(5.0);
242  t->snap[1] = DEG2RAD(1.0);
243 
244  copy_v3_fl(t->num.val_inc, t->snap[1]);
245  t->num.unit_sys = t->scene->unit.system;
246  t->num.unit_use_radians = (t->scene->unit.system_rotation == USER_UNIT_ROT_RADIANS);
247  t->num.unit_type[0] = B_UNIT_ROTATION;
248 
249  if (t->flag & T_2D_EDIT) {
250  t->flag |= T_NO_CONSTRAINT;
251  }
252 
254 }
typedef float(TangentPoint)[2]
@ B_UNIT_ROTATION
Definition: BKE_unit.h:83
#define M_PI
Definition: BLI_math_base.h:38
void mul_m3_v3(const float M[3][3], float r[3])
Definition: math_matrix.c:930
void copy_m3_m4(float m1[3][3], const float m2[4][4])
Definition: math_matrix.c:105
#define DEG2RAD(_deg)
void axis_angle_normalized_to_mat3(float R[3][3], const float axis[3], const 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:674
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)
#define UNUSED(x)
@ ROT_MODE_EUL
#define USER_UNIT_ROT_RADIANS
@ V3D_ORIENT_VIEW
bool applyNumInput(NumInput *n, float *vec)
Definition: numinput.c:207
bool hasNumInput(const NumInput *n)
Definition: numinput.c:185
void ED_area_status_text(ScrArea *area, const char *str)
Definition: area.c:815
@ TFM_ROTATION
Definition: ED_transform.h:47
_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
#define UI_MAX_DRAW_STR
Definition: UI_interface.h:90
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)
#define fmodf(x, y)
#define acosf(x)
#define fabsf(x)
TransDataExtension * ext
@ INPUT_ANGLE
Definition: transform.h:732
@ CON_APPLY
Definition: transform.h:177
void recalcData(TransInfo *t)
@ T_PROP_EDIT
Definition: transform.h:111
@ T_2D_EDIT
Definition: transform.h:118
@ T_NO_CONSTRAINT
Definition: transform.h:106
void initMouseInputMode(TransInfo *t, MouseInput *mi, MouseInputMode mode)
#define FOREACH_TRANS_DATA_CONTAINER(t, th)
Definition: transform.h:813
@ TD_SKIP
void headerRotation(TransInfo *t, char str[UI_MAX_DRAW_STR], float final)
void ElementRotation(TransInfo *t, TransDataContainer *tc, TransData *td, float mat[3][3], const short around)
void transform_mode_default_modal_orientation_set(TransInfo *t, int type)
transform modes used by different operators.
static void applyRotationValue(TransInfo *t, float angle, 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 float large_rotation_limit(float angle)
static void ApplySnapRotation(TransInfo *t, float *value)
void initRotation(TransInfo *t)
void applySnapping(TransInfo *t, float *vec)
bool transform_snap_increment(TransInfo *t, float *r_val)
bool validSnap(const TransInfo *t)
void getSnapPoint(const TransInfo *t, float vec[3])
bool activeSnap(const TransInfo *t)