.. Copyright Gareth McCaughan and Jeffrey Elkner. All rights reserved. CONDITIONS: A "Transparent" form of a document means a machine-readable form, represented in a format whose specification is available to the general public, whose contents can be viewed and edited directly and straightforwardly with generic text editors or (for images composed of pixels) generic paint programs or (for drawings) some widely available drawing editor, and that is suitable for input to text formatters or for automatic translation to a variety of formats suitable for input to text formatters. A copy made in an otherwise Transparent file format whose markup has been designed to thwart or discourage subsequent modification by readers is not Transparent. A form that is not Transparent is called "Opaque". Examples of Transparent formats include LaTeX source and plain text. Examples of Opaque formats include PDF and Postscript. Paper copies of a document are considered to be Opaque. Redistribution and use of this document in Transparent and Opaque forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of this document in Transparent form must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions of this document in Opaque form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution, and reproduce the above copyright notice in the Opaque document itself. - Neither the name of Scripture Union, nor LiveWires nor the names of its contributors may be used to endorse or promote products derived from this document without specific prior written permission. DISCLAIMER: THIS DOCUMENT IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS, CONTRIBUTORS OR SCRIPTURE UNION BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS DOCUMENT, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. Pretty Pictures =============== Introduction ------------ This sheet will teach you a little about doing graphics (pictures or drawings) in Python. It will also explain a very important idea in programming: **functions** or **procedures**. It's quite a long sheet. Don't worry if it takes you a while to get through it all. What you need to know --------------------- * How to edit and run a Python program. * What **coordinates** are --- if you aren't familiar with these, you may want to talk with your math teacher about them. Points, lines and colors ------------------------ In `Sheet 1 <1-intro.html>`__ (*Introducing Python*), you saw a rather boring example of graphics in Python. Let's go over it again, a little more carefully. It began like this: .. sourcecode:: python from gasp import * begin_graphics() and ended with the line ``end_graphics()``. The first line there should be familiar by now. The second tells the computer to make a new window in which you can put lines and circles and things. The last line, ``end_graphics()``, waits for the graphics window to be closed and then exits the program. All the interesting stuff happens in between. .. sourcecode:: python from gasp import * begin_graphics() Line((200, 100), (100, 300)) update_when('key_pressed') # This keeps the graphics window open until # you press a key. end_graphics() To understand these lines, you need to know how Python (actually, this isn't Python itself so much as the ``gasp`` module, which is why you have to type ``from gasp import *`` at the beginning of all your python code.) thinks about graphics. Pixels ~~~~~~ The graphics window --- like everything on the computer screen --- is made up of **pixels**: little squares or rectangles of color. If you look closely at your computer screen, you'll see what we mean. Why the funny name **pixels**? It's short for picture element. You refer to places in the graphics window by their **coordinates**: first, how many pixels the place is to the right of the left-hand edge of the window --- the *x*-coordinate --- and then how many pixels it is above the bottom of the window --- the *y*-coordinate. Here's a picture. .. image:: ../illustrations/coords.svg :alt: Coordinates illustration The **current point** ~~~~~~~~~~~~~~~~~~~~~ When you read `Sheet 1 <1-intro.html>`__ (*Introducing Python*), we hope you actually tried typing in those commands we're talking about now. If you didn't, go back to `Sheet 1 <1-intro.html>`__ (*Introducing Python*) and type them in now. The command beginning with ``Line`` does exactly what it sounds like it should do --- it draws a line. In this case it draws a line starting from the pixel called (200, 100) to the pixel called (100, 300). Because both points have a different *y*-coordinate (second part) and *x*-coordinate (first part) the line is drawn at an angle. You might --- or might not --- find it helpful to imagine a little man, or a robot, walking around the graphics window. When you say ``Line((200, 100), (100, 300))`` the robot walks over to (200, 100), then drops a little marker at the pixel then as it walks over to the second point (100, 300) it draws a line underneath it creating the line you see on the screen. Facing the facts ---------------- Let's try to draw something a little more interesting than a plain old line. How about a face? Later you can even go further than that by drawing a whole person. Unfortunately, it's not easy to draw anything at all like a face with straight lines. Faces are sort of round, after all. So... Going round in circles ~~~~~~~~~~~~~~~~~~~~~~ ...you need to be able to draw things that are sort of round. Circles, for instance. Try this little program. .. sourcecode:: python from gasp import * begin_graphics() Circle((300, 200), 100) update_when('key_pressed') # again, this keeps the graphics window open... end_graphics() The three numbers are the two coordinates of the center of the circle, and the radius of the circle (i.e., the distance from the center to the edge). Try running the same program but with different values for the three numbers, to make sure you understand what they all do. What does this do? Try to guess before you actually run it. .. sourcecode:: python from gasp import * begin_graphics() for r in 100, 110, 120, 130, 140: Circle((300, 200), r) update_when('key_pressed') # you know what this does by now... end_graphics() If you still aren't sure, ask for help... So, what about drawing that face? Try to draw something that looks a bit like this: .. image:: ../illustrations/face.svg :alt: Face illustration In other words, you'll need to draw * One large circle --- the head * Two small circles --- the eyes * *Part* of a circle --- the mouth * Two straight lines --- the nose You'll find this easier if you have a bit of squared paper or graph paper. Failing that, you might find it helpful to draw some for yourself! Then, sketch the face on the squared paper: .. image:: ../illustrations/face-grid.svg :alt: Grid illustration If you're really too lazy, you can just use the face-on-a-grid above! That grid is 10 squares on each side. Let's suppose that the very center of the grid is the point (300, 200) (there's nothing special about that point; it's just somewhere near the middle of the graphics window), and that each of our little grid squares is 10 pixels across. The mouth is a little tricky, so we'll worry about that a little later on. Everything else should be pretty easy to locate. For instance, the big circle has its center at (300, 200) and its radius is 4 small squares, or 40 pixels. The left eye has its center at (285, 210) and its radius is 5 pixels (half a square). We'll let you work out all the other coordinates you need. So: Write a program that draws all of that face except the mouth. Mouthing off ~~~~~~~~~~~~ The mouth, as we said, is harder. You need to be able to draw just *part* of a circle. You can do this by using a special command just like ``Line`` and ``Circle``, this time called ``Arc``. All you need to know is where the center of the arc will be, the radius of the circle, and then two angles. These angles determine where on the circle the computer starts and stops drawing. It turns out that what you need to add to your program is: .. sourcecode:: python Arc((300, 200), 30, 225, 315) That's a bit intimidating, so we'll try to explain. First you give the center of the circle, just as if you were drawing all of it. Then you tell the computer the radius of the circle, just like when you drawing a full circle. The last set of numbers are the angles, since a circle is made up of 360 degrees, that's what you'll use to tell the computer where to start and stop drawing the circle. What should happen when you type ``Arc((300, 200), 30, 225, 20)``? What actually happens? The reason this happens is because when you pass the 360 mark on a circle, it doesn't automatically jump back to 0. Instead it keeps on counting. This means that typing ``Arc((300, 200), 30, 225, 20)`` is the same as typing ``Arc((300, 200), 30, 225, 380)``. .. describe:: Challenge 1 Modify your face-drawing program so that it draws the face in a different place. You'll need to change all the numbers that define positions of points. You *won't* need to change the numbers that say how big the circles are! .. describe:: Challenge 2 Make some other changes to the face, to get the hang of how these graphics commands work. For instance: * Move the eyes up a little. * Add eyebrows --- lines across the eyes, maybe sloping a bit. * Add ears, or hair. * Change the shape of the nose slightly. Two heads are better than one ----------------------------- What if you wanted to draw two of these faces? You could copy out everything in your program twice, and then change one copy. This would work, but it would be boring and easy to get wrong. And if you then wanted a third face, you'd have to go through all the effort again. There's a better way. You already know that Python lets you give names to numbers, strings, lists and other things. It also lets you give names to bits of program. A piece of Python program with a name is called a **function** --- for complicated reasons that don't matter just now. Sometimes the word **procedure** is used instead. For instance, the language Logo -- which you may have used in school -- calls them procedures. Functions ~~~~~~~~~ There's much more about functions in `Sheet F `__ (*Functions*). When you've read the following stuff, you might like to turn to `Sheet F `__. It *might* also be helpful if you get confused. Type in the following: **note**: the *prompt* Python prints at the start of each line will change, like in the ``for`` loop examples way back in `Sheet 1 <1-intro.html>`__ (*Introducing Python*). Don't forget to look out for spaces at the starts of lines! .. sourcecode:: python >>> def shout(): ... print('Python is wonderful!') ... print('And so am I.') ... # Just press Enter here. What you've just done is to teach Python a new trick. If you now say .. sourcecode:: python >>> shout() what do you think will happen? Try it. What you've done is to **define** a new word that Python will understand --- ``def`` is short for **define**. .. describe:: Challenge 3 Write a definition so that saying ``moan()`` will make the machine print:: Python is useless! And so are these worksheets. Try it out and make sure it works. A good argument ~~~~~~~~~~~~~~~ Functions are more than just ways of saving typing, though. Try typing in the following definition, and think about what it might mean. .. sourcecode:: python >>> def twice(x): ... print(x, '+', x, 'is', x + x) ... # Just press Enter here. When you think you understand it (or when you've decided you'll never understand it), experiment with it. Ask Python for ``twice(3)``, or ``twice(99)``, or even ``twice('ouch')``. What's going on here is rather clever. When you say ``twice(7)``, the machine does the stuff inside the definition of ``twice`` (just like it did for ``shout`` and ``moan`` earlier), except that ``x`` is 7. So it's rather as if you'd said .. sourcecode:: python print(7, '+', 7, 'is', 7 + 7) For some reason, things that get inside function definitions in this way are called **arguments**. So, when you say ``twice('beri')``, the string ``'beri'`` is the argument to the function ``twice``. You'll need to know this word to understand what happens if you type ``twice()`` instead of ``twice(7)``. Try it. .. describe:: Challenge 4 Write a function that behaves a bit like ``twice`` but, instead of telling you what ``x + x`` is (where ``x`` is the argument of the function), tells you what ``x * x`` is. Try it out. (Unfortunately it won't work on strings!) A function can have more than one argument. The way this works is pretty obvious once you've seen it, so we'll just give an example. .. sourcecode:: python def add(x, y): print(x, '+', y, '=', x + y) Then you can say ``add(7, 5)`` and the computer will tell you what happens when you add 7 and 5. Built-in functions ~~~~~~~~~~~~~~~~~~ Incidentally, lots of the things you've told Python to do are really functions. For instance, when you say ``Line((100, 200), (300, 150))`` you're just using a function with two arguments (two sets of coordinates). The only difference is that you didn't have to write the definition of the ``Line`` function yourself. As we were saying... ~~~~~~~~~~~~~~~~~~~~ This is all a digression. Before we started blathering about functions, you were thinking about how to draw two --- or more --- of those faces. Maybe you've guessed what's coming next: the right thing to do is to write a *function* that draws a face. The function should take arguments saying where in the graphics window the face should go. When you've done that, drawing 5 faces will just mean 5 uses of your function, instead of having to copy out the face-drawing program 5 times and make lots of changes to each copy. So, what should your face-drawing function look like? It needs to be able to be told where to draw the face, so let's give it two arguments saying where --- like the two arguments to ``move`` or ``draw``. So far, you know that the function will look like this. .. sourcecode:: python def draw_face(x, y): blah blah # Whatever you need to draw a face at (x, y) Obviously filling in the ``blah blah`` is the difficult bit. What exactly should the arguments, ``x`` and ``y``, mean? When you were designing the face, you thought about the positions of all the bits of the face relative to the point right in the middle of the big circle. That was (300, 200), remember? So a reasonable idea would be to say that ``x`` and ``y`` are the coordinates (i.e., the two parts of the name) of the pixel right in the middle of the face. So, saying ``draw_face(300, 200)`` ought to do exactly the same as the program you've already written. .. describe:: Challenge 5 If you're feeling brave, go back to your face-drawing program and try to turn it into a face-drawing function. Then test the function. --- If you're not feeling brave, read on and we'll try to lead you through the process. *Note*: If you want to take up this challenge, you should stop reading right now. To begin with, edit your face-drawing program so that instead of saying .. sourcecode:: python first line second line third line etc it says .. sourcecode:: python def draw_face(x, y): first line second line third line etc You now have a function that takes two arguments, and then ignores them and draws a face centered at (300, 200) whatever the arguments were. Now you need to tidy it up so that the face is centered at (``x``, ``y``) instead. The outline ``````````` The first thing you drew originally was the circle around the outside of the face. This also happens to be the easiest thing to adapt for putting into our function. Originally, you had a line ``Circle((300, 200), 40)``. The first two arguments to ``Circle`` say where the center of the circle is, so they should become ``x, y``. The last argument (``40``) says how big the circle is. All your faces are going to be the same size, so that should be left alone. Incidentally, you'll find all this *much* easier to follow if you look at the face-on-a-grid earlier in this sheet. So the new line should be ``Circle((x, y), 40)``. The eyes ```````` You drew the eyes next. Originally the eyes were at (285, 310) and (315, 310) --- 15 pixels away from the middle horizontally (one in each direction), and both 10 pixels above the center. Well, that's easy enough. Here's how to draw the left eye; you can probably work out how to draw the right eye. .. sourcecode:: python Circle((x-15, y+10), 5) The nose ```````` The nose is made up of two lines. The first one goes from 10 pixels above the very center of the face to 10 pixels leftward and 10 pixels downward from the center. That's easy enough. .. sourcecode:: python Line((x, y+10), (x-10, y-10)) The second line goes from where the first one left off, to a point 10 pixels down and 10 pixels *to the right* from the center of the face. we'll leave drawing that line up to you, The mouth ````````` You might expect this to be harder than the other bits. Instead, it's only about as difficult as figuring out the eyes. The reason for this is that you only need to edit two of the numbers --- the coordinates of the circle. This is because the other numbers --- the radius and the angles --- are all based off of the coordinates of the arc, not the center of the face. We're going to be mean, and leave you to work out exactly how to adapt the line of program that draws the face's mouth. It really isn't very difficult. Does it work? ````````````` Test your function. Add a few lines below it: .. sourcecode:: python draw_face(300, 200) draw_face(400, 200) draw_face(350, 270) and run the program. Do you get three heads? Or a ghastly mess of detached bits of head? Or what? It's important to put those lines *below* the definition of ``draw_face``, because until it's read that definition the computer doesn't know what ``draw_face`` is. The whole person ---------------- If you've had enough of graphics, you might want to skip ahead a little. But if you're still having fun, you might like to use your head-drawing function to make a person-drawing function. .. image:: ../illustrations/stick-basic.svg Here's a simple stick figure. Sketch something like this on graph paper or squared paper, and use it to design a function that draws a stick person. Where I've just drawn a circle for the head, you should actually use the ``draw_face()`` function. .. image:: ../illustrations/stick.svg This may take you a while. You'll have to think carefully about what arguments you give to ``draw_face`` to make the head and the body fit together properly. Make sure your functions work by making your program draw several stick figures in different places on the screen. You may well find it much easier if you start by just drawing a single stick figure anywhere, and then turn it into a function in the same sort of way as you did for the face. .. describe:: Challenge 6 Work out how to give one of your stick figures a hat. .. sourcecode:: python I do not know what should go in here Some nice patterns ------------------ This doesn't have anything to do with the stick figures. We're just including it because we think it looks nice. Give it a try and see if you agree. .. sourcecode:: python from gasp import * begin_graphics() for i in range(0, 400): Line((i, 0), (0, 400-i)) The weird thing about this is that all it does is to draw a lot of straight lines, but the result is to produce a nice smooth curve! Ending it all ------------- At the end of any program that draws graphics, you should actually have a line .. sourcecode:: python end_graphics() to tell the computer that you're finished with the graphics window. When it sees that line, the computer will sit and wait until you close the graphics window --- if it just went ahead and closed it for you, you might not have time to see what it had drawn in the window. That would be a shame. What next? ---------- * Read `Sheet G `__ (*Graphics/Gasp*) for more information on doing graphics with Python * Work out how to draw faces and people of different sizes as well as in different places. Work out how to make them face in different directions, too. Then you can draw a scene in which some of them are talking to others, or standing alone, or whatever. You could also just go on to `Sheet 4 <4-highlow.html>`__ (*Higher! Lower!*), in which you'll write a simple game, which doesn't use graphics, we're afraid.