git.y1.nz

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.