untitled
viviti

James Russell

Johnathan Broadway

 

Pretty Pictures Project Specification

 

OVERVIEW

 

HIGH LEVEL SPECIFICATION

 

PrettyPictures Project Overview

 

The project goal in designing and implementing ‘Pretty Pictures’ is to have a program that manipulates images on a pixel by pixel basis using mathematical formulas and user-defined inputs. The user can then select his/her favorite of these generated images and ‘breed’ these images to produce a new generation of images which inherit characteristics of the chosen images from the previous generation. After several iterations the program is expected to produce images that are aesthetically pleasing to the user. These images can then be saved for human consumption.

 

Program Use and Normal Behavior

 

The program can be executed by opening with the Java 6th Edition, or by using the command prompt. The following command would run the program assuming the current directory contains the file.

 

$ java PrettyPictures

 

The program will display a GUI interface which will allow for user-interaction. An example of the user-interface is shown below.

 

 

 

The program will start by generating a series of random functions which will be used to create the initial generation of images. These images are loaded concurrently. Once these images are loaded, the GUI will allow for the following actions:

 

SELECTION/DESELECTION:

 

Selecting preferred images is accomplished by double clicking on the desired image or selecting the image and pressing the ‘add’ button, represented by a green ‘plus’ sign. This will generate an identical thumbnail in the lower selection panel. This thumbnail can then be de-selected by double clicking the replicated thumbnail or by selecting the thumbnail and clicking the ‘remove’ button, represented by a red ‘minus’ sign.

 

GENERATION HISTORY NAVIGATION:

 

The user will be able to navigate between previous generations of images via the use of “Back” and “Forward” buttons similar to those found in web browsers. Using these buttons does not remove the selected images that are in the lower selection panel, allowing for images from previous generations to be bred with images from the current generation. A drop-menu “History” is also available, which acts in a manner identical to a standard web browser. This is accessed using the ‘history’ button, represented as a folder near the stop and restart buttons.

 

STOP AND RESTART:

 

The user will be able to stop all computation, as well as throw away and restart all computation that has been completed for the current generation. This will be accomplished with “Stop” and “Restart” buttons, represented by standard ‘stop’ and ‘refresh’ icons as used by web browsers. A small icon in the right hand side will indicate that the program is currently loading images by becoming an animated icon. It will revert to a standard still icon when finished.

 

ZOOM FOCUS:

 

The user will be able to display a single image at a higher resolution than the thumbnails by use of a “Zoom” button. This will load a page that features only the image. On this page the user will be able to set the generated resolution and save the image if desired. An example of the zoom page is shown below.

 

On this panel the user will be able to modify the formula for a given image. The format is rigidly specified as follows:

 

FunctionName(ChildFunction)

 

The following inputs are available for user input:

 

AbsVal(ChildFunction)

Add(ChildFunction1, ChildFunction2)

ATan(ChildFunction)

Clip(ChildFunction)

ColorPerlinNoise(ChildFunction)

Constant[xr yg zb] (x, y, z are decimal values between -1, 1)

Cos(ChildFunction)

Divide(ChildFunction1, ChildFunction2)

Exponentiate(ChildFunction) (Power will be randomly generated up to 5)

GrayScalePerlinNoise(ChildFunction)

LCToRGB(ChildFunction)

Log(ChildFunction)

Multiply(ChildFunction1, ChildFunction2)

Negate(ChildFunction)

RGBToLC(ChildFunction)

RoundDown(ChildFunction)

RoundUp(ChildFunction)

Sin(ChildFunction)

Subtract(ChildFunction1, ChildFunction2)

Wrap(ChildFunction)

X[xr yg zb] (x, y, z are decimal values)

Y[xr yg zb] (x, y, z are decimal values)

 

Once the formula is modified, the user may press the ‘Change Function’ icon, represented by a small gear. If the modification is accepted, the image will change. If it fails, the user will be notified.

 

 

SAVING AND OPENING IMAGES:

 

The user will be able to save images through use of the ‘Zoom’ function. Images can be opened into the current generation by pressing the ‘Open’ button and selecting a valid image file. A thumbnail of this image will then be loaded into the current generation. The images can be saved as either a .PNG file or as a .PRP file, which can then be loaded. This file contains the formula for the image and not the image itself, so it takes up very little disk space.

 

