Introduction to Computer Graphics and Java Programming for Artists


School of the Museum of Fine Arts ::: Continuing Education

George Aroush, instructor


Lecture Six -- Colors; More Loops & Controls -- Part II; Sines, Cosines & Curves; Floating Point


 

Readings

  1. The handouts named:
  2. Sample Programs in Batch Six:
  3. "Java How to Program" by Paul J. Deitel, and Harvey M. Deitel

 

Exercises

    Do before our next lecture

    Do at least two of the following exercise.

  1. This exercise will assist you in creating a closed-curve or outline form, storing it in arrays, displaying it, and transforming it. Rewrite last week's array program so that it uses the same values but as float data types. The methods which display, move (translate), re-size (scale) and mutate the form must be re-written to work with floats instead of ints. [Don't forget to have the display routine convert ("cast") the float values to ints when they are sent to drawLine() or any other graphic methods. If you want to work with drawPolygon(), you will have to have two sets of arrays -- ints and floats -- for x and for y. After the data is transformed in float form, copy it to the int arrays with a for loop and (int) casts, and use the int arrays with drawPolygon() method to be displayed.]
  2. Write a program that generates spirals -- either Archimedean, where the distance of the spiral from its center increases at a linear rate (by means of addition), or logarithmic, where the distance from the center increases in an accelerating way (by means of multiplication. Look at Circle.java and think about how it could be modified to produce a spiral.
  3. Use Math.sin() and Math.cos() to generate complex curves. You might want to try something like this: make two variables, j and k, that increase in different ways; by adding or multiplying them by different constants, for instance. Have two more variables, xc and yc, for the center of the screen, and four more variables a, b, c, and d, used as follows. Let the x coordinate be something like: x = a * sin(j) + b * cos(j); and the y coordinate something like: y = c * sin(k) + b * cos(k); j and k should increase fairly slowly (by less then 0.1) to keep this from looking too chaotic!

Back to the top of this page. Back to main Index

 

Using Colors in Java

Color and Gray-Scale Video

When we look closely at a color monitor, we can see that each color pixel is actually comprised of three much smaller patches of Red, Green, and Blue light (RGB). These three colors are primary colors, the basic building blocks of white light. Because these triads of colored light are too small to see individually, the eyes merge them into a single, colored pixel. Each pixel's triads of red, green, and blue light are created by three electron guns (one for each color) in the video tube's neck. The tube's inside surface is coated with red, green, and blue phosphor dots. By controlling the stream of electrons from each gun as the guns create each pixel, the computer's video circuitry controls each pixel's color. (Some video monitors, such as Sony's, use just one electron gun that fires at all three types of phosphors.) For a gray-scale monitor, the red, green and blue guns are replaced with one gun. The different intensity of grays are created by varying the intensity of the gun. A high intensity would be white while a lower intensity could be gray and no intensity (the gun beam is turned off) would be black.

Color Video and Memory

It's obvious that color and gray-scale video require more video memory than monochrome video. All those extra bits we're assigning to each pixel have to come from somewhere. In color PCs, that "somewhere" is video RAM located on the video card. Each brand of a video card has different amount of memory pre installed. The amount of memory make available is what make a give PC capable of displaying more or less color then another PC.

Storing and Representing Color

So a color monitor creates color pixels under the direction of the PC's video circuitry. But how does the PC keep track of colors internally? And how does it know how much red, green, and blue light is required to create a given color? In the world of bit-mapped video, the secret to storing color information is to assign more than one bit to each pixel. These additional bits convey information about pixel's color or shade of gray. By assigning two bits to each pixel, a PC can display 4 colors. Four bits can represent 16 color, and eight bits can represent 256. A fully expanded PC Video Card can assign up to eight bits per pixel. How can two bits represent four colors, or eight, 256? Each pixel's bits can be ON or OFF in different combinations. For example, in two-bits-per-pixel mode, four ON/OFF combination exist:

Thus, the PC's first step in creating a color pixel is to determine the value of the bits assigned to that pixel. This results in a number that the PC uses to retrieve a color description that specifies how much red, green, and blue light will be needed to create that color. That description comes from a special table of data, stored in the video board's memory, called a color look-up table.

