[Author Prev][Author Next][Thread Prev][Thread Next][Author Index][Thread Index]

Re: [pygame] Circular number system?



I use python's module operator (%) to accomplish what I think those ring classes are meant to accomplish.

def AngleDiff(a, b):
  return (a - b + 180.0) % 360.0 - 180.0

the function above will return a number from -180.0 to 180 that does the kind of difference you are talking about. i.e. AngleDiff(370, 20) return -10, cause 20 - 10 = 10, and 370 is the same as 10. So to tell what "direction" one angle is from another, you'd just look at the sign of the AngleDiff

note that the simple code works because python does a mathematically correct mod - where the result of the mod op always matches the sign of the modulo. So if you are used to other languages, you are probably used to the sign of the result matching the left hand side, which is generally less useful and needs a bunch of ifs and crap to write a similar function.

...I've often thought about writing an "Angle" class that is derivied from numbers and overrides arithmetic (so I can say diff = Angle(370) - Angle(20) and diff will be Angle(-10)) but sometimes doing that stuff leads to extra confusion...


On Sun, Feb 22, 2009 at 11:51 AM, Daniel Jo <ostsol@xxxxxxxxx> wrote:
What I mean by "circular" is sort of like a clock, where passing the
upper limit takes one back to the start again.  Two hours passed 11 on
a 12-hour clock is 1 o'clock.  Similarily three hours before 2 is 11
o'clock.  Another example is a compass.  Forty-five degrees left of
north, 0 degrees, is 315 degrees, rather than -45.

My need for it is for the latter example.  In such an example,
comparisons such as "less-than" and "greater-than" aren't so important
as "left-of" and "right-of", respectively.  Anything in the range
[180,360) is left of 0 and anything in the range (0, 180] is right of
0.  Similarily, anything in the range [270,360) and [0,90) is left of
0.

My implementation is more of a utility class than a data-type:

import math

class RingBase (object):
   def __init__ (self, lower, upper):
       self.lower = lower
       self.upper = upper
       self.span = upper - lower

   def __call__ (self, value):
       if value > self.upper:
           loops = int (value / self.span)
           return value - self.span * loops
       if value < self.lower:
           loops = int (abs (value) / self.span) + 1
           return value + self.span * loops
       return value

   def _lo (self, v1, v2):
       v1 = self (v1)
       v2 = self (v2)
       half = self.span * 0.5

       left = v2 - half

       if left < self.lower:
           if v1 >= self.upper - (v2 - self.lower):
               return True
           elif v1 < v2:
               return True
       elif v1 < v2 and v1 > left:
           return True

       return False

   def _ro (self, v1, v2):
       v1 = self (v1)
       v2 = self (v2)
       half = self.span * 0.5

       right = v2 + half

       if right >= self.upper:
           if v1 <= self.lower + (self.upper - v2):
               return True
           elif v1 > v2:
               return True
       elif v1 > v2 and v1 < right:
           return True

       return False

class Ring (RingBase):
   def __init__ (self, lower, upper):
       super (Ring, self).__init__ (lower, upper)

   lo = RingBase._lo
   ro = RingBase._ro

class Radian (RingBase):
   def __init__ (self):
       super (Radian, self).__init__ (0.0, 2.0 * math.pi)

   lo = RingBase._ro
   ro = RingBase._lo

An instance of Ring is callable and is used to clamp a value within
the Ring's limits.  For example. . .

ring = Ring (0, 12)
print ring (13)

. . . yields 1.

print ring (-1)

. . . yields 11.

ring.lo (11, 1)

. . . asks if 11 is left of 1 and returns True.

So is there anything else like this out there?

-Daniel