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

Re: [pygame] QuadMap



DR0ID wrote:
nice!

Some comments would be appreciated. Is it an affine transformation? The hit test look like raytracing code; how does it work?


Well, no can do - I'm looking stuff up in books and online, and barely understanding this myself. For instance - you must specify the quadrilateral corners in topleft, bottomleft, bottomright, topright order for the picture to be upright - why's that? Specifying them in a different order gives various flipping and mirroring effects... but sometimes it crashes. Anyways, if you can figure it out, let me know. :-D

But what I can give you, is a more interesting version that textures the quadrilaterals.

import pygame, math, Numeric

WIDTH   = 800
HEIGHT  = 600

BLACK   = (  0,   0,   0, 255)
WHITE   = (255, 255, 255, 255)
RED     = (255,   0,   0, 255)
YELLOW  = (255, 255,   0, 255)
BLUE    = (  0,   0, 255, 255)
GREEN   = (  0, 255,   0, 255)
GRAY    = (212, 208, 200, 255)
MAGENTA = (255,   0, 255, 255)

class Mesh(object):
    quads = None
    texture = None
    posx = 0
    posy = 0

    def __init__(self):
self.pic = pygame.Surface([WIDTH, HEIGHT], pygame.SRCALPHA, 32).convert_alpha()
        self.quads = []

    def Add(self, quads):
        self.quads.append(quads)

    def Texture(self, texture):
        quad = None
        w, h = self.pic.get_size()
        tw, th = texture.get_size()
        array = pygame.surfarray.pixels3d(texture)
        alpha = pygame.surfarray.pixels_alpha(texture)
        myarray = pygame.surfarray.pixels3d(self.pic)
        myalpha = pygame.surfarray.pixels_alpha(self.pic)
        for quad in self.quads:
            for y in range(quad.miny, quad.maxy):
                for x in range(quad.minx, quad.maxx):
                    offset = quad.PointToOffset([x, y])
                    if offset:
                        x1 = int((tw-1) * offset[0])
                        y1 = int((th-1) * offset[1])
                        myarray[x, y] = array[x1, y1]
                        myalpha[x, y] = alpha[x1, y1]

    def Draw(self, bg):
        bg.blit(self.pic, [self.posx, self.posy])
        for quad in self.quads:
            quad.Draw(bg)

    def PointToOffset(self, pos):
        for quad in self.quads:
            offset = quad.PointToOffset(pos)
            if offset:
                return offset
        return None

    def OffsetToPoint(self, offset):
        for quad in self.quads:
            quad.point = quad.OffsetToPoint(offset)

class Quad(object):
    data  = None
    point = None

    def __init__(self, p1, p2, p3, p4):
        self.p1 = list(p1)
        self.p2 = list(p2)
        self.p3 = list(p3)
        self.p4 = list(p4)
        self.polys = [self.p1, self.p2, self.p3, self.p4]
        self.minx = 99999
        self.maxx = -1
        self.miny = 99999
        self.maxy = -1
        for x, y in self.polys:
            self.minx = min(self.minx, x)
            self.maxx = max(self.maxx, x)
            self.miny = min(self.miny, y)
            self.maxy = max(self.maxy, y)

    def Draw(self, bg):
        pygame.draw.line(bg, RED,    self.p1, self.p2, 1)
        pygame.draw.line(bg, YELLOW, self.p2, self.p3, 1)
        pygame.draw.line(bg, BLUE,   self.p3, self.p4, 1)
        pygame.draw.line(bg, GREEN,  self.p4, self.p1, 1)
        if self.point:
pygame.draw.rect(bg, MAGENTA, [self.point[0]-2, self.point[1]-2, 4, 4], 0)

    def OffsetToPoint(self, offset):
        if offset == None:
            return None
        s, t = offset
        x1, y1 = self.p1
        x2, y2 = self.p2
        x3, y3 = self.p4
        x4, y4 = self.p3
        xa = x1 + t * (x2 - x1)
        ya = y1 + t * (y2 - y1)
        xb = x3 + t * (x4 - x3)
        yb = y3 + t * (y4 - y3)
        X = xa + s * (xb - xa)
        Y = ya + s * (yb - ya)
        return int(X), int(Y)

    def PointToOffset(self, pos):
        x, y = pos[0], pos[1]
