Fork/Clone Ch.07 Graphics Jedi Training
Fork/Clone Ch.07 Graphics Jedi Training
The Cartesian coordinate system, shown following, is the system most people are used to when plotting graphics. This is the system taught in school. The computer uses a similar, but somewhat different, coordinate system. Understanding why it is different requires a quick bit of computer history.
During the early '80s, most computer systems were text-based and did not support graphics. The image following shows an early spreadsheet program run on an Apple II computer that was popular in the '80s. When positioning text on the screen, programmers started at the top calling it line 1. The screen continued down for 24 lines and across for 40 characters.
Even with plain text, it was possible to make rudimentary graphics by just using characters on the keyboard. See the following image and look carefully at how it is drawn. When making this art, characters were still positioned starting with line 1 at the top. Sometimes students have used ASCII art in their Camel and Coin Toss Programs. Search the web for "ASCII art" and many more examples can be found.
Later the character set was expanded to include boxes and other primitive drawing shapes. Characters could be drawn in different colors. As seen following, the graphics got more advanced. Even after computers moved to being able to control individual pixels for graphics, the text-based coordinate system stuck for most drawing modules.
The (x) coordinates work the same as the Cartesian coordinates system. But the (y) coordinates were reversed. Basically the drawing screen covered the lower right quadrant.
As human beings, our brain seems to work better if we can draw graphics in the upper right quadrant. As luck would have it there is a recently released Python Module called "Arcade" created by Paul V. Craven, a professor of computer science at Simpson College in Indianola, Iowa. We will be using his Arcade module heavily during this class. This is exciting because he programmed the module to create drawing windows using the top-right quadrant instead of the classic bottom-right method. This will help our brains not get so confused!
Before we can draw anything, we need to import a “library” of code that has commands for drawing. Computer languages come with a set of built-in commands. Most programs will require more commands than what the computer language loads by default. These sets of commands are called libraries. Some languages have their own special term for these libraries. In the case of Python, they are called modules.
Thankfully, it is easy to import a module. In fact, we already did this when we wanted to use random numbers. We did this by putting import random at the top of our program.
If we want to use the “arcade” library, it's just as easy:
import arcade
Libraries should always be imported at the top of your program. Only comments should appear ahead of an import statement. Technically, you can put the import statement somewhere else, it's just considered really bad code and you would be embarrassed if someone saw it.
Next we need to open a drawing window so you can enter the next line of code:
arcade.open_window(600,600, "Star Wars Art")
How does this command work? To begin, we select the arcade module by typing arcade. Then we separate the module from the command we want to call with a period: . Next, we put in the name of the command to run. Which happens to be open_window.
Commands that we can run are called functions, which we will discuss in much more detail later. Just like the sine and cosine functions in math, we surround the function parameters with parentheses. For example:
my_function(parameters)
The data we need to pass the function are the parameters. In the case of open_window, we need three parameters:
The window width in pixels.
The window height in pixels.
The text that will appear on the title bar.
In the case of width and height, the numbers specify the part of the window you can draw on. The actual window is larger to accommodate the title bar and borders. So a 600x600 window is really 602x632 if you count the title bar and borders.
Documentation of names of the functions and the order of the parameters for any given module, is called the Application Program Interface or “API” for short. You can visit the Arcade API below. You will probably have to do this often to learn how to do things you want to try. This online book will NOT go over every single function you can run in the Arcade module.
If you run this program the window will open and close so fast you may not even see it. To keep the window open, we need to pause until the user hits the close button. To do this, we’ll use the run command in the Arcade library. The run command takes no parameters, but even if a function doesn’t take parameters, you still need to use parentheses.
arcade.run()
If you run the program now you should get a blank drawing window display with a default white background.
Right now we just have a default white as our background. How do we get a different color? We can use the following command:
arcade.set_background_color(arcade.color.STAR_COMMAND_BLUE)
By itself, the function doesn’t actually work. We need two more important commands to tell the Arcade module when to start drawing and when to finish drawing. These command are as follows with all future drawing commands being placed somewhere between them:
arcade.start_render()
'''
Place all Drawing commands in here
'''
arcade.finish_render()
So far our entire code looks like the following and if run, will output the following window.
So where did the STAR_COMMAND_BLUE come from? How do we know how to specify colors? There are two ways we can do this with arcade:
Look at the arcade.color API documentation and specify by name.
Specify the RGB or RGBA color.
To specify colors by name, you can look at the color API documentation and use something like arcade.color.ANTIQUE_FUCHSIA in your program.
If the color you want isn’t in the chart, or you just don’t want to use that chart, you can specify colors by “RGB”. RGB stands for Red, Green, and Blue.
Computers, TVs, color changing LEDs, all work by having three small lights close together. A red light, a green light, and a blue light. Turn all three lights off and you get black. Turn all three lights on and you get white. Just turn on the red, and you get red. Turn on both red and green to get yellow.
We specify how much red, green, and blue to use using numbers. No light is zero. Turn the light on all the way and it is 255. So (0, 0, 0) means no red, no green, no blue. This would be black. (255,255,0) would mean full red, full green and no blue. This would be yellow.
An easy online tool to get RGB values is “colorpicker.com”. You can select the color, and then get the numbers to use when specifying a color. See the image below:
After getting the number, specify the color as a set of three numbers surrounded by parentheses, like this:
arcade.set_background_color((189, 55, 180))
In addition to RGB, you can also specify “Alpha”. The “Alpha Channel” controls how transparent the color is. If you draw a square with an alpha of 255, it will be solid and hide everything behind it. An alpha of 127 will be in the middle, you will see some of the items behind the square. An alpha of 0 is completely transparent and you’ll see nothing of the square.
Below are listed many drawing commands. You should spend some time experimenting with them. Before you start, however, here are several things to keep in mind while drawing in Arcade:
We will only draw in the upper right quadrant. So 0,0 will be in the lower left of the screen, and all negative coordinates will be off-screen.
Each “Point” will be a pixel. So a window that is 800 pixels wide, will have x-coordinates that run from 0 to 800.
All drawing code should be located between arcade.start_render() and arcade.finish_render()
Drawing code that comes later is on top of drawing code that came earlier.
Click here for Drawing Commands API
# lrtb: Left 5, right 35, Top 590, bottom 570, color
arcade.draw_lrtb_rectangle_filled(5, 35, 590, 570, arcade.color.BITTER_LIME)
# alternate method: Center rectangle at (100, 520) with a width of 45 and height of 25
arcade.draw_rectangle_filled(100, 520, 45, 25, arcade.color.BLUSH)
# Center rectangle at (100, 520) with a width of 45 and height of 25, pixel border
arcade.draw_rectangle_outline(100, 320, 45, 25, arcade.color.BLUSH,3)
# Center rectangle at (200, 520) with a width of 45 and height of 25, rotated 45
arcade.draw_rectangle_filled(200, 520, 45, 25, arcade.color.BLUSH, 45)
# Draw a point at (50, 580) that is 5 pixels large
arcade.draw_point(50, 580, arcade.color.RED, 5)
# Draw a line. Start point of (75, 590). End point of (95, 570)
arcade.draw_line(75, 590, 95, 570, arcade.color.BLACK, 2)
# Draw a circle outline centered at (140, 580) with a radius of 18 and a line width of 3.
arcade.draw_circle_outline(140, 580, 18, arcade.color.WISTERIA, 3)
# Draw a circle centered at (190, 580) with a radius of 18
arcade.draw_circle_filled(190, 580, 18, arcade.color.WISTERIA)
# Draw an ellipse centered at (240,580) with a width:30 and height:15.
arcade.draw_ellipse_filled(240, 580, 30, 15, arcade.color.AMBER)
# Draw a polygon outline
point_list = ((30, 240),
(45, 240),
(60, 255),
(60, 285),
(45, 300),
(30, 300))
arcade.draw_polygon_outline(point_list, arcade.color.SPANISH_VIOLET, 3)
# Draw a filled in polygon
point_list = ((150, 240),
(165, 240),
(180, 255),
(180, 285),
(165, 300),
(150, 300))
arcade.draw_polygon_filled(point_list, arcade.color.SPANISH_VIOLET)
# Draw text starting at (10, 450) with a size of 20 points.
arcade.draw_text("May the Force be with you!", 10, 450, arcade.color.YELLOW, 20)
# Load and draw an image to the screen
sith = arcade.load_texture("python.png")
arcade.draw_texture_rectangle(300, 300, sith.width, sith.height, sith)
Let's say we want to draw a picket fence across our window. We need to draw several vertical rectangles as the posts. We can combine what we've learned about drawing with what we've learned about loops.
Putting a rectangle drawing command inside a loop will cause multiple lines being drawn to the screen. But here's the catch. If each rectangle has the same starting coordinates, then each rectangle will draw on top of the other rectangle. It will look like only one rectangle was drawn.
To get around this, it is necessary to offset the coordinates each time through the loop. So the first time through the loop the variable x_offset is zero.
#fence posts
for x_offset in range(0,610,20):
arcade.draw_rectangle_filled(0+x_offset,60,10,30,arcade.color.WHITE)
#rails
arcade.draw_rectangle_filled(300, 67, 600, 5, arcade.color.WHITE)
arcade.draw_rectangle_filled(300, 52, 600, 5, arcade.color.WHITE)
The y-position, width and height of the fence posts will always remain the same. However, each time through the loop the x_offset increases by 20. This causes each fence post to be redrawn 20 pixels over until the last one is draw at position 600,60. Next, we drew two rails.
Can you determine what the following code does?
radius=50; y=50
for i in range(3):
arcade.draw_circle_filled(100,y,radius, arcade.color.WHITE)
y=y+1.8*radius
radius=radius*0.8
What happens if you change it to range(10)?
For the flag project in this chapter's repository, you will probably need to refer to the scaled dimensions in the following image. Can you make the entire flag parametrically? Set your window height originally to:
(A) Hoist of flag = 260 pixels
When you are proud enough of your Pycasso Project, submit your code and screenshot in Canvas.