NakeDraw myNake;
void setup() {
size(801,801);
background(255);
stroke(0);
myNake = new NakeDraw();
}
int drawOnce = 0;
void draw() {
if (drawOnce == 0)
{
myNake.draw();
drawOnce = 1;
}
}
/**
* Class NakeDraw - Implements an algorithm to create an image inspired by Frieder Nake's work publshed 1956-57
* called Nr. 2 ("Klee").
* The image is composed of several repeated and random elements
* 1. Horizontal lines that "break" in the middle and change direction. They never intersect each other.
* 2. These lines create quadrilaterals between them.
* 3. Each quadrilateral can either be empty, have a random set of vertical+parallel lines or have a set of triangles
*
* Nir Rachmel, Interactive art and computational design, Jan 2012 (C)
* A link that helped analyze the original image: http://dada.compart-bremen.de/node/2875.
*/
class NakeDraw {
int numOfRows; // number of rows on the canvas
int numOfColumns; // number of columns on the canvas
PVector[][] verticesArr; // array that holds all the vertices that define the skeleton of the horizontal lines
float[] horizontalSpaces; // array to hold the distance that defines a horizontal section
float baseBandWidth; // basic width between two consequtive horizontal lines (subject to noise)
float verticalNoise; // vertical noise limit
float smallNoise; //
// C'tor
NakeDraw() {
numOfRows = 11; // including both upper and lower frame borders
numOfColumns = 9;
verticesArr = new PVector[numOfRows][numOfColumns];
horizontalSpaces = new float[] {1/6f, 1/6f, 1/12f, 1/6f, 1/12f, 1/12f, 1/6f, 1/12f};
// Basic space between two horizontal lines
baseBandWidth = height / 10;
verticalNoise = baseBandWidth * 0.05;
smallNoise = baseBandWidth / 8;
// Initialize arrray
for (int i=0 ; i < numOfRows ; i = i+1)
{
for (int j=0 ; j < numOfColumns ; j = j+1)
{
verticesArr[i][j] = new PVector(0,0);
}
}
}
// performs the drawing of the image. Using random numbers, the decision is made
// for each quadrilateral if to leave it blank, fill it with horizontal lines or vertical lines.
void draw()
{
// Draw the horizontal rows
drawRows();
// go over each quadrilateral and randomly draw lines / triangles / nothing
int numOfBands = numOfRows -1;
for (int i=0 ; i < numOfBands ; i=i+1)
{
for (int j=0 ; j < numOfColumns-1 ; j=j+1)
{
int pattern = int(random(0,3));
if (pattern == 0)
{
drawVerticalLinesPattern(i,j);
}
if (pattern == 1)
{
drawTriangles(i,j);
}
}
}
// Circles are drawn on the image without relation to the other shapes.
drawCircles();
}
/**
* Draw the horiztonal rows.
*/
void drawRows()
{
// initialize top and bottom row (which are straight lines, part of the frame)
verticesArr[0][0].set(0f,0f,0f);
verticesArr[numOfRows-1][0].set(0f,height,0f);
for (int j=1 ; j < numOfColumns ; j=j+1)
{
float xVal = verticesArr[0][j-1].x + horizontalSpaces[j-1] * width;
verticesArr[0][j].set(xVal, 0f, 0f);
verticesArr[numOfRows-1][j].set(xVal,height,0f);
}
// Set the initial vertices on the left border
for (int i=1 ; i<numOfRows-1 ; i=i+1)
{
float yVal = 0;
yVal = verticesArr[i-1][0].y + baseBandWidth + random(-verticalNoise, verticalNoise);
verticesArr[i][0].set(0f,yVal,0f);
}
// start from each randomly generated vertex and draw a random horizontal line that stretches
// all the way to the right border of the window. This line is not straight, and randomly changes its direction.
for (int i=1 ; i < numOfRows-1 ; i = i+1) // rows
{
for (int j=1 ; j < numOfColumns ; j = j+1) // columns (first is already set)
{
float xVal = verticesArr[i][j-1].x + horizontalSpaces[j-1] * width;
float yVal = verticesArr[i][j-1].y + random(-smallNoise, smallNoise);
verticesArr[i][j].set(xVal, yVal, 0f);
line(verticesArr[i][j-1].x,verticesArr[i][j-1].y,
xVal, yVal);
}
}
}
/**
* A helper function that calculates the y coordinates according to a given x coordinate, row and column
* (that uniquely identifies a straight line on the screen)
*/
float calculateY(int rowNum, int colNum, float xCoordinate, boolean isTop)
{
float yCoordinate = 0;
if (isTop)
{
if (rowNum == 0)
{
yCoordinate = 0;
}
else
{
// y coordinate needs to be calculated for both top line and bottom.
float slopeTop = (verticesArr[rowNum][colNum+1].y - verticesArr[rowNum][colNum].y) /
(verticesArr[rowNum][colNum+1].x - verticesArr[rowNum][colNum].x );
float constValTop = verticesArr[rowNum][colNum].y - slopeTop * verticesArr[rowNum][colNum].x;
// now we have the equation, we can find the top coordinates for the vertical line
yCoordinate = slopeTop * xCoordinate + constValTop;
}
}
if (!isTop)
{
if (rowNum == (numOfRows - 1))
{
yCoordinate = height;
}
else
{
float slopeBottom = (verticesArr[rowNum+1][colNum+1].y - verticesArr[rowNum+1][colNum].y) /
(verticesArr[rowNum+1][colNum+1].x - verticesArr[rowNum+1][colNum].x );
float constValBottom = verticesArr[rowNum+1][colNum].y - slopeBottom * verticesArr[rowNum+1][colNum].x;
yCoordinate = slopeBottom * xCoordinate + constValBottom;
}
}
return yCoordinate;
}
/**
* Draws the random group of vertical lines as constrained by a single quadrilateral.
* Will draw up to 15 lines.
*/
void drawVerticalLinesPattern(int rowNum, int colNum)
{
/* Draw vertical lines */
int numOfLines = int(random(1, 15));
for (int i=0 ; i < numOfLines ; i=i+1)
{
// x coordinate is just a random x between the two adjacent vertices
println("rowNum is " + rowNum + " colNum is " + colNum);
float xCoordinate = random(verticesArr[rowNum][colNum].x, verticesArr[rowNum][colNum+1].x);
float yCoordinateTop = calculateY(rowNum, colNum, xCoordinate, true);
float yCoordinateBottom = calculateY(rowNum, colNum, xCoordinate, false);
line(xCoordinate, yCoordinateTop, xCoordinate, yCoordinateBottom);
}
}
/**
* Draws the random group of triangles as contrained by a single quadrilateral.
* Will draw up to 15 triangles.
**/
void drawTriangles(int rowNum, int colNum)
{
int numOfTriangles = int(random(1,15));
for (int k=0 ; k < numOfTriangles ; k = k+1)
{
// randomly select 3 x's
float x1 = random(verticesArr[rowNum][colNum].x, verticesArr[rowNum][colNum+1].x);
float x2 = random(verticesArr[rowNum][colNum].x, verticesArr[rowNum][colNum+1].x);
float x3 = random(verticesArr[rowNum][colNum].x, verticesArr[rowNum][colNum+1].x);
// x1 will always be on the top line, x2+x3 will be on the bottom line
float y1 = calculateY(rowNum, colNum, x1,true);
float y2 = calculateY(rowNum, colNum, x2, false);
float y3 = calculateY(rowNum, colNum, x3, false );
line(x1,y1,x2,y2);
line(x1,y1,x3,y3);
}
}
/**
* Draws random circles on the screen (up to 10). Radius and locations are random.
* Locations are calculated such as the circle is always completely visible within the given window frame.
**/
void drawCircles()
{
int numOfCircles = int(random(1,11));
noFill();
for (int i=0 ; i < numOfCircles ; i=i+1)
{
float circleRadius = random(1, baseBandWidth);
float xCoordinates = random(circleRadius,width-circleRadius); // we want to make sure the circle can be fit entirely on our canvas
float yCoordinates = random(circleRadius,height-circleRadius); // we want to make sure the circle can be fit entirely on our canvas
ellipse(xCoordinates, yCoordinates, circleRadius, circleRadius);
}
}
} |