Using Perlin noise values for setting a color or x-position was easy. If, say, the x-position for an ellipse ranges from between 0 and width all I need to do is multiply the result of the noise function (which outputs a range between 0 and 1) by width.
float x = width * noise(t);
ellipse(x, 100,20,20);
This range conversion is known as mapping. I mapped a Perlin noise value between 0 and 1 to an x-position between 0 and width. This sort of conversion comes up all the time in programming. Perhaps you want to map the mouse x-position (Ranging between 0 and width) to a color value (ranging between 0 and 255). The math is a bit more complex but manageable.
float r = 255.0 * mouseX / width; //dividing mouseX by width results in a value between 0 and 1 which is then multiplied by 255.
fill(r,0,0);
Now let's consider a more complex scenario. Let's say you are reading values from a sensor that range between 65 and 324. And you want to map those values to a color range between 0 and 255. Now things are getting trickier. Fortunately, Processing includes a map() function that handles the math for converting values from one range to another. map() expects four arguments as listed below:
In this scenario I just described, the value is the sensor reading. The current min and max is the sensor's range 65 and 324. The new min and max is the range fill() expects: 0 and 255.
float r = map(sensor, 65, 324, 0, 255);
fill(r,0,0);
Using min and max to describe the new range isn't exactly accurate. map() will happily invert the relationship as well. If you wanted the shape to appear red when the sensor value is low and black when it is high, you can simply swap the placement of 0 and 255.
float r = map(sensor, 65, 324, 255, 0);
fill(r,0,0);
Following is an example that demonstrates the map() function. Here is the red and blue values of the background are tied to the mouse's x and y position.
void setup() {
size(640,360);
}
void draw() {
float r = map(mouseX, 0, width, 0, 255);
float b = map(mouseY, 0, height,255, 0); //note how the mapping is inverted. the background is mostly blue when the mouse is at the top
background(r, 0, b);
}
// another way of looking at map is map(x, xMin, xMax, zMin, zMax);
Sohcahtoa. Strangely enough, this seemingly nonsense word, Sohcahtoa, is the foundation for a lot of computer graphics work. Any time you need to calculate an angle, determine the distance between points, deal with circles, arcs, lines, and so on, you will find that a basic understanding of trigonometry is essential.
Trigonometry is the study of the relationships between the sides and angles of triangles and Sohcahtoa is a mnemonic device for remembering the definitions of the trigonometric functions, sine, cosine, and tangent.
Any time you display a shape in Processing, you have to specify a pixel location, given as (x,y) coordinates. These coordinates are known as Cartesian coordinates, named for the French mathematician Rene Descartes, who developed the ideas behind Cartesian space.
Another useful coordinate system, known as polar coordinates, describes a point in space as an angle of rotation around the origin and a radius from the origin. You can't use polar coordinates as arguments to a function in Processing. However, the trigonometric formulas allow you to convert those coordinates to Cartesian, which can then be used to draw a shape.
For example, assuming a radius r and an angle theta, I can calculate x and y using the above formula. The functions for sine and cosine in Processing are sin() and cos(). They each take one argument, a floating point angle measured in radians.
float r = 75;
float theta = PI/4; //you could also say float theta = radians(45);
float x = r * cos(theta);
float y = r * sin(theta);
This type of conversion can be useful in certain applications. For example, how would you move a shape along a circular path using Cartesian coordinates? It would be really hard. Using polar coordinates, easy peasy. Simply increment the angle!
Here is how it's done with global variables r and theta.
// a polar coordinate
float r = 75;
float theta = 0;
void setup() {
size(500,500);
background(255);
}
void draw() {
float x = r * cos(theta); //polar coordinates r and theta are converted to cartesian x,y, for use in an ellipse
float y = r * sin(theta);
noStroke();
fill(0);
ellipse(x + width/2, y +height/2, 16, 16); //draw the ellipse at the cartesian x,y with an offset for the center of the window
theta += 0.01; //increment the angle
}
Here is a fun example showing how the sin cycles from -1 to 1 as the angle increases. The new value is used to set the background color of the window.
float angle = 0.0;
void draw() {
float sinval = sin(angle);
println(sinval);
float gray = map(sinval, -1, 1, 0, 255);
background(gray);
angle += 0.1;
}
float angle = 0.0;
float offset = 60;
float scalar = 40;
float speed = 0.05;
void setup() {
size(240,120);
smooth();
}
void draw() {
background(0);
float y1 = offset + sin(angle) * scalar;
float y2 = offset + sin(angle + 0.4) * scalar;
float y3 = offset + sin(angle + 0.8) * scalar;
ellipse(80, y1,40,40);
ellipse(120, y2, 50,50);
ellipse(160, y3, 60,60);
angle += speed;
}
When sin() and cos() are used together, they can produce circular motion as you have already seen. The cos() values provide the x-coordinates and the sin() values the y-coordinates. Both are multiplied by a variable name scalar to change the radius of the movement and summed with an offset value to set the center of the circular motion.
float angle = 0.0;
float offset = 60;
float scalar = 30;
float speed = 0.05;
void setup() {
size(200,200);
smooth();
}
void draw() {
float x = offset + cos(angle) * scalar;
float y = offset + sin(angle) * scalar;
ellipse(x, y, 40,40);
angle += speed;
}
float angle = 0.0;
float offset = 60;
float scalar = 2;
float speed = 0.05;
void setup() {
size(200,200);
fill(0,0,255);
smooth();
}
void draw() {
float x = offset + cos(angle) * scalar;
float y = offset + sin(angle) * scalar;
ellipse(x, y, 2,2);
angle += speed;
scalar +=speed;
}
try it out!