Anson-Book

Here’s the full Spirit Animals PDF: SpiritAnimals_print.pdf

From the Artist’s Note at the beginning of the book:

“Spirit Animals is a computationally generated book of dances, created with Processing 3, an open-source software for creative coding. The dances can be done solo or in groups, with or without music, and are meant for novices and professionals alike. They aim, though do not promise, to be physically possible.

Spirit Animals draws influence from the Fluxus art movement, John Cage’s compositions, Andy Warhol’s Dance Diagrams, and traditional instructional dance diagrams such as the Fox Trot, Tango, and Lindy Hop. The dance names are derived using a random generator combining an adjective and an animal, producing variations on the “Funky Chicken.”

I hope you’ll use Spirit Animals to dance anywhere, at any time, and with anyone. Why walk, when you can dance?”

Spirit Animals marks a new step for me towards creating participatory computational artworks and experiences. I’ve been reading and thinking a lot about the ideas of participation, interaction, and the moment of encounter. I’m very interested in artworks which create situations for people to interact in some physical manner with themselves, the world, and others around them. I’m reading about relational aesthetics, “emancipated spectatorship” and the writings of Claire Bishop, Nicholas Bourriard, Roland Barthes, and Umberto Eco. Conceptually, Spirit Animals is hopefully fun, silly, and has a low barrier to entry for anyone to pick up the book and play. I like that this book/PDF format is easily distributable, and doesn’t require any technology to “perform.” Spirit Animals is also a visual experiment in computationally generated movement patterns. Secretly/not so secretly I have a wild desire to be a choreographer – and have a long history of collaborating with dancers and performers. However, not being a dancer, I’m a bit hopeless at actually creating the choreography, so I enjoy the “computer as collaborator” aspect of the dance creation here. I’m not sure where this goes, but I’m interested in exploring these ideas further.

In terms of the technical aspects of creating this book – let me count the ways….. First of all, this is by far the most complex and longest program I’ve ever written. Still being a beginner with coding and Processing, this was actually a mammoth task. I’ve learned a lot, and feel that the biggest technical skill I worked on here was just graphical – generating the foot patterns and figuring out the layout that made the most sense, and making those pesky curved arrows go where they should. I definitely didn’t work with Basil.js or InDesign but did put some of the text pages together in Illustrator. Rita.js is interesting to me, but will have to be tackled at a later date.

Here are some images from the book:

Here is the code:

// Processing 3.0x code
import processing.pdf.*;

float ax;
float ay;
float bx;
float by;
float cx;
float cy;
float dx;
float dy;

float f;
float g; 

float px;
float py;
float qx;
float qy;

int pageWidth = 72 * 8;
int pageHeight = 72 * 10;

float cellMarginX;
float cellMarginY;

int rightFoot_col = 4;
int rightFoot_row = 7;
int leftFoot_col = 4;
int leftFoot_row = 7;

final int pixelsPerInch = 72;

int nCols = 5;
int nRows = 7;
int nSteps = 7;

float cellSpaceW = pageWidth / nCols;
float cellSpaceH = pageHeight / nRows;

final int gridLineWidth = 3;

IntList usedCells;

PShape leftFoot;
PShape rightFoot;

float footScale = 0.07;
float gridMargin;
float gridWidth;
float gridHeight;
float cellWidth, cellHeight;

float leftFootWidth;
float leftFootHeight;
float rightFootWidth;
float rightFootHeight;

PFont myFont;
PFont myTitles;

int currentCell = (nRows * nCols) /2;

int cellNumber; 

int cellCol = cellNumber % nCols;
int cellRow = cellNumber / nCols;

StringList pageTitles;


//------------------------------


void setup() {
  size(612, 792); // 8.5 x 11  pixelsPerInch * 8.5 and PixelsPerInch * 11

  beginRecord(PDF, "spiritanimals174.pdf");
  //PGraphicsPDF pdf = (PGraphicsPDF) g;

  leftFoot = loadShape("foot_L.svg"); 
  rightFoot = loadShape("foot_R.svg"); 

  leftFootWidth = leftFoot.width * footScale;
  leftFootHeight = leftFoot.height * footScale;
  rightFootWidth = rightFoot.width * footScale;
  rightFootHeight = rightFoot.height * footScale;

  // indent the grid by a half inch all around
  gridMargin = pixelsPerInch * 0.90;

  gridWidth = 612 - (gridMargin * 2);
  gridHeight = 792 - (gridMargin * 2);

  cellWidth = gridWidth / nCols;
  cellHeight = gridHeight / nRows;

  myFont = createFont("Helvetica", 15, true); 
  textFont(myFont); 

  myTitles = createFont ("Helvetica ", 40);
  //textFont(myTitles);

  usedCells = new IntList();

  createPageTitles();

  noLoop();
}


