breep-book

URL  Link to to Zip file:

https://drive.google.com/open?id=1mXvIeBIjU3qrf3-heWDO1dSfpWrZdWjk

Title: The Hands of Gutenberg

Chapter Description: A series of sonnets with first lines containing the word 'hand' generated from the Gutenberg Poetry corpus overlaid on images of multispectral palmprints. (EDIT: I later notices the that the hand lines have moved down the sonnet as the poems progress)

Using the Gutenberg Poetry corpus as my starting point, I wanted to explore ways to generate or find Iambic pentameters. The primary basis for this was that I wanted my final poems to make some semblance of rhyming sense, using the stress structure of the Iambic pentameter to act as a platform in assisting this.

Taking the some 3 millions lines of poetry in the corpus, I created two programs: One to filter out all the Iambic Pentameters that had the word 'Hand' in it (taking primary inspiration for this from a dataset of handprint images that I wanted to use), and another to filter out all the Iambic Pentameters. I discovered that there were only 18 lines that possessed the word hand, nowhere near enough to use to generate all my poems as I initially intended. Instead, I decided to use these lines as the starting lines for 18 poems, creating a structure of starting point that could connect all 25 iterations of the chapter (a notion I was a big fan of).

From this I started to find rhymes for these lines in the larger Iambic line corpus I had found, and again was woefully short of lines. From this I decided to create the structure of randomly finding a line with the body of Iambics and swapping out the last word (which I filtered out with matching structure (noun, adjective etc) with the line before to make the replacement more sensible) with a word that rhymed with the line before and had the Iambic stress structure. From this I had to use RiTa to find rhymes for the lines, from which I ran into a snag of rhyming with plurals (which made rhyming but not grammatical sense) which I sorted by removing the final 's' on each one (it also didn't like old english use of apostrophes, for example walk'd, which I largely also swapped out for e) . With this all completed, I had 18 pairs of lines which I felt could be extended, hence the continuation to 14 lines in the structure of a sonnet using another 6 random line pairings found from the corpus. With these generated, I then had to create a program to filter these collections into JSON files to be used for the generative layouts.

With this completed, I started to sort through all the and images, and divided them into left and right facing hands, and using this divide randomly generated hand pairings for the pages of the book such that the thumbs faced toward the spine and the space left above them could be used to place the text within.

Overall, I had initially intended to use an image set as a starting point but found that for the sake of poetic sense I should use the text. Having generated the Iambics, I sorted through image sets at hand to determine what pairings between image and text could be made, and thus the connection between the hands came about.

I am particularly happy with the outcome, especially the extent to which the poetic license of the sonnets is pushed. However, I feel that this could definitely be taken further, where instead of randomly finding new lines to fill in the sonnets, I would filter the Iambics for their content and generate poems whose lines connected more with the content they possessed. Through this, my swapping of final words to rhymes could also be greater filtered in connection to the line it is being swapped into to ensure more sense as well.

 

 

Finding Iambics with Hand (Initially in P5Js):

 
var data; 
 
function preload(){
	data = loadStrings("finger.txt")
}
 
function setup() {
  createCanvas(400, 400);
  final = createIambs(); 
  print(final); 
}
 
function createIambs() {
  var stressList = []; 
 
  var finalIambs = [];
 
  for (var i = 0; i < data.length; i++){
    currentStress = RiTa.getStresses(data[i]); 
    currentStress = currentStress.replace("/", " "); 
    currentStress = currentStress.replace(" , ", " "); 
    currentStress = currentStress.replace("1/0", "1 0"); 
    currentStress = currentStress.replace("1/1", "1 1"); 
    currentStress = currentStress.replace("0/1", "0 1"); 
    stressList.push(currentStress); 
    if (currentStress == "0 1 0 1 0 1 0 1 0 1"){
      finalIambs.push(data[i]); 
    }
}
  return finalIambs; 
}

Sonnet Builder (Processing):

 
import rita.*;
String startLines[];
String buildLines[]; 
PrintWriter outputter;
String[] finalWords = new String[18]; 
 
String[] testerRhymes = new String[20]; 
 
String[][] dictionary = new String[18][15]; 
 
String[] finalPairings = new String[18]; 
 
