CS21 Lab 6: graphics

Due Saturday, October 27, before midnight


Goals


Programming tips/requirements


1. Circle Art

In this first program you'll create a grid of circles with blended coloring.

circles5x5

circles5x5

Here are a few steps and suggestions to help you get started. Computers need a coordinate system to draw. The Zelle coordinate system is two-dimensional, having an x-axis and y-axis. The x-axis goes from left to right. The y-axis goes from top to bottom. The point (0,0) is at the top, left corner. The bottom right corner has the coordinate (window width, window height). The coordinate (0,0) has a special name, called the origin. To draw a circle, you must give a coordinate which specifies the location of the center as well as a radius. We recommend drawing the Zelle coordinate system on paper and working out the coordinates of the shapes you wish to draw before you start programming.

$ python3 circle.py 
circles_tutorial

circles_tutorial

$ python3 circle.py 
circles_background

circles_background

$ python3 circle.py 
Please enter a number of circles for the width and height: 5
circles_single

circles_single

$ python3 circle.py 
Please enter a number of circles for the width and height: 5
circles5x5_row1

circles5x5_row1

$ python3 circle.py 
Please enter a number of circles for the width and height: 5
circles5x5_grid

circles5x5_grid

$ python3 circle.py 
Please enter a number of circles for the width and height: 5
circles5x5_cyan

circles5x5_cyan

To create a horizontal gradient, we will blend between two colors based on the x-coordinate of the circle's center. When x is close to zero (the left-side of the screen), the circles will have the first color. When x is close to the window's width (the right-side of the screen), it will have the second color. When x is half the window width, the color will be 50% of the first color and 50% of the second color. Let our first color be C1 = [R1, G1, B1]T, our second color be C2 = [R2, G2, B2]T, and the percentage distance along the X-axis be α. The general formula for this gradient is
$$ \left[\begin{array}{c} R \\ G \\ B \end{array} \right] = (1 - \alpha) \left[\begin{array}{c} R_{1} \\ G_{1} \\ B_{1} \end{array} \right] + \alpha \left[\begin{array}{c} R_{2} \\ G_{2} \\ B_{2} \end{array} \right] = \left[\begin{array}{c} (1 - \alpha) R_{1} + \alpha R_{2} \\ (1 - \alpha) G_{1} + \alpha G_{2} \\ (1 - \alpha) B_{1} + \alpha B_{2} \end{array} \right] $$
Let's start by breaking down what this formula means:

Now let's look at some examples. Suppose our window size is 400. What is the color of a circle at location (0,0)? α will be 0/400 = 0, or 0% across the window. Now suppose our first color C1 is yellow [255, 255, 0]T and our second color C2 is blue [0, 0, 255]T. In this case, the gradient color is 100% of the first color and 0% of the second color, which we can see by plugging in our values


$$ \left[\begin{array}{c} 255 \\ 255 \\ 0 \end{array} \right] = (1 - 0) \left[\begin{array}{c} 255 \\ 255 \\ 0 \end{array} \right] + 0 \left[\begin{array}{c} 0 \\ 0 \\ 255 \end{array} \right] $$

Now suppose we have a circle at position (400,0). Now α is 400/400 = 1, or 100% across the window. When we plug in our values, we see that we get 0% of the first color and 100% of the second color.


$$ \left[\begin{array}{c} 0 \\ 0 \\ 255 \end{array} \right] = (1 - 1) \left[\begin{array}{c} 255 \\ 255 \\ 0 \end{array} \right] + 1 \left[\begin{array}{c} 0 \\ 0 \\ 255 \end{array} \right] $$
Verify for yourself that a circle at position (200,0) will be 50% of C1 and 50% of C2. You may use any colors you like. The following screenshot uses the colors yellow and blue.

$ python3 circle.py 
Please enter a number of circles for the width and height: 5
circles5x5

circles5x5

$ python3 circle.py 
Please enter a number of circles for the width and height: 50
circles50x50

circles50x50

2. Aquarium