//---------------------------------------------------------
void draw() 
{

  background(255); // white
  drawDanceFloor();


  // dance some steps!
  usedCells.append(currentCell);

  for (int i = 0; i < nSteps; i++) 
  {
    drawLeftFootInCell(currentCell, i+1);

    int nextCell = getPossibleNextCell();
    while (usedCells.hasValue(nextCell)) 
    {
      nextCell = getPossibleNextCell();
    }

    drawLineFromCellToCell(currentCell, nextCell, i);

    currentCell = nextCell;
    usedCells.append(currentCell);

    drawRightFootInCell(currentCell, i+1);   
    nextCell = getPossibleNextCell();
    while (usedCells.hasValue(nextCell)) 
    {
      nextCell = getPossibleNextCell();
    }

    if (i < nSteps - 1) 
    { 
      stroke (0, 100, 255, 100);
      strokeWeight(2); 
      drawLineFromCellToCell(currentCell, nextCell, 1);

      currentCell = nextCell;
      usedCells.append(currentCell);
    }
  }
  textAlign(CENTER);
  fill(0);
  textFont(myTitles);
  text(pageTitles.get(0), 306, 65);
  println(usedCells);

  //textFont(myTitles, 75);
  fill(0);

  endRecord();
}

//---------------------------------------------------------
void drawLeftFootInCell(int cellNumber, int numberToDraw)
{
  int cellCol = cellNumber % nCols;
  int cellRow = cellNumber / nCols;

  float drawLX = (cellCol * cellWidth) + (cellWidth / 2) - (leftFootWidth / 2) + gridMargin;
  float drawLY = (cellRow * cellHeight) +  (cellHeight / 2) -  (leftFootHeight / 2) + gridMargin;

  shape(leftFoot, drawLX, drawLY, leftFootWidth, leftFootHeight);
  fill(0); 
  text (numberToDraw, drawLX - 10, drawLY + 40);
}

//---------------------------------------------------------
void drawRightFootInCell(int cellNumber, int numberToDraw)
{
  int cellCol = cellNumber % nCols;
  int cellRow = cellNumber / nCols;

  float drawRX = (cellCol * cellWidth) + (cellWidth / 2) - (rightFootWidth / 2) + gridMargin;
  float drawRY = (cellRow * cellHeight) +  (cellHeight / 2) -  (rightFootHeight / 2) + gridMargin;

  shape(rightFoot, drawRX, drawRY, leftFootWidth, leftFootHeight);
  fill(0); 
  text (numberToDraw, drawRX - 10, drawRY + 40);
}

//---------------------------------------------------------
void drawLineFromCellToCell(int fromCell, int toCell, int whichStep)
{
  float fromCellX = topOfCellXCoordinate(fromCell); // ax
  float fromCellY = topOfCellYCoordinate(fromCell); // ay
  float toCellX = topOfCellXCoordinate(toCell); // dx
  float toCellY = topOfCellYCoordinate(toCell); //dy

  float fromCellX_qx = lerp(fromCellX, toCellX, 0.3333); // px
  float fromCellY_qy = lerp(fromCellY, toCellY, 0.3333); // py
  float toCellX_px = lerp(fromCellX, toCellX, 0.6666); // qx
  float toCellY_py = lerp(fromCellY, toCellY, 0.6666); // qy

  float tx = toCellX-fromCellX;
  float ty = toCellY-fromCellY;
  float th = sqrt((tx*tx) + (ty*ty));


  f = 0.13; 
  g = 0.11; 
  if (whichStep%2 == 1) {
    f = 0-f;
  }
  if (whichStep%2 == 1) {
    g = 0-g;
  }
  if (th < cellSpaceW) { f = g = 0; } println("Hey: " + whichStep + " " + th + " " + cellSpaceW); ax = fromCellX; ay = fromCellY; bx = fromCellX_qx - f*ty; by = fromCellY_qy + f*tx; cx = toCellX_px - g*ty; cy = toCellY_py + g*tx; dx = toCellX; dy = toCellY; // direction-sensitive offsets to the line coordinates float offsetx = 15; float offsety = 30; if (tx > 0) { // then I'm going from Left to Right
    ax += offsetx; 
    dx -= offsetx;
  } else if (tx < 0) { // then I'm going from right to left ax -= offsetx; dx += offsetx; } else { // tx == 0 if (ty > 0) { // going down
      ay += offsety;
      dy -= offsety;
    } else if (ty < 0) { // going up ay -= offsety; dy += offsety; } } float sepY = 5; if (ty > 0) { // going down
    ay += sepY;
    dy -= sepY;
  } else if (ty < 0) { // going up ay -= sepY; dy += sepY; } boolean bDrawColoredEllipses = false; if (bDrawColoredEllipses) { float eR = 7; noStroke(); //stroke(0,0,0); strokeWeight(1); fill(255, 0, 0); // A = red ellipse(ax, ay, eR, eR); fill(0, 255, 0); // B = green ellipse(bx, by, eR, eR); fill(0, 0, 255); // C = blue ellipse(cx, cy, eR, eR); fill(255, 255, 0); // D = yellow ellipse(dx, dy, eR, eR); } /* fill(255, 200, 255); // Q = light pink ellipse(fromCellX_qx, fromCellY_qy, 10, 10); fill(100, 100, 255); // P = dark purple ellipse(toCellX_px, toCellY_py, 10, 10); */ // draw the curve noFill(); stroke(255); // white strokeWeight(4); // thick beginShape(); curveVertex(ax, ay); curveVertex(ax, ay); curveVertex(bx, by); curveVertex(cx, cy); curveVertex(dx, dy); curveVertex(dx, dy); endShape(); fill(0); noStroke(); ellipse(ax, ay, 5, 5); noFill(); stroke(0, 0, 0); // black strokeWeight(1); // thin beginShape(); curveVertex(ax, ay); curveVertex(ax, ay); curveVertex(bx, by); curveVertex(cx, cy); curveVertex(dx, dy); curveVertex(dx, dy); endShape(); drawArrowhead(cx, cy, dx, dy); //line(fromCellX, fromCellY, toCellX, toCellY); } //--------------------------------------------------------- float topOfCellXCoordinate (int cellNumber) { int cellCol = cellNumber % nCols; return (cellCol * cellWidth) + (cellWidth / 2) + gridMargin; } //--------------------------------------------------------- float topOfCellYCoordinate (int cellNumber) { int cellRow = cellNumber / nCols; return ((cellRow * cellHeight) + (cellHeight / 2)) + gridMargin; } //--------------------------------------------------------- int getPossibleNextCell() { // both moveX and moveY cannot be zero - int cellCol = currentCell % nCols; int cellRow = currentCell / nCols; int moveX = (round(random(-2, 2))); while ((moveX + cellCol >= nCols) | (moveX + cellCol < 0)) { moveX = (round(random(-2, 2))); } int moveY = (round(random(-2, 2))); while ((moveY + cellRow >= nRows -1) | (moveY + cellRow < 0)) 
  {
    moveY= (round(random(-2, 2)));
  }

  if (moveX == 0 && moveY == 0) 
  {
    // arbitrary
    if (cellCol <= 5) {
      moveX = 3;
    } else {
      moveX -= 2;
    }
  }

  cellCol = cellCol + moveX;
  cellRow = cellRow + moveY;

  return cellRow * nCols + cellCol;
}

