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 consists of three parts.
First, you need to edit your TurtleInterpreter class to make use of the 3D
turtle. Once this is complete, you can try drawing some simple
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 new working folder. Copy your lsystem.py,
turtle_interpreter.py, shapes.py, and tree.py files from last
week. Label them as version 5. Then download
the 3D turtle file (which is documented at
the bottom of this page).
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
At the top of turtle_interpreter.py, import turtleTk3D instead of turtle, and
create a global variable called turtle and initialize it to None.
Second, before calling any other turtle functions in your __init__
method, but after the test of TurtleInterpreter.initialized, put the following
turtle = turtleTk3D.Turtle3D(dx, dy)
The first line tells the function to use the turtle variable in the
global symbol table, and the second line creates a new turtleTk3D
object. By putting the 3D turtle object in a variable called turtle,
expressions like turtle.left(angle) or turtle.forward(distance) still
work as expected.
Make the following additional changes to turtle_interpreter.py.
Edit your hold method so that it calls only turtle.mainloop().
If you have not already done so, make your '[' and ']' cases store and
restore the turtle width in addition to the heading and position.
Edit your orient method so it takes two additional optional arguments:
roll and pitch. The definition should look like:
def orient(self, angle, roll=0, pitch=0)
The function should first call the turtle method setheading
with an argument of 0, then roll by the roll argument
(e.g. turtle.roll(roll)), pitch by the pitch
argument, and yaw by the angle argument.
If you have a goto method, add an optional parameter zpos with
a default value of 0. Then change the turtle.goto call to
include zpos as the third argument.
Do the same with the place method. It should have two required
arguments (xpos and ypos) and four optional arguments (angle,
roll, pitch, and zpos). Note that if you set up the arguments
as place(xpos, ypos, zpos=0, angle=None, pitch=0,
roll=0) it will break your prior code that assumes angle
is the third argument. It's probably better to make zpos the
last argument. Give the new arguments (zpos, pitch, and roll)
default values of 0.
The other change to the place method is in the case where angle is not
None, call your turtle interpreter's orient function (self.orient) with
angle, roll, and pitch as the arguments.
Create additional turtle interpreter methods called roll,
pitch, and yaw, that call the 3D turtle functions roll, pitch
and yaw. These functions will look like your existing width
or color functions.
The turtle.position() method now returns the tuple (x, y, z)
instead of just (x, y). There are likely some places in your
TurtleInterpreter's forward method that need to be updated to
handle the z coordinate. In the jitter and jitter3 cases, you
probably want to add a random offset in z, in addition to x
and y, and make all of your goto calls include the z
Add cases in your drawString method for pitch ( & and ^ ) and
roll (\ and /). The & symbol means to execute the pitch method
with a positive angle (down), and ^ should execute the pitch
method with a negative angle (up). The backslash '\' should
execute the roll with a positive angle (right), and the
forward slash '/' should execute roll with a negative angle
Yaw is still + and -.
Note that you will have to use the string '\\' to represent the
backward slash character because it is a special character (e.g., '\n'
is a newline and '\t' is a tab) .
The 3D turtle color function works slightly differently from
the regular turtle. The primary difference is that it takes as
input r, g, b values, a color string, or a single tuple (r, g,
b) and it returns only a single color value. You'll need to
edit your angle bracket cases '<' and '>' to take into account
that there is no pencolor/fillcolor separation.
You may need to update your code in the turtle interpreter to
use functions offered by the 3D turtle. For example, the 3D
turtle has position and goto methods, but not pos or
Add three optional parameters to the Shape class draw
function. The arguments should be called roll, pitch, and
zpos. Given them all default values of 0. The orientation
parameter already holds the yaw information.
Add zpos, pitch and roll to the place call before drawString.
Update your tree.py file. Add roll, pitch, and zpos to the
parameter list of the Tree class draw method, giving them all
default values of 0. Then pass the three parameters on to the
parent draw function. Then run the test
function 3 using one of the L-systems below.
The Turtle3D class implements a 3D turtle abstraction using the
The default turtle window supports three user operations.
Once drawing is complete, the user can rotate the image using the
first mouse button and clicking and dragging.
The user can close the window and quit the program by typing
Command-q or by typing just q.
The user can reset the view to the default orientation by typing
Command-r or by typing just r.
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 as long as the turtle
does not pitch or roll.
reset(): deletes all drawing and resets the turtle to its
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,
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
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.
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.
hold(): goes into a main loop waiting for user input.
wait(): goes into a main loop waiting for user input.
updateCanvas(): updates the Canvas to draw any new shapes.
When you are done with the lab exercises, you may start on the rest of the project.