Write a program called aquarium.py that generates an animated aquarium, using mouse-clicks from the user. Here is an example:

Here are the requirements and some tips for this program:

Your fish does not have to look exactly like these fish. Feel free to personalize them.


Aquarium Challenge! (no points...just for fun, if you have time)

There are many ways to extend this animation.

Here are some sample videos with ideas


Gradient Challenge! (no points...just for fun, if you have time)

$ python3 circle.py 
Please enter a number of circles for the width and height: 5
circles5x5

circles5x5

In circle.py, modify your getColor(..) function to do a two-way gradient. In this case, we blend in both the X and Y directions and can use up to four colors! You can think of the two-way gradient like so:


$$ \left[\begin{array}{c} R_{x1} \\ G_{x1} \\ B_{x1} \end{array} \right] = (1 - \alpha_x) \left[\begin{array}{c} R_{1} \\ G_{1} \\ B_{1} \end{array} \right] + \alpha_x \left[\begin{array}{c} R_{2} \\ G_{2} \\ B_{2} \end{array} \right] $$


$$ \left[\begin{array}{c} R_{x2} \\ G_{x2} \\ B_{x2} \end{array} \right] = (1 - \alpha_x) \left[\begin{array}{c} R_{3} \\ G_{3} \\ B_{3} \end{array} \right] + \alpha_x \left[\begin{array}{c} R_{4} \\ G_{4} \\ B_{4} \end{array} \right] $$


$$ \left[\begin{array}{c} R \\ G \\ B \end{array} \right] = (1 - \alpha_y) \left[\begin{array}{c} R_{x1} \\ G_{x1} \\ B_{x1} \end{array} \right] + \alpha_y \left[\begin{array}{c} R_{x2} \\ G_{x2} \\ B_{x2} \end{array} \right] $$

For example, to get the screenshot above, we let


Mandelbrot Challenge! (no points...just for fun, if you have time)

Create a new program, mandelbrot.py, that colors circles based on the Mandelbrot set. For this challenge, you should modify circle.py so that getColor(px, py, winSize) uses the Mandelbrot formula to compute a color for each circle.

To start, modify getColor(px, py, winSize) to return white if we're outside the set and black otherwise. A point (px,py) is inside the set based on an "escape condition". We start by computing offsets (x0, y0) based on the circle center (px,py). We then initialize a pair (x,y) = (0,0) and repeatedly update the values (x,y) in a loop. If $ x^2 + y^2 $ becomes greater than 4, the point escapes (!) and we assign it a white color. Otherwise, we assume it is part of the Mandelbrot set, and we assign it a black color. The full pseudocode for getColor is below

    x0 = (px/winSize)*2.25 - 1.5
    y0 = (py/winSize)*2 - 1
    x = 0.0
    y = 0.0
    iteration = 0
    max_iteration = 1000

    while x*x + y*y < 2*2 and iteration < max_iteration
        xtemp = x * x - y * y + x0
        y = 2*x * y + y0
        x = xtemp
        iteration = iteration + 1

    if iteration < max_iteration
       return white
    else:
       return black
    

The following image shows the result of a size 400 window with 200 circles

You can make a more colorful visualization of the Mandelbrot set by defining a palette. A palette is a list of colors, one for every possible iteration that a point (px,py) can escape. To define a palette, create a list in main() and add 1000 random colors to it using a loop. A random color can be generated using random.randrange(255) to generate red, green, and blue color components. Then modify getColor() to take your palette as a fourth parameter. When iteration is less than 1000, use the color from your palette. Otherwise, return black as before.

Below is an example using a size 400 window with 200 circles.


3. Answer the Questionnaire

Each lab has a short questionnaire at the end. Please edit the QUESTIONS-06.txt file in your cs21/labs/06 directory and answer the questions in that file.


Turning in your labs....

Don't forget to run handin21 to turn in your lab files! You may run handin21 as many times as you want. Each time it will turn in any new work. We recommend running handin21 after you complete each program or after you complete significant work on any one program.