ANIMATION:

 

The user will be able to select a pair of images and ‘animate’ between them. If these images are of similar genotype (explained later), the images will fade from one to another very smoothly. If they are of non-similar genotype, the result will be much less smooth. This animation can be previewed and saved in PNG format. Animation is accomplished through use of the ‘animate’ button, represented by a light-blue/blue circle which fades from one color to another.

 

The Animation page will allow for the user to fine tune the animation speed and number of frames. The animation can also be saved as a series of PNG files. An example of the Animation page is shown below.

 

BREEDING GENERATIONS:

 

After selecting the images that the user finds aesthetically pleasing, he/she may then ‘breed’ a new generation of images. By pressing the ‘breed’ button, represented by a green circle inset with a right-facing triangle, the selected images will be compared to each other and the formulas that define them are ‘bred’ using a specialized genetic algorithm: each of the pictures is compared to another and the formulas are mingled together in a manner that depends on how similar they are. The end result will produce an image that is a mix of the two images. This is repeated among the selected images (with pairings chosen at random) until a new generation is completely generated. The selected images are then removed from the selection panel and the process repeats itself.

 

On the lower left panel of the GUI, a set of sliders is available to the user. They are labeled as ‘Red Saturation’, ‘Green Saturation’, ‘Blue Saturation’, and ‘Mutation Percentage’. These saturation sliders can be used to adjust the overall color of an image while rendering. The adjustment takes effect before pressing restart, breed random generation, breed next generation, zoom, and animate. These sliders do not change the underlying function, they merely adjust it’s appearance on screen.


The mutation slider is used to adjust the chance that a function has to ‘mutate’ during breeding. This mutation is (mostly) random, and means that an image can breed with itself if the mutation is set high and produce a very different image.

 

LOW LEVEL SPECIFICATION

 

Functions and function representation

 

The functions that define the images are binary trees built with nodes that are either Zero, Single, or Dual Child functions. All functions take in arguments X and Y (pixel coordinates) and return a three dimensional vector of RGB values. To translate pixel coordinates into RGB values, functions will perform operations on those coordinates as well as the function’s child functions, if they are present. ZeroChildFunctions have no children on which to operate, so they return constants or values derived from the XY coordinates. SingleChildFunctions have one child function, and DualChildFunctions have two.

 

As an example of the representation of a function, consider f(X,Y) = cos(3+X).

This function would be represented as a SingleChildFunction that returns the value of cos(CHILD_0), where CHILD_0 is a DualChildFunction that returns the value of CHILD_1 + CHILD_2. CHILD_1 and CHILD_2 would both be ZeroChildFunctions that return the values of 3 and X, respectively.

 

A function’s operations are determined by its state. The returnValue(function host, int x, int y) method of a function will delegate to the function’s state. The state will perform any manipulation of the x, y coordinates or the host’s children’s return values that are necessary and return the result.

 

The returnValue() method in a function can also take one or two Vectors as arguments (via variable arguments). If these arguments are present, it treats them as the output of its children function(s) rather than the actual output. For instance, a Dual Child Function with the Add State could take in two extra vectors as arguments and instead of returning the sum of the results from its two child functions, it would return the sum of the two input vectors. This functionality is useful when interpolating between functions while animating.

 

Only the Zero Child Functions with the Constant State will internally store a Vector, and this Vector will be created at instantiation (X and Y States store an internal ConstantState, whose Vector they return). When returnValue() is called on a function, the vectors from the lowermost Zero Child nodes in the function are propagated up through the tree and mutated as needed. Because the vectors are mutated during evaluation, ConstantState must also store Red, Green, and Blue values so that it can reset its internal Vector the next time it needs to return it. This scheme eliminates the need to instantiate new Vectors at every node in the function tree while evaluating a function, a process which would be very computationally expensive.

 

Functions and function states contain Strings representing the “path” to the function (example: “model.DualChildFunction” or “model.AddState”). This String is used for dynamic class loading of the functions as well as equality testing. If two functions or states have the same path, then they must be instances of the same class.

 

Generating random functions and function trees

 

