Benutzer-Werkzeuge

Webseiten-Werkzeuge


ss20:neg_drawables

Unterschiede

Hier werden die Unterschiede zwischen zwei Versionen gezeigt.

Link zu dieser Vergleichsansicht

Beide Seiten der vorigen Revision Vorhergehende Überarbeitung
ss20:neg_drawables [2020/09/11 14:27]
srather [Portal]
ss20:neg_drawables [2020/09/11 14:28] (aktuell)
srather [Portal]
Zeile 552: Zeile 552:
 <file python portal.py>​ <file python portal.py>​
 import colors import colors
-from drawable ​import * +from polygon ​import * 
-from ray import *+from matrix ​import *
  
  
-class Polygon(drawable):+class Portal(Polygon):
     """​     """​
-    ​Polygon ​class as 'drawable' ​objects+    ​Portal ​class as a 2-element ​'Polygon' ​object
-    ​Implements functionality ​of abstract drawable class.+    ​In for both sides of the portal there is 
 +    an offset vector and a distortion matrix.
     """​     """​
  
  
-    def __init__(self, ​*pointscolor=colors.BLACK)+    ​# constructor:​ create self as Polygon object 
-        """​Initilise '​self'​ with standard color '​BLACK'​."""​ +    ​def __init__(self, ​startend, M=((1,0),(0,1))M2=Noneoffset=None,​ offset2=Nonecolor=colors.BRIGHT_BLUE):
-        drawable.__init__(self*pointscolor=color) +
- +
- +
-    def draw(selfscreenoffset):+
         """​         """​
-        ​Draw '​self' ​as a polygon on 'screen'.+        ​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)
         """​         """​
  
-        ​if not self: # empty +        ​Polygon.__init__(self, start, end, color=color)
-            return+
  
-        ​polygon ​= self.copy()+        ​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)
  
-        # calculate real position on screen 
-        rect = Vector(*screen.get_rect()[2:​]) 
-        for i, _ in enumerate(polygon):​ 
-            polygon[i] = round(polygon[i] - offset + rect / 2) 
-            if polygon[i].length2() > 30000: # hotfix 
-                return 
  
