Blender V5.0
BLI_dial_2d.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2023 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
8
9#include "BLI_dial_2d.h"
10
11#include "MEM_guardedalloc.h"
12
13#include "BLI_math_vector.h"
14
15struct Dial {
16 /* center of the dial */
17 float center[2];
18
19 /* threshold of the dial. Distance of current position has to be greater
20 * than the threshold to be used in any calculations */
22
23 /* the direction of the first dial position exceeding the threshold. This
24 * is later used as the basis against which rotation angle is calculated */
26
27 /* cache the last angle to detect rotations bigger than -/+ PI */
29
30 /* number of full rotations */
32
33 /* has initial_direction been initialized */
35};
36
37Dial *BLI_dial_init(const float start_position[2], float threshold)
38{
39 Dial *dial = MEM_callocN<Dial>("dial");
40
41 copy_v2_v2(dial->center, start_position);
42 dial->threshold_squared = threshold * threshold;
43
44 return dial;
45}
46
48{
49 MEM_freeN(dial);
50}
51
52float BLI_dial_angle(Dial *dial, const float current_position[2])
53{
54 float current_direction[2];
55
56 sub_v2_v2v2(current_direction, current_position, dial->center);
57
58 /* only update when we have enough precision,
59 * by having the mouse adequately away from center */
60 if (len_squared_v2(current_direction) > dial->threshold_squared) {
61 float angle;
62 float cosval, sinval;
63
64 normalize_v2(current_direction);
65
66 if (!dial->initialized) {
67 copy_v2_v2(dial->initial_direction, current_direction);
68 dial->initialized = true;
69 }
70
71 /* calculate mouse angle between initial and final mouse position */
72 cosval = dot_v2v2(current_direction, dial->initial_direction);
73 sinval = cross_v2v2(current_direction, dial->initial_direction);
74
75 /* Clamp to avoid NAN's in #acos */
76 angle = atan2f(sinval, cosval);
77
78 /* change of sign, we passed the 180 degree threshold. This means we need to add a turn.
79 * to distinguish between transition from 0 to -1 and -PI to +PI,
80 * use comparison with PI/2 */
81 if ((angle * dial->last_angle < 0.0f) && (fabsf(dial->last_angle) > float(M_PI_2))) {
82 if (dial->last_angle < 0.0f) {
83 dial->rotations--;
84 }
85 else {
86 dial->rotations++;
87 }
88 }
89 dial->last_angle = angle;
90
91 return angle + 2.0f * float(M_PI) * dial->rotations;
92 }
93
94 return dial->last_angle;
95}
void BLI_dial_free(Dial *dial)
Dial * BLI_dial_init(const float start_position[2], float threshold)
float BLI_dial_angle(Dial *dial, const float current_position[2])
#define M_PI_2
#define M_PI
MINLINE float len_squared_v2(const float v[2]) ATTR_WARN_UNUSED_RESULT
MINLINE void copy_v2_v2(float r[2], const float a[2])
MINLINE float cross_v2v2(const float a[2], const float b[2]) ATTR_WARN_UNUSED_RESULT
MINLINE void sub_v2_v2v2(float r[2], const float a[2], const float b[2])
MINLINE float dot_v2v2(const float a[2], const float b[2]) ATTR_WARN_UNUSED_RESULT
MINLINE float normalize_v2(float n[2])
static double angle(const Eigen::Vector3d &v1, const Eigen::Vector3d &v2)
Definition IK_Math.h:117
Read Guarded memory(de)allocation.
nullptr float
void * MEM_callocN(size_t len, const char *str)
Definition mallocn.cc:118
void MEM_freeN(void *vmemh)
Definition mallocn.cc:113
#define fabsf
#define atan2f
float center[2]
float initial_direction[2]
float last_angle
float threshold_squared
bool initialized
int rotations