To generate random functions, we maintain arrays of the names of each type of function. To generate a random function, we index into the array with a random number and use dynamic class loading to create an instance of the proper function. Constants, exponents, and other private fields are set to random values by the default constructors of the functions that possess them.

 

To create random function trees, we first randomly generate the root node and add it to a queue. Then, while the queue is not empty, we pop the first function from the queue and check to see if it is a Dual, Single, or Zero child function. If it is Dual or Single, we create two or one new random functions, set them as the current function’s children, and add them to the queue. If it is a Zero child function, we do nothing. When the queue is empty, the function tree has been fully generated.

 

Psuedocode for random function tree generation:

 

rootNode = new randomFunction

FuncQ.add(rootNode)

 

While(FuncQ not empty)

{

currentFunction = FuncQ.pop()

 

if (currentFunction is a Dual Child Function){

newFunction1 = new randomFunction

newFunction2 = new randomFunction

currentFunction.setChildren(newFunction1, newFunction2)

FuncQ.add(newFunction1)

FuncQ.add(newFunction2)

}

 

else if (currentFunction is a Single Child Function){

newFunction1 = new randomFunction currentFunction.setChild (newFunction1)

FuncQ.add(newFunction1)

}

 

}

 

Images

 

Images are represented as BufferedImages. The BufferedImage will be written a column of pixels at a time (using the setRGB() method) from a thread which will be created to handle evaluation of the image function. This will allow for multiple images to be evaluated concurrently.

 

It should be noted that the TilingExternalImage and ClippingExternalImage states both scale the image they load into the proper (-1, 1) range, basically meaning they are a StretchExternalImage. We found that this implementation was more natural in our setup, and using Tiling and Clipping would have required arbitrary constraints.

 

Getting images from functions

 

To get an image from a function, the function is evaluated for each (X,Y) pixel coordinate of the image, translated to the [-1,1] range. The resulting RGB vectors are scaled to the [0,255] range and converted to RGB int values that can be fed into a BufferedImage. A reference to the BufferedImage is passed to the View right after the thread responsible for calculating the pixel values of the image is started, so unevaluated pixels will initially be displayed as black. As thread evaluates each column of pixels, the column of black pixels will be replaced with the newly calculated values.

 

It should be noted that in our version, X and Y state functions hold onto a local constant they use to modify their return value. This ensures that the images returned have more color variance than would normally be the case. We found (through experimentation) that this made for Prettier Pictures™.

 

Concurrent image evaluation

 

Threads are used to calculate and display each image simultaneously in real time. Each thread will first tell the view that it is calculating an image so that the “Busy” icon will be displayed and then add itself to a list of running threads maintained by the model. It will then get a random function tree via the method described above and loop through all pixels of the image evaluating the function for those coordinates (which it will first translate to the [-1,1] scale). It will scale the RGB vector returned by the function for each pixel to the [-1,1] scale and then convert it to an int format usable by the BufferedImage. The thread will write each of these RGB int values to an array until it reaches the end of a column of pixels. After each column, it will write the entire array of RGB int values to the BufferedImage so that the image will be updated one column at a time. The thread will then check to see if the “Stop” button has been pressed, and if so, it will terminate and remove itself from the list of working threads. Otherwise, the thread will traverse and calculate the entire image before doing this.

 

The BufferedImage evaluated by the thread is actually passed to the model before it is evaluated, but the thread retains a reference to it and writes in the RGB values as they are calculated.

 

Threads are controlled through synchronization around a set of currently running Thread IDs. Threads place themselves into this to indicate the model is busy; the last thread to leave this (runningThreads.isEmpty()) will indicate to the view that the model is now finished computing.

 

The threads all modify independent images and do not access shared variables, so most work can be done independently. However, when updating the view, Swing’s threading policy requires that the Event-dispatching thread be the only thread allowed to modify swing components. This means the use of the SwingUtilities.invokeLater method was required.

 

To stop the threads from working, they all loop around the same Boolean condition. If this is set to true, all the threads will begin removing themselves from the running threads set. The last thread out the door will reset the stop condition to false.

 

Function/image breeding

 

When breeding two functions, pieces of a function tree may be randomly reassigned or swapped with other functions, depending on the level of similarity of the functions. If the two functions to be bred have head nodes and first-level child nodes with the same states, the program will traverse both trees until it encounters a node where the trees are different. This node in the child tree will be randomly chosen from one of the two corresponding nodes in the parent trees.

 

