Search code examples
pythonpyglet

Can I draw using normalized coordinates in pyglet?


In the pyglet graphics documentation, it demonstrates how to draw points in 2d space using the screen coordinates

pyglet.graphics.draw(2, pyglet.gl.GL_POINTS,
    ('v2i', (10, 15, 30, 35))
)

I want to be able to draw using the normalized coordinates, which range from [-1, 1] on each axis, but using the draw call normally, I must give the vertices in terms of pixel coordinates.

The reason I want to do this is that I will be drawing functions of both domain and range [-1, 1], so it is very fitting to draw using the normalized coordinates and not have to worry about converting.


Solution

  • So, assuming my math is correct (which it most certainly isn't), these functions should return what you're looking for:

    def normalize(cords, constraint):
        x_ratio, y_ratio = 1/constraint.width, 1/constraint.height
    
        result = []
        for x,y in zip(*[iter(cords)] * 2):
            nx = ((x-(constraint.width/2))*x_ratio)*2
            ny = ((y-(constraint.height/2))*y_ratio)*2
            result += [nx, ny]
    
            print(x,y, '->', nx, ny)
    
        return result
    
    def complicate(cords, constraint):
        x_ratio, y_ratio = 1/constraint.width, 1/constraint.height
    
        result = []
        for x,y in zip(*[iter(cords)] * 2):
            nx = ((x/x_ratio)/2)+(constraint.width/2)
            ny = ((y/y_ratio)/2)+(constraint.height/2)
            result += [nx, ny]
    
            print(x, y, '->', nx, ny)
        return result
    

    And you could use them as such:

    pyglet.graphics.draw(2, pyglet.gl.GL_POINTS,
        ('v2i', normalize((10, 15, 30, 35), window))
    )
    

    or

    pyglet.graphics.draw(2, pyglet.gl.GL_POINTS,
        ('v2i', complicate((-0.975, -0.95, -0.925, -0.883), window))
    )
    

    You probably want a generic way of storing the two worlds in one place. And work with them as one singular object that makes sense no matter what you put in. So you can treat both normalized coordinates and coordinates as the same thing.

    You could do this by doing something like this:

    class cordinates():
        def __init__(self, cords, constraint=None):
            if not constraint: constraint = window
            self.constraint = constraint
    
            if min(cords) > -1 and max(cords) < 1:
                self.type = 'normalized'
            else:
                self.type = 'complicated'
    
            self.cords = cords
    
        def normalize(self, constraint=None):
            if not constraint: constraint = self.constraint
            x_ratio, y_ratio = 1/constraint.width, 1/constraint.height
    
            result = []
            for x,y in zip(*[iter(self.cords)] * 2):
                nx = ((x-(constraint.width/2))*x_ratio)*2
                ny = ((y-(constraint.height/2))*y_ratio)*2
                result += [nx, ny]
    
                print(x,y, '->', nx, ny)
    
            return result
    
        def complicate(self, constraint=None):
            if not constraint: constraint = self.constraint
            x_ratio, y_ratio = 1/constraint.width, 1/constraint.height
    
            result = []
            for x,y in zip(*[iter(self.cords)] * 2):
                nx = ((x/x_ratio)/2)+(constraint.width/2)
                ny = ((y/y_ratio)/2)+(constraint.height/2)
                result += [nx, ny]
    
                print(x,y, '->', nx, ny)
    
            return result
    
        def __iter__(self, *args, **kwargs):
            return self.cords
    
        def __repr__(self, *args, **kwargs):
            return str(self.cords)
    
    pyglet.graphics.draw(2, pyglet.gl.GL_POINTS,
        ('v2i', coordinates((-0.975, -0.95, -0.925, -0.883)).complicate() )
    )
    
    pyglet.graphics.draw(2, pyglet.gl.GL_POINTS,
        ('v2i', coordinates((10, 15, 30, 35)) )
    )
    

    You could even build away .complicate() by doing guess-work based off self.type and always return coordinates.

    def __iter__(self, *args, **kwargs):
        if self.type == 'normalized':
            return self.complicate()
        else:
            return self.cords
    

    I normally just work with one type of object, so I might not be the best teacher in this matter. But this makes sense to me, and hopefully it's useful for you. Best of luck!