The purpose of these laboratories is the implementation some of the algorithms discussed during the lectures. Ideally, we would implement everything, but due to time constraints, we will consider only the most important topics.
The first couple of exercises are going to be based on programming and the language of choice is going to be JavaScript. For drawing on the screen, we will use a very convenient library p5.js, which is an extension of the graphical programming language known as Processing. The main goal for these laboratories is to learn the algorithms, so a language was chosen that is both simplest and easiest to setup and use on any computer.
If you prefer to work in your own code editor or an IDE you are allowed to do so. For everyone else, we recommend working in a web environment specifically designed for JS programming, eg:
PLEASE NOTE! You should save your progress during the labs at all times. Some of the above solutions allow saving of your programs "in the cloud" (you usually have to register/login), but otherwise you should definitely copy the contents of your scripts into some safe space (ie. a file on your personal hard drive). Having a backup copy of all your previous labs will help with resolving any issues you may have with the final grade at the end of the semester.
After opening one of the above pages, you will probably notice some code already written inside a text field. You can delete that and leave an empty page, to start with. In some of the websites, you will have at your disposal several fields you can edit. For most of the exercises it will be easiest if you primarily use the HTML field. To check how it works, enter the following code in the HTML field:
<script type="text/javascript">console.log('Hello world!');</script>
By clicking Run you will see the result of your program. If the page doesn't contain a console, you can press F12 on the keyboard to view the developer console built into your browser.
To start drawing, we first have to load our graphics library. Erase the previous code and enter the following into the HTML field:
<script src="//cdnjs.cloudflare.com/ajax/libs/p5.js/0.5.7/p5.js"></script>
Right beneath that add the following two empty functions:
<script>
function setup() {
}
function draw() {
}
</script>
The first function called setup is executed only once, right at the start of the programs. Its purpose is to configure the environment and initialize global variables. The second function draw is activated whenever the library wants to draw something. If all we want to draw is a static image, this function will also be executed once, but if we make an animation, it will be executed multiple times per second.
The first thing we need to do in the setup function is to define the size of the canvas (drawing area):
createCanvas(800,600);
This command creates a special HTML element on the page, which is used for drawing operations. Try to resize the panels of the programming environment in order to fully observe the canvas above.
The second issue we need to decide is whether we want to draw a single image or make an animation. For images, we need to run the following command, which will cause a single execution of the draw function:
noLoop();
For animations, we can use the following command, which will cause the draw method to execute 25 times each second:
frameRate(25);
For the majority of this exercise, we will use the former method, ie. noLoop.
When it comes to drawing, the first thing we usually need to do is to erase the whole image and set the background to a specific color:
background(100);
This command can take as arguments both a single value, which defines a color in shades of gray, or three values, which define a color as a mix of red, green and blue components.
To display a simple line of text, we can use the following command:
text("Hello world!",20,20);
Where the first argument is the text we wish to display and the following two arguments are the X and Y coordinates of its location on the screen (you can test this by clicking Run).
To change the font and its size, you can add the following command, before the one above:
textFont("courier", 24);
Now we have the basics of how to use the library, and you can refer to the documentation to see exactly what you can do and how:
But let's change the topic for a minute
Even though the p5 library includes many drawing algorithms implementations, during these labs we will deliberately avoid them. Most of the exercises will be performed directly on the individual pixels of the screen (of our canvas).
Erase the contents of the draw function from the above experiments and leave only the background being set to black (value 0). In order to change the color of individual pixels, we first need to know how to iterate through them using the for loop. The code below will iterate through all the coordinate values of the canvas (the width and height variables are provided by the library):
for(y=0; y<height; y++)
for(x=0; x<width; x++) {
}
The inner loop will iterate over the individual pixels from left to right, and the outer loop will go through the individual rows, from top to bottom. To set a value (color) of the pixel, we use the following command:
set(x, y, color(100));
First two arguments are obviously the X and Y coordinates from the loop, but the third can take on various types of values: a number representing a greyscale value, an array of 4 numbers, representing a color pixel using its R,G,B and A values, an image object or simply a color object. The latter can be retrieved using the convenient color method as displayed above. This method can also accept either one gray scale or 3 RGB value arguments.
Finally, in order to refresh the changes to the pixel buffer on the screen, we need to execute the following command (underneath and outside of the for loop):
updatePixels();
The draw function can sometimes fail to run completely. This is because the library has a built in watchdog timer that terminates the function if it is too slow. This is especially important if we are trying to make animations. To disable this functionality (and make sure the function always completely finishes drawing) the following comment has to be added right at the start of the function (after the initial curly bracket):
//noprotect
To receive the full points fro this task, use the above described method to fill the screen with a magenta color of any "intensity".
Before you start doing this task, make sure you have saved the previous task somewhere and/or open a new tab with the same page and copy the necessary code from the previous solution.
The goal of this task is to draw a grayscale gradient, running smoothly from left to right, start with black and ending in white.
The task is easily accomplished by assigning the value of the X coordinate as the grayscale color of the pixel. This will however work only partially. The gradient will work for a short time (on the left side of the image), but the whole right side will be completely white.
This is caused by the fact that the canvas is 800 pixels wide, however we only have 256 different shades of gray. After drawing the final white pixel (at coordinate 255), the latter colors will be clipped to the maximum allowable value of 255. The same would be true for values below 0 (if we attempted to draw them).
To correct this problem we need to change the value being set in the color function in such a way in order to:
Computing such a value is simple:
After you manage to draw the above gradient, save the solution and modify it to receive a more complex color gradient, as shown below. This consists of two components:
The first point can be solved in a similar manner as above, but the value of the blue component should be computed from the sum of X and Y coordinates normalized to the sum of width and height of the canvas. The red and green components can be initially set to 0.
The second point is a bit more complicated:
d=sqrt(dx*dx+dy*dy)
This value should be entered into the R and G channels in such a way to receive the image above. One of the value has to increase from 0 to 255 with increasing d, while the other has to decrease from 255 to 0 accordingly.
Before doing this task, make sure to save the previous.
For this task, reproduce the image below by manipulating pixels on the screen. You can use only the methods described above and the random function.
random(10,20);
floor(random(10,20));
for(y=50,w=0; y<200; y++,w+=2)
Draw the Sierpinski's triangle as presented here:
The iterative algorithm giving the above solution is as follows:
The above algorithm generates an image of the Sierpinski's triangle between and 3 points on the screen in an iterative manner. The more iteration you use to run the algorithm, the better the final result. The algorithm works because the mathematical definition of the Sierpinski's triangle fractal says that any point belonging to the set is exactly on the midpoint of a line connecting a corner of the triangle and some other point belonging to this set.
If you want, you can try and animate the above image by changing the location of the triangle corners in every frame of the animation. You also need to change the noLoop() command in the setup function to a frameRate(25) command. Then you can change the corner coordinates (x1/y1, x2/y2, x3/y3) by adding some random vectors (dx1/dy1, dx2/dy2, dx3/dy3) to them at the start of the draw function. You can also implement bouncing of the corners by inverting these vectors if the value if the corner pixel leaves the canvas area (as in a simple bouncing ball animation). This animation will probably not run very smoothly. This is because instead of using the set command, we should be directly manipulating the pixels array, as described on this page.