If the two breeding trees do not have the same head node, a random node in one tree will be selected and replaced by a random node from the other tree.

 

Two utility functions are helpful in this process: getRandomNode(IFunction tree) and randomizeDifferentNodes(IFunction func1, IFunction func2).

 

getRandomNode(IFunction tree) picks a random number less than the number of nodes in the tree and traverses the tree, incrementing a counter at each node. When it reaches the node at which counter is the same as the pre-selected random number, it returns that node.

 

Pseudocode for getRandomNode(IFunction tree):

 

int size = number of nodes in tree

//nodeIndex is the index of the node that will be returned

int nodeIndex = random int between 0 and size

//counter keeps track of which node we are processing

int counter = 0;

 

//create a new queue and add the root node to it

functionQ = new queue of function nodes;

functionQ.add(tree);

//while the queue isn't empty, traverse the tree adding child nodes to //the queue. each time a node is

//evaluated, increment the counter. If the counter is the same as the //randomly selected nodeIndex,

//we have reached the node that we want to return.

While(the queue isn’t empty){

//grab the top function from the queue

Function = queue.poll();

 

//increment the counter

counter++;

//check to see if we’ve reached the node we want to return

if(counter == nodeIndex){

return the function.

}

//otherwise add the kids of this function, if it has any

else {

queue.add(function’s children);

}

}//close while

 

RandomizeDifferentNodes(IFunction func1, IFunction func2) takes two functions which should be similar (i.e., have head nodes with the same state) and traverses them both. It creates a new “offspring” function that is a clone of one of the parents. When it finds corresponding nodes between the two functions that are unequal, it replaces that node in the offspring function with the corresponding node from one of the parents at random. This function performs most of the “breeding” for similar functions.

 

Pseudocode for randomizeDifferentNodes(IFunction func1, IFunction func2):

 

//make queues for both functions, and the parent nodes.

Queues func1Q, func2Q, parentsQ

 

//clone the original functions so that the manipulations we’re

//about to perform don’t mess them up

IFunction newGen = clone(func1)

IFunction oldGen = clone(func2)

 

//add the first nodes to the queues.

newQ.add(newGen);

oldQ.add(oldGen);

 

//add a dummy function to the parents queue

parentsQ.add(dummy function with no state)

//while the queues aren't empty

While(newQ and oldQ aren’t empty){

 

//pop the first node in each queue

IFunction newFunc = newQ.poll();

IFunction oldFunc = oldQ.poll();

IFunction parent = newQparents.poll();

//if the two nodes we’re looking at aren’t the same,

//i.e., they don’t have the same state

If(newFunc and oldFunc aren’t the same){

//replace this new node with this old node with 50% //probability

If (a random number is > 50){

Parent.setChild(clone(oldFunc))

}

}

}

 

//return the newGeneration.

return newGen;

 

When two images are bred, there will be a small chance for mutation. Mutation will be accomplished by selecting a random node from the tree and randomly reassigning its state. For instance, a ZeroChildFunction with ConstantState could become a ZeroChildFunction with XState, or a SingleChildFunction with NegateState could become a SingleChildFunction with RoundUpState. This is easily accomplished via the getRandomNode function.

 

Animation

 

When animating between two images, a genetic cross dissolve technique is used. The trees are traversed recursively, and when dissimilar nodes are found, they are interpolated between for the user-selected number of frames.

 

To generate the set of images displayed by the View, the Model uses a technique very similar to the normal image display method. A list of images is created with length equal to the user-specified number of frames in the animation, and for each image in the list, a thread is created that will calculate the RGB values for each pixel in the image. The main difference from the typical image loading technique is that instead of directly evaluating the functions, this thread uses the interpolateFunctions method.

 

InterpolateFunctions(func1, func2, int x, int y, int frame, int totalFrames) is used to interpolate between functions. Func1 and 2 are the functions, x and y are the pixel coordinates (scaled to [-1, 1]), frame is the current frame number of the animation, and totalFrames is the number of total frames in the entire animation. The method works by recursively evaluating each node both functions. If the corresponding nodes from the functions are equal (i.e., have the same state), the value of a recursive call of interpolateFunctions() on the children of those nodes is returned if they have children, and if they do not have children, their value is returned. If the corresponding nodes from the functions are different (have different states), both function nodes are evaluated and an intermediate value is returned based on the position of the “frame” input relative to the number of total frames. The returned value will be

 

