Generative Drawings

Quin Kennedy


Based on: Generative Drawings by Manfred Mohr, 1976

Category: direct


Description:

This sketch does not run in the browser.






/* 
Part of the ReCode Project (http://recodeproject.com)
Based on "Generative Drawings" by Manfred Mohr
Originally published in "Computer Graphics and Art" v1n4, 1976
Copyright (c) 2012 Quin Kennedy - OSI/MIT license (http://recodeproject/license).
*/


static final int cellWidth = 30;
static final int boxSize = 7;
static int numEdges = 9;
static final int cubeEdges = 12;
static final int numUnique = factorial(cubeEdges)/(factorial(numEdges)*factorial(cubeEdges-numEdges));
static final int numCellsWide = ceil(sqrt(numUnique));
static final int gutterWidth = 10;
static final int canvasWidth = numCellsWide*cellWidth+gutterWidth*2;
static final boolean showMirror = true;
static final boolean closerMatch = true;

public static final int factorial(int n){
  int output = 1;
  for(;n>0; n--){
    output *= n;
  }
  return output;
}

void setup(){
  size(canvasWidth * (showMirror ? 2 : 1), canvasWidth, P3D);
  noLoop();
}

void draw(){
  background(0);
  noFill();
  stroke(255);
  strokeWeight(1.5);
  ortho();
  drawGrid();
  drawSet();
  if (showMirror){
    translate(width/2, 0, 0);
    drawGrid();
    numEdges = cubeEdges-numEdges;
    drawSet();
  }
}

void drawSet(){
  pushMatrix();
  int currCubeEdges = (closerMatch ? initCubeFlags() : 0);
  float rX = PI/3;//random(TWO_PI);
  float rY = 0;//random(TWO_PI);
  float rZ = PI*2/3;//random(TWO_PI);
  translate(gutterWidth+cellWidth/2., numCellsWide*cellWidth+gutterWidth-cellWidth/2., 0);
  for(int i = 0; i < numCellsWide; i++){
    pushMatrix();
    for(int j = 0; j < numCellsWide; j++){
      pushMatrix();
      rotateX(rX);
      rotateY(rY);
      rotateZ(rZ);
      scale(boxSize);
      if (closerMatch){
        drawCube(currCubeEdges);
        currCubeEdges = getNextCube(currCubeEdges);
      } else {
        for(;currCubeEdges <= 0xfff; currCubeEdges++){
          if (numFlags(currCubeEdges) == numEdges){
            drawCube(currCubeEdges);
            currCubeEdges++;
            break;
          }
        }
      }
      popMatrix();
      translate(0, -cellWidth, 0);
    }
    popMatrix();
    translate(cellWidth, 0, 0);
  }
  popMatrix();
}

int numFlags(int f){
  int output = 0;
  while(f > 0){
    if ((f & 1) == 1){
      output++;
    }
    f >>= 1;
  }
  return output;
}

void drawGrid(){
  int currX = gutterWidth;
  for(int i = 0; i <= numCellsWide; i++, currX += cellWidth){
      line(currX, gutterWidth, currX, numCellsWide*cellWidth+gutterWidth);
      line(gutterWidth, currX, numCellsWide*cellWidth+gutterWidth, currX);
  }
}

int initCubeFlags(){
  int firstFlags = 0;
  for(int i = 0; i < numEdges; i++){
    firstFlags <<= 1;
    firstFlags++;
  }
  return firstFlags;
}

int getNextCube(int sideFlags){
  return incrementFlags(sideFlags, cubeEdges-1);
}

int incrementFlags(int sideFlags, int lastValidIndex){
  int myMask = 1;
  int bitMask = 1;
  for(int i = 1; i <= lastValidIndex; i++){
    bitMask <<= 1;
    myMask <<= 1;
    myMask++;
  }
  if ((sideFlags & bitMask) > 1){
    //if there are no flags below us, we are done, return 0;
    if ((sideFlags & (myMask >> 1)) == 0){
      return 0;
    }
    //move the flags below us
    int nextFlags = incrementFlags(sideFlags, lastValidIndex-1);
    //if we receive 0, we are done with the whole series
    if (nextFlags == 0){
      return 0;
    }
    //otherwise reset our flag to directly after theirs
    while(bitMask > 0 && (nextFlags & (bitMask >> 1)) == 0){
      bitMask >>= 1;
    }
    return (nextFlags | bitMask);
  } else {
    //move this flag up by one position
    //find where the flag is
    while(bitMask > 0 && (sideFlags & (bitMask >> 1)) == 0){
      bitMask >>= 1;
    }
    //mask my section, remove the current flag and add the new flag
    return (((sideFlags & myMask) ^ (bitMask >> 1)) | bitMask);
  }
}

void drawCube(int sideFlags){
  if (closerMatch){
    if ((sideFlags & 0x1) != 0){
      line(1, -1, -1, 1, 1, -1);
    }
    if ((sideFlags & 0x2) != 0){
      line(1, 1, 1, 1, -1, 1);
    }
    if ((sideFlags & 0x4) != 0){
      line(-1, -1, 1, -1, 1, 1);
    }
    if ((sideFlags & 0x8) != 0){
      line(-1, -1, -1, 1, -1, -1);
    }
    if ((sideFlags & 0x10) != 0){
      line(1, -1, -1, 1, -1, 1);
    }
    if ((sideFlags & 0x20) != 0){
      line(-1, -1, 1, 1, -1, 1);
    }
    if ((sideFlags & 0x40) != 0){
      line(-1, -1, -1, -1, -1, 1);
    }
    if ((sideFlags & 0x80) != 0){
      line(-1, -1, -1, -1, 1, -1);
    }
    if ((sideFlags & 0x100) != 0){
      line(-1, 1, -1, 1, 1, -1);
    }
    if ((sideFlags & 0x200) != 0){
      line(1, 1, 1, 1, 1, -1);
    }
    if ((sideFlags & 0x400) != 0){
      line(1, 1, 1, -1, 1, 1);
    }
    if ((sideFlags & 0x800) != 0){
      line(-1, 1, -1, -1, 1, 1);
    }
  } else {
    if ((sideFlags & 0x1) != 0){
      line(-1, -1, -1, -1, -1, 1);
    }
    if ((sideFlags & 0x2) != 0){
      line(-1, -1, -1, -1, 1, -1);
    }
    if ((sideFlags & 0x4) != 0){
      line(-1, -1, -1, 1, -1, -1);
    }
    if ((sideFlags & 0x8) != 0){
      line(1, 1, 1, 1, 1, -1);
    }
    if ((sideFlags & 0x10) != 0){
      line(1, 1, 1, 1, -1, 1);
    }
    if ((sideFlags & 0x20) != 0){
      line(1, 1, 1, -1, 1, 1);
    }
    if ((sideFlags & 0x40) != 0){
      line(-1, -1, 1, 1, -1, 1);
    }
    if ((sideFlags & 0x80) != 0){
      line(-1, -1, 1, -1, 1, 1);
    }
    if ((sideFlags & 0x100) != 0){
      line(-1, 1, -1, 1, 1, -1);
    }
    if ((sideFlags & 0x200) != 0){
      line(-1, 1, -1, -1, 1, 1);
    }
    if ((sideFlags & 0x400) != 0){
      line(1, -1, -1, 1, 1, -1);
    }
    if ((sideFlags & 0x800) != 0){
      line(1, -1, -1, 1, -1, 1);
    }
  }
}