if not (x >= self.minx and x <= self.maxx and y >= self.miny and y <= self.maxy):
            return None
        x1, y1 = self.p1
        x2, y2 = self.p2
        x3, y3 = self.p4
        x4, y4 = self.p3

        Ax = x2 - x1
        Ay = y2 - y1
        Bx = x4 - x3
        By = y4 - y3
        Cx = x3 - x1
        Cy = y3 - y1
        Dx = x  - x1
        Dy = y  - y1
        Ex = Bx - Ax
        Ey = By - Ay

        a = -Ax * Ey + Ay * Ex
        b = Ey * Dx - Dy * Ex + Ay * Cx - Ax * Cy
        c = Dx * Cy - Dy * Cx

        det = b * b - 4 * a * c
        if det >= 0:
            if abs(a) < 0.001:
                t = -c / float(b)
            else:
                t = (-b - math.sqrt(det)) / float(2 * a)
            denom = (Cx + Ex * t)
            if denom > 0.01:
                s = (Dx - Ax * t) / float(denom)
            else:
                s = (Dy - Ay * t) / float(Cy + Ey * t)

            if (t >= 0) and (t <= 1) and  (s >= 0) and (s <= 1):
                return s, t

def LoadPic(filename = 'test2.jpg'):
    try:
        pic = pygame.image.load(filename).convert_alpha()
    except:
pic = pygame.Surface([100, 100], pygame.SRCALPHA, 32).convert_alpha()
        pic.fill(RED, [0, 0, 50, 50])
        pic.fill(YELLOW, [50, 0, 50, 50])
        pic.fill(BLUE, [0, 50, 50, 50])
        pic.fill(GREEN, [50, 50, 50, 50])
        pic = AdjustAlpha(pic, .3)
    return pic

def AdjustAlpha(pic, adj = .5):
    alphaarray = pygame.surfarray.array_alpha(pic).astype(Numeric.Float)
    alphaarray[:, :] = Numeric.clip(alphaarray * adj, 0, 255)
pygame.surfarray.pixels_alpha(pic)[:, :] = alphaarray.astype(Numeric.UInt8)
    return pic


def main():
    pygame.init()
    bg = pygame.display.set_mode([WIDTH, HEIGHT], 0, 32)
    quit = 0
    pic = LoadPic()
    mesh = Mesh()
    w, h = pic.get_size()
    x, y = 280, 20
    mesh.Add(Quad([x, y], [x, y+h], [x+w, y+h], [x+w, y]) )
    x, y = 280, 300
    mesh.Add(Quad([x, y+h], [x, y], [x+w, y], [x+w, y+h]) )
    x, y = 0, 400
    w, h = 200, 150
    mesh.Add(Quad([x+w, y], [x, y], [x, y+h], [x+w, y+h]) )
    mesh.Add(Quad([ 20,  50], [120,  30], [150, 110], [ 10, 130]) )
    mesh.Add(Quad([210, 170], [250, 260], [100, 240], [120, 150]) )
    if mesh.Texture(pic):
        pass
    else:
        while not quit:
            bg.fill(GRAY)
            mesh.Draw(bg)
            pygame.display.flip()
            pygame.event.pump()
            moved = 0
            for e in pygame.event.get():
                if e.type == pygame.QUIT:
                    quit = 1
                    break
                elif e.type == pygame.MOUSEMOTION:
                    if not moved:
                        moved = 1
                        offset = mesh.PointToOffset(e.pos)
                        mesh.OffsetToPoint(offset)
    pygame.quit()


if __name__ == '__main__':
    main()