(func1.returnValue * (frame/totalFrames)) + (func2.returnValue * ((totalFrames – 1 – frame) / (totalFrames - 1)))

 

Where the “-1”’s are present to account for the fact that “frame” begins counting at 0. The parent nodes of these interpolated nodes will operate on the results of the interpolation rather than their actual child node outputs; this is accomplished using the overloaded version of returnValue in IFunction that takes in vectors and operates on these vectors, treating them as the output of child nodes. The interpolated values are passed in, and the actual child nodes remain unevaluated. In this way many interpolations between 2 functions can be produced without actually modifying either function.

 

Saving/loading

 

Users will have the option of saving images to PNG format or PRP format. The PRP format is just a String representation of a function saved with a .prp extension. If the user tries to load a .PRP file, it will parse the function and create the image. If the image cannot be created, the exception will be caught and the user notified. When the user loads a PNG file from their system, the program will construct a ZeroChildFunction that uses the loaded image to translate XY pixel coordinates into RGB values. This function can then be used to display, breed, and animate the image.

 

Animations can be saved as a series of PNG files. The number of files will correspond to the user-specified number of frames in the animation.

 

Classes

 

Controller

 

PrettyPictures.java

 

This is the controller class for PrettyPictures, containing the main method (which takes no arguments). It creates the View and Model instances and initializes both, creating anonymous inner class adapters for communication between them.

Model

 

Model.java : This class provides all major functionality of the program. It handles history tracking, image caching, image generation, function breeding, function animation, random function generation, and image saving and loading.

 

Function breeding, animation, saving/loading, and random generation are all discussed above in the Function/image breeding, Animation, Saving/loading, and Generating random functions and function trees, respectively.

 

The model uses a method called getImage(IFunction func, int width, int height) to get an image of the specified dimensions from a function. This is used whenever an image is needed, except in animation when interpolation is necessary (as discussed in the Animation section above). When a user requests a zoomed version of an image, this method is used to produce it – the width and height inputs will simply be larger than those of a thumbnail.

 

Since the View doesn’t have access to formulas, the arguments to the breeding and animation methods will be BufferedImages. Therefore the Model needs to be able to look up a function given the image that resulted from that function. This ability is provided by a hashmap<BufferedImage, IFunction> stored in the model.

 

The user has the ability to modify the red, green, and blue saturation of displayed images via the use of sliders in the GUI. These modifications do not affect the underlying functions of the images, rather, the sliders control float values that are simply added into the RGB vector results of function evaluation for each pixel in an image. The model keeps track of the current positions of the sliders in order to correctly set the saturation levels for each generated image.

 

The model maintains a list of all running threads. New threads are generated whenever an image is created from a formula. When a user stops the current generation while it is loading, the view calls the stop() method in the model. This method sets a flag called _stop to true. All threads check this flag at each iteration, so once it is set, the threads will cease whatever they were doing, remove themselves from the list of running threads, and tell the view adapter that the view is no longer busy.

 

IModel2View.java

 

This class is the Model 2 View adapter that allows communication between model and view. It is created anonymously in the PrettyPictures class.

IFunction.java : IFunctions represent compound functions in a binary tree format. A general explanation can be found in the Functions and function representation section above.

DualChildFunction.java : DualChildFunctions represent functions with two child nodes (arguments), such as addition, subtraction, multiplication, and division.

 

IDualChildState.java : A state of a dual child function. This state will represent one of the dual child operations listed above.

 

SingleChildFunction.java : SingleChildFunctions represent functions with one child node (argument), such as negation, rounding up, color space conversion, etc.

 

ISingleChildState.java : A state of a single child function. This state will represent a single argument operation.

 

ZeroChildFunction.java : ZeroChildFunctions represent functions with no child nodes (arguments), such as constants, X, Y, and external images.

 

IZeroChildState.java : A state of a zero child function. This state will simply return a numeric value, whether it be a constant vector, a scaled vector based on the X or Y arguments, or a vector based on the RGB value of the (X,Y) pixel of an external image.

 