RGB Space

Each color has a red, a green, and a blue component, hence the name RGB. The data structures used within the PC express each RGB component as an integer value. Each R, G, and B can have a value from 0 to 255. RGB color is additive; that is, as the value of a component is increased, the amount of that component in the total color increases. An RGB color is black if all three components are set to 0, or white if each component is set to 255. Pixel values between these two extremes can be combined to represent all the possible colors.

Drawing in Color

To draw in color using Java we must instruct the java.awt.Graphic object to use a specific color. To do so we will have to create a color object and assign it to the Graphic object. This is done by the following calls:

java.awt.Color rgb = new java.awt.Color(0, 255, 0);
g.setColor(rgb);

In this example, we created a new java.awt.Color object called rgb. We assigned to the rgb object the green color and then gave the object to the java.awt.Graphic object via the call to g.setColor(). After this call, any drawing we do using the the java.awt.Graphic object is done using the select color.


Back to the top of this page. Back to main Index

 

The float data type; Casting

The float Data Type

Java has a variety of types of variables, which hold different sized numbers or characters, and are interpreted by the compiler and Virtual Machine in different ways. Up to now, we have used only the int or integer data type. The float or floating point data type can express any decimal number, but can only approximate the number. Floats consist of a decimal number between (1.0000000000) and (9.999999999999) (called the mantissa) and a power of (10) (called the exponent); although this is usually internal to the computer and you just see a decimal number. Thus 123.45678 would be stored as (1.2345678 * 10) to the 2nd power, and .00345 would be stored as 3.45 * 10 to the -3rd power (by stored we mean the way the computer holds them inside its memory.) Floats can express very large and very small numbers, such as 10 to the 99th power or 19 to the -40th power or (0.0000000000000000000000000000000000000001) Floats are useful for dealing with velocities and directions that "fall between the cracks" of int's, and for dealing with angles and sines and cosines, which is how they are used in the demo programs for this handout. To use a floating point variable, declare it at the start of the program:

float     x, y;

(as an example,) where x and y are two floating point variables.

If this x and y pair were to be used in a any of the drawing methods, or any other method which requires integers, it would be necessary to cast or convert each float into an integer variable. Casting is done with the name of the data type being placed in parentheses before the variable to be converted. Thus, to plot the floats x and y, use:

g.drawLine( (int) x, (int) y, (int) x, (int) y );

x and y get converted into int's before being sent to Pixel().

Because Java is very sensitive to data type and enforces it rigidly, we need to take an extra step to tell the compiler that a given value should be treated as a float. For example, when we assign the value 5.0 to x as in this example:

float     x;
x = 5.1;

The Java compiler will complain and issue an error message. By default, the Java compiler considers the value 5.1 to be of a different data type then float, a double data type, which we will cover latter on. To tell the compiler that the value 5.1 is a float data type we will need to add an "f" to the end of the number:

float     x;
x = 5.1f;

This tells the compiler that 5.1 is a float number.

Using float with Graphics

ExplodeOne.java uses arrays of floats to locate and move the points -- x[], y[], mx[] and my[]. This allows the points to move fractions of pixels and have much more variety of directions. Inside the initial for loop, x[] and y[] are given values of 320.0 and 240.0, so they all start at the center of a 640 by 480 screen. mx[] and my[] are given random values between -4.0 and 4.0 and -3.0 and 3.0. Notice that Random() has to be converted from an int to a float with the (float) cast operation:

mx[point] = (float) Random( 7 ) - 3.5;

Random(7) returns values from 0 to 7, which are converted to a floating point range of 0.0 to 7.0. However, only the eight float values of 0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, and 7.0 will occur. Subtracting 3.5 from this range will result in values from -3.5 to 3.5

As for my array, each my[] gets a value of:

(float) Random(5) - 2.5

which results in the values -2.5, -1.5, -.5, .5, 1.5, and 2.5 With six my[] values and 8 my[] values, only 48 directions and speeds can occur, so the explosion looks rather stylized (perhaps Art Deco.)