//---------------------------------------------------------

void drawArrowhead(float fromCellX, float fromCellY, float fromCellX_halfway, float fromCellY_halfway) {
  float hx = fromCellX_halfway - fromCellX; 
  float hy = fromCellY_halfway - fromCellY; 

  float len = dist(fromCellX, fromCellY, fromCellX_halfway, fromCellY_halfway);
  float dh = sqrt(hx*hx + hy*hy); //same!
  float ang = atan2 (hy, hx); // hY first!! just is. 
  if (abs(hx) < 0.0001) {
    ang = 0-ang;
  }

  float arrowSize = constrain(len/4, 10, 20); 
  pushMatrix(); 
  translate(fromCellX_halfway, fromCellY_halfway);
  rotate(ang + radians(70)); 
  line(0, 0, 0, arrowSize); 
  popMatrix(); 
  pushMatrix(); 
  translate(fromCellX_halfway, fromCellY_halfway);
  rotate(ang - radians(70)); 
  line(0, 0, 0, -arrowSize); 
  popMatrix();
}



//--------------------------------------------------------

void drawDanceFloor()
{
  stroke(255, 255, 255, 30); 
  strokeWeight(gridLineWidth); 

  // draw horizontal lines
  // number of lines  draw are (nRows + 1)

  // set initial draw point
  float drawX = gridMargin;
  float drawY = gridMargin;

  println(cellHeight);

  for (int row = 0; row <= (nRows + 1); row++) {
    line(drawX, drawY, drawX + gridWidth, drawY);
    // bump drawY down to next row
    drawY += cellHeight;
  }

  // draw vertical lines

  // set initial draw point
  drawX = gridMargin;
  drawY = gridMargin;

  for (int col = 0; col <= (nCols + 1); col++) {
    line(drawX, drawY, drawX, drawY + gridHeight);
    // bump drawY down to next row
    drawX += cellWidth;
  }
}

// animals and adjectives collected at random from https://github.com/dariusk/corpora/ with 7 numbers generated by random.org integer generator
void createPageTitles()
{
  StringList myAnimals = new StringList(new String[]{"cougar", "elk", "skunk", "tapir", "whale", "wolf", "newt"});
  StringList myAdjectives = new StringList(new String[]{"vaulting", "sparing", "one-eyed", "gleaming", "doctrinal", "designing", "brimstone"});

  pageTitles = new StringList();
  for (int k=0; k < nSteps; k++)
  {
    String animal;
    String adjective;
    if (myAnimals.size() == 1)
    {
      animal = myAnimals.get(0);
      adjective = myAdjectives.get(0);
    } else
    {
      int randomAnimal = (int)random(myAnimals.size());
      animal = myAnimals.get(randomAnimal);
      myAnimals.remove(randomAnimal);
      int randomAdjective = (int)random(myAdjectives.size());
      adjective = myAdjectives.get(randomAdjective);
      myAdjectives.remove(randomAdjective);
    }

    pageTitles.append(adjective + " " + animal);
  }

  println(pageTitles);
}