SameBoy | Accurate GB/GBC emulator |
| download: https://git.y1.nz/archives/sameboy.tar.gz | |
| README | Files | Log | Refs | LICENSE |
JoyKit/JOYAxes2D.m
1 #import "JOYAxes2D.h"
2 #import "JOYElement.h"
3
4 @interface JOYAxes2D()
5 @property unsigned rotation; // in 90 degrees units, clockwise
6 @end
7
8 @implementation JOYAxes2D
9 {
10 JOYElement *_element1, *_element2;
11 double _state1, _state2;
12 int32_t _initialX, _initialY;
13 int32_t _minX, _minY;
14 int32_t _maxX, _maxY;
15 }
16
17 + (NSString *)usageToString: (JOYAxes2DUsage) usage
18 {
19 if (usage < JOYAxes2DUsageNonGenericMax) {
20 return inline_const(NSString *[], {
21 @"None",
22 @"Left Stick",
23 @"Right Stick",
24 @"Middle Stick",
25 @"Pointer",
26 })[usage];
27 }
28 if (usage >= JOYAxes2DUsageGeneric0) {
29 return [NSString stringWithFormat:@"Generic 2D Analog Control %d", usage - JOYAxes2DUsageGeneric0];
30 }
31
32 return [NSString stringWithFormat:@"Unknown Usage 2D Axes %d", usage];
33 }
34
35 - (NSString *)usageString
36 {
37 return [self.class usageToString:_usage];
38 }
39
40 - (uint64_t)uniqueID
41 {
42 return _element1.uniqueID | (uint64_t)self.combinedIndex << 32;
43 }
44
45 - (NSString *)description
46 {
47 return [NSString stringWithFormat:@"<%@: %p, %@ (%llx); State: %.2f%%, %.2f degrees>", self.className, self, self.usageString, self.uniqueID, self.distance * 100, self.angle];
48 }
49
50 - (instancetype)initWithFirstElement:(JOYElement *)element1 secondElement:(JOYElement *)element2
51 {
52 self = [super init];
53 if (!self) return self;
54
55 _element1 = element1;
56 _element2 = element2;
57
58
59 if (element1.usagePage == kHIDPage_GenericDesktop) {
60 uint16_t usage = element1.usage;
61 _usage = JOYAxes2DUsageGeneric0 + usage - kHIDUsage_GD_X + 1;
62 }
63 _initialX = 0;
64 _initialY = 0;
65 _minX = element1.max;
66 _minY = element2.max;
67 _maxX = element1.min;
68 _maxY = element2.min;
69
70 return self;
71 }
72
73 - (NSPoint)value
74 {
75 return NSMakePoint(_state1, _state2);
76 }
77
78 - (int32_t)effectiveMinX
79 {
80 int32_t rawMin = _element1.min;
81 int32_t rawMax = _element1.max;
82 if (_initialX == 0) return rawMin;
83 if (_minX <= (rawMin * 2 + _initialX) / 3 && _maxX >= (rawMax * 2 + _initialX) / 3 ) return _minX;
84 if ((_initialX - rawMin) < (rawMax - _initialX)) return rawMin;
85 return _initialX - (rawMax - _initialX);
86 }
87
88 - (int32_t)effectiveMinY
89 {
90 int32_t rawMin = _element2.min;
91 int32_t rawMax = _element2.max;
92 if (_initialY == 0) return rawMin;
93 if (_minX <= (rawMin * 2 + _initialY) / 3 && _maxY >= (rawMax * 2 + _initialY) / 3 ) return _minY;
94 if ((_initialY - rawMin) < (rawMax - _initialY)) return rawMin;
95 return _initialY - (rawMax - _initialY);
96 }
97
98 - (int32_t)effectiveMaxX
99 {
100 int32_t rawMin = _element1.min;
101 int32_t rawMax = _element1.max;
102 if (_initialX == 0) return rawMax;
103 if (_minX <= (rawMin * 2 + _initialX) / 3 && _maxX >= (rawMax * 2 + _initialX) / 3 ) return _maxX;
104 if ((_initialX - rawMin) > (rawMax - _initialX)) return rawMax;
105 return _initialX + (_initialX - rawMin);
106 }
107
108 - (int32_t)effectiveMaxY
109 {
110 int32_t rawMin = _element2.min;
111 int32_t rawMax = _element2.max;
112 if (_initialY == 0) return rawMax;
113 if (_minX <= (rawMin * 2 + _initialY) / 3 && _maxY >= (rawMax * 2 + _initialY) / 3 ) return _maxY;
114 if ((_initialY - rawMin) > (rawMax - _initialY)) return rawMax;
115 return _initialY + (_initialY - rawMin);
116 }
117
118 - (bool)updateState
119 {
120 int32_t x = [_element1 value];
121 int32_t y = [_element2 value];
122 if (x == 0 && y == 0) return false;
123
124 if (_initialX == 0 && _initialY == 0) {
125 _initialX = x;
126 _initialY = y;
127 }
128
129 double old1 = _state1, old2 = _state2;
130 {
131 int32_t value = x;
132
133 if (_initialX != 0) {
134 _minX = MIN(value, _minX);
135 _maxX = MAX(value, _maxX);
136 }
137
138 double min = [self effectiveMinX];
139 double max = [self effectiveMaxX];
140 if (min == max) return false;
141
142 _state1 = (value - min) / (max - min) * 2 - 1;
143 }
144
145 {
146 int32_t value = y;
147
148 if (_initialY != 0) {
149 _minY = MIN(value, _minY);
150 _maxY = MAX(value, _maxY);
151 }
152
153 double min = [self effectiveMinY];
154 double max = [self effectiveMaxY];
155 if (min == max) return false;
156
157 _state2 = (value - min) / (max - min) * 2 - 1;
158 }
159
160 if (_state1 < -1 || _state1 > 1 ||
161 _state2 < -1 || _state2 > 1) {
162 // Makes no sense, recalibrate
163 _state1 = _state2 = 0;
164 _initialX = _initialY = 0;
165 _minX = _element1.max;
166 _minY = _element2.max;
167 _maxX = _element1.min;
168 _maxY = _element2.min;
169 }
170
171
172 double temp = _state1;
173 switch (_rotation & 3) {
174 case 0: break;
175 case 1:
176 _state1 = -_state2;
177 _state2 = temp;
178 break;
179 case 2:
180 _state1 = -_state1;
181 _state2 = -_state2;
182 break;
183 case 3:
184 _state1 = _state2;
185 _state2 = -temp;
186 break;
187 }
188
189 return old1 != _state1 || old2 != _state2;
190 }
191
192 - (double)distance
193 {
194 return MIN(sqrt(_state1 * _state1 + _state2 * _state2), 1.0);
195 }
196
197 - (double)angle
198 {
199 double temp = atan2(_state2, _state1) * 180 / M_PI;
200 if (temp >= 0) return temp;
201 return temp + 360;
202 }
203
204 @end
This webpage is intended to be an accessible preview of this repository. To get a fuller picture, clone it and use the git CLI.