ExplodeTwo.java deals with this by using a wider range of random integers and scaling them down:

mx[point] = ( (float) Random(799) - 399.0 ) / 100.0;

The idea is to get float values for mx[] that are accurate within 0.01, so they could be 4.0, 3.99, 3.98, etc. down to -4.0. This will cover a range of 9.0 (from -4.0 to 4.0.) So 100 times the range of random integers is generated and then shifted over by 399.0 into the negative, giving a range of -399.0 to 400.0 It is next divided by 100.0; now it ranges from -3.99 to 4.0


Back to the top of this page. Back to main Index

 

More Loops: do..while Loop

do..while

The do...while loop is a loop statement that will allow the loop to happen at least once, that is the body of the loop will execute at least once -- the for and while loops don't necessarily do so if their starting conditions aren't met, because they do a test first and then enter the body of the loop if the test was true. The do...while provides a way to allow the body of the loop to be executed at least once no mater what is the condition of the test result.

This is done by doing the testing at the end of the loop body. The body of the loop is first executed then the testing takes place if the testing result is true then the body of the loop is re-executed again. Here's an example that counts down from 10 to 0.

int     count;

count = 10;
do      /* start of do..while () loop */
{
    System.out.println(count);
    count--;        /* count = count - 1 */
} while (count >= 0);   /* end of do..while() loop -- loop well */
                        /* continue until 'count' is below 0 */

See ExplodeTwo.java for an example of a do...while loop.


Back to the top of this page. Back to main Index

 

Sines & Cosines

Using Java's sin() and cos() methods

Sines and cosines are methods that are derived from circular movement. They are ideal for generating curves of various kinds, particularly circles, ellipses, and spirals. Assume that we have a unit circle -- with a diameter of 1.0 -- centered on the origin (x = y = 0) A point is traveling around the circle; it can be located by the angle its radius makes with the x axis. For a given angle, the cosine of the angle is the x coordinate of the point, and the sine is the y coordinate. For larger circles, the sine or cosine are multiplied by the radius. Sines and cosines vary between -1.0 and +1.0, so they have to be represented by floats data type.

Java has Math.sin() and Math.cos() methods that return float values for a given sine and cosine angle. Angles in Java are expressed in radians -- 1 radiance is about 59 degrees, 3.14 radiance is 180 degrees. Radiance are measured by taking the radius of a circle and wrapping it around the circle. Since the circumference of the circle is: (2 * PI * the-radius) (2 * PI) or about 6.28 radii will wrap around the circle. A radiance is simply the radius of circle bent around it. One degree is about 0.017 radiance.

The program Trig.java displays the sine and cosine functions as waves. A for loop has an angle vary from 0.0 to (2 * PI) (about 6.28) -- the equivalent in radiance of 360 degrees. A variable, angInc, is calculated so that it is (1 / 640th) of a circle. This will result in one entire wave-form being drawn across the screen.

Circles

The Circle.java program uses Math.sin() and Math.cos() to draw a dotted line circle. Two constants, X_CENTER and Y_CENTER, hold the coordinates of the center of the screen. A float variable, angle, is used to step through the points that will make up the circle. Another float variable, radius, holds the value 100.0 The amount to increase the angle each time through the loop is determined by the number of points to plot. The main loop of Circle.java is a for loop that sets angle to 0.0 for stating point. The loop will continue until angle is greater than 6.28 radiance (or 360 degrees), and angle will increase by angInc each time.

Each point in the circle is drawn by calculating the cosine and sine for x and y. For x, the cosine, Math.cos(angle), is multiplied by radius, to enlarge the circle to the desired size. Since Math.cos() gives values that are positive and negative, X_CENTER is added to it to move the circle over to the center of the screen. A similar process is done with Math.sin() for the y coordinate.


Back to the top of this page. Back to main Index.

 

Sample Programs -- Batch Six

ExplodeOne.java

