Source code for redeclipse.vector

from math import sin, cos, pi, floor, atan2


[docs]class BaseVector(object): def __hash__(self): return (0 << 31) + (floor(self.x) << 20) + (floor(self.y) << 10) + floor(self.z) def __eq__(self, other): return isinstance(other, BaseVector) and \ self.x == other.x and \ self.y == other.y and \ self.z == other.z def __init__(self, x, y, z): self.x = x self.y = y self.z = z def __add__(self, other): if self.__class__ == other.__class__: return self.__class__( self.x + other.x, self.y + other.y, self.z + other.z, ) else: raise Exception("addition not defined for dissimilar classes") def __sub__(self, other): if self.__class__ == other.__class__: return self.__class__( self.x - other.x, self.y - other.y, self.z - other.z, ) else: raise Exception("addition not defined for dissimilar classes") def __mul__(self, m): return self.__class__( self.x * m, self.y * m, self.z * m ) def __truediv__(self, m): return self.__class__( self.x / m, self.y / m, self.z / m ) def __floordiv__(self, m): return self.__class__( floor(self.x / m), floor(self.y / m), floor(self.z / m) ) def __repr__(self): return 'BV(%s, %s, %s)' % (self.x, self.y, self.z) # return 'BV(%s, %s, %s)VBV(%s, %s, %s)' % (self.x, self.y, self.z, floor(self.x), floor(self.y), floor(self.z)) def __getitem__(self, key): if key == 0: return self.x elif key == 1: return self.y elif key == 2: return self.z raise Exception("Could not access %s" % key) def __len__(self): return 3 def __iter__(self): yield self.x yield self.y yield self.z def __lt__(self, other): """ https://math.stackexchange.com/questions/54655/how-to-compare-points-in-multi-dimensional-space This ordering isn't a super logical one, but it is a functional one which is what we need to provide sorted() ability. """ if self.x < other.x: return True elif self.x == other.x: if self.y < other.y: return True elif self.y == other.y: if self.z < other.z: return True return False def __le__(self, other): return self == other or self < other
[docs] def vox(self): """ Return a voxel-appropriate version of self (i.e. apply floor to drag it to the right location.) """ return self.__class__(floor(self.x), floor(self.y), floor(self.z))
[docs] def rotate(self, deg): """ Return a new vector, rotated by the specified number of degrees. :param deg: degrees (0, 90, 180, ...) or orientation (+x, -x, ...) :type deg: str or int Note that rotation is counterclockwise around the z=+1 axis. I.e. use the right handle rule. :returns: a new ``redeclipse.vector.BaseVector``, rotated as needed. :rtype: redeclipse.vector.BaseVector """ if not isinstance(deg, int): y = deg.y x = deg.x theta = atan2(y, x) else: theta = pi * deg / 180 # We allow 0.5 increments for properly centered cubes. So we need to do # the math in double, round it there, then divide by two to ensure # everything is in (0, 0.5, 1, 1.5, ...) return self.__class__( round((2 * self.x * cos(theta)) - (2 * self.y * sin(theta))) / 2, round((2 * self.x * sin(theta)) + (2 * self.y * cos(theta))) / 2, self.z )
[docs] def offset_rotate(self, deg, offset=None): """ Return a new vector, rotated by the specified number of degrees. :param deg: angle (EAST, NORTH, WEST, SOUTH) :type deg: redeclipse.vector.CoarseVector :param offset: point around which to rotate :type offset: redeclipse.vector.BaseVector (or child thereof) Note that rotation is counterclockwise around the z=+1 axis. I.e. use the right handle rule. :returns: a new ``redeclipse.vector.BaseVector``, rotated as needed. :rtype: redeclipse.vector.BaseVector """ # TODO: We could auto-vox_off, not sure if this is useful? return (self + offset).rotate(deg) - offset
[docs]class FineVector(BaseVector): def __hash__(self): return (1 << 31) + (floor(self.x) << 20) + (floor(self.y) << 10) + floor(self.z) def __eq__(self, other): if isinstance(other, CoarseVector): # If the other one is coarse, convert the other to fine so they're # the same. tmp = other.fine() return \ self.x == tmp.x and \ self.y == tmp.y and \ self.z == tmp.z elif isinstance(other, FineVector): # Otherwise just compare as-is return \ self.x == other.x and \ self.y == other.y and \ self.z == other.z else: return False def __repr__(self): rp = super().__repr__() return 'FV(%s)' % rp
[docs] def fine(self): return self
def __add__(self, other): a = self.fine() b = other.fine() return FineVector( a.x + b.x, a.y + b.y, a.z + b.z, ) def __sub__(self, other): a = self.fine() b = other.fine() return FineVector( a.x - b.x, a.y - b.y, a.z - b.z, )
[docs] def coarse(self): return CoarseVector( self.x // 8, self.y // 8, self.z // 8 )
[docs] def entity(self): return self.fine() * 4
[docs]class CoarseVector(BaseVector): def __hash__(self): return (2 << 31) + (floor(self.x) << 20) + (floor(self.y) << 10) + floor(self.z) def __eq__(self, other): if isinstance(other, CoarseVector): # If we're both coarse, compare as-is return \ self.x == other.x and \ self.y == other.y and \ self.z == other.z elif isinstance(other, FineVector): # If the other one is fine, convert ourselves to find so we can # compare tmp = self.fine() return \ tmp.x == other.x and \ tmp.y == other.y and \ tmp.z == other.z else: return False def __repr__(self): rp = super().__repr__() return 'CV(%s)' % rp
[docs] def fine(self): newvec = self * 8 return FineVector( newvec.x, newvec.y, newvec.z )
def __add__(self, other): if isinstance(other, CoarseVector): return CoarseVector( self.x + other.x, self.y + other.y, self.z + other.z, ) elif isinstance(other, FineVector): a = self.fine() b = other.fine() return FineVector( a.x + b.x, a.y + b.y, a.z + b.z, ) else: raise Exception("Unsupported operand") def __sub__(self, other): if isinstance(other, CoarseVector): return CoarseVector( self.x - other.x, self.y - other.y, self.z - other.z, ) elif isinstance(other, FineVector): a = self.fine() b = other.fine() return FineVector( a.x - b.x, a.y - b.y, a.z - b.z, ) else: raise Exception("Unsupported operand")
[docs] def entity(self): return self.fine() * 4