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
17 /** \file
18 * \ingroup bli
19 */
20
21 #include "BLI_dial_2d.h"
22 #include "BLI_math.h"
23
24 #include "MEM_guardedalloc.h"
25
26 struct Dial {
27 /* center of the dial */
28 float center[2];
29
30 /* threshold of the dial. Distance of current position has to be greater
31 * than the threshold to be used in any calculations */
32 float threshold_squared;
33
34 /* the direction of the first dial position exceeding the threshold. This
35 * is later used as the basis against which rotation angle is calculated */
36 float initial_direction[2];
37
38 /* cache the last angle to detect rotations bigger than -/+ PI */
39 float last_angle;
40
41 /* number of full rotations */
42 int rotations;
43
44 /* has initial_direction been initialized */
45 bool initialized;
46 };
47
BLI_dial_init(const float start_position[2],float threshold)48 Dial *BLI_dial_init(const float start_position[2], float threshold)
49 {
50 Dial *dial = MEM_callocN(sizeof(Dial), "dial");
51
52 copy_v2_v2(dial->center, start_position);
53 dial->threshold_squared = threshold * threshold;
54
55 return dial;
56 }
57
BLI_dial_angle(Dial * dial,const float current_position[2])58 float BLI_dial_angle(Dial *dial, const float current_position[2])
59 {
60 float current_direction[2];
61
62 sub_v2_v2v2(current_direction, current_position, dial->center);
63
64 /* only update when we have enough precision,
65 * by having the mouse adequately away from center */
66 if (len_squared_v2(current_direction) > dial->threshold_squared) {
67 float angle;
68 float cosval, sinval;
69
70 normalize_v2(current_direction);
71
72 if (!dial->initialized) {
73 copy_v2_v2(dial->initial_direction, current_direction);
74 dial->initialized = true;
75 }
76
77 /* calculate mouse angle between initial and final mouse position */
78 cosval = dot_v2v2(current_direction, dial->initial_direction);
79 sinval = cross_v2v2(current_direction, dial->initial_direction);
80
81 /* clamp to avoid nans in acos */
82 angle = atan2f(sinval, cosval);
83
84 /* change of sign, we passed the 180 degree threshold. This means we need to add a turn.
85 * to distinguish between transition from 0 to -1 and -PI to +PI,
86 * use comparison with PI/2 */
87 if ((angle * dial->last_angle < 0.0f) && (fabsf(dial->last_angle) > (float)M_PI_2)) {
88 if (dial->last_angle < 0.0f) {
89 dial->rotations--;
90 }
91 else {
92 dial->rotations++;
93 }
94 }
95 dial->last_angle = angle;
96
97 return angle + 2.0f * (float)M_PI * dial->rotations;
98 }
99
100 return dial->last_angle;
101 }
102