/*
 *  Program:    ExplodeOne.java
 *  Purpose:    Float data type demo
 *  Author:     George Aroush
 *  Date:       1/1/1998
 *  Change Log: None
 *
 *  Full description of Program:
 *      Demonstrates float. Uses floating point variables and a do-while loop
 *      to display an explosion of points.
 */

import java.awt.*;

public class ExplodeOne extends java.applet.Applet
{
    final int   NUM_PTS = 200;
    final int   NUM_FRAMES = 150;
    final float X_CENTER = 640 / 2;
    final float Y_CENTER = 480 / 2;

    public void paint(Graphics g)
    {
        float   x[] = new float[NUM_PTS];
        float   y[] = new float[NUM_PTS];
        float   mx[] = new float[NUM_PTS];
        float   my[] = new float[NUM_PTS];
        int     i, frame;

        /* start with all points in center of screen; calculate a random x and y movement for each point */
        for (i = 0; i < NUM_PTS; i++)
        {
            x[i] = X_CENTER;
            y[i] = Y_CENTER;

                /* Random(7) returns an integer between 0 and 7.  It is converted to a */
                /* float between 0.0 and 7.0.  Subtracting 3.5 shifts the value toward */
                /* the negative, so it will between	-3.5 and +3.5.  A similar process */
                /* makes my[] be between -2.5 and +2.5. Note that only 7 values are */
                /* possible -- -3.5, -2.5, -.5, .5, 1.5, 2.5 & 3.5	*/

            mx[i] = (float) (Random(7) - 3.5);

                /* Similarly, my[i] can have only 6 values -- -2.5, -1.5, -.5, .5, 1.5, & 2.5. */
                /* The resulting explosion, with 6 * 8 or 48 directions and speeds has an */
                /* overly structured look */

            my[i] = (float) (Random(5) - 2.5);
        }

        frame = 0;      /* init our indexter */

        do
        {
            DisplayPoints(x, y, NUM_PTS, g);
            MovePoints(x, y, NUM_PTS, mx, my);

            frame++;
        }
        while (frame < NUM_FRAMES);
    }


    /*
     *	void	DisplayPoints()
     *	
     *	Displays an array of "float" points
     */
    void	DisplayPoints(float x[], float y[], int n, Graphics g)
    {
        int	i;

            /* Pixel() requires integer arguments, so the float values of x */
            /* and y must be "cast" to ints */
        for (i = 0; i < n; i++)
            g.drawLine((int) x[i], (int) y[i], (int) x[i], (int) y[i]);
    }


    /*
     *	void	MovePoints()
     *
     *	Translates an array of "float" points
     */
    void	MovePoints(float x[], float y[], int n, float mx[], float my[])
    {
        int	i;

        for (i = 0; i < n; i++)
        {
            x[i] += mx[i];
            y[i] += my[i];
        }
    }


    /*
     *  int     Random()
     *
     *  Generate a random number between 0 to range
     */
    public int  Random(int range)
    {
        return ((int) (Math.random() * range));
    }

}

ExplodeOne.htm

<html>
<head>
<title>ExplodeOne</title>
</head>
<body>
    <hr>
    <applet code=ExplodeOne width=640 height=480></applet>
    <hr>
</body>
</html>

ExplodeTwo.java

/*
 *  Program:    ExplodeTwo.java
 *  Purpose:    do-while loop demo
 *  Author:     George Aroush
 *  Date:       1/1/1998
 *  Change Log: None
 *
 *  Full description of Program:
 *      Uses floating point variables and a do-while loop to display an
 *      explosion of points.
 */

import java.awt.*;
import java.applet.*;

public class ExplodeTwo extends Applet
{
    final int   NUM_PTS = 200;
    final int   NUM_FRAMES = 150;
    final float X_CENTER = 640 / 2;
    final float Y_CENTER = 480 / 2;

