Commits

Marcus von Appen committed 49a2c9a

- fixed sdl2.mouse.SDL_WarpMouseInWindow() wrapper
- added sdl2.ext - advanced modules, classes and functions for applications
- added examples

Comments (0)

Files changed (63)

 PYTHONPATH ?= $(top_srcdir)
 SUBDIRS = \
 	$(top_srcdir)/sdl2 \
+	$(top_srcdir)/sdl2/ext \
 	$(top_srcdir)/sdl2/test \
 	$(top_srcdir)/sdl2/test/resources \
 	$(top_srcdir)/sdl2/test/util \
+About PySDL2
+============
+PySDL2 is a wrapper around the SDL2 library and as such similar to the
+discontinued PySDL project. In contrast to PySDL, it has no licensing
+restrictions, nor does it rely on C code, but uses ctypes instead.
+
+Installation
+============
+PySDL2 is easy to install and integrate within your own projects. Just
+follow Python's standard procedure of installing packages or look for a
+prebuilt package for your operating system or distribution.
+
+Documentation
+=============
+If you just started with SDL and PySDL2, it is strongly recommended
+that you read through the tutorial of the documentation to understand.
+You can find the documentation at *doc/html*.
+
+License
+=======
+This library is given to the public domain. There are no licensing
+restrictions. Please see *doc/copying.rst* for further details.

examples/__init__.py

+"""Examples for PySDL2.
+
+This package contains the examples for sdl2.
+"""
+import os
+from sdl2.ext import Resources
+
+__all__ = ["RESOURCES"]
+
+_fpath = os.path.dirname(os.path.abspath(__file__))
+RESOURCES = Resources(os.path.join(_fpath, "resources"))

examples/colorpalettes.py

+"""A simple example for filling rectangular areas."""
+import sys
+
+try:
+    # Try to import SDL2. The import might fail, if the SDL2 DLL could not be
+    # loaded. In that case, just print the error and exit with a proper
+    # error code.
+    import sdl2.ext as sdl2ext
+    from sdl2 import events as sdlevents
+    # Import the pre-defined color palettes
+    import sdl2.ext.colorpalettes as colorpalettes
+except ImportError:
+    import traceback
+    traceback.print_exc()
+    sys.exit(1)
+
+
+# This function will draw the passed palette colors onto the window surface.
+# The function does not require the window surface itself, any surface is fine,
+# since the window surface acquired in run() does not differ from any other
+# surface (not entirely true, but it offers and supports the same
+# functionality).
+def draw_palette(surface, palette):
+    # Fill the entire surface with a black color. This is done by simply
+    # passing a 0 value to the fill argument. We could also create a
+    # Color(0, 0, 0) instance here, but this would be the same.
+    sdl2ext.fill(surface, 0)
+
+    # Calculate the average width (roughly cut) to be used for each palette
+    # value. When running the example, you will notice a black gap on the
+    # right for some palettes. This s caused by the implicit cut behaviour
+    # of the // operator. Since we can only operate pixel-wise, there are
+    # no fractions to be used.
+    width, height = surface.w, surface.h
+    rw = width // len(palette)
+
+    # Create the area to be filled with the palette values. we always start
+    # at the top-left corner, use the calculated width and the entire height
+    # of the window surface. As you will see below, we then only advance
+    # horizontically by the calculated width to draw stripes.
+    # Play around with different height values and start offsets to see what
+    # happens
+    rect = [0, 0, rw, height]
+
+    # Loop over all colors and fill a portion of the surface with them. As
+    # above, we use fill() to fill the surface with the palette color, but now
+    # we provide an area (the third argument) to avoid filling the whole
+    # surface. Instead, the provided area makes sure, that we only fill a
+    # certain part.
+    for color in palette:
+        sdl2ext.fill(surface, color, rect)
+        rect[0] += rw
+
+
+def run():
+    # You know those from the helloworld.py example.
+    # Initialize the video subsystem, create a window and make it visible.
+    sdl2ext.init()
+    window = sdl2ext.Window("Color Palettes", size=(800, 600))
+    window.show()
+
+    # Explicitly acquire the window's surface to draw on. We used the
+    # SpriteRenderer class in helloworld.py, which did the drawing magic for
+    # us. Now we will do it ourselves, so we have to get a surface to draw on.
+    # NOTE: if you intend to use textures or the SDL renderers, you must not
+    # use the method.
+    windowsurface = window.get_surface()
+
+    # A simple mapping table for the builtin color palettes. We will use
+    # the table to look up the color palette to draw an the title to set below.
+    palettes = (
+        ("Mono Palette", colorpalettes.MONOPALETTE),
+        ("2-bit Gray Palette", colorpalettes.GRAY2PALETTE),
+        ("4-bit Gray Palette", colorpalettes.GRAY4PALETTE),
+        ("8-bit Gray Palette", colorpalettes.GRAY8PALETTE),
+        ("3-bit RGB Palette", colorpalettes.RGB3PALETTE),
+        ("CGA Palette", colorpalettes.CGAPALETTE),
+        ("EGA Palette", colorpalettes.EGAPALETTE),
+        ("VGA Palette", colorpalettes.VGAPALETTE),
+        ("Web Palette", colorpalettes.WEBPALETTE),
+        )
+
+    # A storage variable for the palette we are currently on, so that we know
+    # which palette to draw next.
+    curindex = 0
+
+    # Since it is not that nice to have a black window right at the start of
+    # the application, we will set the window's title to the first entry
+    # of our mapping tables. Afterwards, we will draw the matching palette
+    # to the window surface.
+    window.title = palettes[0][0]
+    draw_palette(windowsurface, palettes[0][1])
+
+    # The event loop. In helloworld.py we used the TestEventProcessor class,
+    # since there was not much to do. Now however, we want to react on the user
+    # input. Every time the user clicks around in our window, we want to
+    # show the next palette. Once we reached the last palette within the
+    # mapping table, we will start again with the first one.
+    running = True
+    while running:
+        # This will check for any events that piled up since the last check.
+        # If one or multiple events were  found (such as a click, a mouse
+        # movement, keyboard input, etc.), we will retrieve them.
+        events = sdl2ext.get_events()
+
+        # In case there was no event, we do not need to do anything. This
+        # might happen, if  e.g. the user works with another application. Since
+        # get_events() does not wait for an event to occur (that'd mean your
+        # application blocks until there is an event), we have to handle
+        # this.
+        for event in events:
+            # The received events can contain different information. There
+            # might be mouse movements, clicks, keyboard hits and many more.
+            # All of those carry different information. A mouse movement will
+            # contain the mouse cursor position, while a keyoard hit will
+            # contain the key that was pressed. Depending on that, we need to
+            # handle the occured event in a different way, which is done here.
+            #
+            # In case of a special QUIT event, the user wants to quit the
+            # application, just as you are used to closing an editor.
+            # If the user wants to quit the application, we should let him do
+            # so. This is done by breaking out of the while loop.
+            if event.type == sdlevents.SDL_QUIT:
+                running = False
+                break
+
+            # We received a mouse button press event. As you can see from the
+            # type, the user pressed the mouse button, but did not necesarily
+            # release  it. As such, it is not a typical click, but only 50% of
+            # it, which is sufficient for our case here.
+            if event.type == sdlevents.SDL_MOUSEBUTTONDOWN:
+                # If the user pressed the button, we want to draw the next
+                # palette and update the window title accordingly. We do this
+                # by increasing the storage variable and - in case it reached
+                # the last entry, set it back to the first entry.
+                curindex += 1
+                if curindex >= len(palettes):
+                    curindex = 0
+                window.title = palettes[curindex][0]
+                draw_palette(windowsurface, palettes[curindex][1])
+                # If we found a single click (there might be many more)
+                # we will break out of checking the rest of events.
+                # Improved implementations could use the type= argument
+                # for get_events() to filter specific events and
+                # ignore anything else.
+                break
+
+        # Once the events were properly handled, we will refresh the window,
+        # since it might have happened that the user moved the window around,
+        # pressed a button or did something else. In all those cases, we want
+        # the palettes to be shown, so we need to refresh the window. This will
+        # cause the  window internally to copy its surface information (those
+        # we used to draw the palette on) to the screen, where the window
+        # currently is  placed on.
+        # Comment this line out to see what happens!
+        window.refresh()
+
+    # As for helloworld.py, we have to call sdl2ext.quit(), since we also
+    # called sdl2ext.init().
+    sdl2ext.quit()
+    return 0
+
+
+if __name__ == "__main__":
+    sys.exit(run())
+"""User interface examples."""
+import sys
+
+# Import the Color class, so we can create RGBA color values.
+from sdl2.ext.color import Color
+
+# Try to import SDL2. The import might fail, if the SDL2 DLL could not be
+# loaded. In that case, just print the error and exit with a proper
+# error code.
+try:
+    import sdl2.ext as sdl2ext
+    import sdl2.events as sdlevents
+except ImportError:
+    import traceback
+    traceback.print_exc()
+    sys.exit(1)
+
+# Define some global color constants
+WHITE = Color(255, 255, 255)
+GREY = Color(200, 200, 200)
+
+# Import the resources, so we have easy access to the example images.
+from sdl2.ext.resources import Resources
+RESOURCES = Resources(__file__, "resources")
+
+
+# A callback for the Button.motion event.
+def onmotion(button, event):
+    print("Mouse moves over the button!")
+
+
+# A callback for the Button.click event.
+def onclick(button, event):
+    print("Button was clicked!")
+
+
+# A callback for the TextEntry.input event.
+def oninput(entry, event):
+    print("Input received with text '%s'" % event.text.text)
+    print("Text on the entry now is '%s'" % entry.text)
+
+
+# A callback for the TextEntry.edit event.
+def onedit(entry, event):
+    print("Edit received with text '%s', start '%d', length '%d'" %
+          (event.text.text, event.text.start, event.text.length))
+
+
+def run():
+    # You know those from the helloworld.py example.
+    # Initialize the video subsystem, create a window and make it visible.
+    sdl2ext.init()
+    window = sdl2ext.Window("UI Elements", size=(800, 600))
+    window.show()
+
+    spritefactory = sdl2ext.SpriteFactory(sdl2ext.SOFTWARE)
+    # If you want hardware-accelerated rendering, use video.TEXTURE instead
+    # and pass a renderer along:
+    #
+    # renderer = video.RenderContext(window)
+    # factory = video.SpriteFactory(video.TEXTURE, renderer=renderer)
+    #
+
+    # Create a UI factory, which will handle several defaults for
+    # us. Also, the UIFactory can utilises software-based UI elements as
+    # well as hardware-accelerated ones; this allows us to keep the UI
+    # creation code clean.
+    uifactory = sdl2ext.UIFactory(spritefactory)
+
+    # Create a simple Button sprite, which reacts on mouse movements and
+    # button presses and fill it with a white color. All UI elements
+    # inherit directly from the TextureSprite (for TEXTURE) or SoftwareSprite
+    # (for SOFTWARE), so everything you can do with those classes is also
+    # possible for the UI elements.
+    button = uifactory.from_image \
+        (sdl2ext.BUTTON, RESOURCES.get_path("button.bmp"))
+    button.position = 50, 50
+
+    # Create a TextEntry sprite, which reacts on keyboard presses and
+    # text input.
+    entry = uifactory.from_image \
+        (sdl2ext.TEXTENTRY, RESOURCES.get_path("textentry.bmp"))
+    entry.position = 50, 200
+
+    # Create a CheckButton sprite. The CheckButton is a specialised
+    # Button, which can switch its state, identified by the 'checked'
+    # attribute by clicking.
+    checkbutton = uifactory.from_image \
+        (sdl2ext.CHECKBUTTON, RESOURCES.get_path("button.bmp"))
+    checkbutton.position = 200, 50
+
+    # Bind some actions to the button's event handlers. Whenever a click
+    # (combination of a mouse button press and mouse button release), the
+    # onclick() function will be called.
+    # Whenever the mouse moves around in the area occupied by the button, the
+    # onmotion() function will be called.
+    # The event handlers receive the issuer of the event as first argument
+    # (the button is the issuer of that event) and the SDL event data as second
+    # argument for further processing, if necessary.
+    button.click += onclick
+    button.motion += onmotion
+
+    # Bind some actions to the entry's event handlers. The TextEntry
+    # receives input events, once it has been activated by a mouse
+    # button press on its designated area. The UIProcessor class takes
+    # care of this internally through its activate() method.  If the
+    # TextEntry is activated, SDL_TEXTINPUT events are enabled by the
+    # relevant SDL2 functions, causing input events to occur, that are
+    # handled by the TextEntry.
+    entry.input += oninput
+    entry.editing += onedit
+
+    # Since all gui elements are sprites, we can use the
+    # SpriteRenderer class, we learned about in helloworld.py, to
+    # draw them on the Window.
+    spriterenderer = spritefactory.create_sprite_renderer(window)
+
+    # Create a new UIProcessor, which will handle the user input events
+    # and pass them on to the relevant user interface elements.
+    uiprocessor = sdl2ext.UIProcessor()
+
+    running = True
+    while running:
+        events = sdl2ext.get_events()
+        for event in events:
+            if event.type == sdlevents.SDL_QUIT:
+                running = False
+                break
+            # Pass the SDL2 events to the UIProcessor, which takes care of
+            # the user interface logic.
+            uiprocessor.dispatch([button, checkbutton, entry], event)
+
+        # Render all user interface elements on the window.
+        spriterenderer.render((button, entry, checkbutton))
+
+    sdl2ext.quit()
+    return 0
+
+
+if __name__ == "__main__":
+    sys.exit(run())

