Graphical Elements
The goal of this assignment is to build a solid basis for 2-D graphics programming by implementing representations of colors and points. Additionally, you will gain experience implementing larger, more realistic interfaces with detailed—perhaps even fussy—requirements.
2 Getting Started
Before writing code of your own, you will likely want to look over the code that is provided:
-
Boundable.java
(Javadoc) contains an interface for querying objects for their bounding boxes. (A bounding box is a rectangle that full encloses something.) You will need to implement this interface for your point classes. The bounding box of a point is the point—or in other words, all four corners of the box are the same as the point. -
BoundingBox.java
(Javadoc) contains an interface and implementation for working with bounding boxes. It is nearly complete, but for full functionality it depends on a point class that you will implement. -
Color.java
(Javadoc) contains an interface for working with RGBA colors, which you will implement. -
Interpolable.java
(Javadoc) contains interfaceInterpolable
, which supports linear interpolation between values in a space. You will implement this interface for points and for colors. -
Point.java
(Javadoc) contains an interface for computing with 2-D points. The interface is parametrized by the type of the coordinates,Double
orInteger
, though it is intended thatPoint<Double>
s be represented using unboxeddouble
s andPoint<Integer>
s be represented using unboxedint
s. This is an unusual situation, since a generic class cannot use primitive value types based on its parameter; instead, we must be careful to construct onlyPoint
values using the correct representations to match the returned type.One unusual thing about points is that multiple implementations of the same interface (
Point<Integer>
orPoint<Double>
) must compare equal if their components are equal, but noPoint<Integer>
is equal to anyPoint<Double>
. When you implementint
anddouble
point classes, you will need to figure out how to make this work.
Additionally, you may want to review the documentation for the pixel_buffer
framework.
- Download the
pixel_buffer.jar
JAR file containing the compiled pixel_buffer framework and add it to your project’s classpath. Make sure that you can import classes from it. (As in Problem Set 5, how to do this will depend on your IDE, and help will be available on Piazza.)
3 Your Task
3.1 Points
The first part of your task is to write the two points classes, DoublePoint
(which implements Point<Double>
) and IntegerPoint
(which implements Point<Integer>
), which represent 2-D points or vectors. Both classes should be immutable and final
. These classes have several peculiar requirements:
-
The coordinates of
DoublePoint
must be stored asdouble
s, so there is no conversion necessary to return them asdouble
s. -
The coordinates of
IntegerPoint
must be stored asint
s, so there is no conversion necessary to return them asint
s. -
If
p1
andp2
are bothPoint<Double>
s, thenp1.equals(p2)
whenp1.x() == p2.x()
andp1.y() == p2.y()
, even ifp1
andp2
are objects of different classes. -
If
p1
andp2
are bothPoint<Integer>
s, thenp1.equals(p2)
whenp1.x() == p2.x()
andp1.y() == p2.y()
, even ifp1
andp2
are objects of different classes. -
If
p1
is aPoint<Integer>
andp2
is aPoint<Double>
, then it is never the case thatp1.equals(p2)
orp2.equals(p1)
.
The goals here are twofold:
-
When computing with
Point<Integer>
s (resp.Point<Double>
s), we should be able to mix and match concrete implementations of the same interface (with the same parameter) without noticing. -
We should not mix up
Point<Integer>
s withPoint<Double>
s, because they can give different results due to rounding.
-
In a file
DoublePoint.java
, write a classDoublePoint
(Javadoc) that implements the interfacePoint<Double>
. This class must define one public static factory method:public Point<Double> point(double x, double y)
, which returns aDoublePoint
with the given coordinates.
Be sure thoroughly test your implementation.
-
In a file
IntegerPoint.java
, write a classIntegerPoint
(Javadoc) that implements the interfacePoint<Integer>
. This class must define two public static factory methods:-
public Point<Integer> point(int x, int y)
, which returns aIntegerPoint
with the given coordinates. -
public Point<Integer> point(double x, double y)
, which returns aIntegerPoint
with the given coordinates (but rounded withMath.round
).
Be sure thoroughly test your implementation.
-
3.2 Colors
Next, we will implement the interface Color
(Javadoc). As with the point classes above, our implementation of Color
s will use static factories rather than public constructors, which gives the class more control over object creation and initialization. No public class implementing Color
is required—instead, you are free to define as many (non-public) classes as you like in order for your static factories to return Color
objects.
-
In a file
Colors.java
, write an uninstantiable classColors
that defines the followingpublic
items.- Three constants2:
public static final Color TRANSPARENT
representing the fully transparent RGBA valuepublic static final Color BLACK
representing RGBA blackpublic static final Color WHITE
representing RGBA white
- Six static factory methods that return immutable
Color
objects:public static Color rgb(double, double, double)
constructs an opaque color object from 0.0-to-1.0 RGB componentspublic static Color rgba(double, double, double, double)
constructs a color object from 0.0-to-1.0 RGB components and an alpha componentpublic static Color rgb(int, int, int)
constructs an opaque color object from 0-to-255 RGB componentspublic static Color rgba(int, int, int, int)
constructs a color object from 0-to-255 RGB components and an alpha componentpublic static Color rgb(int)
constructs an opaque color object given a packed 24-bit RGB color valuepublic static Color rgba(int)
constructs a color object given a packed 32-bit RGBA color value
These six methods are closely related, and as such you should be able to avoid redundancy by having them delegate to each other as appropriate.
More detailed specifications of these methods appear in the formatted documentation for class
Colors
. Don’t forget to test your implementation. - Three constants2:
Note that because Color
is an interface, your implementation of colors needs to interoperate with other classes that might implement Color
. In order to preserve your code’s invariants, you need to be careful not to trust the values returned by methods of Color
objects that your methods are passed, since they may be from an implementation other than yours.
3.3 Bounding Boxes
I wrote BoundingBox.java
for you, except that several methods of class BoundingBox
currently throw an exception rather than returning the correct result, because the result depends on your implementation of Point<Double>
. Now that your implementation of class DoublePoint
is complete, BoundingBox
should be able to take advantage of it.
- Fix methods
topLeft()
(line 99),topRight()
(line 104),bottomLeft()
(line 109), andbottomRight()
(line 114) to return the correct points,
At this point, the tests in BoundingBoxTest.java
should pass using my BoundingBox
and your DoublePoint
. You need not write any additional BoundingBox
tests.
3.4 Make a Pretty Picture
Use the pixel_buffer
framework to write a program that renders something interesting to the screen. Can you do more interesting things by factoring out some functionality, and perhaps parameterizing it? If you make something especially neat, post a screen shot to Piazza.