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.
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 |
|
semantics |
|
---|---|---|---|
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, |
an RGB color where each value can be any constant |
[1, -1, 0.25]
[0.5, 1, 1] |
String |
between quotes> |
an image of the given file name is read, |
"foo.jpg"
"images/mickey.gif" |
Variable |
[a-zA-z0-9]+ |
an expression represented by a word |
a
bugs q45 |
Assignment |
|
assigns an expression to a variable |
a = 1.0
bugs = "foo.png" a = bugs |
Unary Operator |
|
prefixes an expression
! // negate (i.e., invert) a color |
!a
!(t + a * 0.1) |
Binary Operator |
|
combines two expressions into a single expression (in precedence order)
* // times / // divide + // plus - // minus |
a + b
a / 2 a + 10 * c |
Unary Functions |
|
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 | a function that takes two or more expressions as its arguments
perlinColor(expr, expr) // create random color based on 2D noise perlinBW(expr, expr) // create grey scale color based on 2D noise imageWrap(string, expr, expr) // imports image, tiling it so it may be repeated imageClip(string, expr, expr) // imports image, clipping it so it only appears once random() // returns random color (actually no arguments :) |
perlinColor(x, y)
perlinBW(y, x+x) |
|
Parentheses |
|
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.
For example, here are several images generated from basic expressions.
Extensions
There are many extensions to the basic specifications possible; some are listed below (note, you can add more operators to the set listed above, but that will not be worth as much extra credit (but may lead to more interesting images)). 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). If you do all of the above well, the maximum grade you can receive is an A-.
Finally, 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. 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. These extensions must further the good design of your program and not simply be hacks of code added at the last minute. If you do not have time to implement an extension, partial extra credit may be given for excellent justification of how your design either supports adding such a feature already or how it would need to changed sufficiently to support such a feature. However, your design should support adding any of these features reasonably easily (and your project artifact should discuss how to do so).
These extensions make it easier to generate expressions automatically rather than typing them in manually:
- easily combine saved expressions into new expressions
- generate expressions randomly
- generate expressions based on a string
- "breed" new expressions by combining old expressions
- allow the user other ways to control or influence the probability of generating terms in a random expression
These extensions focus on how expressions are used to generate images (rather than simply evaluating them for each pixel):
- iterate the expression to create fractals
- solve for the roots of the expression
- animate an expression over time rather than simply creating a single image
- a time parameter that varies from zero to one (or any range) turns any expression into an animation (if it goes zero to one and back to zero, then it can be looped)
- if breeding is implemented as well, the animation can be the "genetic cross-dissolves" between the parent and the child
These extensions make the GUI easier to use:
- display the current defined variable names and their values (and perhaps thumbnail sized images)
- allow users to save a history (or a favorites list or rankings) of old expressions
- allow users to view multiple images at once (either in separate windows, tabs, or a grid of thumbnails)
- allow users to change the size of the image displayed as a result of evaluating the expression
- allow users to zoom in or out on a part of the image (note, this changes the (x, y) domain used to compute the image)
- allow users to "debug" expressions by using the mouse to display the point and evaluated values at that point
- allow users to refer to history of expressions in this session by entering their history number prefixed by "$" (i.e., "$3")
- allow users to use the up and down arrows to move through the history of expressions
These extensions focus on making the "back-end" code more general and efficient:
- allow users to enter expressions without spaces between key tokens
- prune the expression trees based on useless arithmetic branches (e.g., multiplication by 1 or 0)
- recognize identical sub-expressions to avoid recalculating them when necessary
- recognize sub-expressions that do not depend on either x or y so they can be computed only once per image
- recognize common sub-expressions within saved favorites so they can be given a higher probability when generating new expressions
- use generics to allow your original Arithmetica program and this one share the same expression classes
Resources
- Artificial Evolution for Computer Graphics by Karl Sims (the original paper and inspiration for this project)
- Manipulating Parameters for Algorithmic Image Generation by Mike King
Deliverables
- Wednesday, September 16, vision and estimate due
- Create a web site for your project that includes
- a name for your team and each team member's primary and secondary responsibility on the project
- a vision for the project: describe what your team wants to accomplish with this project, specifically the priority of the extensions your team intends to implement
- a list of issues that arise as you try to pin down the requirements, e.g., vague, ambiguous, conflicting requirements
- a description of the classes you envision as part of implementing this project
- a justification of which team member's implementation you intend to use that notes specific changes you think will be necessary in the current code to implement this project's new features
- an estimate of how long you expect it to take you to complete this project; include as specific a task list as possible to support your estimate (i.e., break each of the requirements into several steps, the smaller the better your estimate will be)
- No code is required for this deliverable. Make this information available on a web page (linked to by everyone on the team), and email the URL to Professor Duvall. We will check this page frequently to check on your team's progress, so you will need to update this site as you develop your project.
- This week you must meet with your mentor TA to discuss your design and your implementation plan
- Create a web site for your project that includes
- Monday, September 21, Version 1.0 of the project is due
- This version should allow the user to input an expression interactively that includes at least one function and display an image from the resulting expression
- Document your project in the style of a project artifact that describes the current state of your project
- Create at least ten additional input and output files for use in automatically testing your program. Your test cases should test an expression directly on a color, rather than a complete image.
- After submitting this deliverable, your team must demo this program and discuss its design with your mentor TA
- Wednesday, September 30, Final Version of the project is due
- This should be the final version of the program, including all documentation (external and in code)
- Create at least ten additional input and output files for use in automatically testing your program.
- After submitting this deliverable, your team must demo this program and discuss your final design with your mentor TA.
- Friday, October 2, individual project analysis is due.