examples/helloworld.py

+"""The almighty Hello World! example"""
+# We'll use sys to properly exit with an error code.
+import sys
+
+# Try to import the video system. Since mule.video makes use of
+# mule.sdl, the import might fail, if the SDL DLL could not be
+# loaded. In that case, just print the error and exit with a proper
+# error code.
+try:
+    import sdl2.ext as sdl2ext
+except ImportError:
+    import traceback
+    traceback.print_exc()
+    sys.exit(1)
+
+# Create a resource container, so that we can easily access all
+# resource, we bundle with our application. We are using the current
+# file's location and define the "resources" subdirectory as the
+# location, in which we keep all data.
+from sdl2.ext import Resources
+RESOURCES = Resources(__file__, "resources")
+
+
+
+def run():
+    # Initialize the video system - this implicitly initializes some
+    # necessary parts within the SDL2 DLL used by the video module.
+    #
+    # You SHOULD call this before using any video related methods or
+    # classes.
+    sdl2ext.init()
+
+    # Create a new window (like your browser window or editor window,
+    # etc.) and give it a meaningful title and size. We definitely need
+    # this, if we want to present something to the user.
+    window = sdl2ext.Window("Hello World!", size=(592, 460))
+
+    # By default, every Window is hidden, not shown on the screen right
+    # after creation. Thus we need to tell it to be shown now.
+    window.show()
+
+    factory = sdl2ext.SpriteFactory(sdl2ext.SOFTWARE)
+    # If you want hardware-accelerated rendering, use video.TEXTURE instead
+    # and pass a renderer along:
+    #
+    # renderer = video.RenderContext(window)
+    # factory = video.SpriteFactory(video.TEXTURE, renderer=renderer)
+    #
+    
+    # Creates a simple rendering system for the Window. The
+    # SpriteRenderer can draw Sprite objects on the window.
+    spriterenderer = factory.create_sprite_renderer(window)
+
+    # Creates a new 2D pixel-based surface to be displayed, processed or
+    # manipulated. We will use the one of the shipped example images
+    # from the resource package to display.
+    sprite = factory.from_image(RESOURCES.get_path("hello.bmp"))
+
+    # Display the surface on the window. This will copy the contents
+    # (pixels) of the surface to the window. The surface will be
+    # displayed at surface.position on the window. Play around with the
+    # surface.x and surface.y values or surface.position (which is just
+    # surface.x and surface.y grouped as tuple)!
+    spriterenderer.render(sprite)
+
+    # Set up an example event loop processing system. This is a necessity,
+    # so the application can exit correctly, mouse movements, etc. are
+    # recognised and so on. The TestEventProcessor class is just for
+    # testing purposes and does not do anything meaningful.  Take a look
+    # at its code to better understand how the event processing can be
+    # done and customized!
+    processor = sdl2ext.TestEventProcessor()
+
+    # Start the event processing. This will run in an endless loop, so
+    # everything following after processor.run() will not be executed
+    # before some quitting event is raised.
+    processor.run(window)
+
+    # We called video.init(), so we have to call video.quit() as well to
+    # release the resources hold by the SDL DLL.
+    sdl2ext.quit()
+
+
+if __name__ == "__main__":
+    sys.exit(run())

examples/opengl.py