Utilities.java : The Utilities class contains useful static methods that are used throughout the Model class, as well as helper methods for breeding and animation. In particular, it contains the methods randomizeDifferentNodes and getRandomNode (which are discussed in the Breeding section above), as well as a method that finds the number of nodes in a function tree by the following method:

Pseudocode for getFunctionSize(IFunction function):

 

int counter = 1;

for all children of function{

counter += getFunctionSize(child function);

}

return counter;

Utilities contains numerous methods for scaling and manipulating function input and output. These include scaling x,y pairs to the [-1,1] range, scaling 3-dimensional vectors to the [-1,1] range, scaling 3-dimensional vectors in the [-1,1] range to the [0,255] range (and vice versa), and translating 3-dimensional RGB value vectors into RGB ints usable by the BufferedImage class (and vice versa).

 

Utilities contains a method loadFunction(String state) that uses dynamic class loading to create a new instance of a function with a given state, which is passed to the method as a String describing the state’s path (such as “model.AddState”). This is used in creating random generations (as discussed in the Generating random functions and function trees section) as well as in function cloning.

 

Utilities contains methods that are used to “clone” functions, creating independent copies of existing functions. These are used in the breeding process to create copies of parent functions that can be modified without mutating the original parent. The two methods that are used in this process are copyFunctionNode(IFunction func), which creates a cloned copy of a single function node (disregarding its child nodes), and cloneFunction(IFunction func) which clones an entire function, children and all.

 

CopyFunctionNode uses the loadFunction method described above. It pulls the “path” String from the state of the input function and uses it to create a new function node with the same state. It then sets any relevant fields in the new state and returns the node.

CloneFunction uses recursive calls to copyFunctionNode in order to copy an entire function.

Pseudocode for cloneFunction(IFunction func):

 

IFunction result = copyFunctionNode(func);

If(func has children){

Result.setChildren(cloneFunction(func’s children));

}

return result;

 

AreSimilar(IFunction func1, IFunction func2) that determines if two functions are similar by comparing the states of their head nodes and first-level child nodes. If these states are the same for both functions, then they are similar and have the same genotype for breeding purposes.

 

Utilities also contains methods for generating random numbers, which are used throughout the model.

 

View

 

View.java

 

The view is a fairly standard swing implementation of a GUI. It handles threading through the use of SwingUtilities.invokeLater(), which hands a runnable instance to the Event-Dispatching-Thread, which is the only thread allowed to modify swing components.

 

The choice of a black GUI was purely a programmer’s aesthetical choice. Had we a bit more time, a color choice could have been implemented that would allow user-end selection of the background color. After all, we aren’t designing user-end commercial database software. It should look different™.

 

IView2Model.java

 

This class is the View 2 Model adapter that will allow communication from View to Model. It is created anonymously in the PrettyPictures class.

 

Analysis

 

Evaluating an image function to display the image consists of a tree traversal of x nodes and returning n pixels, so it should be O(n log x).

Breeding two images should be O(log n) for two similar trees with n nodes. Breeding dissimilar images is O(1) because a node is randomly selected and replaced.

Animating two images is O(log n), as it is comparable to displaying an image repeatedly for a set number of frames.

 

Testing

Unit Tests

·         Unit tests will be implemented for all public methods of the ‘model’ package. Tests will be implemented in the ‘test’ package, class ‘AllTests’.

 

Functional Tests

·         Functional tests will be performed for all ‘view’ components. Examples include:

o        Ensure concurrent loading of images.

o        Ensure button press combinations do not cause deadlock or exceptions to be thrown.

o        Animate between two images and make sure that the animation displays correctly.

o        Check that all UI buttons perform their assigned actions.

·         Breeding will be tested functionally by breeding images and printing out the resulting function, making sure that it was correctly generated from its parent functions.

 

 

Time Table

Task

Estimate

Order

Completed

Actual

Controller

 

 

 

 

PrettyPictures

1 hr

1, jcr2

4/2, jcr2

1 hr

View2Model

1 hr

1, jcr2

4/6 jcr2

1 hr

Model2View

1 hr

1, jcr2

4/6, jcr2

1 hr