-        # point +    def __getattr__(self, attr): 
-        if len(polygon== 1+        """​ 
-            ​pygame.gfxdraw.pixel(screen,​ *polygon[0]self.color) +        Return 'self[0]' and 'self[1]' as attribute '​start'​ and '​end'​
-        ​# line +        ​"""​ 
-        ​elif len(polygon) ​== 2+ 
-            ​pygame.draw.aaline(screen, ​self.color, polygon[0], polygon[1]) +        ​if attr == '​start'​
-        ​# polygon +            ​return ​self[0] 
-        else+        ​if attr == '​end'​
-            ​pygame.gfxdraw.aapolygon(screen,​ polygon, ​self.color) +            ​return ​self[1]
-            pygame.gfxdraw.filled_polygon(screen,​ polygon, self.color)+
  
  
-    def draw_shadow(self, ​screenoffset, source, color=colors.DARK_GRAY):+    def go_through(self, ​dirworld):
         """​         """​
-        ​Draw draw a shadow ​of 'self' on '​screen' ​respecting ​the light-'source'.+        ​Return the portal world of 'world' ​for the side in direction ​'dir'.
         """​         """​
  
-        ​if not self: # empty +        ​portal_world = []
-            return+
  
-        ​# point +        ​for object in world: 
-        elif len(self== 1: +            port = object.__class__(*object
-            ​Ray(self[0],​ self[0] - source, color=color).draw(screen,​ offset)+            ​port.__dict__ ​object.__dict__
  
-        ​line +            ​gone through side 1 
-        elif len(self) ​== 2+            if dir.is_between(self.end - self.start, self.start - self.end): 
-            # edgecasewould lead to DivisionByZeroError +                for i, _ in enumerate(object)
-            if self[0] == source or self[1] == source+                    if self.offset is None
-                for point in self: +                        # middle of portal ​in portal world 
-                    ​Ray(point,​ point - source, color=color).draw(screen, offset+                        ​offset ​self.M.prod((self.start + self.end/ 2) - (self.start + self.end) / 2 
-                ​return+                    ​else:​ 
 +                        offset = self.offset
  
-            ​corners of screen area +                    ​apply distortion and offset 
-            rect = screen.get_rect() +                    port[i] = self.M.prod(object[i]) - offset
-            corner = Vector(*rect[2:]) / 2 +
-            corners ​+
-                corner * Vector1, 1), +
-                corner * Vector(-1, 1), +
-                corner * Vector(-1,​-1),​ +
-                corner * Vector( 1,-1), +
-                corner * Vector( 1, 1), +
-                corner * Vector( 1,-1), +
-            ] +
-            # rays from source to both ends +
-            ray1 = Ray(self[0], self[0] - source) +
-            ray2 = Ray(self[1],​ self[1] ​source) +
-            # garantees anticlockwise order (important for calculations) +
-            if ray1.dir.angle_to(ray2.dir) > 180: +
-                ray1, ray2 = ray2, ray1+
  
-            ​creates minimal polygon +                ​if world gets mirrored the side-sensitive portals have to be reverted 
-            ​shadow = Polygon(color=color+                if type(objectis Portal: 
-            ​shadow.append(ray2.offscreen(screen,​ offset)) +                    if self.M.i.angle_to(self.M.j> 180: 
-            shadow.append(ray2.start) +                        ​port[0]port[1] = port[1], port[0]
-            shadow.append(ray1.start+
-            ​shadow.append(ray1.offscreen(screenoffset))+
  
-            # adds corners of screen to polygon if needed +            # gone through side 2 
-            for i, _ in enumerate(corners[1:​-1]): +            ​else: 
-                if corners[i].is_between(ray1.dir,​ ray2.dir)+                ​for i, _ in enumerate(object): 
-                    if corners[i-1].is_between(ray1.dir, ray2.dir)+                    if self.offset2 is None
-                        shadow.append(offset ​corners[i-1]) +                        # middle of portal in portal world 
-                    shadow.append(offset + corners[i]+                        offset2 = self.M2.prod((self.start + self.end/ 2) - (self.start self.end/ 2 
-                    ​if corners[i+1].is_between(ray1.dir,​ ray2.dir)+                    ​else
-                        ​shadow.append(offset + corners[i+1]) +                        ​offset2 = self.offset2
-                    break+
  
-            ​finally draws calculated shadow +                    ​apply distortion and offset 
-            ​shadow.draw(screen, offset)+                    ​port[i] = self.M2.prod(object[i]- offset2
  
-        ​polygon +                ​if world gets mirrored the side-sensitive portals have to be reverted 
-        else# draws shadow for every line in polygon +                if type(object) is Portal
-            ​for ​i, _ in enumerate(self[:-1]): +                    if self.M2.i.angle_to(self.M2.j> 180
-                ​Polygon(self[i], self[i+1]).draw_shadow(screen,​ offset, source) +                        port[0], port[1] = port[1], port[0]
-            Polygon(self[-1], self[0]).draw_shadow(screen,​ offset, source)+
  
 +            # add distorted object to portal world
 +            portal_world.append(port)
  
-    def dist_to(self,​ source): +        return portal_world
-        """​ +
-        Return euclidean distance to the player ('​source'​). +
-        """​+
  
-        def sdf_line(a, b, p): 
-            """​ 
-            Return distance from point p to line a-b. 
-            (source: https://​youtu.be/​PMltMdi1Wzg) 
-            """​ 
  
-            if b == a: +    def portal_world(selfworldsource): 
-                h = 0 +        ​portal_world = []
-            else: +
-                h = min(1max(0(p-a).dot(b-a) / (b-a).dot(b-a))+
-            ​return ((p-a) - h * (b-a)).length()+
  
-        ​if not self# empty +        ​for object in world
-            ​return float('​inf'​)+            ​port = object.__class__(*object) 
 +            port.__dict__ = object.__dict__
  
-        ​minimum distance to a side +            ​gone through ​side 1 
-        m = float('​inf'​+            ​if ​(self.start - source).is_between(self.end - self.start, self.start - self.end): 
-        for i, _ in enumerate(self): +                for i, _ in enumerate(object): 
-            ​m ​min(m, sdf_line(self[i-1], ​self[i], source)) +                    if self.offset is None: 
-        ​return m+                        # 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
  
-    def cut(self, source, portal): +                # if world gets mirrored the side-sensitive portals have to be reverted 
-        """​ +                if type(objectis Portal
-        Return 'self' but cut to fit in the view from '​source'​ though '​portal'​. +                    ​if ​self.M.i.angle_to(self.M.j) > 180: 
-        This algorithm isn't perfect, but the best I got at the moment+                        ​port[0],​ port[1] = port[1], port[0]
-        """​+
  
-        def position(point): +            # gone through side 2 
-            """​ +            else: 
-            Return the area '​point' ​is in+                for i, _ in enumerate(object): 
-            -1 -- in the triangle between source and portal ​line +                    if self.offset2 ​is None
-             0 -- to the right of visible area +                        # middle of portal in portal ​world 
-             1 -- in the visible ​portal ​area +                        ​offset2 = self.M2.prod((self.start + self.end) / 2(self.start + self.end) / 2 
-             ​2 -- to the left of visible area +                    ​else:​ 
-            """​+                        offset2 = self.offset2
  
-            vec point - source +                    # apply distortion and offset 
-            if vec.is_between(ray1.dir, ray2.dir):​ +                    port[i] ​self.M2.prod(object[i]) - offset2
-                if (point - ray1.start).is_between(ray1.dir,​ border.dir):​ +
-                    return 1 +
-                else: +
-                    return ​-+
-            if vec.is_between(null,​ ray1.dir):​ +
-                return 0 +
-            if vec.is_between(ray2.dir,​ null): +
-                return 2+
  
-        ​rays that are borders of different areas +                ​if world gets mirrored the side-sensitive portals have to be reverted 
-        ray1 = Ray(portal.start,​ portal.start ​source) # area 0 <--> 1 +                if type(objectis Portal: 
-        ray2 = Ray(portal.end, portal.end - source# area 1 <--> 2 +                    if self.M2.i.angle_to(self.M2.j) > 180: 
-        ​border = Ray(ray1.start, ray2.start - ray1.start) # area -1 <--> 1 +                        ​port[0]port[1] ​port[1]port[0]
-        null = -(ray1.dir + ray2.dir) # area 0 <--> 2 +
-        # garantees anticlockwise order (important for calculations) +
-        if ray1.dir.angle_to(ray2.dir) > 180: +
-            ray1ray2 ray2ray1+
  
-        rest Polygon() +            # add distorted and truncated object to portal world 
-        rest.__dict__ = self.__dict__+            cut port.cut(source, self
 +            if cut: # not empty 
 +                portal_world.append(cut)
  
-        ​if not self: # empty +        return ​portal_world
-            ​return ​rest+
  
-        # edgecase for when a portal is in its own portal world in the same place 
-        if len(self) == 2: 
-            if self[0] in portal and self[1] in portal: 
-                return rest 
  
-        # algorithm for cutting polygon +    def draw(self, ​screen, offset): 
-        nxt_pos = position(self[-1]) +        """​ 
-        for i_ in enumerate(self): +        Draw the endpoints of 'self' on '​screen'​. 
-            ​cur_pos = nxt_pos +        """​
-            ​nxt_pos = position(self[i])+
  
-            if cur_pos == 1# visible +        for point in self
-                rest.append(self[i-1])+            ​Polygon(point).draw(screen, offset)
  
-            # same position, nothing changes 
-            if nxt_pos == cur_pos: 
-                continue 
  
-            # different positionintersections with visible area needed +    def draw_shadow(selfscreen, offset, source): 
-            inter = []+        """​ 
 +        Draw draw a shadow of '​self'​ on '​screen'​ respecting the light-'​source'​. 
 +        """​
  
-            ​through ​the left or right boundary of visible area -> intersection +        ​draw the shadow ​in the same color as the background to wipe the canvas 
-            if 1 in (cur_pos, nxt_pos): +        ​Polygon.draw_shadow(self, ​screenoffsetsource, color=colors.GRAY)
-                if 0 in (cur_pos, nxt_pos): +
-                    inter += ray1.intersect(Ray(self[i-1]self[i] - self[i-1])) +
-                elif 2 in (cur_posnxt_pos): +
-                    inter += ray2.intersect(Ray(self[i-1]self[i] - self[i-1])) +
-                if not inter: # empty +
-                    inter +border.intersect(Ray(self[i-1],​ self[i] - self[i-1]))+
  
-            ​line goes outside ​the visible area around the portal -> edges of portal +        ​draw shadows for the end points ​of portal 
-            elif -1 in (cur_pos, nxt_pos)+        for point in self
-                if 0 in (cur_posnxt_pos)+            ​Polygon(point).draw_shadow(screenoffset, source)
-                    inter += [ray1.start] +
-                else: # 2 +
-                    inter += [ray2.start]+
  
-            # line goes from the left to the right -> may intersect visible area 
-            else: # 0 and 2 
-                if cur_pos == 0: 
-                    inter += ray1.intersect(Ray(self[i-1],​ self[i] - self[i-1])) 
-                    inter += ray2.intersect(Ray(self[i-1],​ self[i] - self[i-1])) 
-                else: # 2 
-                    inter += ray2.intersect(Ray(self[i-1],​ self[i] - self[i-1])) 
-                    inter += ray1.intersect(Ray(self[i-1],​ self[i] - self[i-1])) 
  
-            # add intersections +    def cut(self, source, portal): 
-            for pt in inter: +        """​ 
-                if pt.length2() > 10000: # hotfix +        Return '​self'​ but cut to fit in the view from '​source'​ though '​portal'​. 
-                    ​print(f"CalculationError while cutting {self} at {ray1ray2}:​\n ​   {pt} is very big"+        Makes use of '​Polygon.cut'​
-                else+        """​ 
-                    rest.append(pt)+ 
 +        rest = Polygon.cut(self, source, portal
 +        if len(rest) == 2
 +            ​return Portal(*reversed(rest[:2]), M=self.M, color=self.color)
  
-        return ​rest+        return ​Polygon()
  
 </​file>​ </​file>​
ss20/neg_drawables.1599827273.txt.gz · Zuletzt geändert: 2020/09/11 14:27 von srather