Nir Rachmel | 01-20-2012

by nir @ 10:34 pm 20 January 2012

Following is an image created by an algorithm inspired by Nake’s work from 1965.

 
Following is the code that generates an image following the same concepts as Nake's pioneering work in <a title="digital art" href="http://dam.org/artists/phase-one/frieder-nake" target="_blank">digital art</a> using a plotter.
 
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 &lt; numOfRows ; i = i+1)
    {
      for (int j=0 ; j &lt; 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 &lt; numOfBands ; i=i+1)
    {
       for (int j=0 ; j &lt; 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 &lt; 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&lt;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 &lt; numOfRows-1 ; i = i+1) // rows
    {
      for (int j=1 ; j &lt; 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 &lt; 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 &lt; 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 &lt; 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);
    }
  }
 
}

This work is licensed under a Creative Commons Attribution-Noncommercial-Share Alike 3.0 Unported License.
(c) 2023 Interactive Art and Computational Design, Spring 2012 | powered by WordPress with Barecity