Model

 

 

 

 

Defining Functions

4 hr

1, jtb5020

4/2, jtb5020

2 hr

Generating Images

from Functions

5 hr

3, jtb5020

4/14, jtb5020, jcr2

4 hr

Generating Random

Functions

3 hr

3.a, jtb5020/jcr2

4/14, jtb5020/jcr2

4 hr

Breeding Image

Functions

6 hr

4, jtb5020

4/21, jtb5020

8 hr

Parsing User Function

Input (?)

5 hr

7, jtb5020

4/22, jcr2

4 hr

Generating Animations

5 hr

5, jtb5020

4/21, jtb5020

2 hr

Threading Processes

6 hr

2, jtb5020

4/10, jtb5020

2 hr

Image Input/Output

2 hr

6, jtb5020

4/17, jcr2

1 hr

View

 

 

 

 

UI Layout

5 hr

2, jcr2

4/2, jcr2

4 hr

Back/Forward

2 hr

7, jcr2

4/17, jcr2

2 hr

Stop/Refresh

1 hr

6, jcr2

4/17, jcr2

3 hr

History

2 hr

5, jcr2

4/17, jcr2

4 hr

Loading Images

3 hr

3, jcr2

4/8, jcr2

3 hr

Loading Concurrently

2 hr

4, jcr2

4/10, jcr2

2 hr

Loading Animations

4 hr

9, jcr2

-

-

Zoom Page

1 hr

2, jcr2

4/10, jcr2

2 hr

Accept User Input

2 hr

8, jcr2

 

 

Manipulating Selected

Images

2 hr

8.a, jcr2

4/17, jcr2

2 hr

Opening Files

3 hr

8.b, jcr2

4/17, jcr2

2 hr

Mutation/Alteration

Input

2 hr

8.c, jcr2

4/21, jcr2

2 hr

Input Function

Changes

1 hr

8.d, jcr2

4/22, jcr2

1 hr

Allow Saving/

Renaming Pages

1 hr

8.e, jcr2

4/20, jcr2

1 hr

Finding Pretty Icons

1 hr

1, jcr2

4/2, jcr2

1 hr

 

 

 

 

 

Testing and Debugging

 

 

 

 

Sanitizing View Input

Options

5 hr

10, jcr2

4/22, jcr2/jtb5020

6 hr

Spec Update and

Javadoc

3 hr

9, jtb5020 / 11, jcr2

4/22, jcr2/jtb5020

4 hr

 

 

 

 

 

Total

71 hr

 

 

69 hr

 

Function Samples:

 

AbsVal(TilingExternalImage)

 

ATan(TilingExternalImage)

 

Add(TilingExternalImage, X(Constant[-1.7358335r -1.7285873g 0.61650896b]))

 

 

Clip(TilingExternalImage)

ColorPerlinNoise(TilingExternalImage)

 

Constant[-0.21378076r -1.7041718g 0.46798754b]

 

Cos(TilingExternalImage)

 

Divide(TilingExternalImage, X(Constant[-1.7358335r -1.7285873g 0.61650896b]))

 

GrayScalePerlinNoise(TilingExternalImage)

 

LC_To_RGB(TilingExternalImage)

 

Log(TilingExternalImage)

 

Multiply(TilingExternalImage, X(Constant[-1.7358335r -1.7285873g 0.61650896b]))

 

Negate(TilingExternalImage)

 

Pow(TilingExternalImage, 2.0)

 

RGB_To_LC(TilingExternalImage)

 

Round_Down(TilingExternalImage)

 

Round_Up(TilingExternalImage)

 

Sin(TilingExternalImage)

 

Subtract(TilingExternalImage, X(Constant[-1.7358335r -1.7285873g 0.61650896b]))

 

TilingExternalImage

 

Wrap(TilingExternalImage)

 

X(Constant[-1.7358335r -1.7285873g 0.61650896b])

 

Y(Constant[-1.0471748r -1.0145581g -0.17606437b])

 

Cool Pictures

 

 

 

 

 

 

 

 

 

 


Web Hosting · Blog · Guestbooks · Message Forums · Mailing Lists
Easiest Website Builder ever! · Build your own toolbar · Free Talking Character · Email Marketing
powered by a free webtools company bravenet.com