void setup(){
 
  startLines = loadStrings("handIambs.txt");
  buildLines = loadStrings("gutenbergIambics.txt"); 
 
  outputter = createWriter("data/finalPoems.txt");
 
  int numberStartLines = startLines.length;
  int numberBuildLines = buildLines.length; 
 
 
  for (int i = 0; i < numberStartLines; i++){
    String currentStartLine = startLines[i]; 
 
    String[] dataRows = currentStartLine.split(" ");
 
    String finalWord = dataRows[dataRows.length-1]; 
 
    finalWords[i] = finalWord; 
 
    String[] rhymes = RiTa.rhymes(finalWord); 
 
    String currentType = ""; 
 
    if (RiTa.isNoun(finalWord)){
      currentType = "Noun";
    }
 
    if (RiTa.isVerb(finalWord)){
      currentType = "Verb";
    }
 
    if (RiTa.isAdjective(finalWord)){
      currentType = "Adjective";
    }
 
    if (RiTa.isAdverb(finalWord)){
      currentType = "Adverb";
    }
 
    String currentStress = RiTa.getStresses(finalWord); 
 
    //for (int k = 0; k < rhymes.length; k ++){
 
    int k = 0; 
    while ( k < rhymes.length){
      String currentRhyme = rhymes[k]; 
 
      String currentRhymeType = ""; 
 
      if (RiTa.isNoun(finalWord)){
        currentRhymeType = "Noun";
        }
 
      if (RiTa.isVerb(finalWord)){
        currentRhymeType = "Verb";
        }
 
      if (RiTa.isAdjective(finalWord)){
        currentRhymeType = "Adjective";
        }
 
      if (RiTa.isAdverb(finalWord)){
        currentRhymeType = "Adverb";
        }
 
      String currentRhymeStress = RiTa.getStresses(currentRhyme);  
 
      if (currentRhymeType != currentType){
        rhymes = concat(subset(rhymes, 0, k-1), subset(rhymes, k+1, rhymes.length-1 - k)); 
        }
 
      /*if (currentStress != currentRhymeStress){
        rhymes = concat(subset(rhymes, 0, k-1), subset(rhymes, k+1, rhymes.length-1 - k)); 
      }*/
 
      k += 1; 
    }
    //print(rhymes); 
 
    Boolean wordFound = false; 
 
    while (wordFound == false){
      int randomIndex = floor(random(405)); 
 
      String currentRandomLine = buildLines[randomIndex]; 
 
      String[] randomLine = currentRandomLine.split(" ");
 
      String randomFinalWord = randomLine[randomLine.length-1]; 
 
      String currentRhymeType = "";
 
      if (RiTa.isNoun(randomFinalWord)){
        currentRhymeType = "Noun";
        }
 
      if (RiTa.isVerb(randomFinalWord)){
        currentRhymeType = "Verb";
        }
 
      if (RiTa.isAdjective(randomFinalWord)){
        currentRhymeType = "Adjective";
        }
 
      if (RiTa.isAdverb(randomFinalWord)){
        currentRhymeType = "Adverb";
        }
 
      if (currentRhymeType == currentType){
        String randomRhyme = rhymes[floor(random(rhymes.length))]; 
 
 
        randomLine[randomLine.length-1] = randomRhyme; 
 
        print(randomLine); 
 
        wordFound = true; 
      }
 
      finalPairings[i] = currentStartLine + "\n" + randomLine;  
 
    }
 
  }
  }
 
 
 
 
 
void draw(){
}

JSON Builder (Processing):

 
JSONArray json;
 
String[] lines; 
 
JSONObject[] jsonList = new JSONObject[20]; 
 
 
void setup() {
 
  for (int a = 0; a < 25; a++){
 
  lines = loadStrings("testingFinalSonnets" + a + ".txt");
 
  json = new JSONArray(); 
 
  for (int i=0; i < 20; i++){
 
    JSONObject currentSonnet = new JSONObject(); 
 
    if (i == 0){
      currentSonnet.setInt("Id", 0); 
      currentSonnet.setString("text", "The Hands of Gutenberg");
      json.setJSONObject(i, currentSonnet);
      continue;
      }
 
    if (i == 19){
      currentSonnet.setInt("Id", 19); 
      currentSonnet.setString("text", "");
      json.setJSONObject(i, currentSonnet);
      continue;
      }
 
 
    String currentString = join(subset(lines, i*14, 14), "\n"); 
 
    currentSonnet.setInt("Id", i);
    currentSonnet.setString("text", currentString); 
 
 
    json.setJSONObject(i, currentSonnet);
 
   }
 
   saveJSONArray(json, "data/finalSonnets" + a + ".json");
  }
}

