Home

Colors!

Colors!

You are probably quite familiar with the RGB representation of colors: using three values to indicate the intensity of red, green, and blue. The method setRGB method of BufferedImage uses this representation, and so does the Color class

For many purposes, however, the HSV representation is more suitable. It represents a color using three components:

Displaying a rainbow

Our first program will display a window like this:

Rainbow output for v = 255

In this image, the hue changes from 0 at the left edge to 359 at the right edge: you see that hue is cyclic modulo 360, and that hue zero corresponds to red. The saturation changes from 0 at the top edge to 255 at the bottom edge. The value is constant at 255, but can be changed from the command line.

We first need to learn how to display a window. You need to have the CS109UI module installed, see the installation instructions.

Download the example script uitest1.scala. You can run the example from the command line by saying:

$ scala uitest1.scala 
CS109 UI version 2015/03/31

Read the section "Basic Usage" of the cs109ui documentation.

Our program rainbow.scala will take one command line parameter, the value v (if the command line parameter is omitted, use v = 255). It should then create a BufferedImage of width 360 and height 256, and fill pixel \((x,y)\) of this image with the color with hue \(x\), saturation \(y\), and value v. To set the pixel, use the setRGB method of BufferedImage.

Here is a function that converts from HSV representation to RGB representation:

// Input h in [0,359], s in [0,255], v in [0,255]
// Output r,g,b in [0,255]
def hsvtorgb(h: Int, s: Int, v: Int): (Int, Int, Int) = {
  if (s == 0) {
    // no color, just grey
    (v, v, v)
  } else {
    val sector = h / 60
    val f = (h % 60) 
    val p = v * ( 255 - s ) / 255
    val q = v * ( 15300 - s * f ) / 15300
    val t = v * ( 15300 - s * ( 60 - f )) / 15300
    sector match {
      case 0 => (v, t, p)
      case 1 => (q, v, p)
      case 2 => (p, v, t)
      case 3 => (p, q, v)
      case 4 => (t, p, v)
      case 5 => (v, p, q)
    }
  }
}

Remember to include the cs109ui module when you run your program, like this:

$ scala rainbow.scala 180
CS109 UI version 2015/03/31

Here is the output for a few different v-values:

Rainbow output for v = 180
Rainbow output for v = 120
Rainbow output for v = 60

Animating the color

Now read the section "Updating the display" of the cs109ui documentation, and change the program as follows: It no longer takes a command line parameter. Instead the v-value starts with v = 255, but then it continuously changes until it reaches v = 0. The program should terminate automatically after that.

Change the v-value by one every time you call show, and wait for 100 milliseconds. You should also change the title of the window so that it always shows the current v-value.

How good is your color vision?

Let's now write a program that can test your color vision. It displays a window like this:

Color vision tester window

There are 16 squares with a randomly chosen color. 15 squares have the same color, one is slightly different. The user should enter the coordinates of the square with the different color ("2b" would be the correct answer in this case). Here is a sample dialog:
 $ scala colorguess.scala 
CS109 UI version 2015/03/31
Which square has a different color? (x to exit) 3a
That is correct
You answered 1 of 1 tests correctly.
Which square has a different color? (x to exit) 1b
That is correct
You answered 2 of 2 tests correctly.
Which square has a different color? (x to exit) 3a
That is correct
You answered 3 of 3 tests correctly.
Which square has a different color? (x to exit) 2b
That is correct
You answered 4 of 4 tests correctly.
Which square has a different color? (x to exit) 1d
That is correct
You answered 5 of 5 tests correctly.
Which square has a different color? (x to exit) 3b
That is not correct.  Square 4c has a different color.
Press Enter for the next question>
You answered 5 of 6 tests correctly.
Which square has a different color? (x to exit) 2d
That is not correct.  Square 2c has a different color.
Press Enter for the next question>
You answered 5 of 7 tests correctly.
Which square has a different color? (x to exit) 1c
That is correct
You answered 6 of 8 tests correctly.
Which square has a different color? (x to exit) x

Note that when the answer is correct, the program immediately displays the next test. When the answer is not correct, however, we need to give the user the chance to look at the colors again after revealing the correct answer—that's why the program says "Press Enter for the next question".

Your program should have one command line parameter, an integer that we will call delta. It indicates the distance between the different color and the standard color. If no command line argument is given, use the value delta = 20.

The following function generates a random color in HSV space:

def randomHSV(): (Int, Int, Int) = {
  ((math.random * 360).toInt, 
   128 + (math.random * 128).toInt,
   128 + (math.random * 128).toInt)
}

(Note that I'm excluding colors that are too dark or too grayish.)

The special color differs only in its hue from the standard color (saturation and value are the same). Flip a coin (for instance, using (math.random * 2).toInt). With probability \(1/2\), add delta to the hue, otherwise add 360 - delta to the hue (don't forget to do this operation modulo 360).

Keep showing colors to the user until the user types "x".

Here is an example for delta = 5. Can you distinguish the different color?

Color vision tester window for delta = 5