Archive for the ‘api design’ Category


Attribute handling

June 27, 2010

One new aspect I’ve already mentioned in another post is that common drawing options like width, aa, rounded corners or color (for some pens) are stored as attributes of pen objects to keep parameter lists of shape methods short. But now, you need a few lines each time you want to draw a new shape with other options:

pen = SolidPen(color=pygame2.Color(20, 210, 50))
pen.width = 5
pen.rect(screen, any_rect)
pen.color = pygame2.Color(120, 250, 250)
pen.width = 3
pen.rect(screen, any_other_rect)

Ok, this can be really annoying. Therefore I added a way to use these options (which are still object attributes) also as keyword parameters:

pen = SolidPen(color=pygame2.Color(20, 210, 50))
pen.width = 5
print pen.width # output: 5
pen.rect.screen, any_rect) # draws rect with width=5
pen.rect(screen, any_other_rect, width=3) # draws rect with width=3
print pen.width # output: 5
# set pen.width = 1 (the actual attribute) and then draw the rect
pen.rect(screen, a_third_rect, width=1, apply=True)
print pen.width # output: 1

Thus, you can use keyword parameters to change options, but you don’t have to.  To draw some shapes all in one color you set it once and then don’t need to repeat yourself over and over again.


Yay, it works! Drawing broad lines the pygamedraw way

June 23, 2010

The the basic shape algorithms are mostly implemented and I get more and more to the parts of implementing new features and improving stuff. Today I worked one the Pen.line() method or more specifically the drawing of broad poly-lines through more than two points.

First, lets have a look how the current implementation does it:

drawing a long line with pygame.draw.lines and width = 20Well, that’s just a multiple call of pygame.draw.line() which is also badly implemented as it simply adds half of the width on both sides to either the x or y values of the endpoints (and thus different angles result in different line widths). This is just ugly and nearly unusable.

So I had to fix that and do a bit more than calling Pen.line(surf, point1, point2). But what? There are quite a few ways how to draw the corners and so I added the next attribute to the Pen class, called line_style.

Currently there are 4 different line styles (and no more planned because I cannot think of any further (useful) style(s), but if you do, please tell me and I may think about it): DENTED_VERT, SHARP_VERT, FLAT_VERT and ROUND_VERT.

First of all, I did some studies and tests and thought about lines and how to draw and connect them to nice-looking polylines:

I started with a simple row of single line elements.

Then reduced it to pure data.

Some more tests and a first, ugly hacked version ensued. Then I rewrote it and did a cleaner implementation using an own Vert class to hold the data and do some calculations.

At the end, it looks like this:

Pen.line_style = DENTED_VERT

Pen.line_style = SHARP_VERT (yes, I know there is still a small pixel bug)

Pen.line_style = FLAT_VERT

Pen.line_style = ROUND_VERT

I also added a first style for line endings (more styles will come later):

Pen.line_style = ROUND_VERT Pen.line_start = ROUND_ENDING Pen.line_ending = ROUND_ENDING

Yay, that’s a lot better and more flexible than the current pygame/sdl implementation and thas is what this whole GSoC project is all about! 🙂


Masks as intermediate

May 8, 2010

One of the first things I knew about the structure of the new draw module was that I wanted to separate the definition of shapes and the actual drawing because shapes won’t change but the drawing methods and algorithms should be changeable. To easily implement this and also get a comfortable api that makes it easier to deal with (lots of?) optional parameters I decided to use an object-oriented approach and write a base class that defines the shapes and child classes that implement different algorithms by overwriting a certain method of the base class.

Now I needed some interface, a way to pass shapes to the draw method without drawing it.

First I thought I could call the draw method per pixel and directly set pixels on the destination surface like just passing the position of a pixel (that should be set) to the draw method, that then would calculate the color and set the pixel on the surface.

Then I started thinking about algorithms to define shapes and it became clear that it is a lot easier to operate on a “neutral” place where you just have two options per pixel:  to set or not to set, to draw or not to draw, to be or not to be. And this is exactly what masks are made for!

On a regular surface with any initial image data you don’t know if you have set a certain pixel or not because you don’t know the color it had before you started drawing. Of course it may be possible somehow and you could use a list of affected pixels and their initial color and then … but its easier and cleaner to use masks that are optimized and thus fast enough.

So the basic api layout will look something like this:

class Pen(object):
    base class that defines all shape methods
    - do not use directly! -
    def line(self, surf, ...):
        # ...

    # ... (more methods to draw all kinds of shapes)

    def draw_mask(self, surf, mask, offset):
        draw ``mask`` onto ``surf`` with the given ``offset``
        - to be overwritten by child classes -
        raise NotImplementedError

class PlainColorPen(object):
    fill shapes with a plain color
    def __init__(self, color):
        self.color = color

    def draw_mask(self, surf, mask, offset):
        # ...

class TexturedPen(object):
    create textured shapes
    def __init__(self, texture):
        # ...

    def draw_mask(self, surf, mask, offset):
        # ...

# ... (more XXXPen classes)