Generative Layout (Processing) (Starter Code courtesy of Golan Levin):

 
// Export a multi-page PDF alphabet book. 
 
import processing.pdf.*;
boolean bExportingPDF;  
// See https://processing.org/reference/libraries/pdf/index.html
 
int nPages; 
PImage imageArray[];
JSONArray jsonPages;
int outputPageCount = 0; 
 
PFont highQualityFont32;
PFont highQualityFont100;
float inch = 72; 
 
String pdfName; 
 
//=======================================================
void setup() {
 
  // The book format is 6" x 9". 
  // Each inch is 72 pixels (or points). 
  // 6x72=432, 9*72=648 
 
  // USE THESE COMMANDS WHEN YOU'RE NOT EXPORTING PDF, 
  // AND YOU JUST WANT SCREEN DISPLAY:
  //size(432, 648); 
  bExportingPDF = false;
 
   size(432, 648, PDF, "24-breep.pdf");
   bExportingPDF = true;
 
  int m = 24; 
 
 
    if (m / 10 == 0){
      pdfName = "0" + m + "-breep.pdf";
      }
 
    else{
      pdfName = m + "-breep.pdf";
    }
 
    //size(432, 648, PDF, "00-breep.pdf");
    //bExportingPDF = true;
 
    //--------------------
    // For high-quality (vector) typography:
    // See https://processing.org/reference/libraries/pdf/index.html
    // "Another option for type, is to use createFont() with a TrueType font 
    // (some OpenType fonts also work). Any font that shows up in PFont.list() 
    // should work, or if not, adding a .ttf file to the data directory 
    // and calling createFont("fontname.ttf") will also work.
 
    highQualityFont32 = createFont("vag.ttf", 32);
    highQualityFont100 = createFont("vag.ttf", 100);
 
    jsonPages = loadJSONArray("text/finalSonnets" + m + ".json");
    nPages = jsonPages.size(); 
 
    // Load the images from the data folder,
    // Using the filenames from the JSON array. 
 
    imageArray = new PImage[nPages];
 
    for (int i=0; i<nPages; i++) {
      JSONObject aPage = jsonPages.getJSONObject(i);
      int currentHand = (i + 1) + (m*10); 
 
      String imageFileName = ""; 
 
      if (i % 2 == 0){
        imageFileName = "images/joint/" + "rightHand" + currentHand + ".jpg"; 
      }
      else{
        imageFileName = "images/joint/" + "leftHand" + currentHand + ".jpg";
      }
 
      imageArray[i] = loadImage(imageFileName);
    }
}
 
//=======================================================
void draw() {
  if (bExportingPDF) {
    drawForPDFOutput();
  } else {
    drawForScreenOutput();
  }
}
 
//=======================================================
void drawForPDFOutput() {
 
  // When finished drawing, quit and save the file
  if (outputPageCount >= nPages) {
    exit();
  } else {
    drawPage(outputPageCount); 
    PGraphicsPDF pdf = (PGraphicsPDF) g;  // Get the renderer
    if (outputPageCount < (nPages-1)) {
      pdf.nextPage();  // Tell it to go to the next page
    }
  }
 
  outputPageCount++;
}
 
 
//=======================================================
void drawForScreenOutput() {
  int whichPageIndex = (int) map(mouseX, 0, width, 0, nPages);
  drawPage(whichPageIndex);
}
 
 
 
//=======================================================
void drawPage(int whichPageIndex) {
  background(255);
  whichPageIndex = constrain(whichPageIndex, 0, nPages-1); 
  JSONObject whichPageObject = jsonPages.getJSONObject(whichPageIndex); 
 
  // Fetch and render image
  PImage whichImage = imageArray[whichPageIndex];
  float imgWRaw = whichImage.width; 
  float imgHRaw = whichImage.height;
  float imgW = max(imgWRaw, width*0.85);
  float imgScale = imgW/imgWRaw;
  float imgH = imgHRaw* imgScale;
  float imgX = (width - imgW)/2.0;
  float imgY = height - imgH - inch*1.5;
  image(whichImage, 0, 0, width, height); 
 
 
  // Assemble body text
  String captionString = whichPageObject.getString("text"); 
 
 
  // Display body text
  textFont(highQualityFont32);
  fill(255); 
  textAlign(CENTER); 
  textSize(15); 
  if (whichPageIndex % 2 == 0){
    text(captionString, (width/3) + 20, height/10);
    }
  else{
    text(captionString, ((width/3) * 2) - 20, height/10);
    }
}