CS 151: Lab #11

Lab Exercise 11: 3D Turtle

Main course page

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.

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.

  1. Create a new working folder. Copy your lsystem.py, interpreter.py, shape.py, and tree.py files from last week. Label them as version 5. Then download the 3D turtle file.
  2. 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 Interpreter objects.

    At the top of interpreter.py, import turtleTk3D instead of turtle, and create a global variable called turtle and initialize it to None.

    Second, if you have a call to turtle.setup(), delete it.

    Third, before calling any other turtle functions in your __init__ method, but after the test of Interpreter.initialized, put the following two lines.

    global turtle
    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.

  3. Make the following additional changes to 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)

      Then have the function call the turtle method setheading with an argument of 0, then roll with the roll argument, pitch with the pitch argument, and yaw with the angle argument.

    • If you have a goto method, add an optional parameter z with a default value of 0. Then change the turtle.goto call to include z as the third argument.
    • Do the same with the place method. It should have two required arguments (x and y) and four optional arguments (yaw (angle), roll, pitch, and z). Note that if you set up the arguments as place(x, y, z=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 z the last argument. Give the new arguments (z, 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 interpreter's orient function (self.orient) with angle, roll, and pitch as the arguments.

    • Create additional 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 Interpreter's forward method that need to be updated to handle the z coordinate. In the jitter and broken 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 coordinate.
    • 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 (left).

      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 brack cases '<' and '>' to take into account that there is no pencolor/fillcolor separation.
    • When you are done with these changes, try out first test program.
  4. Make the following changes to shape.py.

    • Add three optional fields 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.
  5. Run test function two. It is a simple example of how to begin building a 3D scene.
  6. 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.

Once you have finished the lab, go ahead and get started on project 11.

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.