Visualize long texts and edit the content in Processing

edited July 2016 in Library Questions

i'm trying to recreate this poster to understand how to visualize long texts in processing. For the moment that is what i wrote:

import processing.pdf.*;

PFont f;
String txt [];
String myData [];
ArrayList romeopos = new ArrayList();
ArrayList julietpos  = new ArrayList();



void setup() {
  size(600, 800, P2D);
  background(255);
  f = createFont("Garamond", 9);
  txt=loadStrings("test.txt");  


  myData = new String[txt.length];
  myData = txt[0].split(" ");
}



void draw() {
  noLoop();
  fill(50);
  textFont(f);

  for (int i=0; i<myData.length; i++) {

    if ( myData[i].toLowerCase().contains("romeo"))
      romeopos.add(i);
    if ( myData[i].toLowerCase().contains("juliet"))
      julietpos.add(i);
  }

  // text(txt[0], 10, 10, width-20, height-10);
  // println (myData);
}       

i don't know how to continue, could you help me? the .txt is Romeo and Juliet written in one line.

Tagged:

Answers

  • i think you'd need to save the x and y position of the start of each Romeo and each Juliet. which means printing the text letter by letter and keeping track yourself. using text() won't do it - there's no way of mapping from position within string onto screen position, especially with a proportionally spaced font.

    read a letter
    print it
    check for Romeo or Juliet and save position
    increment x by the width of the letter
    if x is past the right hand side of the page then
      x = left hand side of the page
      move y to next line (add the height of the letters)
    repeat until end
    draw connecting lines
    
  • this can put out a text and gives positions

    PFont font1; 
    
    float upperMargin= 40; 
    float leftMargin=44; 
    float rightMargin=210;  
    
    int rightMarginAdd=3; 
    
    void setup() {
    
      size(1100, 900);
    
      background(111);
      fill(255);
      font1=createFont("Arial", 14);
      textFont(font1);
    }
    
    void draw() {
    
      background(111);
    
      // Steuerung R = Red
      // Steuerung N = Normal
    
      // take it 32 times
      String s1 = repeatMe("This is #R written in red#N - this isn't, you know. Good! ", 32);  
    
      controlRightMargin();
      drawRect();
    
      textMy(s1);
    } // func 
    
    // ----------------------------------------------------------
    
    void textMy(String s1) {
    
      // you can't use text() with 5 params here, but the idea is implemented. 
    
      // this could be rewritten in a way that we collect all letters until # then 
      // text() them, then do the command and collect them again and text them. 
    
      float xpos=leftMargin; 
      float ypos=40;
    
      String[] stringArray=splitTokens(s1, " ");
    
      for (int i2 = 0; i2 < stringArray.length; i2++) {
    
        String s=stringArray[i2]+" ";
    
        for (int i = 0; i<s.length(); i++) {
    
          if (s.charAt(i)=='#') {
            // special character 
    
            switch(s.charAt(i+1)) {
              // which special command? 
    
            case 'R':
              fill(255, 0, 0); // red
              break; 
    
            case 'N':
              fill(255);  // white / normal
              break;
            }//switch
    
            // we move 2 characters onwards now 
            i+=2;
          } // if
          else {
            // normal Text      
            text(s.charAt(i), xpos, ypos);
            println (s.charAt(i), xpos, ypos);
            xpos+=textWidth(s.charAt(i));
          } // else
        }//for
    
        if (xpos+textWidth(stringArray[i2]) > rightMargin) { 
          xpos=leftMargin; 
          ypos+=22;
        }// if
    
        if (i2<stringArray.length-1) {
          if (xpos+textWidth(stringArray[i2+1]) > rightMargin) { 
            xpos=leftMargin; 
            ypos+=22;
          }// if
        }
      }//for
    } // func 
    
    String repeatMe(String strLocal, int intHowMany) {
      String buffer=""; 
      for (int i=0; i<intHowMany; i++) {
        buffer += strLocal;
      }
      println (buffer);
      return buffer;
    }//func
    
    void controlRightMargin() {
    
      rightMargin+=rightMarginAdd;
    
      // keep rightMarginAdd correct
      if (leftMargin+rightMargin>width-33)
        rightMarginAdd=-abs(rightMarginAdd); // neg
    
      if (rightMargin<leftMargin+22)
        rightMarginAdd=abs(rightMarginAdd); // pos
    }
    
    void drawRect() {
      noFill(); 
      stroke(255);
      rect(leftMargin-3, upperMargin-16, rightMargin-leftMargin+5, height-30);
      fill(255);
    }
    //
    
  • here the word "is" is marked

    PFont font1; 
    
    float upperMargin= 40; 
    float leftMargin=44; 
    float rightMargin=210;  
    
    int rightMarginAdd=3; 
    
    void setup() {
    
      size(1100, 900);
    
      background(111);
      fill(255);
      font1=createFont("Arial", 14);
      textFont(font1);
    }
    
    void draw() {
    
      background(111);
    
      // Steuerung R = Red
      // Steuerung N = Normal
    
      // take it 32 times
      String s1 = repeatMe("This is #R written in red#N - this isn't, you know. Good! ", 32);  
    
      controlRightMargin();
      drawRect();
    
      textMy(s1);
    } // func 
    
    // ----------------------------------------------------------
    
    void textMy(String s1) {
    
      // you can't use text() with 5 params here, but the idea is implemented. 
    
      // this could be rewritten in a way that we collect all letters until # then 
      // text() them, then do the command and collect them again and text them. 
    
      float xpos=leftMargin; 
      float ypos=40;
    
      String[] stringArray=splitTokens(s1, " ");
    
      for (int i2 = 0; i2 < stringArray.length; i2++) {
    
        String s=stringArray[i2]+" ";
    
        if (s.equals("is ")) {
          fill(255, 7, 7);
          ellipse(xpos, ypos, 7, 7);
        }
    
        for (int i = 0; i<s.length(); i++) {
    
          if (s.charAt(i)=='#') {
            // special character 
    
            switch(s.charAt(i+1)) {
              // which special command? 
    
            case 'R':
              fill(255, 0, 0); // red
              break; 
    
            case 'N':
              fill(255);  // white / normal
              break;
            }//switch
    
            // we move 2 characters onwards now 
            i+=2;
          } // if
          else {
            // normal Text      
            text(s.charAt(i), xpos, ypos);
            println (s.charAt(i), xpos, ypos);
            xpos+=textWidth(s.charAt(i));
          } // else
        }//for
    
        if (xpos+textWidth(stringArray[i2]) > rightMargin) { 
          xpos=leftMargin; 
          ypos+=22;
        }// if
    
        if (i2<stringArray.length-1) {
          if (xpos+textWidth(stringArray[i2+1]) > rightMargin) { 
            xpos=leftMargin; 
            ypos+=22;
          }// if
        }
      }//for
    } // func 
    
    String repeatMe(String strLocal, int intHowMany) {
      String buffer=""; 
      for (int i=0; i<intHowMany; i++) {
        buffer += strLocal;
      }
      println (buffer);
      return buffer;
    }//func
    
    void controlRightMargin() {
    
      if (!keyPressed) {
        rightMargin+=rightMarginAdd;
    
        // keep rightMarginAdd correct
        if (leftMargin+rightMargin>width-33)
          rightMarginAdd=-abs(rightMarginAdd); // neg
    
        if (rightMargin<leftMargin+22)
          rightMarginAdd=abs(rightMarginAdd); // pos
      }
    }
    
    
    void drawRect() {
      noFill(); 
      stroke(255);
      rect(leftMargin-3, upperMargin-16, rightMargin-leftMargin+5, height-30);
      fill(255);
    }
    //
    
  • I prefer koogs solution because you don't have to worry about punctuation marks because they are attached to the words in your array. I would suggest that you work word by word rather than letter by letter.

    Initialise print position (x, y) 
    While there are words left in the array and y is less than display height 
      Read the next word 
      Note the text width of the word
      if x plus word width is past the right hand side of the page then
        x = left hand side of the page
        move y to next line (add the height of the words)
      check for Romeo or Juliet and save position
      Draw word 
      increment x by the width of the word
      increment x by width of a space 
    While end 
    draw connecting lines
    
  • edited July 2016

    Thank you all for your quick response, for the moment i was following Chrisir response since i haven't really understood the other, i'm just at the beginning with learning processing. after i get in a for loop:

          for (int i2 = 0; i2 < stringArray.length; i2++) {
    
        String s=stringArray[i2]+" ";
    
        if (s.toLowerCase().contains("romeo")) {
          ellipse(xpos, ypos, 7, 7);
          romeopos.add(xpos);
          romeopos.add(ypos);
        } else if (s.toLowerCase().contains("sampson")) {
          julietpos.add(xpos);
          julietpos.add(ypos);
    
          ellipse(xpos, ypos, 7, 7);
    
          println(julietpos);
        }
    

    romeopos/ julietpos being arraylists, how can i get the "coordinates" out of the for() ? is it possibile to append an array to an array? or i'm focusing on the wrong way?

  • romeopos/ julietpos being arraylists

    arraylists of what though? what are you storing in them?

    imo they need to be arraylists of PVectors so you can

    romeopos.add(new PVector(xpos, ypos));
    

    to save both x and y of the word

  • The picture below was done using text from The Scarlet Pimpernel by Baroness Emma Orczy. It displays the first 2168 words from the book and links all occurrences of the word "scarlet" (7) and links them.

    The sketch to do this was based on the algorithm I posted earlier and is just 52 lines of code. I can post the code here but I understand if you want to create the code yourself, after all that is the best way of learning to program. The decision is yours. :)

    scarlet

  • could you? i think i'm missing some big steps

  • Answer ✓

    If you want any of the code explained then just ask.

    PFont font;
    float fh = 4, deltaY = fh + 5;
    float x, y;
    String[] text;
    ArrayList<PVector> word1 = new ArrayList<PVector>();
    
    void setup() {
      size(800, 800);
      font = createFont("Garamond", fh);
      background(255);
      fill(0);
      processAndDrawText();
      displayLines(word1, color(255,0,0));
      save("data/scarlet.jpg");
    }
    
    void displayLines(ArrayList<PVector> locations, int col) {
      if (locations.size() > 1) {
        stroke(col);
        strokeWeight(1);
        // Convert list to array
        PVector[] locs = (PVector[]) locations.toArray(new PVector[locations.size()]);
        for (int i = 0; i < locs.length - 1; i++)
          for (int j = i + 1; j < locs.length; j++)
            line(locs[i].x, locs[i].y, locs[j].x, locs[j].y);
      }
    }
    
    void processAndDrawText() {
      String[] temp = loadStrings("scarlet.txt");
      text = temp[0].split(" ");
      // Initialise print position
      x = 0;
      y = deltaY;
      int idx = 0;
      float spaceWidth = textWidth(" ");
      while (idx < text.length && y < height + deltaY) {
        String word = text[idx];
        float wordWidth = textWidth(word);
        if (x + wordWidth > width) {
          x = 0;
          y += deltaY;
        }
        if (word.toLowerCase().contains("scarlet")) {
          word1.add(new PVector(x, y));
        }
        text(word, x, y);                  // draw word
        x = x + wordWidth + spaceWidth;    // Advance x to next word position
        idx++;   // move to next word
      }
      println("Number of words = " + idx);
    }
    
  • why .contains() ?

  • edited July 2016

    because the split is on the space character so might have "Scarlet."

  • it's just that it's at the start of the string so why not startsWith()? and using contains() could be dangerous if you were looking for something that could have a prefix - 'brought' and 'thought' would return an erroneous true if looking for 'ought' for instance. (you're safe with Romeo, Juliet or Scarlet though, i guess)

    tbh, i wouldn't've bothered with word-wrapping - people will be looking at the lines and not the right-hand edge. i might like to highlight the words in the relevant colour and join their middles rather than their starts though.

  • Thank you, i'm understanding more about arrays and pvector, btw what exactly is deltaY?

  • deltaY is the value you need to add to y position to get it onto the next row. he's calculated it based on the font height on line 2. (delta being the greek letter often used in maths circles to mean 'difference')

  • tbh, i wouldn't've bothered with word-wrapping - people will be looking at the lines and not the right-hand edge. i might like to highlight the words in the relevant colour and join their middles rather than their starts though.

    All these are very easy to do with simple modifications to my code but as this is not my project I will leave that for the OP to figure out :)

Sign In or Register to comment.