doxygen: add newline after \file
[blender.git] / source / blender / blenlib / intern / BLI_dial_2d.c
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
48
49 Dial *BLI_dial_initialize(const float start_position[2], float threshold)
50 {
51         Dial *dial = MEM_callocN(sizeof(Dial), "dial");
52
53         copy_v2_v2(dial->center, start_position);
54         dial->threshold_squared = threshold * threshold;
55
56         return dial;
57 }
58
59 float BLI_dial_angle(Dial *dial, const float current_position[2])
60 {
61         float current_direction[2];
62
63         sub_v2_v2v2(current_direction, current_position, dial->center);
64
65         /* only update when we have enough precision,
66          * by having the mouse adequately away from center */
67         if (len_squared_v2(current_direction) > dial->threshold_squared) {
68                 float angle;
69                 float cosval, sinval;
70
71                 normalize_v2(current_direction);
72
73                 if (!dial->initialized) {
74                         copy_v2_v2(dial->initial_direction, current_direction);
75                         dial->initialized = true;
76                 }
77
78                 /* calculate mouse angle between initial and final mouse position */
79                 cosval = dot_v2v2(current_direction, dial->initial_direction);
80                 sinval = cross_v2v2(current_direction, dial->initial_direction);
81
82                 /* clamp to avoid nans in acos */
83                 angle = atan2f(sinval, cosval);
84
85                 /* change of sign, we passed the 180 degree threshold. This means we need to add a turn.
86                  * to distinguish between transition from 0 to -1 and -PI to +PI,
87                  * use comparison with PI/2 */
88                 if ((angle * dial->last_angle < 0.0f) &&
89                     (fabsf(dial->last_angle) > (float)M_PI_2))
90                 {
91                         if (dial->last_angle < 0.0f)
92                                 dial->rotations--;
93                         else
94                                 dial->rotations++;
95                 }
96                 dial->last_angle = angle;
97
98                 return angle + 2.0f * (float)M_PI * dial->rotations;
99         }
100
101         return dial->last_angle;
102 }