Thu Le's Project 9
Skip to end of metadata
Go to start of metadata

You are viewing an old version of this page. View the current version.

Compare with Current View Page History

« Previous Version 8 Current »

Project 9: Unique Trees & Shapes

Summary: 

The goal of this project is to use class objects and the principles of inheritance to create a complex scene (like the museum scene in Project 3) with highly diverse trees. We started the project in lab by modifying our L-system.py files to allow it to read L-systems with multiple choice of replacement for each rule, so that the trees drawn from them will all be different despite having the same iteration number and size. We also created a new super-class called “Shapes” with methods to draw and change the colors, sizes and angles of objects in this class. From this class, I derived several child classes to draw simple shapes like Square, Triangle, Rectangle, Diamond, and so on. I also made a separated Tree class (derived from Shapes) to draw trees from the new stochastic L-systems. With these classes, I drew an indoor scene that encapsulated another scene with trees in it. Next, I wrote functions to draw some square tiles and arrange those tiles into an array. My main extensions for this project include making non-square, curvy tiles with multiple tile functions, creating an animation from the painting in the indoor scene, and deriving a new class called Spiral from Shapes that has a dynamically generated string.

The assignment:

Task 1 – making the Tree class

Compared to the parent Shapes class, my Tree class has two more fields for iteration number and L-system. The Tree class also has methods to import L-systems from a text file and change the iteration of a Tree object, and the draw method of Tree needs to call the buildString method before drawing the tree.

To test my Tree class, I created a single Tree object and draw it at three separate locations, as shown in the image below.

One tree is slanted since I set its orientation to 80. We can see that although all three trees are the same object, they are all different since a new string is stochastically generated each time the draw method is called.

Task 2 – deriving new classes from Shapes

In the file Shapes.py, I created 6 more shape classes: Rectangle, Diamond (a rhombus), Crystal (a hexagon with two very long sides), Circle, Curvy Diamond (a rhombus-like shape with curvy sides) and Spiral. I also added a drawFill method to the Shape super-class, so all its child classes can be drawn either with or without filling. The image below is a demonstration of my shapes and shows that they can be filled, scaled and rotated (Spiral is shown in Extension).

Rectangle and Crystal each have two dimensions (width and length) while the Shape class only has one field for distance. To overcome this, I set the distance of Rectangle and Crystal to 1 and multiplied the character F in their string by their width and length, so that the turtle can draw the sides with the appropriate sizes. Similarly, when the turtle has to turn by different angles while drawing the Diamond, I set the angle to the greatest common divisor of the necessary angle values and multiplied the ‘-’ symbol to get the right angles. As for Circle and Curvy Diamond, I used the C character I already have in the drawstring method of TurtleInterpreter to draw a circle and added the new characters Q and q to draw curves. Q draws a quarter of a circle clockwise, while q draws a quarter of a circle counter-clockwise.

Task 3 – creating an indoor scene

 

For my indoor scene, I made four composite object functions:

Table: draws a table from three rectangles. The table top can be filled or white (not filled)

Light: draws a chandelier from rectangles and diamonds. The diamonds are drawn by for loops and are alternately filled and unfilled.

Fish: draws a fish from two triangles and a circle.

Castle: draws a castle with dark red roof from rectangles and triangles.

All four functions have parameters for location and scale, and take in one or two colors.

I then made two scene-drawing functions: picture and indoorScene. Picture combines the fish and castle functions with a Tree object (imported from tree.py) to draw an underwater scene. I draw the picture frame last so that none of the tree branches will be outside the frame, since the trees are stochastically generated each time Python draws the scene. I only made one Tree object and used its setIter method to change the iteration number to made the group of trees more diverse.

The indoorScene function is the main function and combines the table, light and picture functions to draw an indoor scene. Both of my scene-drawing function also take in parameters for location and scale.

Note that the objects in my scenes are drawn one at a time since I put a turtle.update() command at the end of the drawString method. I needed this because I did not want to complicate my code by importing turtle_interpreter to use its hold method, so I had a problem with hiding the turtle after drawing the scene (i.e. if turtle.ht() is used without turtle.update(), the turtle appears as a white space on the image when the last shape drawn is not filled). Adding a turtle.update() command after the drawing finished would fix this, so I found it most efficient to include it in drawString.

Task 4 – making a mosaic


I created three square tiles for the mosaic with images of the sun, moon and a comet. For the comet, I used the random package to draw small diamonds at random positions and with random shades of cyan to form the comet tail. To make sure the tail has the right shape, I used a for loop to draw 6 rows of diamonds such that higher rows have more diamonds and a larger variation in positions (by combining random.random() with the loop control variable).

