import colors from polygon import * from matrix import * class Portal(Polygon): """ Portal class as a 2-element 'Polygon' object. In for both sides of the portal there is an offset vector and a distortion matrix. """ # constructor: create self as Polygon object def __init__(self, start, end, M=((1,0),(0,1)), M2=None, offset=None, offset2=None, color=colors.BRIGHT_BLUE): """ Initilises 'self' with standard color 'BRIGHT_BLUE'. offset -- for side 1 (std: None == portal will remain in same place) offset2 -- for side 2 (std: None == portal will remain in same place) M -- distortion matrix for side 1 (std: unit matrix) M2 -- distortion matrix for side 2 (std: None == inverse of M) """ Polygon.__init__(self, start, end, color=color) self.M = Matrix(*M) if M2 is None: self.M2 = self.M.inverse() else: self.M2 = Matrix(*M2) if offset is None: self.offset = None else: self.offset = Vector(*offset) if offset2 is None: self.offset2 = None else: self.offset2 = Vector(*offset2) def __getattr__(self, attr): """ Return 'self[0]' and 'self[1]' as attribute 'start' and 'end'. """ if attr == 'start': return self[0] if attr == 'end': return self[1] def go_through(self, dir, world): """ Return the portal world of 'world' for the side in direction 'dir'. """ portal_world = [] for object in world: port = object.__class__(*object) port.__dict__ = object.__dict__ # gone through side 1 if dir.is_between(self.end - self.start, self.start - self.end): for i, _ in enumerate(object): if self.offset is None: # middle of portal in portal world offset = self.M.prod((self.start + self.end) / 2) - (self.start + self.end) / 2 else: offset = self.offset # apply distortion and offset port[i] = self.M.prod(object[i]) - offset # if world gets mirrored the side-sensitive portals have to be reverted if type(object) is Portal: if self.M.i.angle_to(self.M.j) > 180: port[0], port[1] = port[1], port[0] # gone through side 2 else: for i, _ in enumerate(object): if self.offset2 is None: # middle of portal in portal world offset2 = self.M2.prod((self.start + self.end) / 2) - (self.start + self.end) / 2 else: offset2 = self.offset2 # apply distortion and offset port[i] = self.M2.prod(object[i]) - offset2 # if world gets mirrored the side-sensitive portals have to be reverted if type(object) is Portal: if self.M2.i.angle_to(self.M2.j) > 180: port[0], port[1] = port[1], port[0] # add distorted object to portal world portal_world.append(port) return portal_world def portal_world(self, world, source): portal_world = [] for object in world: port = object.__class__(*object) port.__dict__ = object.__dict__ # gone through side 1 if (self.start - source).is_between(self.end - self.start, self.start - self.end): for i, _ in enumerate(object): if self.offset is None: # middle of portal in portal world offset = self.M.prod((self.start + self.end) / 2) - (self.start + self.end) / 2 else: offset = self.offset # apply distortion and offset port[i] = self.M.prod(object[i]) - offset # if world gets mirrored the side-sensitive portals have to be reverted if type(object) is Portal: if self.M.i.angle_to(self.M.j) > 180: port[0], port[1] = port[1], port[0] # gone through side 2 else: for i, _ in enumerate(object): if self.offset2 is None: # middle of portal in portal world offset2 = self.M2.prod((self.start + self.end) / 2) - (self.start + self.end) / 2 else: offset2 = self.offset2 # apply distortion and offset port[i] = self.M2.prod(object[i]) - offset2 # if world gets mirrored the side-sensitive portals have to be reverted if type(object) is Portal: if self.M2.i.angle_to(self.M2.j) > 180: port[0], port[1] = port[1], port[0] # add distorted and truncated object to portal world cut = port.cut(source, self) if cut: # not empty portal_world.append(cut) return portal_world def draw(self, screen, offset): """ Draw the endpoints of 'self' on 'screen'. """ for point in self: Polygon(point).draw(screen, offset) def draw_shadow(self, screen, offset, source): """ Draw draw a shadow of 'self' on 'screen' respecting the light-'source'. """ # draw the shadow in the same color as the background to wipe the canvas Polygon.draw_shadow(self, screen, offset, source, color=colors.GRAY) # draw shadows for the end points of portal for point in self: Polygon(point).draw_shadow(screen, offset, source) def cut(self, source, portal): """ Return 'self' but cut to fit in the view from 'source' though 'portal'. Makes use of 'Polygon.cut'. """ rest = Polygon.cut(self, source, portal) if len(rest) == 2: return Portal(*reversed(rest[:2]), M=self.M, color=self.color) return Polygon()