Hier werden die Unterschiede zwischen zwei Versionen gezeigt.
Beide Seiten der vorigen Revision Vorhergehende Überarbeitung Nächste Überarbeitung | Vorhergehende Überarbeitung | ||
ss20:neg_drawables [2020/09/11 14:27] srather [Floor] |
ss20:neg_drawables [2020/09/11 14:28] (aktuell) srather [Portal] |
||
---|---|---|---|
Zeile 551: | Zeile 551: | ||
==== Portal ==== | ==== Portal ==== | ||
<file python portal.py> | <file python portal.py> | ||
+ | import colors | ||
from polygon import * | from polygon import * | ||
from matrix import * | from matrix import * | ||
- | import colors | ||
class Portal(Polygon): | class Portal(Polygon): | ||
""" | """ | ||
- | Portal class as drawable object. Implements functionality of abstract drawable class. | + | 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 | # constructor: create self as Polygon object | ||
- | def __init__(self, start, end, M=((1,0),(0,1)), M2=None, offset=None, offset2=None, color=(0,0,255)): | + | 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) | Polygon.__init__(self, start, end, color=color) | ||
+ | |||
self.M = Matrix(*M) | self.M = Matrix(*M) | ||
if M2 is None: | if M2 is None: | ||
Zeile 580: | Zeile 591: | ||
- | # Overrides attribute access | ||
def __getattr__(self, attr): | def __getattr__(self, attr): | ||
""" | """ | ||
Zeile 590: | Zeile 600: | ||
if attr == 'end': | if attr == 'end': | ||
return self[1] | return self[1] | ||
- | if attr == 'M': | ||
- | return self.__dict__['M'] | ||
- | if attr == 'offset': | ||
- | return self.__dict__['offset'] | ||
- | if attr == 'color': | ||
- | return self.__dict__['color'] | ||
- | |||
- | raise AttributeError(f"{self.__class__.__name__} has no Attribute {attr}.") | ||
def go_through(self, dir, world): | def go_through(self, dir, world): | ||
+ | """ | ||
+ | Return the portal world of 'world' for the side in direction 'dir'. | ||
+ | """ | ||
+ | |||
portal_world = [] | portal_world = [] | ||
Zeile 607: | Zeile 613: | ||
port.__dict__ = object.__dict__ | port.__dict__ = object.__dict__ | ||
+ | # gone through side 1 | ||
if dir.is_between(self.end - self.start, self.start - self.end): | if dir.is_between(self.end - self.start, self.start - self.end): | ||
for i, _ in enumerate(object): | for i, _ in enumerate(object): | ||
if self.offset is None: | 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 | offset = self.M.prod((self.start + self.end) / 2) - (self.start + self.end) / 2 | ||
else: | else: | ||
offset = self.offset | offset = self.offset | ||
+ | # apply distortion and offset | ||
port[i] = self.M.prod(object[i]) - 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 type(object) is Portal: | ||
if self.M.i.angle_to(self.M.j) > 180: | if self.M.i.angle_to(self.M.j) > 180: | ||
port[0], port[1] = port[1], port[0] | port[0], port[1] = port[1], port[0] | ||
+ | |||
+ | # gone through side 2 | ||
else: | else: | ||
for i, _ in enumerate(object): | for i, _ in enumerate(object): | ||
if self.offset2 is None: | 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 | offset2 = self.M2.prod((self.start + self.end) / 2) - (self.start + self.end) / 2 | ||
else: | else: | ||
offset2 = self.offset2 | offset2 = self.offset2 | ||
+ | # apply distortion and offset | ||
port[i] = self.M2.prod(object[i]) - offset2 | 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 type(object) is Portal: | ||
if self.M2.i.angle_to(self.M2.j) > 180: | if self.M2.i.angle_to(self.M2.j) > 180: | ||
port[0], port[1] = port[1], port[0] | port[0], port[1] = port[1], port[0] | ||
+ | # add distorted object to portal world | ||
portal_world.append(port) | portal_world.append(port) | ||
Zeile 644: | Zeile 660: | ||
port.__dict__ = object.__dict__ | port.__dict__ = object.__dict__ | ||
+ | # gone through side 1 | ||
if (self.start - source).is_between(self.end - self.start, self.start - self.end): | if (self.start - source).is_between(self.end - self.start, self.start - self.end): | ||
for i, _ in enumerate(object): | for i, _ in enumerate(object): | ||
if self.offset is None: | 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 | offset = self.M.prod((self.start + self.end) / 2) - (self.start + self.end) / 2 | ||
else: | else: | ||
offset = self.offset | offset = self.offset | ||
+ | # apply distortion and offset | ||
port[i] = self.M.prod(object[i]) - 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 type(object) is Portal: | ||
if self.M.i.angle_to(self.M.j) > 180: | if self.M.i.angle_to(self.M.j) > 180: | ||
port[0], port[1] = port[1], port[0] | port[0], port[1] = port[1], port[0] | ||
+ | |||
+ | # gone through side 2 | ||
else: | else: | ||
for i, _ in enumerate(object): | for i, _ in enumerate(object): | ||
if self.offset2 is None: | 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 | offset2 = self.M2.prod((self.start + self.end) / 2) - (self.start + self.end) / 2 | ||
else: | else: | ||
offset2 = self.offset2 | offset2 = self.offset2 | ||
+ | # apply distortion and offset | ||
port[i] = self.M2.prod(object[i]) - offset2 | 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 type(object) is Portal: | ||
if self.M2.i.angle_to(self.M2.j) > 180: | if self.M2.i.angle_to(self.M2.j) > 180: | ||
port[0], port[1] = port[1], port[0] | port[0], port[1] = port[1], port[0] | ||
+ | # add distorted and truncated object to portal world | ||
cut = port.cut(source, self) | cut = port.cut(source, self) | ||
if cut: # not empty | if cut: # not empty | ||
Zeile 677: | Zeile 703: | ||
def draw(self, screen, offset): | def draw(self, screen, offset): | ||
- | pass | + | """ |
+ | Draw the endpoints of 'self' on 'screen'. | ||
+ | """ | ||
+ | |||
+ | for point in self: | ||
+ | Polygon(point).draw(screen, offset) | ||
- | # overrides draw_shadow() inherited from Polygon class | ||
def draw_shadow(self, screen, offset, source): | def draw_shadow(self, screen, offset, source): | ||
""" | """ | ||
- | Draws shadow of portal | + | Draw draw a shadow of 'self' on 'screen' respecting the light-'source'. |
- | Called by 'self.draw_shadow(screen, offset)' | + | |
""" | """ | ||
+ | # draw the shadow in the same color as the background to wipe the canvas | ||
Polygon.draw_shadow(self, screen, offset, source, color=colors.GRAY) | Polygon.draw_shadow(self, screen, offset, source, color=colors.GRAY) | ||
- | # Polygon.draw_shadow(self, screen, offset, source, color=(150,150,150)) | ||
+ | # draw shadows for the end points of portal | ||
for point in self: | for point in self: | ||
- | Ray(point, point - source, color=(0,0,0)).draw(screen, offset) | + | Polygon(point).draw_shadow(screen, offset, source) |
- | # Implements cut() from abstract drawable class | ||
def cut(self, source, portal): | 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) | rest = Polygon.cut(self, source, portal) | ||
- | if len(rest) >= 2: | + | if len(rest) == 2: |
return Portal(*reversed(rest[:2]), M=self.M, color=self.color) | return Portal(*reversed(rest[:2]), M=self.M, color=self.color) | ||