# ShapeElementList Explanation#

If you are drawing a lot of items on your screen, the `arcade.ShapeElementList` can speed your drawing. How does it work?

Say we have a screen with about 9,600 rectangles:

Those rectangles are created with 38,400 points + 9,600 colors + 9,600 angles. They need to be sent to the graphics card 60 times per second. This quickly balloons into 3.5 million numbers being sent to the graphics card each second to display a bunch of rectangles that arenâ€™t even moving.

What we want to do instead is package all these rectangles up, send them to the graphics card, and then have them be drawn in just one command. This takes us from 3.5 million items per second down to about 60.

There are three examples here:

• First, no shape lists. Just drawing rectangles.

• Second, each rectangle is individually buffered to the GPU.

• Third, all rectangles are buffered to the GPU as and drawn as a group.

## Example One - No Shape Lists#

shape_list_demo_1.py#
 ``` 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67``` ```""" This demo shows the speed of drawing a full grid of squares using no buffering. For me this takes about 0.16 seconds per frame. It is slow because we load all the points and all the colors to the card every time. If Python and Arcade are installed, this example can be run from the command line with: python -m arcade.examples.shape_list_demo_1 """ import arcade import timeit SCREEN_WIDTH = 1200 SCREEN_HEIGHT = 800 SCREEN_TITLE = "Shape List Demo 1" SQUARE_WIDTH = 5 SQUARE_HEIGHT = 5 SQUARE_SPACING = 10 class MyGame(arcade.Window): """ Main application class. """ def __init__(self, width, height, title): super().__init__(width, height, title) arcade.set_background_color(arcade.color.DARK_SLATE_GRAY) self.draw_time = 0 def on_draw(self): """ Render the screen. """ # This command has to happen before we start drawing self.clear() # Start timing how long this takes draw_start_time = timeit.default_timer() # --- Draw all the rectangles for x in range(0, SCREEN_WIDTH, SQUARE_SPACING): for y in range(0, SCREEN_HEIGHT, SQUARE_SPACING): arcade.draw_rectangle_filled(x, y, SQUARE_WIDTH, SQUARE_HEIGHT, arcade.color.DARK_BLUE) # Print the timing output = f"Drawing time: {self.draw_time:.3f} seconds per frame." arcade.draw_text(output, 20, SCREEN_HEIGHT - 40, arcade.color.WHITE, 18) self.draw_time = timeit.default_timer() - draw_start_time def main(): MyGame(SCREEN_WIDTH, SCREEN_HEIGHT, SCREEN_TITLE) arcade.run() if __name__ == "__main__": main() ```

## Example Two - Simple Shape Lists#

shape_list_demo_2.py#
 ``` 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76``` ```""" This demo shows using buffered rectangles to draw a grid of squares on the screen. For me this runs about 0.002 seconds per frame. It is faster than demo 1 because we aren't loading the vertices and color to the card again and again. It could be faster though, if we group all rectangles together. If Python and Arcade are installed, this example can be run from the command line with: python -m arcade.examples.shape_list_demo_2 """ import arcade import timeit SCREEN_WIDTH = 1200 SCREEN_HEIGHT = 800 SCREEN_TITLE = "Shape List Demo 2" SQUARE_WIDTH = 5 SQUARE_HEIGHT = 5 SQUARE_SPACING = 10 class MyGame(arcade.Window): """ Main application class. """ def __init__(self, width, height, title): super().__init__(width, height, title) arcade.set_background_color(arcade.color.DARK_SLATE_GRAY) self.draw_time = 0 self.shape_list = None def setup(self): # --- Create the vertex buffers objects for each square before we do # any drawing. self.shape_list = arcade.ShapeElementList() for x in range(0, SCREEN_WIDTH, SQUARE_SPACING): for y in range(0, SCREEN_HEIGHT, SQUARE_SPACING): shape = arcade.create_rectangle_filled(x, y, SQUARE_WIDTH, SQUARE_HEIGHT, arcade.color.DARK_BLUE) self.shape_list.append(shape) def on_draw(self): """ Render the screen. """ # This command has to happen before we start drawing self.clear() # Start timing how long this takes draw_start_time = timeit.default_timer() # --- Draw all the rectangles self.shape_list.draw() output = f"Drawing time: {self.draw_time:.3f} seconds per frame." arcade.draw_text(output, 20, SCREEN_HEIGHT - 40, arcade.color.WHITE, 18) self.draw_time = timeit.default_timer() - draw_start_time def main(): window = MyGame(SCREEN_WIDTH, SCREEN_HEIGHT, SCREEN_TITLE) window.setup() arcade.run() if __name__ == "__main__": main() ```

## Example Three - Complex Shape Lists#

shape_list_demo_3.py#
 ``` 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101``` ```""" This demo shows drawing a grid of squares using a single buffer. We calculate the points of each rectangle and add them to a point list. We create a list of colors for each point. We then draw all the squares with one drawing command. This runs in about 0.000 seconds for me. It is much more complex in code than the prior two examples, but the pay-off in speed is huge. If Python and Arcade are installed, this example can be run from the command line with: python -m arcade.examples.shape_list_demo_3 """ import arcade import timeit SCREEN_WIDTH = 1200 SCREEN_HEIGHT = 800 SCREEN_TITLE = "Shape List Demo 3" HALF_SQUARE_WIDTH = 2.5 HALF_SQUARE_HEIGHT = 2.5 SQUARE_SPACING = 10 class MyGame(arcade.Window): """ Main application class. """ def __init__(self, width, height, title): super().__init__(width, height, title) arcade.set_background_color(arcade.color.DARK_SLATE_GRAY) self.draw_time = 0 self.shape_list = None def setup(self): self.shape_list = arcade.ShapeElementList() # --- Create all the rectangles # We need a list of all the points and colors point_list = [] color_list = [] # Now calculate all the points for x in range(0, SCREEN_WIDTH, SQUARE_SPACING): for y in range(0, SCREEN_HEIGHT, SQUARE_SPACING): # Calculate where the four points of the rectangle will be if # x and y are the center top_left = (x - HALF_SQUARE_WIDTH, y + HALF_SQUARE_HEIGHT) top_right = (x + HALF_SQUARE_WIDTH, y + HALF_SQUARE_HEIGHT) bottom_right = (x + HALF_SQUARE_WIDTH, y - HALF_SQUARE_HEIGHT) bottom_left = (x - HALF_SQUARE_WIDTH, y - HALF_SQUARE_HEIGHT) # Add the points to the points list. # ORDER MATTERS! # Rotate around the rectangle, don't append points caty-corner point_list.append(top_left) point_list.append(top_right) point_list.append(bottom_right) point_list.append(bottom_left) # Add a color for each point. Can be different colors if you want # gradients. for i in range(4): color_list.append(arcade.color.DARK_BLUE) shape = arcade.create_rectangles_filled_with_colors(point_list, color_list) self.shape_list.append(shape) def on_draw(self): """ Render the screen. """ # This command has to happen before we start drawing self.clear() # Start timing how long this takes draw_start_time = timeit.default_timer() # --- Draw all the rectangles self.shape_list.draw() output = f"Drawing time: {self.draw_time:.3f} seconds per frame." arcade.draw_text(output, 20, SCREEN_HEIGHT - 40, arcade.color.WHITE, 18) self.draw_time = timeit.default_timer() - draw_start_time def main(): window = MyGame(SCREEN_WIDTH, SCREEN_HEIGHT, SCREEN_TITLE) window.setup() arcade.run() if __name__ == "__main__": main() ```