CPSC 225, Spring 2019
Lab 2: Artistic Inheritance
In this lab, you will work with a small class hierarchy that represents various kinds of "art." The art in question is just abstract images drawn using basic drawing subroutines. You will add classes to the hierarchy to represent specific artworks. You will also learn something about using a JavaFX GraphicsContext for drawing.
This lab will use JavaFX for graphics. On our lab computers, a default Eclipse project will give errors on code that uses JavaFX classes. (This is not an issue if you are using JDK 8 from Oracle on your own computer.) To avoid the errors, you have to change the "JRE" selection in the New Java Project dialog when you create the project. Change the JRE for the project to "Use Default JRE":
You should create a project to hold your work for this lab. Copy all six files from /classes/cs225/lab2-files into the src directory of your project.
The work for this lab is due by the beginning of next week's lab. You should create a folder named lab2 inside your homework folder in /classes/cs225/homework. Copy all the files from your project into that folder. I will print out all Java files from that folder, except for the original six that I gave you. That will be at least four new files, defining different kinds of "art."
Drawing with GraphicsContext
JavaFX uses a class named GraphicsContext for drawing. The basic API is introduced in Section 3.9.1 in the textbook, with some further information in Section 6.2. The most relevant details are repeated here. (I'm assuming that you already know about "pixels" and coordinate systems.)
You will want to know something about color. Colors in JavaFX are represented by the class javafx.scene.paint.Color. (Java has other Color classes. When you import Color into your program, make sure that you import the right one.) The Color class defines many constants, such as Color.WHITE and Color.CORNFLOWERBLUE, representing specific colors. It also has several "factory methods" for making arbitrary colors. For example,
Color c = Color.color( red, green blue );
creates a color from three double numbers in the range 0.0 to 1.0, representing the amounts of red, green, and blue in the color. (By using Math.random() for each of the parameters, you can make a random color.)
Color c = Color.hsb( hue, saturation, brightness );
uses a hue (or basic color) in the range 0.0 to 360.0, with saturation and brightness in the range 0.0 to 1.0. A simple way to get a random bright, saturated color is
Color c = Color.hsb( 360*Math.random(), 1.0, 1.0 );
By using smaller values for the second and third parameters, you can get less saturated, darker colors.
You can add a fourth parameter to either method to get a translucent color. The fourth parameter is a double in the range 0.0 to 1.0 that tells how opaque the color is. For example, Color.hsb(360*math.random(),1,1,0.5) makes a random 50% transparent color.
Drawing is done by "filling" and "stroking" shapes. Stroking is like dragging a pen along a line or along the outline of a shape. A GraphicsContext has properties that determine the appearance of strokes and fills. Drawing is done on a "canvas." Points on the canvas are specified using a pair of coordinates (x,y), where x and y are measured in pixels, but can be of type double. The coordinates (0,0) refer to the upper left corner of the canvas. The x coordinate increases from left to right, while y increases from top to bottom. A canvas has a fixed width and height, measured in pixels. If g is an object of type GraphicsContext, you can use the following basic drawing commands. (For commands for drawing text, polygons, and even images, see Section 6.2, if you are interested.)
- g.setFill(c) is called to set the color to be used for filling shapes. The parameter, c is an object belonging to a class Color. For example, if you want to fill shapes with red, you would say "g.setFill(Color.RED);". The specified color is used for all subsequent fill operations up until the next time g.setFill() is called. Note that previously drawn shapes are not affected!
- g.setStroke(c) is called to set the color to be used for stroking shapes. It works similarly to g.setFill.
- g.setLineWidth(w) sets the size of the pen that will be used for subsequent stroke operations, where w is measured in pixels and can be of type double.
- g.strokeLine(x1,y1,x2,y2) draws a line from the point with coordinates (x1,y1) to the point with coordinates (x2,y2). The width of the line is 1, unless a different line width has been set by calling g.setLineWidth(), and the color is black unless a different color has been set by calling g.setStroke().
- g.strokeRect(x,y,w,h) draws the outline of a rectangle with vertical and horizontal sides. This subroutine draws the outline of the rectangle whose top-left corner is x pixels from the left edge of the drawing area and y pixels down from the top. The horizontal width of the rectangle is w pixels, and the vertical height is h pixels. Color and line width are set by calling g.setStroke() and g.setLineWidth().
- g.fillRect(x,y,w,h) is similar to g.strokeRect() except that it fills in the inside of the rectangle instead of drawing an outline, and it uses the color set by g.setFill().
- g.strokeOval(x,y,w,h) draws the outline of an oval. The oval just fits inside the rectangle that would be drawn by g.strokeRect(x,y,w,h). To get a circle, use the same value for w and for h.
- g.fillOval(x,y,w,h) is similar to g.strokeOval() except that it fills in the inside of the oval instead of drawing an outline.
The file Gallery.java is the main program for this lab — it is the only file that you can run as an application. If you run it now, it will open a window with a large area that contains two "artworks." One of the artworks is animated. These are sample artworks that I have created; they are defined by the files InfiniteMotion.java and PseudoPollock.java, which are provided as examples. Your assignment for this lab is to create at least four new classes defining different kinds of art and to arrange artworks defined by your classes on the gallery wall. The exact requirements for your art are discussed below.
The file Art.java defines the abstract class Art. An object of type Art represents a rectangular area on which you can draw using a GraphicsContext. Art is a subclass of Canvas, which is the JavaFX class that supports drawing with a GraphicsContext. The width and height of the canvas must be specified in the constructor and will not change. The Art class defines an instance variable g of type GraphicsContext for drawing on the canvas, and instance variables width and height giving the size of the canvas. You can use g, width, and height in any subclass of Art without redeclaring them. The Art class defines an abstract method draw() for drawing on the canvas. Any concrete subclass of Art must provide a definition for draw(). The class PseudoPollock is a subclass of Art in which the draw method just draws a lot of random lines.
The file AnimatedArt.java defines a subclass of Art. AnimatedArt is still an abstract class, because it does not define the draw() method. The difference is that when animations are running, the draw() method will be called over and over, and it can draw a different picture in each frame. There are about 60 frames per second. The AnimatedArt class defines a new instance variable, frameNumber, that tells which frame number is being drawn. You can use frameNumber in a subclass of AnimatedArt. The InfiniteMotion class is a subclass of AnimatedArt that implements the same animation that is discussed in Section 6.9.3.
The file ClickArt.java defines a subclass of Art that represents interactive artworks that the user can click with the mouse. It is an abstract class because it defines the abstract method doClickAt(x,y). This method is called when the user clicks on the canvas. The parameters, (x,y), are the coordinates of the point where the user clicked the mouse. A subclass of ClickArt must provide a definition for doClickAt. (ClickArt already defines a draw() method that simply fills the canvas with a background color.) I have not provided an example subclass of ClickArt.
Your assignment is to make at least four new art classes and use them in Gallery.java. One of the four classes must be a direct subclass of Art that makes pictures similar to either the second or third example in the following image. (These "artworks" were vaguely inspired by actual artists; see below. The first picture was produced by my PseudoPollock class.)
For the second picture, the requirement is that you fill the canvas with rows and columns of randomly colored rectangles, and that you also draw something (not necessarily a circle) inside each rectangle. For the third picture, the requirement is that you draw randomly colored and randomly positioned horizontal and vertical lines. Don't just draw all the vertical lines on top of all the horizontal lines (or vice versa); mix them up so that for example a horizontal lines can go above some vertical lines and under some other vertical lines. (I would certainly encourage you to do both of these images, but it's not required.)
A second new class must be a subclass of AnimatedArt. There are two ways to make animated art. One is to just add something to the current picture each time draw() is called. The second is to erase the current image by filling the canvas with a background color, and then draw a new image from scratch. Remember that by using frameNumber in your drawing, you can make a different image in each frame.
A third new class must be a subclass of ClickArt. You need to define the doClickAt() method to do something interesting. It should be at least a little more interesting than simply drawing a rectangle or oval at the point where the user clicks. Maybe draw a set of concentric circles at that point, or a set of lines that radiate from that point.
The fourth new class can be anything you like. It can be animated, clickable, or plain art. Try to be creative!
Of course, you also have to arrange your artworks on the gallery wall. In the file Gallery.java, you will only need to modify the setup() method (line 36). You will not need to change anything outside that method, and the only things that you need to understand are the three addArt() methods for adding art to the gallery wall. You should try to make an attractive display. You should include more than four artworks. Remember that you can include several objects made from the same class! You can also use my sample art if you want.
Here is some more information about the real abstract artworks that inspired the three examples shown above.
Picture #1: Pseudo Pollack. For my own random art, I used a for loop to draw a large number of lines. Jackson Pollock is known for large canvases covered with splashes and dribbles of paint. To get something that is vaguely in the Pollack style, I drew a large number of random lines of different colors. My version also selects the number of lines at random. It draws bright, saturated lines on a random gray background.
Picture #2: Kan't be Kandinsky. Some of the canvases by Wassily Kandinsky feature a grid of repetitive patterns in different colors. To imitate this style, you should cover the drawing area with small rectangles. Each rectangle should show a similar pattern, but the colors should vary randomly. In my version, I fill each rectangle with a random dark background color, then draw a brightly colored circle inside the rectangle. You don't have to follow this pattern exactly, but you should do some kind of repetitive pattern. You will need nested for loops to draw the image.
Picture #3: Mangled Mondrian. Many paintings by Piet Mondrian show rectangular areas bounded by vertical and horizontal bars. My Mondrian-like artwork shows randomly colored vertical and horizontal bars on a white background; they are based on an exercise used in CS124 by Professor John Vaughn some years ago. In these drawings, to draw one of the bars, I first decide randomly whether to make it horizontal or vertical. When I drew the bars, I tried to add a slight 3D effect by drawing a brighter line along one edge of each bar and a darker line along the other edge. (I used the methods c.brighter() and c.darker() from the Color class, where c was the color of the bar.)