To combine tiles into a mosaic, I used two for loops to draw Nx tiles on a row and Ny rows of tiles.

Extensions

Extension 1 – animated scene

Below are the screen shots of a few frames in my animation

In the file animation.py, wrote a function to make an animated underwater scene in which the fish can move across the scene. The background is drawn by a modified version of the picture function from indoorscene.py. I wrote another function – animation() – which draws two fish with the same size as in the background inside a blue square  (same color as background). The fish can be in two different positions depending on whether the animation frame number is even or odd, and one of them is the fish’s position in the background. In the main function, I first draw the background and then call the animation function many times with decreasing x coordinates. In other words, a blue square with two new fish is drawn on top and slightly to the left of the fish in the background each time, thus creating the impression that they are moving.

Extension 2 – multiple, non-square tiles

I wrote three square tile functions and arrange them in the mosaic in roughly equal number. I arranged the tiles by testing whether the remainder when the sum of the row and column numbers at each position is divided by 3 is 0, 1, or 2, and assigned a tile to each case.

I also created a new tile function in mosaic.py that draws a Curvy Diamond object and one of the three square tile functions inside depending on the argument of the “shape”parameter, so that I could draw all three types of tiles with just one function. I them wrote the function mosaic2 to put the curvy tiles into an array. This function is more complex than the original mosaic function since odd and even columns start at different y-coordinates. I also gave the tiles random background colors and mathematically modified the output of random.random() such that the sun tiles would tend to have the lightest colors while the comet tiles would tend to have the darkest colors.

Extension 3 -- Spirals

I created the shape class Spiral without a fixed string. Instead, it has two methods to build a string: decrease_distance and increase_angle. Since the shape I wanted was a spiral, I figured there were two ways to create a string for that shape, which is either drawing lines with decreasing length but have the same angle between one another (decrease_distance), or drawing lines with the same length while increasing the angle that the turtle turns after each line (increase_angle). To perform its function, decrease_distance adds a number of F’s, as specified by the “step” parameter, and a turn by a fixed angle to the string (initially empty). It then adds more F’s  to the string, but one fewer than the last time, followed by another turn. This process continues until the number reaches one. Similarly, increase_angle adds one F and many turn symbols ("") to the string each time, as the number of "" added goes from 1 to the specified number of steps. Therefore, a greater number of steps causes the spiral to curve more. Then, in my testSpiral function, I call decrease_distance and then increase_angle six times in a for loop before calling the draw method. The results highly varied depending on the number of steps for each method. Below are some examples (not to scale):

a. when increase_angle step is 100 and decrease_distance step increases from 5 to 20 to 35


b. when increase_angle step is 100 and decrease_distance step increases from 10 to 25 to 40:

c. when increase_angle step is 100 and decrease_distance step increases from 15 to 30 to 45

d. when increase_angle step is 75 and decrease_distance step is 35:

e. when I added more commands

decrease_distance( 35 )

increase_angle( 50 )

decrease_distance( 35 )

increase_angle( 50 )

increase_angle( 50 )

decrease_distance( 35 )

increase_angle( 50 )

increase_angle( 50 )

decrease_distance( 35 )

decrease_distance( 35 )

increase_angle( 50 )

increase_angle( 50 )

decrease_distance( 35 )

decrease_distance( 35 )

Overall, the result is quite unpredictable. However, we can see that the decrease_distance method tends to make slowly coiling spirals while the increase_angle method tends to make a quick, sharp curve with a small thick coil at the end. This shape class seems to have potential in creating complex spiral patterns, but it should be improved to make the curves smoother. 

Other extensions:

I used characters other than F to draw curves, as in the shapes Circle and Curvy Diamond. In addition, I increased the flexibility of the test function in tree.py by allowing it to take the name of the L-system file from the command line, so we can use it to quickly test many L-system. However, I also supplied a default file so that the program can still run if the user does not specify the filename on the command line. I also used the random package to create a variable tail for the comet and randomly choose the color for the non-square mosaic.

What I learned:

Firstly, I learned how to use inheritance to efficiently create new classes. I also got a lot of practice in managing a chain of programs that import other programs, like turtle_interpreter, lsystem, shapes, trees, and all my other python files for this project. Lastly, I learned how to design a dynamically created string and devise my own mutator methods to modify the string.

Acknowledgement:

I discussed this project with Prof. Ying Li.

Labels
  • No labels