CompSci 108
Spring 2010
The Software Studio

Instead of simply building and evaluating expressions of integer values as in Arithmetica, you should allow the user to create expressions that evaluate to colors and then eventually to images. This may sound a little strange, but the results can be quite spectacular. Here is the general idea: an image is really a expression that maps points (x,y) to colors (r,g,b). This program simply evaluates that expression for each pixel (x-, y-point) in the image and stores resulting color.

For this program, a color represents three real numbers, one each for the red, green and blue component, where the component values range from -1 to 1. For example, [-1,-1,-1] is black, [1,-1,-1] is red, and [1,1,-1] is yellow. During computation of an expression, the value of each component should not be restricted to this range, but the final result of the expression should be clamped to within the range -1 to 1. By default, like the colors, the domain of the image over X and Y is from -1 to 1. The upper left corner of the image will be (-1,-1) and the lower right corner will be (1, 1). Thus, the size of the image will need to be mapped to the domain (in this case, [-1, 1]). Before the expression is evaluated for each pixel, the variables x and y should be set to the current point to be evaluated.

For example, here are several images generated from basic expressions.

Your program should allow a user to input expressions interactively, or from a file, and display the resulting image. The syntax of the allowed expressions is given below:

expression syntax semantics examples
Constant
<any real number>
[-]?[0-1]+(.[0-9]+)?
a real valued number
note, to avoid potential ambiguity in parsing there should not be a space between the negative sign and the value
.87
1
-0.4
Color
[ constant,
constant,
constant ]
an RGB color where each value can be any constant
[1, -1, 0.25]
[0.5, 1, 1]

Variable
<any alpha-numeric string>
[a-zA-z0-9]+
an expression represented by a word
note, two variables x and y should always be defined to be the current coordinate in the image domain
bugs
q45
Assignment
var = expr
assigns an expression to a variable
a = 1.0
bugs = x
Unary Operator
op expr
prefixes an expression 
!   // negate (i.e., invert) a color
!a
!(t + a * 0.1)
Binary Operator
expr op expr
combines two expressions into a single expression (in precedence order)
*   // times
/   // divide
+   // plus
-   // minus
% // mod ^ // exponentiate
a + b
a / 2
a + 10 * c
Unary Functions
fun(expr)
a function that takes an expression as its single argument
floor   // round down
ciel    // round up
abs     // absolute value
clamp   // clamp results to [-1, 1]
wrap    // wrap results around [-1, 1] (i.e., 1.5 -> -0.5)
sin     // sine
cos     // cosine
tan     // tangent
atan    // arc tangent
log     // log
rgbToYCrCb // convert color to luminance / chrominance space
yCrCbtoRGB // convert color to RGB from luminance / chrominance space
sin(a * b)
abs(x) - y / 2
Multi-Argument Functions
fun(expr,...)
a function that takes two or more expressions as its arguments
perlinColor  // create random color based on two given values
perlinBW     // create grey scale color based on two given values
random()     // returns random color (actually no arguments :)
        
perlinColor(x, y)
perlinBW(y, x+x)
Parentheses
(expr)
raises an expression's precedence
(a + b) * 3
!(bugs - 0.1)

Operators have the following precedence from left to right (listed from highest to lowest):

() parentheses
! unary operators
*, /, % multiplicative operators
+, - additive operators
= assignment

Note, not all of these functions are defined continuously. You should have appropriate error handling (i.e., divide-by-zero should silently return zero). If a function is scalar, i.e., typically operates on a single value (e.g., sin(x)), then it should be applied to each of the color components in turn.

Extensions

The extensions below are intended to stretch your design further and to differentiate your program from others in order to capture the algorithmic art market. These extensions must further the good design of your program by being planned from the start, not simply be added at the last minute. Your team must extend the program beyond the core specifications given above if you want to be considered for a grade in the A range. However, your design should support adding any of these features reasonably easily. To show this, your project artifact should describe how your design either supports adding three unimplemented features or how it would need to changed sufficiently to support a feature (preferably from different categories).

From the stand point of your grade, the most important thing is that your program is designed well (i.e., there is a clear separation between the syntax and semantics of the expression language and that it is clearly possible to change either by adding only O(1) line to your existing code). This means your design should be open to adding new kinds of expressions while closed to changing the evaluation and parsing code. The requirements above and suggestions below are intended to help you to realize such a design.

Next in importance to your grade, your project should be thoroughly tested to prove to the course staff that your confidence in it is justified. You should include whatever data files, driver programs, or unit tests you have used in your submission (as well as documentation on how to use them).

These extensions make it easier to generate expressions automatically rather than typing them in manually:

These extensions focus on how expressions are used to generate images (rather than simply evaluating them for each pixel):

These extensions make the GUI easier to use:

These extensions focus on making the "back-end" code more general and efficient:

Resources