Suppose we build an expression in the variables x and y from the following primitives:
(... * ...)
average(... , ...)
sin(pi * ...)
cos(pi * ...)
.
x
(x * y)
(x * cos(pi * y))
average(y, (x * cos(pi * y)))
If we compute the value of such an expression at any point with -1 ≤ x, y ≤ 1, then the result will also be between -1 and 1. (How do we prove this?) For example, if we evaluate the above four example expressions for the x = 0.5 and y = -0.5 we obtain the values
0.5
(0.5 * -0.5) = -0.25
(0.5 * cos(pi * -0.5)) = 0.5 * 0 = 0
average(-0.5, (0.5 * cos(pi * -0.5))) = average(-0,5, 0) = -0.25
Now consider an image of, for example, 800 by 800 pixels. Each pixel has
an x- and y-coordinate. For the x-coordinate, xCoordinate
, we have
that 0 ≤ xCoordinate
< 800. We want to distribute these
x-coordinates evenly over the interval [-1, 1]. This can be done by mapping
xCoordinate
to 2 * xCoordinate / 800 - 1
. For example,
the x-coordinate 600 is mapped to 0.5. In the same way, we can map the y-coordinate,
yCoordinate
, to a point in the interval [-1, 1]. For example, the
y-coordinate 200 is mapped to -0.5.
As we have just seen, we can assign to each pixel of an image a pair (x, y)
with -1 ≤ x, y ≤ 1. Recall that the value of the above introduced expressions
at such a point (x, y) is an element in the interval [-1, 1].
That is, such an expression assigns to each pixel an element in [-1, 1].
For example, the pixel (600, 200) is mapped to (0.5, -0.5) and the expression
average(y, (x * cos(pi * y)))
subsequently maps that to -0.25.
A black and white image assigns to each pixel a grey scale value in the interval [0, 255].
As we have seen, we can use the above expressions to map each pixel to an element in
the interval [-1, 1]. Hence, by transforming the interval [-1, 1] to the interval [0, 255],
we obtain a grey scale value for each pixel. For a given value
in the interval
[-1, 1], we have that 255 * (1 + value) / 2.0
is in the interval [0, 255].
For example, for the pixel (600, 200), the expression average(y, (x * cos(pi * y)))
gives the grey scale 255 * (1 + -0.25) / 2.0 = 96
. So every expression defines
a gray scale value for each pixel and, hence, defines a black and white image. The image defined
by the expression average(y, (x * cos(pi * y)))
is given below.
A color image assigns to each pixel an amount of red, an amount of green and an amount
of blue. All three values are in the interval [0, 255]. Hence, given three expressions,
we obtain the amount of red, green and blue for each pixel, that is, we obtain a color
image. For example, if we take the expression average(y, (x * cos(pi * y)))
for red, the expression (x * cos(pi * y))
for green and the expression
(x * y)
for blue we obtain the following image.
The app Generator generates an image from three randomly created expressions. The utility class Expressions creates random expressions. As we can see, the utility Expressions uses numerous classes from the package lab.art. Its API can be found here. This lab consists of implementing some of the classes of lab.art.
We introduce the class Expression to represent expressions in general, and the classes Product, Average, Sine, Cosine, VariableX and VariableY to represent expressions of the form
(... * ...)
average(... , ...)
sin(pi * ...)
cos(pi * ...)
x
y
(x * cos(pi * y))
has x
as its first subexpression and
cos(pi * y)
as its second subexpression. Similarly, an average expression
has also two subexpressions. Sine and cosine expressions only have a single subexpression.
For example, the expression cos(pi * y)
has the subexpression y
.
The variables x and y do not have any subexpressions.
Since both product and average expressions have two subexpressions, we introduce the class BinaryExpression. Both the Product and Average class extend the BinaryExpression class, since a product expression is-a binary expression and an average expression is-a binary expression. A binary expression has two subexpressions, which is captured by aggregation.
Sine and cosine expressions have only one subexpression. For that reason, we introduce the class UnaryExpression. The Sine and Cosine class both extend the UnaryExpression class, since a sine expression is-a unary expression and a cosine expression is-a unary expression as well. A unary expression has just one subexpression, which is captured by aggregation.
Since the classes BinaryExpression and UnaryExpression have only been introduced to prevent code duplication, we want to prevent clients from instantiating these classes. Therefore, both classes are abstract.
The class Expression plays a central role. For example, a BinaryExpression is-an Expression and has two Expressions. Since the class Expression "combines" the abstract classes BinaryExpression and UnaryExpression, it is abstract itself as well.
Consider the (x * cos(pi * y))
expression. This expression
is represented by a Product object, which is-an Expression and has
two Expressions. To evaluate this expression at a point, say (0.5, -0.5), we
have to evaluate the subexpressions x
and cos(pi * y)
at that point as well. Since these subexpressions are represented by
Expression objects, the Expression class needs to
contain the evaluate
method. Note though that we can only
provide a concrete implementation of the evaluate
method in the
classes Product, Average, Sine, Cosine, VariableX and VariableY. Therefore,
the evaluate
method of the Expression class is abstract.
public String toString(String pattern) { String first = this.??????????.toString(); String second = this.??????????.toString(); return String.format(pattern, first, second); }In the above method, first the string representation of the first subexpression is stored in first. Next, the string representation of the second subexpression is stored in second. Finally, first and second take the place of the first and second %s in pattern and the resulting string is returned. For example, if first is "x" and second is "y" and pattern is "average(%s, %s)" then the result is "average(x, y)".
a
and b
as its arguments and returns (a + b) / 2
is an example of
such a method. Implement the evaluate method as follows.
public double evaluate(double x, double y, BinaryOperator operator) { double first = this.??????????.evaluate(x, y); double second = this.??????????.evaluate(x, y); return operator.operation(first, second); }In the above method, the first subexpression is evaluated at the given x- and y-coordinate. Next, the second subexpression is evaluated at the given x- and y-coordinate. Finally, the operation of the operator is applied to first and second and the result is returned.
public double evaluate(double x, double y) { BinaryOperator average = ??????????; return ??????????; }Delegate to the evaluate method of the superclass BinaryExpression.
submit 1030 lab6 group.txt lab/art/VariableX.javaIf something fails, you may see something like
submitted: group.txt (17 bytes) submitted: VariableX.java (1270 bytes) All files successfully submitted. Your VariableX class compiled successfully. evaluate method failed expected:<0.9196875615337363> but was:<0.0> java.lang.AssertionError: evaluate method failed expected:<0.9196875615337363> but was:<0.0> SUBMITTED FILE VariableX.java HAS BEEN REMOVED. PLEASE SUBMIT AGAIN.Fix your code and submit again. If you successfully submitted your code, you will see something like this
submitted: group.txt (17 bytes) submitted: VariableX.java (1270 bytes) All files successfully submitted. Your VariableX class compiled successfully. Your VariableX class passed all the tests. Your code contains no style errors. Your group.txt file has been successfully validated.
submit -l 1030 lab6After submitting your VariableX class, you should see something like this
The following files have been submitted: group.txt.submitted 17 bytes VariableX.java.submitted 1270 bytes
submit 1030 art art1.jpgYou may submit multiple (differently named) files.
/** * Returns the expression represented by the given string. * * @param expression string representation of an expression. * @return the expression represented by the given string. * @throws Exception if the given string does not represent an expression. */ public static Expression fromString(String expression) throws Exception
This assignment is based on a nifty assignment designed by Christopher A Stone.