Lab Exercise 12: 3D Turtle
The purpose of this lab is to give you one more chance to extend your drawing system. You should have a reasonable understanding of how all the pieces fit together, so now we're going to swap out the standard 2D turtle and put in a 3D turtle. All of your 2D turtle programs should continue to work just fine, but now you can make 3D shapes as well. In addition, you'll be able to rotate your completed drawings with the mouse.
The lab and assignment consist of three parts.
- First, you need to edit your Transformer class to make use of the 3D turtle. Once this is complete, you can try drawing some simple shapes.
- Second, you need to implement the 3D symbols for L-systems so you can draw 3D trees.
- Third, you need to update your Shape class and possibly some of its children to make use of the 3rd dimension.
The goal is to be able to make 3D shapes and scenes as easily as 2D. You should be able to create a Cube class, for example, as well as 3D trees and other L-system shapes that work exactly the same way as their 2D counterparts.
- Create a folder called Lab12. Download the 3D turtle file. Once you have it downloaded, run it by itself just to make sure the scene draws and rotates correctly when you click and drag the left mouse button. Feel free to look at the code. It's all in python. The test method of the Turtle3D class, for example, is just a set of turtle commands.
- Make a copy of your versions of the files transformer.py, shapes.py, and tree.py, and put them in the Lab12 folder.
One difference between the standard turtle and the 3D turtle is that
the latter is implemented as a class. Therefore, you need to create
a single instance of the turtle object that will be used by all
In the Transformer file, first create a global variable called turtle at the top of the file. Assign it the value None.
Second, in the Transformer init function, replace the turtleUtils.setup line with the following two lines. The first line tells python to use the global version of turtle variable, and the second line creates a Turtle3D object and tells it to make the initial window 800x800. By putting the Turtle3D object in a variable called turtle, we can still use things like turtle.forward, turtle.left, and turtle.right.
global turtle turtle = turtleTk3D.Turtle3D(800, 800)
So what else needs to be changed? You probably want to go through
the following list for the Transformer file.
- Add methods to the Transformer class called roll, pitch, and yaw, which call their turtle3D counterparts.
- Add a method to the Transformer class called width, which just calls its turtled3D counterpart.
- Edit the Transformer place function so it has an optional zpos argument with a default value of zero.
- The turtle.position() method now returns (x, y, z) instead of just (x, y). There are likely some places in your Transformer forward method that need updating handle the z coordinate. As the turtle.goto function now has a third (optional) argument, you may want to jitter the 'broken' and 'jitter' cases in the z direction in exactly the same way you're jittering the x and y directions.
- Add cases in your drawString method for pitch (& and ^) and for roll (\ and /). In each case the first symbol turns by -angle and the second symbol turns by +angle. Note that you will have to use the string '\\' for the forward slash because it is a special escape character.
Now what needs to be changed in shapes.py? The answer is not much.
- Add three optional fields to the draw function, placing the new arguments at the end of the argument list. The arguments should be called roll, pitch, and zpos. Each argument should have a default value of 0. (The orientation parameter already holds the yaw.)
- Add zpos to the argument list of the Transformer place call.
- Make a width field for the Shape class. Then make a setWidth accessor method. Then, in the Shape draw method, pass self.width into the width function of the Transformer object before you draw the string.
- Right after you call the orient method, call the Transformer roll method with the roll argument. Immediately after, call the pitch Transformer method with the pitch argument.
- Try running the test file basicscene.py. This is a simple example of how to begin building a 3D scene.
- What needs to be changed in tree.py? Again, the answer is not much. Just add the roll, pitch, and zpos arguments and their default values (all 0) to the draw method. Then pass the roll, pitch, and zpos arguments on to the parent draw method.
- Make a new shape class that generates a 3D shape, something like a cube. Test it out using each of your drawing styles. Place several copies of the object into the scene, making use of the zpos argument to give depth to the scene.
When you are done with the lab exercises, go ahead and get started on the assignment.
Appendix: Turtle3D Documentation
The Turtle3D class implements a 3D turtle abstraction using the Tkinter package.
The Turtle3D class includes the following methods for public use.
__init__: the constructor function has six optional arguments.
- winx (default 800) is the horizontal window size
- winy (default 800) is the vertical window size
- title (default 'Turtle 3D')
- position (default (0, 0, 0) ) is the initial 3D position of the turtle
- heading (default (1.0, 0.0, 0.0)) is the initial forward direction of the turtle
- up (default (0.0, 0.0, 1.0)) is the initial up direction, which defines left and right (yaw) relative to the forward direction. The default values mean that left and right work identically to the standard 2D turtle.
- reset(): deletes all drawing and resets the turtle to its initial position.
- forward(distance): goes forward in the current turtle direction. If the pen is down, it creates a line.
goto(xnew, ynew, znew): The function can take one, two or
three parameters. If the pen is down, it creates a line.
- One parameter: xnew is treated as a two-element tuple (x, y) and the turtle is placed at (x, y, 0).
- Two parameters: xnew and ynew are used and znew is set to 0.
- Three parameters: the turtle is placed at (xnew, ynew, znew).
- left(angle): turn left (yaw) as defined by the current heading and up direction.
- right(angle): turn right (yaw) as defined by the current heading and up direction.
- yaw(angle): go left (positive) or right (negative) relative to the current turtle orientation.
- pitch(angle): go up (positive) or down (negative) relative to the current turtle orientation.
- roll(angle): rotate right (positive) or left (negative) about the current turtle heading.
- width(w): set the width of the pen to w
- color(r, g, b): sets the pen color. Arguments can be the standard X11 color strings defined in rgb.txt or r, g, b values. The first argument can be a tuple (r, g, b) or the color values can be given individually. Color values should be in [0, 1].
- up(): pick up the pen
- down(): put down the pen
- tracer(blah): does nothing (no visible turtle)
- circle(radius, theta): draws a circle of the given radius. The theta argument is optional and permits drawing only the angular fraction of the circle specified.
- position(): returns a tuple with (x, y, z)
- heading(): returns a tuple of two tuples with the current heading and up vectors. ( (hx, hy, hz), (ux, uy, uz) )
- setheading(heading): ideally heading should be two vectors representing the turtle's forward and up directions. If a single scalar is provided, the turtle is set so the turtle is in the drawing plane (up of (0, 0, 1)) and is rotated according to the argument. This gives it the same functionality as the 2D turtle.
- fill(q): if q is True, the turtle will begin to store points until the fill function is called with False as the argument. Then it fills in the area defined by the points. The turtle must visit at least three points after fill(True) is called and before fill(False) is called in order to generate a polygon.
- nudge(n): allows the user to adjust the turtle's coordinate system by nudging the forward vector in the specified direction. The argument n should be a 3-element sequence (list or tuple). A value like (0.0, -0.1, 0.0) nudges the turtle's orientation down (i.e. like gravity).
- cube(distance): takes one optional parameter (size) and draws a cube.
- picture( filename ): reads in the picture from the given file and places it so the upper left corner is at the turtle location. Pictures do not rotate or change position. The image must be either a gif, ppm, or png image. You can use the convert tool to modify any image to a ppm or gif (pngs are greyscale). Note: this function is currently commented out, as is the line import ImageTk at the top of the python file. If your system has ImageTk installed, you can uncomment the import statement and the picture function.
- setRightMouseCallback(func): takes one argument, which should be a function with an argument (other than self for a class method) called event. The mouse click location will be in event.x and event.y. Note that the click location will be in window coordinates, not turtle coordinates.
- window2turtle(x, y): takes in window coordinates (like those in event.x and event.y above) and returns a tuple (x', y', 0) with the corresponding turtle coordinates in the default view.
- wait(): goes into a main loop waiting for user input.
- updateCanvas(): updates the Canvas to draw any new shapes.