+"""OpenGL rendering simulation"""
+import sys
+import random
+
+try:
+    import mule.video as video
+    import mule.sdl.events as sdlevents
+    import mule.sdl.timer as sdltimer
+    import mule.sdl.render as sdlrender
+    import mule.sdl.video as sdlvideo
+    import mule.sdl.rect as sdlrect
+except ImportError:
+    import traceback
+    traceback.print_exc()
+    sys.exit(1)
+
+# Import the resources, so we have easy access to the example images.
+from sdl2.ext import Resources
+RESOURCES = Resources(__file__, "resources")
+
+def run():
+    # Initialize the video subsystem, create a window and make it visible.
+    video.init()
+    window = video.Window("Particles", size=(800, 600),
+                          flags=sdlvideo.SDL_WINDOW_OPENGL)
+    window.show()
+
+    renderer = video.RenderContext(window)
+
+    # The almighty event loop. You already know several parts of it.
+    running = True
+    while running:
+        for event in video.get_events():
+            if event.type == sdlevents.SDL_QUIT:
+                running = False
+                break
+
+    video.quit()
+    return 0
+
+if __name__ == "__main__":
+    sys.exit(run())

examples/particles.py

+"""Particle simulation"""
+import sys
+import ctypes
+import random
+
+# Try to import SDL2. The import might fail, if the SDL2 DLL could
+# not be loaded. In that case, just print the error and exit with a
+# proper error code.
+try:
+    import sdl2.ext as sdl2ext
+    import sdl2.events as sdlevents
+    import sdl2.mouse as sdlmouse
+    import sdl2.timer as sdltimer
+    import sdl2.render as sdlrender
+    import sdl2.rect as sdlrect
+except ImportError:
+    import traceback
+    traceback.print_exc()
+    sys.exit(1)
+
+# Import the particles module, so we have access to all relevant parts
+# for dealing with particles.
+import sdl2.ext.particles as particles
+# We will create some systems and an entity for creating the particle
+# simulation. Hence we will need some things from the ebs module.
+from sdl2.ext.ebs import Entity, System, World
+
+
+# Import the resources, so we have easy access to the example images.
+from sdl2.ext.resources import Resources
+RESOURCES = Resources(__file__, "resources")
+
+
+# The Particle class offered by sdl2.ext.particles only contains the life
+# time information of the particle, which will be decreased by one each
+# time the particle engine processes it as well as a x- and
+# y-coordinate. This is not enough for us, since we want them to have a
+# velocity as well to make moving them around easier. Also, each
+# particle can look different for us, so we also store some information
+# about the image to display on rendering in ptype.
+#
+# If particles run out of life, we want to remove them, since we do not
+# want to flood our world with unused entities. Thus, we store a
+# reference to the entity, the particle belongs to, too. This allows use
+# to remove them easily later on.
+class CParticle(particles.Particle):
+    def __init__(self, entity, x, y, vx, vy, ptype, life):
+        super(CParticle, self).__init__(x, y, life)
+        self.entity = entity
+        self.type = ptype
+        self.vx = vx
+        self.vy = vy
+
+
+# A simple Entity class, that contains the particle information. This
+# represents our living particle object.
+class EParticle(Entity):
+    def __init__(self, world, x, y, vx, vy, ptype, life):
+        self.cparticle = CParticle(self, x, y, vx, vy, ptype, life)
+
+
+# A callback function for creating new particles. It is needed by the
+# ParticleEngine and the requirements are explained below.
+def createparticles(world, deadones, count=None):
+    if deadones is not None:
+        count = len(deadones)
+    # Create a replacement for each particle that died. The particle
+    # will be created at the current mouse cursor position (explained
+    # below) with a random velocity, life time, and image to be
+    # displayed.
+    for c in range(count):
+        x = world.mousex
+        y = world.mousey
+        vx = random.random() * 3 - 1
+        vy = random.random() * 3 - 1
+        life = random.randint(20, 100)
+        ptype = random.randint(0, 2)  # 0-2 denote the image to be used
+        # We do not need to assign the particle to a variable, since it
+        # will be added to the World and we do not need to do perform
+        # any post-creation operations.
+        EParticle(world, x, y, vx, vy, ptype, life)
+
+
+# A callback function for updating particles. It is needed by the
+# ParticleEngine and the requirements are explained below.
+def updateparticles(world, particles):
+    # For each existing, living particle, move it to a new location,
+    # based on its velocity.
+    for p in particles:
+        p.x += p.vx
+        p.y += p.vy
+
+
+# A callback function for deleting particles. It is neede by the
+# ParticleEngine and the requirements are explained below.
+def deleteparticles(world, deadones):
+    # As written in the comment for the CParticle class, we will use the
+    # stored entity reference of the dead particle components to delete
+    # the dead particles from the world.
+    world.delete_entities(p.entity for p in deadones)
+
+
+# Create a simple rendering system for particles. This is somewhat
+# similar to the TextureSprinteRenderer from mule.video. Since we
+# operate on particles rather than sprites, we need to provide our own
+# rendering logic.
+class ParticleRenderer(System):
+    def __init__(self, renderer, images):
+        # Create a new particle renderer. The surface argument will be
+        # the targets surface to do the rendering on. images is a set of
+        # images to be used for rendering the particles.
+        super(ParticleRenderer, self).__init__()
+        # Define, what component instances are processed by the
+        # ParticleRenderer.
+        self.componenttypes = (CParticle,)
+        self.renderer = renderer
+        self.images = images
+
+    def process(self, world, components):
+        # Processing code that will render all existing CParticle
+        # components that currently exist in the world. We have a 1:1
+        # mapping between the created particle entities and associated
+        # particle components; that said, we render all created
+        # particles here.
+
+        # We deal with quite a set of items, so we create some shortcuts
+        # to save Python the time to look things up.
+        #
+        # The SDL_Rect is used for the blit operation below and is used
+        # as destination position for rendering the particle.
+        r = sdlrect.SDL_Rect()
+
+        # The SDL2 blit function to use. This will take an image
+        # (SDL_Texture) as source and copies it on the target.
+        dorender = sdlrender.SDL_RenderCopy
+
+        # And some more shortcuts.
+        sdlrenderer = self.renderer.renderer
+        images = self.images
+        # Before rendering all particles, make sure the old ones are
+        # removed from the window by filling it with a black color.
+        self.renderer.clear(0x0)
+
+        # Render all particles.
+        for particle in components:
+            # Set the correct destination position for the particle
+            r.x = int(particle.x)
+            r.y = int(particle.y)
+
+            # Select the correct image for the particle.
+            img = images[particle.type]
+            r.w, r.h = img.size
+            # Render (or blit) the particle by using the designated image.
+            dorender(sdlrenderer, img.texture, None, r)
+        self.renderer.present()
+
+def run():
+    # Create the environment, in which our particles will exist.
+    world = World()
+
+    # Set up the globally available information about the current mouse
+    # position. We use that information to determine the emitter
+    # location for new particles.
+    world.mousex = 400
+    world.mousey = 300
+
+    # Create the particle engine. It is just a simple System that uses
+    # callback functions to update a set of components.
+    engine = particles.ParticleEngine()
+
+    # Bind the callback functions to the particle engine. The engine
+    # does the following on processing:
+    # 1) reduce the life time of each particle by one
+    # 2) create a list of particles, which's life time is 0 or below.
+    # 3) call createfunc() with the world passed to process() and
+    #    the list of dead particles
+    # 4) call updatefunc() with the world passed to process() and the
+    #    set of particles, which still are alive.
+    # 5) call deletefunc() with the world passed to process() and the
+    #    list of dead particles. deletefunc() is respsonible for
+    #    removing the dead particles from the world.
+    engine.createfunc = createparticles
+    engine.updatefunc = updateparticles
+    engine.deletefunc = deleteparticles
+    world.add_system(engine)
+
+    # We create all particles at once before starting the processing.
+    # We also could create them in chunks to have a visually more
+    # appealing effect, but let's keep it simple.
+    createparticles(world, None, 300)
+
+    # Initialize the video subsystem, create a window and make it visible.
+    sdl2ext.init()
+    window = sdl2ext.Window("Particles", size=(800, 600))
+    window.show()
+
+    renderer = sdl2ext.RenderContext(window)
+    factory = sdl2ext.SpriteFactory(sdl2ext.TEXTURE, renderer=renderer)
+
+    # Create a set of images to be used as particles on rendering. The
+    # images are used by the ParticleRenderer created below.
+    images = (factory.from_image(RESOURCES.get_path("circle.png")),
+              factory.from_image(RESOURCES.get_path("square.png")),
+              factory.from_image(RESOURCES.get_path("star.png"))
+              )
+
+    # Center the mouse on the window. We use the SDL2 functions directly
+    # here. Since the SDL2 functions do not know anything about the
+    # video.Window class, we have to pass the window's SDL_Window to it.
+    sdlmouse.SDL_WarpMouseInWindow(window.window, world.mousex, world.mousey)
+
+    # Hide the mouse cursor, os it does not show up - just show the
+    # particles.
+    sdlmouse.SDL_ShowCursor(0)
+
+    # Create the rendering system for the particles. This is somewhat
+    # similar to the SoftSpriteRenderer, but since we only operate with
+    # hundreds of particles (and not sprites with all their overhead),
+    # we need an own rendering system.
+    particlerenderer = ParticleRenderer(renderer, images)
+    world.add_system(particlerenderer)
+
+    # The almighty event loop. You already know several parts of it.
+    running = True
+    while running:
+        for event in sdl2ext.get_events():
+            if event.type == sdlevents.SDL_QUIT:
+                running = False
+                break
+
+            if event.type == sdlevents.SDL_MOUSEMOTION:
+                # Take care of the mouse motions here. Every time the
+                # mouse is moved, we will make that information globally
+                # available to our application environment by updating
+                # the world attributes created earlier.
+                world.mousex = event.motion.x
+                world.mousey = event.motion.y
+                # We updated the mouse coordinates once, ditch all the
+                # other ones. Since world.process() might take several
+                # milliseconds, new motion events can occur on the event
+                # queue (10ths to 100ths!), and we do not want to handle
+                # each of them. For this example, it is enough to handle
+                # one per update cycle.
+                sdlevents.SDL_FlushEvent(sdlevents.SDL_MOUSEMOTION)
+                break
+        world.process()
+
+    sdl2ext.quit()
+    return 0
+
+if __name__ == "__main__":
+    sys.exit(run())

