CS 151: Lab #11

Lab Exercise 11: Not Quite Straight Lines

Main course page

The purpose of this lab is to introduce you to the concept of non-photorealistic rendering [NPR] and give you some practice with designing modifications to your current system to make NPR easy to implement.

If you're interested in seeing more examples of NPR, check out the NPR resources page.


In lab today we'll be editing the Shape class and the Transformer class to implement one version of NPR that does a reasonable job of simulating a crayon sketch. The goal is to make the change in such a way that all of the classes you've developed so far will work without modification. The design choice in our existing system that made it possible to do this was the use of the Transformer class to handle all of the actual drawing commands.

To implement various NPR methods, we're going to enable the Transformer class to execute the 'F' case in different ways. We'll create a field in the Transformer class that holds the current style and then draw the line corresponding to the 'F' symbol differently, depending on the style field.

To give the capability to select styles to the Shape objects, we'll also add a style field to the Shape class so different objects can draw themselves using different styles.

  1. On the Desktop, make a folder called lab11. Put copies of your shapes.py, transformer.py, lsystem3.py, tree.py, and turtleUtils.py files into the lab11 folder.

    If you don't have access to your prior files, or you want to start fresh, you can do the lab using the following three files.

  2. In the Transformer class init function, add fields to the object called linestyle and jitterSigma. You can use your own names, if you wish, but that's how I'll refer to them in the lab. (Just make sure whatever names you use do not conflict with any of the class method names.) Give linestyle the value 'normal' and jitterSigma the value 2.
  3. In the Transformer class, create a method def style(self, s) that sets the linestyle field to the argument s.
  4. In the Transformer class, create a method def jitter(self, j) that sets the jitterSigma field to the argument j.
  5. In the Transformer class, create a method def forward(self, distance). The main structure of the function is a big if-statement that tests the self.linestyle field and takes the appropriate action depending upon its value. In lab we're going to implement two cases: 'normal' and 'jitter'.

    If the linesetyle field is 'normal', then just have the turtle go forward by the value in distance.

    Otherwise, if the linestyle field is 'jitter', then implement the following algorithm.

    # get the current position of the turtle and store it in x0 and y0.
    # pick up the turtle
    # move the turtle forward by distance
    # get the current position of the turtle and store it in xf and yf
    # save the current width of the turtle to the variable curwidth (turtle._pen._width)
    # send the turtle to the position (x0 + jx, y0 + jy), jx and jy are random offsets
    # put the turtle down
    # set the turtle width to curwidth + dw, dw is a random value in the range [0, 2]
    # send the turtle to the position (xf + kx, yf + ky), kx, and ky are random offsets
    # pick the turtle up
    # send the turtle to the position (xf, yf)
    # set the turtle width to curwidth
    # put the turtle down

    For the random position offsets, use the random.gauss(mean, sigma) function with a mean of 0 (first argument) and a standard deviation of self.jitterSigma (second argument).

  6. In the Transformer class, edit the 'F' case in drawString so it calls the forward method you just wrote with distance as its argument. The 'F' case should no longer call the turtle forward command.
  7. In the Shape class init method, create fields for jitter and style. Initialize the jitter field to 2 and the style field to 'normal'.
  8. In the Shape class, add accessor methods setJitter and setStyle to set the class fields. The test program below uses these functions, so please use these names.
  9. In the Shape class, edit the draw method so it calls the style and jitter methods of the Transformer class, passing the values stored in self.jitter and self.style. Then run the following test program.
  10. Make a copy of testjitter.py and call it testscene.py. Delete everything in the main() function except the Transformer().wait() call at the end. Make your own scene that mixes 'normal' and 'jitter' style objects. If you have your Lsystem and Tree classes, try including at least one tree using the jitter style and experiment with different values for the jitter sigma (setJitter).

When you are done with the lab exercises, go ahead and get started on the assignment.