    public void paint(Graphics g)
    {
        float	x[] = new float[NUM_PTS], y[] = new float[NUM_PTS];
        float   mx[] = new float[NUM_PTS], my[] = new float[NUM_PTS];
        int     i, frame;

            /* start with all points in center of of screen; calculate a */
            /* random x and y movement for each point */
        for (i = 0; i < NUM_PTS; i++)
        {
            x[i] = X_CENTER;
            y[i] = Y_CENTER;

                /* Random(800)-399.0 generates 800 values between -399.0 and 400.0. */
                /* Dividing this by 100.0 results in 800 values between -3.99 and */
                /* 4.0 -- the pattern will be much less	structured than that of */
                /* ExplodeOne.java */

            mx[i] = ((float) Random(799) - 399) / 100;
            my[i] = ((float) Random(599) - 299) / 100;
        }

        frame = 0;

        do
        {
            DisplayPoints(x, y, NUM_PTS, g);
            MovePoints(x, y, NUM_PTS, mx, my);

            frame++;
        }
        while (frame < NUM_FRAMES);
    }


    /*
     *	void	DisplayPoints()
     *	
     *	Displays an array of "float" points
     */
    void	DisplayPoints(float x[], float y[], int n, Graphics g)
    {
        int	i;

            /* Pixel() requires integer arguments, so the float values of x */
            /* and y must be "cast" to ints */
        for (i = 0; i < n; i++)
            g.drawLine((int) x[i], (int) y[i], (int) x[i], (int) y[i]);
    }


    /*
     *	void	MovePoints()
     *
     *	Translates an array of "float" points
     */
    void	MovePoints(float x[], float y[], int n, float mx[], float my[])
    {
        int	i;

        for (i = 0; i < n; i++)
        {
            x[i] += mx[i];
            y[i] += my[i];
        }
    }


    /*
     *  int     Random()
     *
     *  Generate a random number between 0 to range
     */
    public int  Random(int range)
    {
        return ((int) (Math.random() * range));
    }
}

ExplodeTwo.htm

<html>
<head>
<title>ExplodeTwo</title>
</head>
<body>
    <hr>
    <applet code=ExplodeTwo width=640 height=480></applet>
    <hr>
</body>
</html>

Scale.java

/*
 *  Program:    Scale.java
 *  Purpose:    do-while loop demo
 *  Author:     George Aroush
 *  Date:       1/1/1998
 *  Change Log: None
 *
 *  Full description of Program:
 *      Scaling: changing the size of the cluster by decreasing x and y by
 *      floating point values --  based on "ArrayFour.java" from last week.
 */

import java.awt.*;
import java.applet.*;

public class Scale extends Applet
{
    final int   ONE_SEC = 1000;
    final int   NUM_PTS = 6;
    final int   NUM_CLUST = 22;

    public void paint(Graphics g)
    {
        float	x[] = { 160, 160, 173, 167, 153, 147 };
        float	y[] = { 98,  86,  95,  108, 108, 95 };
        float	xScale, yScale;
        int     count = 0;

        xScale = 1.1f;
        yScale = 1.2f;

        Color rgb = new Color(0, 0, 127);
        g.setColor(rgb);

        while (count < NUM_CLUST)   /* plot 20 clusters */
        {
            TheCluster(x, y, NUM_PTS, g);   /* plot one cluster */
            ScaleCluster(x, y, NUM_PTS, xScale, yScale);    /* resize the cluster */

            Delay(ONE_SEC);

            count++;
        }
    }


    /*
     *	void	TheCluster()
     *
     *	Plots a cluster of points: x, y, are pointers to arrays of integers; n is the number of points to plot.
     */
    void	TheCluster(float x[], float y[], int n, Graphics g)
    {
        int     point = 0;

        while (point < n)       /* plot the points */
            g.drawLine((int) x[point], (int) y[point], (int) x[point], (int) y[point++]);
    }


    /*
     *  void    ScaleCluster()
     *	
     *  Increases or decreases spatial size of cluster by floating point values
     *  for both x and y.
     */
    void	ScaleCluster(float x[], float y[], int num, float xScale, float yScale)
    {
        int     point;

            /* scale or resize the cluster */
        for (point = 0; point < num; ++point)
        {
            x[point] *= xScale;
            y[point] *= yScale;
        }
    }


    /*
     *  int     Random()
     *
     *  Generate a random number between 0 to range
     */
    public int  Random(int range)
    {
        return ((int) (Math.random() * range));
    }