examples/pixelaccess.py

+"""Direct pixel access examples."""
+import sys
+
+# Try to import SDL2. The import might fail, if the SDL2 DLL could not be
+# loaded. In that case, just print the error and exit with a proper
+# error code.
+try:
+    import sdl2.ext as sdl2ext
+    import sdl2.events as sdlevents
+except ImportError:
+    import traceback
+    traceback.print_exc()
+    sys.exit(1)
+
+# Define black and white as global values, so we can access them throughout
+# the code.
+BLACK = sdl2ext.Color(0, 0, 0)
+WHITE = sdl2ext.Color(255, 255, 255)
+
+
+# This function will use a rectangular area and fill each second horizontal
+# line with a white color on the passed surface.
+def draw_horizontal_stripes(surface, x1, x2, y1, y2):
+    # Fill the entire surface with a black color. In contrast to
+    # colorpalettes.py we use a Color() value here, just to demonstrate that
+    # it really works.
+    sdl2ext.fill(surface, BLACK)
+
+    # Create a 2D view that allows us to directly access each individual pixel
+    # of the surface. The PixelView class is quite slow, since it uses an non-
+    # optimised read-write access to each individual pixel and offset. It works
+    # on every platform, though.
+    pixelview = sdl2ext.PixelView(surface)
+
+    # Loop over the area bounds, considering each fourth line and every column
+    # on the 2D view. The PixelView uses a y-x alignment to access pixels.
+    # This mkeans that the first accessible dimension of the PixelView denotes
+    # the horizontal lines of an image, and the second the vertical lines.
+    for y in range(y1, y2, 4):
+        for x in range(x1, x2):
+            # Change the color of each individual pixel. We can assign any
+            # color-like value here, since the assignment method of the
+            # PixelView will implicitly check and convert the value to a
+            # matching color for its target surface.
+            pixelview[y][x] = WHITE
+
+    # Explicitly delete the PixelView. Some surface types need to be locked
+    # in order to access their pixels directly. The PixelView will do that
+    # implicitly at creation time. Once we are done with all necessary
+    # operations, we need to unlock the surface, which will be done
+    # automatically at the time the PixelView is garbage-collected.
+    del pixelview
+
+# as draw_horizontal_stripes(), but vertical
+def draw_vertical_stripes(surface, x1, x2, y1, y2):
+    sdl2ext.fill(surface, BLACK)
+    pixelview = sdl2ext.PixelView(surface)
+    for x in range(x1, x2, 4):
+        for y in range(y1, y2):
+            pixelview[y][x] = WHITE
+    del pixelview
+
+
+def run():
+    # You know those from the helloworld.py example.
+    # Initialize the video subsystem, create a window and make it visible.
+    sdl2ext.init()
+    window = sdl2ext.Window("Pixel Access", size=(800, 600))
+    window.show()
+
+    # As in colorpalettes.py, explicitly acquire the window's surface to
+    # draw on.
+    windowsurface = window.get_surface()
+
+    # We implement the functionality as it was done in colorpalettes.py and
+    # utilise a mapping table to look up the function to be executed, together
+    # with the arguments they should receive
+    functions = ((draw_horizontal_stripes, (windowsurface, 300, 500, 200, 400)),
+                 (draw_vertical_stripes, (windowsurface, 300, 500, 200, 400)),
+                 )
+
+    # A storage variable for the function we are currently on, so that we know
+    # which function to execute next.
+    curindex = 0
+
+    # The event loop is nearly the same as we used in colorpalettes.py. If you
+    # do not know, what happens here, take a look at colorpalettes.py for a
+    # detailled description.
+    running = True
+    while running:
+        events = sdl2ext.get_events()
+        for event in events:
+            if event.type == sdlevents.SDL_QUIT:
+                running = False
+                break
+            if event.type == sdlevents.SDL_MOUSEBUTTONDOWN:
+                curindex += 1
+                if curindex >= len(functions):
+                    curindex = 0
+                # In contrast to colorpalettes.py, our mapping table consists
+                # of functions and their arguments. Thus, we get the currently
+                # requested function and argument tuple and execute the
+                # function with the arguments.
+                func, args = functions[curindex]
+                func(*args)
+                break
+        window.refresh()
+    sdl2ext.quit()
+    return 0
+
+
+if __name__ == "__main__":
+    sys.exit(run())
+"""The Pong Game."""
+import sys
+
+try:
+    import sdl2.ext as sdl2ext
+    import sdl2.events as sdlevents
+    import sdl2.timer as sdltimer
+    import sdl2.keycode as sdlkc
+except ImportError:
+    import traceback
+    traceback.print_exc()
+    sys.exit(1)
+from sdl2.ext.ebs import Applicator, Entity, World
+
+BLACK = sdl2ext.Color(0, 0, 0)
+WHITE = sdl2ext.Color(255, 255, 255)
+PADDLE_SPEED = 3
+BALL_SPEED = 3
+
+
+class CollisionSystem(Applicator):
+    def __init__(self, minx, miny, maxx, maxy):
+        super(CollisionSystem, self).__init__()
+        self.componenttypes = (Velocity, sdl2ext.Sprite)
+        self.ball = None
+        self.minx = minx
+        self.miny = miny
+        self.maxx = maxx
+        self.maxy = maxy
+
+    def _overlap(self, item):
+        sprite = item[1]
+        if sprite == self.ball.sprite:
+            return False
+
+        left, top, right, bottom = sprite.area
+        bleft, btop, bright, bbottom = self.ball.sprite.area
+
+        return bleft < right and bright > left and \
+            btop < bottom and bbottom > top
+
+    def process(self, world, componentsets):
+        collitems = [comp for comp in componentsets if self._overlap(comp)]
+        if len(collitems) != 0:
+            self.ball.velocity.vx = -self.ball.velocity.vx
+
+            sprite = collitems[0][1]
+            ballcentery = self.ball.sprite.y + \
+                self.ball.sprite.size[1] // 2
+            halfheight = sprite.size[1] // 2
+            stepsize = halfheight // 10
+            degrees = 0.7
+            paddlecentery = sprite.y + halfheight
+            if ballcentery < paddlecentery:
+                factor = (paddlecentery - ballcentery) // stepsize
+                self.ball.velocity.vy = -int(round(factor * degrees))
+            elif ballcentery > paddlecentery:
+                factor = (ballcentery - paddlecentery) // stepsize
+                self.ball.velocity.vy = int(round(factor * degrees))
+            else:
+                self.ball.velocity.vy = -self.ball.velocity.vy
+
+        if self.ball.sprite.y <= self.miny or \
+                self.ball.sprite.y + self.ball.sprite.size[1] >= self.maxy:
+            self.ball.velocity.vy = -self.ball.velocity.vy
+
+        if self.ball.sprite.x <= self.minx or \
+                self.ball.sprite.x + self.ball.sprite.size[0] >= self.maxx:
+            self.ball.velocity.vx = -self.ball.velocity.vx
+
+
+class MovementSystem(Applicator):
+    def __init__(self, minx, miny, maxx, maxy):
+        super(MovementSystem, self).__init__()
+        self.componenttypes = (Velocity, sdl2ext.Sprite)
+        self.minx = minx
+        self.miny = miny
+        self.maxx = maxx
+        self.maxy = maxy
+
+    def process(self, world, componentsets):
+        for velocity, sprite in componentsets:
+            swidth, sheight = sprite.size
+            sprite.x += velocity.vx
+            sprite.y += velocity.vy
+
+            sprite.x = max(self.minx, sprite.x)
+            sprite.y = max(self.miny, sprite.y)
+
+            pmaxx = sprite.x + swidth
+            pmaxy = sprite.y + sheight
+            if pmaxx > self.maxx:
+                sprite.x = self.maxx - swidth
+            if pmaxy > self.maxy:
+                sprite.y = self.maxy - sheight
+
+
+class TrackingAIController(Applicator):
+    def __init__(self, miny, maxy):
+        super(TrackingAIController, self).__init__()
+        self.componenttypes = (PlayerData, Velocity, sdl2ext.Sprite)
+        self.miny = miny
+        self.maxy = maxy
+        self.ball = None
+
+    def process(self, world, componentsets):
+        for pdata, vel, sprite in componentsets:
+            if not pdata.ai:
+                continue
+
+            sheight = sprite.size[1]
+            centery = sprite.y + sheight // 2
+            if self.ball.velocity.vx < 0:
+                # ball is moving away from the AI
+                if centery < self.maxy // 2 - PADDLE_SPEED:
+                    vel.vy = PADDLE_SPEED
+                elif centery > self.maxy // 2 + PADDLE_SPEED:
+                    vel.vy = -PADDLE_SPEED
+                else:
+                    vel.vy = 0
+            else:
+                bcentery = self.ball.sprite.y + self.ball.sprite.size[1] // 2
+                if bcentery < centery + sheight // 3:
+                    vel.vy = -PADDLE_SPEED
+                elif bcentery > centery - sheight // 3:
+                    vel.vy = PADDLE_SPEED
+                else:
+                    vel.vy = 0
+
+
+class SoftwareRenderer(sdl2ext.SoftwareSpriteRenderer):
+    def __init__(self, window):
+        super(SoftwareRenderer, self).__init__(window)
+
+    def render(self, components):
+        sdl2ext.fill(self.surface, BLACK)
+        super(SoftwareRenderer, self).render(components)
+
+
+class TextureRenderer(sdl2ext.TextureSpriteRenderer):
+    def __init__(self, renderer):
+        super(TextureRenderer, self).__init__(renderer)
+        self.renderer = renderer
+
+    def render(self, components):
+        tmp = self.renderer.color
+        self.renderer.color = BLACK
+        self.renderer.clear()
+        self.renderer.color = tmp
+        super(TextureRenderer, self).render(components)
+
+
+class Velocity(object):
+    def __init__(self):
+        super(Velocity, self).__init__()
+        self.vx = 0
+        self.vy = 0
+
+
+class PlayerData(object):
+    def __init__(self):
+        super(PlayerData, self).__init__()
+        self.ai = False
+        self.points = 0
+
+
+class Player(Entity):
+    def __init__(self, world, sprite, posx=0, posy=0, ai=False):
+        self.sprite = sprite
+        self.sprite.position = posx, posy
+        self.velocity = Velocity()
+        self.playerdata = PlayerData()
+        self.playerdata.ai = ai
+
+
+class Ball(Entity):
+    def __init__(self, world, sprite, posx=0, posy=0):
+        self.sprite = sprite
+        self.sprite.position = posx, posy
+        self.velocity = Velocity()
+
+
+def run():
+    sdl2ext.init()
+    window = sdl2ext.Window("The Pong Game", size=(800, 600))
+    window.show()
+
+    if "-hardware" in sys.argv:
+        print("Using hardware acceleration")
+        renderer = sdl2ext.RenderContext(window)
+        factory = sdl2ext.SpriteFactory(sdl2ext.TEXTURE, renderer=renderer)
+    else:
+        print("Using software rendering")
+        factory = sdl2ext.SpriteFactory(sdl2ext.SOFTWARE)
+
+    # Create the paddles - we want white ones. To keep it easy enough for us,
+    # we create a set of surfaces that can be used for Texture- and
+    # Software-based sprites.
+    sp_paddle1 = factory.from_color(WHITE, size=(20, 100))
+    sp_paddle2 = factory.from_color(WHITE, size=(20, 100))
+    sp_ball = factory.from_color(WHITE, size=(20, 20))
+
+    world = World()
+
+    movement = MovementSystem(0, 0, 800, 600)
+    collision = CollisionSystem(0, 0, 800, 600)
+    aicontroller = TrackingAIController(0, 600)
+    if factory.sprite_type == sdl2ext.SOFTWARE:
+        spriterenderer = SoftwareRenderer(window)
+    else:
+        spriterenderer = TextureRenderer(renderer)
+
+    world.add_system(aicontroller)
+    world.add_system(movement)
+    world.add_system(collision)
+    world.add_system(spriterenderer)
+
+    player1 = Player(world, sp_paddle1, 0, 250)
+    player2 = Player(world, sp_paddle2, 780, 250, True)
+    ball = Ball(world, sp_ball, 390, 290)
+    ball.velocity.vx = -BALL_SPEED
+    collision.ball = ball
+    aicontroller.ball = ball
+
+    running = True
+    while running:
+        for event in sdl2ext.get_events():
+            if event.type == sdlevents.SDL_QUIT:
+                running = False
+                break
+            if event.type == sdlevents.SDL_KEYDOWN:
+                if event.key.keysym.sym == sdlkc.SDLK_UP:
+                    player1.velocity.vy = -PADDLE_SPEED
+                elif event.key.keysym.sym == sdlkc.SDLK_DOWN:
+                    player1.velocity.vy = PADDLE_SPEED
+            elif event.type == sdlevents.SDL_KEYUP:
+                if event.key.keysym.sym in (sdlkc.SDLK_UP, sdlkc.SDLK_DOWN):
+                    player1.velocity.vy = 0
+        sdltimer.SDL_Delay(10)
+        world.process()
+
+
+if __name__ == "__main__":
+    sys.exit(run())

