2 * ***** BEGIN GPL LICENSE BLOCK *****
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License
6 * as published by the Free Software Foundation; either version 2
7 * of the License, or (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software Foundation,
16 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18 * Contributor(s): none yet.
20 * ***** END GPL LICENSE BLOCK *****
23 /** \file blender/editors/transform/transform_input.c
24 * \ingroup edtransform
31 #include "DNA_screen_types.h"
34 #include "BLI_utildefines.h"
38 #include "transform.h"
40 #include "MEM_guardedalloc.h"
42 /* ************************** INPUT FROM MOUSE *************************** */
44 static void InputVector(TransInfo *t, MouseInput *mi, const double mval[2], float output[3])
46 convertViewVec(t, output, mval[0] - mi->imval[0], mval[1] - mi->imval[1]);
49 static void InputSpring(TransInfo *UNUSED(t), MouseInput *mi, const double mval[2], float output[3])
54 dx = ((double)mi->center[0] - mval[0]);
55 dy = ((double)mi->center[1] - mval[1]);
56 ratio = hypot(dx, dy) / (double)mi->factor;
61 static void InputSpringFlip(TransInfo *t, MouseInput *mi, const double mval[2], float output[3])
63 InputSpring(t, mi, mval, output);
66 /* values can become really big when zoomed in so use longs [#26598] */
67 if ((int64_t)((int)mi->center[0] - mval[0]) * (int64_t)((int)mi->center[0] - mi->imval[0]) +
68 (int64_t)((int)mi->center[1] - mval[1]) * (int64_t)((int)mi->center[1] - mi->imval[1]) < 0)
74 static void InputSpringDelta(TransInfo *t, MouseInput *mi, const double mval[2], float output[3])
76 InputSpring(t, mi, mval, output);
80 static void InputTrackBall(TransInfo *UNUSED(t), MouseInput *mi, const double mval[2], float output[3])
82 output[0] = (float)(mi->imval[1] - mval[1]);
83 output[1] = (float)(mval[0] - mi->imval[0]);
85 output[0] *= mi->factor;
86 output[1] *= mi->factor;
89 static void InputHorizontalRatio(TransInfo *t, MouseInput *mi, const double mval[2], float output[3])
91 const int winx = t->ar ? t->ar->winx : 1;
93 output[0] = ((mval[0] - mi->imval[0]) / winx) * 2.0f;
96 static void InputHorizontalAbsolute(TransInfo *t, MouseInput *mi, const double mval[2], float output[3])
100 InputVector(t, mi, mval, vec);
101 project_v3_v3v3(vec, vec, t->viewinv[0]);
103 output[0] = dot_v3v3(t->viewinv[0], vec) * 2.0f;
106 static void InputVerticalRatio(TransInfo *t, MouseInput *mi, const double mval[2], float output[3])
108 const int winy = t->ar ? t->ar->winy : 1;
110 output[0] = ((mval[1] - mi->imval[1]) / winy) * 2.0f;
113 static void InputVerticalAbsolute(TransInfo *t, MouseInput *mi, const double mval[2], float output[3])
117 InputVector(t, mi, mval, vec);
118 project_v3_v3v3(vec, vec, t->viewinv[1]);
120 output[0] = dot_v3v3(t->viewinv[1], vec) * 2.0f;
123 void setCustomPoints(TransInfo *UNUSED(t), MouseInput *mi, const int mval_start[2], const int mval_end[2])
127 mi->data = MEM_reallocN(mi->data, sizeof(int) * 4);
131 data[0] = mval_start[0];
132 data[1] = mval_start[1];
133 data[2] = mval_end[0];
134 data[3] = mval_end[1];
137 static void InputCustomRatioFlip(TransInfo *UNUSED(t), MouseInput *mi, const double mval[2], float output[3])
142 const int *data = mi->data;
146 dx = data[2] - data[0];
147 dy = data[3] - data[1];
149 length = hypot(dx, dy);
151 mdx = mval[0] - data[2];
152 mdy = mval[1] - data[3];
154 distance = (length != 0.0) ? (mdx * dx + mdy * dy) / length : 0.0;
156 output[0] = (length != 0.0) ? (double)(distance / length) : 0.0;
160 static void InputCustomRatio(TransInfo *t, MouseInput *mi, const double mval[2], float output[3])
162 InputCustomRatioFlip(t, mi, mval, output);
163 output[0] = -output[0];
166 struct InputAngle_Data {
171 static void InputAngle(TransInfo *UNUSED(t), MouseInput *mi, const double mval[2], float output[3])
173 struct InputAngle_Data *data = mi->data;
174 double dx2 = mval[0] - (double)mi->center[0];
175 double dy2 = mval[1] - (double)mi->center[1];
176 double B = sqrt(dx2 * dx2 + dy2 * dy2);
178 double dx1 = data->mval_prev[0] - (double)mi->center[0];
179 double dy1 = data->mval_prev[1] - (double)mi->center[1];
180 double A = sqrt(dx1 * dx1 + dy1 * dy1);
182 double dx3 = mval[0] - data->mval_prev[0];
183 double dy3 = mval[1] - data->mval_prev[1];
185 /* use doubles here, to make sure a "1.0" (no rotation) doesn't become 9.999999e-01, which gives 0.02 for acos */
186 double deler = (((dx1 * dx1 + dy1 * dy1) +
187 (dx2 * dx2 + dy2 * dy2) -
188 (dx3 * dx3 + dy3 * dy3)) / (2.0 * (((A * B) != 0.0) ? (A * B) : 1.0)));
189 /* ((A * B) ? (A * B) : 1.0) this takes care of potential divide by zero errors */
193 dphi = saacos((float)deler);
194 if ((dx1 * dy2 - dx2 * dy1) > 0.0) dphi = -dphi;
196 /* If the angle is zero, because of lack of precision close to the 1.0 value in acos
197 * approximate the angle with the opposite side of the normalized triangle
198 * This is a good approximation here since the smallest acos value seems to be around
199 * 0.02 degree and lower values don't even have a 0.01% error compared to the approximation
213 dphi = sqrt(dx * dx + dy * dy);
214 if ((dx1 * dy2 - dx2 * dy1) > 0.0) dphi = -dphi;
217 data->angle += ((double)dphi) * (mi->precision ? (double)mi->precision_factor : 1.0);
219 data->mval_prev[0] = mval[0];
220 data->mval_prev[1] = mval[1];
222 output[0] = data->angle;
225 static void InputAngleSpring(TransInfo *t, MouseInput *mi, const double mval[2], float output[3])
229 InputAngle(t, mi, mval, output);
230 InputSpring(t, mi, mval, toutput);
232 output[1] = toutput[0];
235 void initMouseInput(TransInfo *UNUSED(t), MouseInput *mi, const float center[2], const int mval[2], const bool precision)
238 mi->precision = precision;
240 mi->center[0] = center[0];
241 mi->center[1] = center[1];
243 mi->imval[0] = mval[0];
244 mi->imval[1] = mval[1];
249 static void calcSpringFactor(MouseInput *mi)
251 mi->factor = sqrtf(((float)(mi->center[1] - mi->imval[1])) * ((float)(mi->center[1] - mi->imval[1])) +
252 ((float)(mi->center[0] - mi->imval[0])) * ((float)(mi->center[0] - mi->imval[0])));
254 if (mi->factor == 0.0f) {
255 mi->factor = 1.0f; /* prevent Inf */
259 void initMouseInputMode(TransInfo *t, MouseInput *mi, MouseInputMode mode)
261 /* incase we allocate a new value */
262 void *mi_data_prev = mi->data;
264 mi->use_virtual_mval = true;
265 mi->precision_factor = 1.0f / 10.0f;
269 mi->apply = InputVector;
270 t->helpline = HLP_NONE;
273 calcSpringFactor(mi);
274 mi->apply = InputSpring;
275 t->helpline = HLP_SPRING;
277 case INPUT_SPRING_FLIP:
278 calcSpringFactor(mi);
279 mi->apply = InputSpringFlip;
280 t->helpline = HLP_SPRING;
282 case INPUT_SPRING_DELTA:
283 calcSpringFactor(mi);
284 mi->apply = InputSpringDelta;
285 t->helpline = HLP_SPRING;
288 case INPUT_ANGLE_SPRING:
290 struct InputAngle_Data *data;
291 mi->use_virtual_mval = false;
292 mi->precision_factor = 1.0f / 30.0f;
293 data = MEM_callocN(sizeof(struct InputAngle_Data), "angle accumulator");
294 data->mval_prev[0] = mi->imval[0];
295 data->mval_prev[1] = mi->imval[1];
297 if (mode == INPUT_ANGLE) {
298 mi->apply = InputAngle;
301 calcSpringFactor(mi);
302 mi->apply = InputAngleSpring;
304 t->helpline = HLP_ANGLE;
307 case INPUT_TRACKBALL:
308 mi->precision_factor = 1.0f / 30.0f;
309 /* factor has to become setting or so */
311 mi->apply = InputTrackBall;
312 t->helpline = HLP_TRACKBALL;
314 case INPUT_HORIZONTAL_RATIO:
315 mi->apply = InputHorizontalRatio;
316 t->helpline = HLP_HARROW;
318 case INPUT_HORIZONTAL_ABSOLUTE:
319 mi->apply = InputHorizontalAbsolute;
320 t->helpline = HLP_HARROW;
322 case INPUT_VERTICAL_RATIO:
323 mi->apply = InputVerticalRatio;
324 t->helpline = HLP_VARROW;
326 case INPUT_VERTICAL_ABSOLUTE:
327 mi->apply = InputVerticalAbsolute;
328 t->helpline = HLP_VARROW;
330 case INPUT_CUSTOM_RATIO:
331 mi->apply = InputCustomRatio;
332 t->helpline = HLP_NONE;
334 case INPUT_CUSTOM_RATIO_FLIP:
335 mi->apply = InputCustomRatioFlip;
336 t->helpline = HLP_NONE;
344 /* if we've allocated new data, free the old data
345 * less hassle then checking before every alloc above */
346 if (mi_data_prev && (mi_data_prev != mi->data)) {
347 MEM_freeN(mi_data_prev);
350 /* bootstrap mouse input with initial values */
351 applyMouseInput(t, mi, mi->imval, t->values);
354 void setInputPostFct(MouseInput *mi, void (*post)(struct TransInfo *t, float values[3]))
359 void applyMouseInput(TransInfo *t, MouseInput *mi, const int mval[2], float output[3])
363 if (mi->use_virtual_mval) {
364 /* update accumulator */
365 double mval_delta[2];
367 mval_delta[0] = (mval[0] - mi->imval[0]) - mi->virtual_mval.prev[0];
368 mval_delta[1] = (mval[1] - mi->imval[1]) - mi->virtual_mval.prev[1];
370 mi->virtual_mval.prev[0] += mval_delta[0];
371 mi->virtual_mval.prev[1] += mval_delta[1];
374 mval_delta[0] *= (double)mi->precision_factor;
375 mval_delta[1] *= (double)mi->precision_factor;
378 mi->virtual_mval.accum[0] += mval_delta[0];
379 mi->virtual_mval.accum[1] += mval_delta[1];
381 mval_db[0] = mi->imval[0] + mi->virtual_mval.accum[0];
382 mval_db[1] = mi->imval[1] + mi->virtual_mval.accum[1];
385 mval_db[0] = mval[0];
386 mval_db[1] = mval[1];
390 if (mi->apply != NULL) {
391 mi->apply(t, mi, mval_db, output);
399 eRedrawFlag handleMouseInput(TransInfo *t, MouseInput *mi, const wmEvent *event)
401 eRedrawFlag redraw = TREDRAW_NOTHING;
403 switch (event->type) {
406 if (event->val == KM_PRESS) {
407 t->modifiers |= MOD_PRECISION;
408 /* shift is modifier for higher precision transforn */
410 redraw = TREDRAW_HARD;
412 else if (event->val == KM_RELEASE) {
413 t->modifiers &= ~MOD_PRECISION;
415 redraw = TREDRAW_HARD;