    /*
     *  void    Delay()
     *
     *  Simply pauses for some time
     */
    public void Delay(int delayTime)
    {
        try
        {
            Thread.sleep(delayTime);    /* call Java's sleep method */
        }
        catch (InterruptedException e)
        {
            /* when the sleep() call above is over, Java will */
            /* be interrupted and we fall into this block of code */
            /* because our intention is simply slow down things */
            /* wed do nothing in our exception code and just get out */
        }
    }
}

Scale.htm

<html>
<head>
<title>Scale</title>
</head>
<body>
    <hr>
    <applet code=Scale width=640 height=480></applet>
    <hr>
</body>
</html>

Trig.java

/*
 *  Program:    Trig.java
 *  Purpose:    sin, cos demo
 *  Author:     George Aroush
 *  Date:       1/1/1998
 *  Change Log: None
 *
 *  Full description of Program:
 *      Demonstrates sin() and cos() functions -- draws sine and cosine curves
 */

import java.applet.*;
import java.awt.*;

public class Trig extends java.applet.Applet
{
    final int   Y_CENTER = 480 / 2;
    final float PI = 3.1415926535f;

    public void paint(Graphics g)
    {
        Color   rgb;
        float	angle, angInc;
        float	height = 170.0f;
        int     x = 0;
        int     y;

            /* There are 2 * PI radians in 360 degrees -- angInc will be */
            /* used to draw a point in the sine and cosine curves for */
            /* each x in the x axis of the hi-res screen */

        angInc = 2.0f * PI / 640.0f;

        rgb = new Color(0, 255, 0);
        g.setColor(rgb);

        g.drawLine(0, Y_CENTER, 639, Y_CENTER);     /* x axis line */

            /* draw simultaneous sine and cosine curves */
        for (angle = 0.0f; angle < 2.0f * PI; angle += angInc)
        {
            y = Y_CENTER + (int) (height * Math.sin(angle));
            rgb = new Color(255, 0, 0);
            g.setColor(rgb);
            g.drawLine(x, y, x, y);     /* sine curve plot */

            y = Y_CENTER + (int) (height * Math.cos(angle));
            rgb = new Color(0, 0, 255);
            g.setColor(rgb);
            g.drawLine(x, y, x, y);     /* cosine curve plot */

            x++;
        }
    }
}

Trig.htm

<html>
<head>
<title>Trig</title>
</head>
<body>
    <hr>
    <applet code=Trig width=640 height=480></applet>
    <hr>
</body>
</html>

Circle.java

/*
 *  Program:    Circle.java
 *  Purpose:    do-while loop demo
 *  Author:     George Aroush
 *  Date:       1/1/1998
 *  Change Log: None
 *
 *  Full description of Program:
 *      Makes a dotted circle, showing further use of sin() and cos() functions.
 */

import java.applet.*;
import java.awt.*;

public class ExplodeOne extends Applet
{
    final int   X_CENTER = 640 / 2;
    final int   Y_CENTER = 480 / 2;

    public void paint(Graphics g)
    {
        float	angle, numPix, angInc;
        float	radius = 100.0f;
        int     x, y;

            /* number of pixels determines how much to increase angle for each pixel plotted */
        numPix = 400;
        angInc = (float) (2.0 * Math.PI / numPix);

        g.drawLine(X_CENTER, Y_CENTER, X_CENTER, Y_CENTER);

            /* Draw a dotted circle, using cos() for the x coordinates,	 and sin() for the y coordinates. */
        for (angle = 0.0f; angle < 2.0 * Math.PI; angle += angInc)
        {
            x = X_CENTER + (int) (radius * Math.cos(angle));
            y = Y_CENTER + (int) (radius * Math.sin(angle));

            g.drawLine(x, y, x, y);
        }
    }
}

Circle.htm

<html>
<head>
<title>Circle</title>
</head>
<body>
    <hr>
    <applet code=Circle width=640 height=480></applet>
    <hr>
</body>
</html>

Back to the top of this page. Back to main Index.