examples/resources/button.bmp

Added
New image

examples/resources/circle.png

Added
New image

examples/resources/font.bmp

Added
New image

examples/resources/hello.bmp

Added
New image

examples/resources/square.png

Added
New image

examples/resources/star.png

Added
New image

examples/resources/testimage.svg

Added
New image
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/"
+   xmlns:cc="http://creativecommons.org/ns#"
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+   xmlns:svg="http://www.w3.org/2000/svg"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:xlink="http://www.w3.org/1999/xlink"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   width="744.09448819"
+   height="1052.3622047"
+   id="svg2"
+   version="1.1"
+   inkscape:version="0.48.2 r9819"
+   sodipodi:docname="testimage.svg">
+  <defs
+     id="defs4">
+    <linearGradient
+       id="linearGradient14956">
+      <stop
+         id="stop14968"
+         offset="0"
+         style="stop-color:#7f007f;stop-opacity:0.49803922;" />
+      <stop
+         style="stop-color:#7f007f;stop-opacity:0;"
+         offset="1"
+         id="stop14960" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient14936">
+      <stop
+         style="stop-color:#7f007f;stop-opacity:1;"
+         offset="0"
+         id="stop14938" />
+      <stop
+         id="stop14946"
+         offset="0.79677802"
+         style="stop-color:#7f007f;stop-opacity:1" />
+      <stop
+         id="stop14944"
+         offset="1"
+         style="stop-color:#7f007f;stop-opacity:0.49803922;" />
+      <stop
+         style="stop-color:#7f007f;stop-opacity:0;"
+         offset="1"
+         id="stop14940" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient14920">
+      <stop
+         style="stop-color:#7f007f;stop-opacity:1;"
+         offset="0"
+         id="stop14922" />
+      <stop
+         id="stop14990"
+         offset="0.5"
+         style="stop-color:#7f007f;stop-opacity:0.49803922;" />
+      <stop
+         style="stop-color:#7f007f;stop-opacity:0;"
+         offset="1"
+         id="stop14924" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient14936"
+       id="linearGradient14976"
+       x1="158.84203"
+       y1="510.64783"
+       x2="421.15793"
+       y2="510.64783"
+       gradientUnits="userSpaceOnUse" />
+  </defs>
+  <sodipodi:namedview
+     id="base"
+     pagecolor="#ffffff"
+     bordercolor="#666666"
+     borderopacity="1.0"
+     inkscape:pageopacity="0.0"
+     inkscape:pageshadow="2"
+     inkscape:zoom="1.75"
+     inkscape:cx="326.64951"
+     inkscape:cy="622.07845"
+     inkscape:document-units="px"
+     inkscape:current-layer="layer1"
+     showgrid="false"
+     inkscape:window-width="1251"
+     inkscape:window-height="964"
+     inkscape:window-x="3"
+     inkscape:window-y="49"
+     inkscape:window-maximized="0" />
+  <metadata
+     id="metadata7">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+        <dc:title></dc:title>
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <g
+     inkscape:label="Layer 1"
+     inkscape:groupmode="layer"
+     id="layer1">
+    <rect
+       style="fill:#666666;fill-opacity:1;stroke:none"
+       id="rect2987"
+       width="30"
+       height="30"
+       x="60"
+       y="198.07646"
+       inkscape:tile-cx="75"
+       inkscape:tile-cy="213.07646"
+       inkscape:tile-w="30"
+       inkscape:tile-h="30"
+       inkscape:tile-x0="60"
+       inkscape:tile-y0="198.07646" />
+    <use
+       x="0"
+       y="0"
+       inkscape:tiled-clone-of="#rect2987"
+       xlink:href="#rect2987"
+       id="use12942" />
+    <use
+       x="0"
+       y="0"
+       inkscape:tiled-clone-of="#rect2987"
+       xlink:href="#rect2987"
+       transform="translate(0,33)"
+       id="use12944" />
+    <use
+       x="0"
+       y="0"
+       inkscape:tiled-clone-of="#rect2987"
+       xlink:href="#rect2987"
+       transform="translate(0,66)"
+       id="use12946" />
+    <use
+       x="0"
+       y="0"
+       inkscape:tiled-clone-of="#rect2987"
+       xlink:href="#rect2987"
+       transform="translate(0,99)"
+       id="use12948" />
+    <use
+       x="0"
+       y="0"
+       inkscape:tiled-clone-of="#rect2987"
+       xlink:href="#rect2987"
+       transform="translate(0,132)"
+       id="use12950" />
+    <use
+       x="0"
+       y="0"
+       inkscape:tiled-clone-of="#rect2987"
+       xlink:href="#rect2987"
+       transform="translate(0,165)"
+       id="use12952" />
+    <use
+       x="0"
+       y="0"
+       inkscape:tiled-clone-of="#rect2987"
+       xlink:href="#rect2987"
+       transform="translate(0,198)"
+       id="use12954" />
+    <use
+       x="0"
+       y="0"
+       inkscape:tiled-clone-of="#rect2987"
+       xlink:href="#rect2987"
+       transform="translate(0,231)"
+       id="use12956" />
+    <use
+       x="0"
+       y="0"
+       inkscape:tiled-clone-of="#rect2987"
+       xlink:href="#rect2987"
+       transform="translate(0,264)"
+       id="use12958" />
+    <use
+       x="0"
+       y="0"
+       inkscape:tiled-clone-of="#rect2987"
+       xlink:href="#rect2987"
+       transform="translate(0,297)"
+       id="use12960" />
+    <use
+       x="0"
+       y="0"
+       inkscape:tiled-clone-of="#rect2987"
+       xlink:href="#rect2987"
+       transform="translate(0,330)"
+       id="use12962" />
+    <use
+       x="0"
+       y="0"
+       inkscape:tiled-clone-of="#rect2987"
+       xlink:href="#rect2987"
+       transform="translate(0,363)"
+       id="use12964" />
+    <use
+       x="0"
+       y="0"
+       inkscape:tiled-clone-of="#rect2987"
+       xlink:href="#rect2987"
+       transform="translate(0,396)"
+       id="use12966" />
+    <use
+       x="0"
+       y="0"
+       inkscape:tiled-clone-of="#rect2987"
+       xlink:href="#rect2987"
+       transform="translate(0,429)"
+       id="use12968" />
+    <use
+       x="0"
+       y="0"
+       inkscape:tiled-clone-of="#rect2987"
+       xlink:href="#rect2987"
+       transform="translate(33,0)"
+       id="use12970" />
+    <use
+       x="0"
+       y="0"
+       inkscape:tiled-clone-of="#rect2987"
+       xlink:href="#rect2987"
+       transform="translate(33,33)"
+       id="use12972" />
+    <use
+       x="0"
+       y="0"
+       inkscape:tiled-clone-of="#rect2987"
+       xlink:href="#rect2987"
+       transform="translate(33,66)"
+       id="use12974" />
+    <use
+       x="0"
+       y="0"
+       inkscape:tiled-clone-of="#rect2987"
+       xlink:href="#rect2987"
+       transform="translate(33,99)"
+       id="use12976" />
+    <use
+       x="0"
+       y="0"
+       inkscape:tiled-clone-of="#rect2987"
+       xlink:href="#rect2987"
+       transform="translate(33,132)"
+       id="use12978" />
+    <use
+       x="0"
+       y="0"
+       inkscape:tiled-clone-of="#rect2987"
+       xlink:href="#rect2987"
+       transform="translate(33,165)"
+       id="use12980" />
+    <use
+       x="0"
+       y="0"
+       inkscape:tiled-clone-of="#rect2987"
+       xlink:href="#rect2987"
+       transform="translate(33,198)"
+       id="use12982" />
+    <use
+       x="0"
+       y="0"
+       inkscape:tiled-clone-of="#rect2987"
+       xlink:href="#rect2987"
+       transform="translate(33,231)"
+       id="use12984" />
+    <use
+       x="0"
+       y="0"
+       inkscape:tiled-clone-of="#rect2987"
+       xlink:href="#rect2987"
+       transform="translate(33,264)"
+       id="use12986" />
+    <use
+       x="0"
+       y="0"
+       inkscape:tiled-clone-of="#rect2987"
+       xlink:href="#rect2987"
+       transform="translate(33,297)"
+       id="use12988" />
+    <use
+       x="0"
+       y="0"
+       inkscape:tiled-clone-of="#rect2987"
+       xlink:href="#rect2987"
+       transform="translate(33,330)"
+       id="use12990" />
+    <use
+       x="0"
+       y="0"
+       inkscape:tiled-clone-of="#rect2987"
+       xlink:href="#rect2987"
+       transform="translate(33,363)"
+       id="use12992" />
+    <use
+       x="0"
+       y="0"
+       inkscape:tiled-clone-of="#rect2987"
+       xlink:href="#rect2987"
+       transform="translate(33,396)"
+       id="use12994" />
+    <use
+       x="0"
+       y="0"
+       inkscape:tiled-clone-of="#rect2987"
+       xlink:href="#rect2987"
+       transform="translate(33,429)"
+       id="use12996" />
+    <use
+       x="0"
+       y="0"
+       inkscape:tiled-clone-of="#rect2987"
+       xlink:href="#rect2987"
+       transform="translate(66,0)"
+       id="use12998" />
+    <use
+       x="0"
+       y="0"
+       inkscape:tiled-clone-of="#rect2987"
+       xlink:href="#rect2987"
+       transform="translate(66,33)"
+       id="use13000" />
+    <use
+       x="0"
+       y="0"
+       inkscape:tiled-clone-of="#rect2987"
+       xlink:href="#rect2987"
+       transform="translate(66,66)"
+       id="use13002" />
+    <use
+       x="0"
+       y="0"
+       inkscape:tiled-clone-of="#rect2987"
+       xlink:href="#rect2987"
+       transform="translate(66,99)"
+       id="use13004" />
+    <use
+       x="0"
+       y="0"
+       inkscape:tiled-clone-of="#rect2987"
+       xlink:href="#rect2987"
+       transform="translate(66,132)"
+       id="use13006" />
+    <use
+       x="0"
+       y="0"
+       inkscape:tiled-clone-of="#rect2987"
+       xlink:href="#rect2987"
+       transform="translate(66,165)"
+       id="use13008" />
+    <use
+       x="0"
+       y="0"
+       inkscape:tiled-clone-of="#rect2987"
+       xlink:href="#rect2987"
+       transform="translate(66,198)"
+       id="use13010" />
+    <use
+       x="0"
+       y="0"
+       inkscape:tiled-clone-of="#rect2987"
+       xlink:href="#rect2987"
+       transform="translate(66,231)"
+       id="use13012" />
+    <use
+       x="0"
+       y="0"
+       inkscape:tiled-clone-of="#rect2987"
+       xlink:href="#rect2987"
+       transform="translate(66,264)"
+       id="use13014" />
+    <use
+       x="0"
+       y="0"
+       inkscape:tiled-clone-of="#rect2987"
+       xlink:href="#rect2987"
+       transform="translate(66,297)"
+       id="use13016" />
+    <use
+       x="0"
+       y="0"
+       inkscape:tiled-clone-of="#rect2987"
+       xlink:href="#rect2987"
+       transform="translate(66,330)"
+       id="use13018" />
+    <use
+       x="0"
+       y="0"
+       inkscape:tiled-clone-of="#rect2987"
+       xlink:href="#rect2987"
+       transform="translate(66,363)"
+       id="use13020" />
+    <use
+       x="0"
+       y="0"
+       inkscape:tiled-clone-of="#rect2987"
+       xlink:href="#rect2987"
+       transform="translate(66,396)"
+       id="use13022" />
+    <use
+       x="0"
+       y="0"
+       inkscape:tiled-clone-of="#rect2987"
+       xlink:href="#rect2987"
+       transform="translate(66,429)"
+       id="use13024" />
+    <use
+       x="0"
+       y="0"
+       inkscape:tiled-clone-of="#rect2987"
+       xlink:href="#rect2987"
+       transform="translate(99,0)"
+       id="use13026" />
+    <use
+       x="0"
+       y="0"
+       inkscape:tiled-clone-of="#rect2987"
+       xlink:href="#rect2987"
+       transform="translate(99,33)"
+       id="use13028" />
+    <use
+       x="0"
+       y="0"
+       inkscape:tiled-clone-of="#rect2987"
+       xlink:href="#rect2987"
+       transform="translate(99,66)"
+       id="use13030" />
+    <use
+       x="0"
+       y="0"
+       inkscape:tiled-clone-of="#rect2987"
+       xlink:href="#rect2987"
+       transform="translate(99,99)"
+       id="use13032" />
+    <use
+       x="0"
+       y="0"
+       inkscape:tiled-clone-of="#rect2987"
+       xlink:href="#rect2987"
+       transform="translate(99,132)"
+       id="use13034" />
+    <use
+       x="0"
+       y="0"
+       inkscape:tiled-clone-of="#rect2987"
+       xlink:href="#rect2987"
+       transform="translate(99,165)"
+       id="use13036" />
+    <use
+       x="0"
+       y="0"
+       inkscape:tiled-clone-of="#rect2987"
+       xlink:href="#rect2987"
+       transform="translate(99,198)"
+       id="use13038" />
+    <use
+       x="0"
+       y="0"
+       inkscape:tiled-clone-of="#rect2987"
+       xlink:href="#rect2987"
+       transform="translate(99,231)"
+       id="use13040" />
+    <use
+       x="0"
+       y="0"
+       inkscape:tiled-clone-of="#rect2987"
+       xlink:href="#rect2987"
+       transform="translate(99,264)"
+       id="use13042" />
+    <use
+       x="0"
+       y="0"
+       inkscape:tiled-clone-of="#rect2987"
+       xlink:href="#rect2987"
+       transform="translate(99,297)"
+       id="use13044" />
+    <use
+       x="0"
+       y="0"
+       inkscape:tiled-clone-of="#rect2987"
+       xlink:href="#rect2987"
+       transform="translate(99,330)"
+       id="use13046" />
+    <use
+       x="0"
+       y="0"
+       inkscape:tiled-clone-of="#rect2987"
+       xlink:href="#rect2987"
+       transform="translate(99,363)"
+       id="use13048" />
+    <use
+       x="0"
+       y="0"
+       inkscape:tiled-clone-of="#rect2987"
+       xlink:href="#rect2987"
+       transform="translate(99,396)"
+       id="use13050" />
+    <use
+       x="0"
+       y="0"
+       inkscape:tiled-clone-of="#rect2987"
+       xlink:href="#rect2987"
+       transform="translate(99,429)"
+       id="use13052" />
+    <use
+       x="0"
+       y="0"
+       inkscape:tiled-clone-of="#rect2987"
+       xlink:href="#rect2987"
+       transform="translate(132,0)"
+       id="use13054" />
+    <use
+       x="0"
+       y="0"
+       inkscape:tiled-clone-of="#rect2987"
+       xlink:href="#rect2987"
+       transform="translate(132,33)"
+       id="use13056" />
+    <use
+       x="0"
+       y="0"
+       inkscape:tiled-clone-of="#rect2987"
+       xlink:href="#rect2987"
+       transform="translate(132,66)"
+       id="use13058" />
+    <use
+       x="0"
+       y="0"
+       inkscape:tiled-clone-of="#rect2987"
+       xlink:href="#rect2987"
+       transform="translate(132,99)"
+       id="use13060" />
+    <use
+       x="0"
+       y="0"
+       inkscape:tiled-clone-of="#rect2987"
+       xlink:href="#rect2987"
+       transform="translate(132,132)"
+       id="use13062" />
+    <use
+       x="0"
+       y="0"
+       inkscape:tiled-clone-of="#rect2987"
+       xlink:href="#rect2987"
+       transform="translate(132,165)"
+       id="use13064" />
+    <use
+       x="0"
+       y="0"
+       inkscape:tiled-clone-of="#rect2987"
+       xlink:href="#rect2987"
+       transform="translate(132,198)"
+       id="use13066" />
+    <use
+       x="0"
+       y="0"
+       inkscape:tiled-clone-of="#rect2987"
+       xlink:href="#rect2987"
+       transform="translate(132,231)"
+       id="use13068" />
+    <use
+       x="0"
+       y="0"
+       inkscape:tiled-clone-of="#rect2987"
+       xlink:href="#rect2987"
+       transform="translate(132,264)"
+       id="use13070" />
+    <use
+       x="0"
+       y="0"
+       inkscape:tiled-clone-of="#rect2987"
+       xlink:href="#rect2987"
+       transform="translate(132,297)"
+       id="use13072" />
+    <use
+       x="0"
+       y="0"
+       inkscape:tiled-clone-of="#rect2987"
+       xlink:href="#rect2987"
+       transform="translate(132,330)"
+       id="use13074" />
+    <use
+       x="0"
+       y="0"
+       inkscape:tiled-clone-of="#rect2987"
+       xlink:href="#rect2987"
+       transform="translate(132,363)"
+       id="use13076" />
+    <use
+       x="0"
+       y="0"
+       inkscape:tiled-clone-of="#rect2987"
+       xlink:href="#rect2987"
+       transform="translate(132,396)"
+       id="use13078" />
+    <use
+       x="0"
+       y="0"
+       inkscape:tiled-clone-of="#rect2987"
+       xlink:href="#rect2987"
+       transform="translate(132,429)"
+       id="use13080" />
+    <use
+       x="0"
+       y="0"
+       inkscape:tiled-clone-of="#rect2987"
+       xlink:href="#rect2987"
+       transform="translate(165,0)"
+       id="use13082" />
+    <use
+       x="0"
+       y="0"
+       inkscape:tiled-clone-of="#rect2987"
+       xlink:href="#rect2987"
+       transform="translate(165,33)"
+       id="use13084" />
+    <use
+       x="0"
+       y="0"
+       inkscape:tiled-clone-of="#rect2987"
+       xlink:href="#rect2987"
+       transform="translate(165,66)"
+       id="use13086" />
+    <use
+       x="0"
+       y="0"
+       inkscape:tiled-clone-of="#rect2987"
+       xlink:href="#rect2987"
+       transform="translate(165,99)"
+       id="use13088" />
+    <use
+       x="0"
+       y="0"
+       inkscape:tiled-clone-of="#rect2987"
+       xlink:href="#rect2987"
+       transform="translate(165,132)"
+       id="use13090" />
+    <use
+       x="0"
+       y="0"
+       inkscape:tiled-clone-of="#rect2987"
+       xlink:href="#rect2987"
+       transform="translate(165,165)"
+       id="use13092" />
+    <use
+       x="0"
+       y="0"
+       inkscape:tiled-clone-of="#rect2987"
+       xlink:href="#rect2987"
+       transform="translate(165,198)"
+       id="use13094" />
+    <use
+       x="0"
+       y="0"
+       inkscape:tiled-clone-of="#rect2987"
+       xlink:href="#rect2987"
+       transform="translate(165,231)"
+       id="use13096"
+       style="fill-opacity:1;fill-rule:nonzero;" />
+    <use
+       x="0"
+       y="0"
+       inkscape:tiled-clone-of="#rect2987"
+       xlink:href="#rect2987"
+       transform="translate(165,264)"
+       id="use13098" />
+    <use
+       x="0"
+       y="0"
+       inkscape:tiled-clone-of="#rect2987"
+       xlink:href="#rect2987"
+       transform="translate(165,297)"
+       id="use13100" />
+    <use
+       x="0"
+       y="0"
+       inkscape:tiled-clone-of="#rect2987"
+       xlink:href="#rect2987"
+       transform="translate(165,330)"
+       id="use13102" />
+    <use
+       x="0"
+       y="0"
+       inkscape:tiled-clone-of="#rect2987"
+       xlink:href="#rect2987"
+       transform="translate(165,363)"
+       id="use13104" />
+    <use
+       x="0"
+       y="0"
+       inkscape:tiled-clone-of="#rect2987"
+       xlink:href="#rect2987"
+       transform="translate(165,396)"
+       id="use13106" />
+    <use
+       x="0"
+       y="0"
+       inkscape:tiled-clone-of="#rect2987"
+       xlink:href="#rect2987"
+       transform="translate(165,429)"
+       id="use13108" />
+    <use
+       x="0"
+       y="0"
+       inkscape:tiled-clone-of="#rect2987"
+       xlink:href="#rect2987"
+       transform="translate(198,0)"
+       id="use13110" />
+    <use
+       x="0"
+       y="0"
+       inkscape:tiled-clone-of="#rect2987"
+       xlink:href="#rect2987"
+       transform="translate(198,33)"
+       id="use13112" />
+    <use
+       x="0"
+       y="0"
+       inkscape:tiled-clone-of="#rect2987"
+       xlink:href="#rect2987"
+       transform="translate(198,66)"
+       id="use13114" />
+    <use
+       x="0"
+       y="0"
+       inkscape:tiled-clone-of="#rect2987"
+       xlink:href="#rect2987"
+       transform="translate(198,99)"
+       id="use13116" />
+    <use
+       x="0"
+       y="0"
+       inkscape:tiled-clone-of="#rect2987"
+       xlink:href="#rect2987"
+       transform="translate(198,132)"
+       id="use13118" />
+    <use
+       x="0"
+       y="0"
+       inkscape:tiled-clone-of="#rect2987"
+       xlink:href="#rect2987"
+       transform="translate(198,165)"
+       id="use13120" />
+    <use
+       x="0"
+       y="0"
+       inkscape:tiled-clone-of="#rect2987"
+       xlink:href="#rect2987"
+       transform="translate(198,198)"
+       id="use13122" />
+    <use
+       x="0"
+       y="0"
+       inkscape:tiled-clone-of="#rect2987"
+       xlink:href="#rect2987"
+       transform="translate(198,231)"
+       id="use13124" />
+    <use
+       x="0"
+       y="0"
+       inkscape:tiled-clone-of="#rect2987"
+       xlink:href="#rect2987"
+       transform="translate(198,264)"
+       id="use13126" />
+    <use
+       x="0"
+       y="0"
+       inkscape:tiled-clone-of="#rect2987"
+       xlink:href="#rect2987"
+       transform="translate(198,297)"
+       id="use13128" />
+    <use
+       x="0"
+       y="0"
+       inkscape:tiled-clone-of="#rect2987"
+       xlink:href="#rect2987"
+       transform="translate(198,330)"
+       id="use13130" />
+    <use
+       x="0"
+       y="0"
+       inkscape:tiled-clone-of="#rect2987"
+       xlink:href="#rect2987"
+       transform="translate(198,363)"
+       id="use13132" />
+    <use
+       x="0"
+       y="0"
+       inkscape:tiled-clone-of="#rect2987"
+       xlink:href="#rect2987"
+       transform="translate(198,396)"
+       id="use13134" />
+    <use
+       x="0"
+       y="0"
+       inkscape:tiled-clone-of="#rect2987"
+       xlink:href="#rect2987"
+       transform="translate(198,429)"
+       id="use13136" />
+    <use
+       x="0"
+       y="0"
+       inkscape:tiled-clone-of="#rect2987"
+       xlink:href="#rect2987"
+       transform="translate(231,0)"
+       id="use13138" />
+    <use
+       x="0"
+       y="0"
+       inkscape:tiled-clone-of="#rect2987"
+       xlink:href="#rect2987"
+       transform="translate(231,33)"
+       id="use13140" />
+    <use
+       x="0"
+       y="0"
+       inkscape:tiled-clone-of="#rect2987"
+       xlink:href="#rect2987"
+       transform="translate(231,66)"
+       id="use13142" />
+    <use
+       x="0"
+       y="0"
+       inkscape:tiled-clone-of="#rect2987"
+       xlink:href="#rect2987"
+       transform="translate(231,99)"
+       id="use13144" />
+    <use
+       x="0"
+       y="0"
+       inkscape:tiled-clone-of="#rect2987"
+       xlink:href="#rect2987"
+       transform="translate(231,132)"
+       id="use13146" />
+    <use
+       x="0"
+       y="0"
+       inkscape:tiled-clone-of="#rect2987"
+       xlink:href="#rect2987"
+       transform="translate(231,165)"
+       id="use13148" />
+    <use
+       x="0"
+       y="0"
+       inkscape:tiled-clone-of="#rect2987"
+       xlink:href="#rect2987"
+       transform="translate(231,198)"
+       id="use13150" />
+    <use
+       x="0"
+       y="0"
+       inkscape:tiled-clone-of="#rect2987"
+       xlink:href="#rect2987"
+       transform="translate(231,231)"
+       id="use13152" />
+    <use
+       x="0"
+       y="0"
+       inkscape:tiled-clone-of="#rect2987"
+       xlink:href="#rect2987"
+       transform="translate(231,264)"
+       id="use13154" />
+    <use
+       x="0"
+       y="0"
+       inkscape:tiled-clone-of="#rect2987"
+       xlink:href="#rect2987"
+       transform="translate(231,297)"
+       id="use13156" />
+    <use
+       x="0"
+       y="0"
+       inkscape:tiled-clone-of="#rect2987"
+       xlink:href="#rect2987"
+       transform="translate(231,330)"
+       id="use13158" />
+    <use
+       x="0"
+       y="0"
+       inkscape:tiled-clone-of="#rect2987"
+       xlink:href="#rect2987"
+       transform="translate(231,363)"
+       id="use13160" />
+    <use
+       x="0"
+       y="0"
+       inkscape:tiled-clone-of="#rect2987"
+       xlink:href="#rect2987"
+       transform="translate(231,396)"
+       id="use13162" />
+    <use
+       x="0"
+       y="0"
+       inkscape:tiled-clone-of="#rect2987"
+       xlink:href="#rect2987"
+       transform="translate(231,429)"
+       id="use13164" />
+    <use
+       x="0"
+       y="0"
+       inkscape:tiled-clone-of="#rect2987"
+       xlink:href="#rect2987"
+       transform="translate(264,0)"
+       id="use13166" />
+    <use
+       x="0"
+       y="0"
+       inkscape:tiled-clone-of="#rect2987"
+       xlink:href="#rect2987"
+       transform="translate(264,33)"
+       id="use13168" />
+    <use
+       x="0"
+       y="0"
+       inkscape:tiled-clone-of="#rect2987"
+       xlink:href="#rect2987"
+       transform="translate(264,66)"
+       id="use13170" />
+    <use
+       x="0"
+       y="0"
+       inkscape:tiled-clone-of="#rect2987"
+       xlink:href="#rect2987"
+       transform="translate(264,99)"
+       id="use13172" />
+    <use
+       x="0"
+       y="0"
+       inkscape:tiled-clone-of="#rect2987"
+       xlink:href="#rect2987"
+       transform="translate(264,132)"
+       id="use13174" />
+    <use
+       x="0"
+       y="0"
+       inkscape:tiled-clone-of="#rect2987"
+       xlink:href="#rect2987"
+       transform="translate(264,165)"
+       id="use13176" />
+    <use
+       x="0"
+       y="0"
+       inkscape:tiled-clone-of="#rect2987"
+       xlink:href="#rect2987"