Life as Clay

Processing: HTML table monster concept

leave a comment »


I was working on a website for a client the other day when I sketched out a table that I needed to code in ERB / HTML. I doodled a bit, filling it in with patterns for the various header levels. I liked the appearance and decided to write a Processing script that would generate similar doodles.

Here is the original

 

Here’s an example of the output from the script:

There are a few bugs, but I decided that the output satiated my need to create these monsters. Here’s the code. (Oh, it’s also on OpenProcessing.org right here…)

 

Grid grid;
ArrayList creatures = new ArrayList();

void setup() {
  size(400, 300);

  grid = new Grid(40, 30, false);

  smooth();
  background(255);
}

void draw() {
  background(255);
  grid.display();
}

void mouseClicked() {
  PVector pointToCheck = new PVector(mouseX, mouseY);
  boolean okToMakeCreature = true;

  for (int i = 0; i < grid.claimedSectors.size(); i++) {
    if (isInSectorCompare(pointToCheck, (Sector)grid.claimedSectors.get(i))) {
      print("SECTOR CLAIMED ALREADY.\n");
      okToMakeCreature = false;
      break;
    }
  }
  if (okToMakeCreature) {
    Creature c = new Creature(pointToCheck); 
    creatures.add(c);
    print(creatures.size() + " creatures.\n");
  }
}

boolean isInSectorCompare(PVector pointToCheck, Sector s) {
  if (pointToCheck.x >= s.topLeft.x && pointToCheck.x < s.topRight.x && pointToCheck.y >= s.topLeft.y && pointToCheck.y < s.bottomLeft.y && pointToCheck.x > s.borderThreshold && pointToCheck.x < width - s.borderThreshold && pointToCheck.y > s.borderThreshold && pointToCheck.y < height - s.borderThreshold) {
    return true;
  } 
  else {
    return false;
  }
}

class Creature {
  PVector startPt, direction, destination;

  int segRangeLow  = 3;
  int segRangeHigh = 7;
  int segSizeLow   = 10;
  int segSizeHigh  = 15;
  int numSegments;
  int[] segmentSizes;
  int creatureStrokeWeight = 1;

  ArrayList patternsUsed = new ArrayList();

  ArrayList creatureSectors = new ArrayList();

  Sector startSector;

  color patternColor;

  Creature(PVector startLocation) {
    startPt = startLocation;

    // Establish how many segments the creature will have
    numSegments = (int)random(segRangeLow, segRangeHigh);
    segmentSizes = new int[numSegments];
    String desc = "";

    for (int i = 0; i < numSegments; i++) {
      segmentSizes[i] = (int)random(segSizeLow, segSizeHigh); 
      desc += segmentSizes[i] ;
      desc += " ";
    }
    print("\nThis creature will have " + numSegments + " segments of sizes: " + desc + "\n");

    patternColor = color((int)random(0,200));

    findStartSector();
    buildSegments();
    buildBorders();
  }

  void buildBorders() {
    for (int i = 0; i < creatureSectors.size(); i++) {
      // Get center
      Sector s = (Sector)creatureSectors.get(i);
      float sWidth    = s.secWidth;
      float sHeight   = s.secHeight;
      PVector sCenter = s.centerPoint();

      ArrayList borderSectors = new ArrayList();

      borderSectors.add(grid.sectorWithPoint(new PVector(sCenter.x, sCenter.y - sHeight)));
      borderSectors.add(grid.sectorWithPoint(new PVector(sCenter.x + sWidth, sCenter.y)));
      borderSectors.add(grid.sectorWithPoint(new PVector(sCenter.x, sCenter.y + sHeight)));
      borderSectors.add(grid.sectorWithPoint(new PVector(sCenter.x - sWidth, sCenter.y)));

      // Check each border sector to see if it is in this creature
      for (int j = 0; j < 4; j++) {
        if (creatureSectors.contains((Sector)borderSectors.get(j)) == false) {
          // We are in here because the sector is the last in a particular direction
          // s is the active sector
          
          switch(j) {
           case 0:
              s.drawBorderTop = true;
              break;
           case 1: 
              s.drawBorderRight = true;
              break;
           case 2:
              s.drawBorderBottom = true;
              break;
           case 3:
              s.drawBorderLeft = true;
              break;
          } 
        }
      }
    }
  }

  void buildSegments() {
    // Seed with the start sector
    boolean atStart = true;
    Sector thisSector = null;

    for (int i = 0; i < numSegments; i++) {
      
      // Pattern variables to be used with this segment
      int patternDensity = (int)random(7, 20); // Randomize this slightly at a later point

      while (patternDensity % 4 > 0) {
        patternDensity +=1;
      }
      print("Pattern Density: " + patternDensity + "\n"); 
      int patternType    = (int)random(1, 10);

      // Checks to make sure that each pattern is only used once with each Creature
      while (patternsUsed.contains (patternType)) {
        patternType = (int)random(1, 10);
        print("picking a new pattern...\n");
      }
      patternsUsed.add(patternType);

      // Iterate for each sector in segment
      for (int j = 0; j < segmentSizes[i]; j++) {

        // Null sector is expected at start
        if (atStart) {
          thisSector = pickSector(thisSector, atStart);
        }

        // Helps avoid problems when there is a null sector
        if (thisSector != null) {
          if (!atStart) {
            thisSector = pickSector(thisSector, atStart);
          }
          
          thisSector.setUpPattern(patternType, patternDensity, patternColor);
          thisSector.sectorStrokeWeight = creatureStrokeWeight;
          
          thisSector.drawBorderTop = false;
          thisSector.drawBorderRight = false;
          thisSector.drawBorderBottom = false;
          thisSector.drawBorderLeft = false;
          
          creatureSectors.add(thisSector);
        }

        atStart = false;
      }
    }
  }

  Sector pickSector(Sector previous, boolean atStart) {
    Sector thisSector = null;
    if (atStart) {
      thisSector = startSector;
    } 
    else {
      boolean sectorFound = false;
      int counter = 0;

      PVector prevCenter = previous.centerPoint();
      float prevWidth  = previous.secWidth;
      float prevHeight = previous.secHeight;

      while (sectorFound == false) {
        int direction = (int)random(0, 4) + 1;
        //print("direction: " + direction + "\n"); 

        PVector pointToTry = null;

        switch (direction) {
        case 1: // Up
          pointToTry = new PVector(prevCenter.x, prevCenter.y - prevHeight);
          break;
        case 2: // Right
          pointToTry = new PVector(prevCenter.x + prevWidth, prevCenter.y);
          break;
        case 3: // Down
          pointToTry = new PVector(prevCenter.x, prevCenter.y + prevHeight);
          break;
        case 4: // Left
          pointToTry = new PVector(prevCenter.x - prevWidth, prevCenter.y);
          break;
        }

        boolean proceed = grid.sectorWithPointIsAvailable(pointToTry);

        if (proceed) {
          thisSector = grid.sectorWithPoint(pointToTry);
          sectorFound = true; 
          break;
        }

        counter += 1;
        if (counter > 4) {
          print ("CAN'T FIND SUITABLE DIRECTION \n");
          break;
        }
      }
    }

    int secIndex = grid.unclaimedSectors.indexOf(thisSector);
    //print("SecIndex: " + secIndex + "\n");
    //grid.unclaimedSectors.remove(secIndex);
    //grid.claimedSectors.add(thisSector);

    return thisSector;
  }

  void findStartSector() {
    // Number of sectors
    int numSectors = grid.sectors.size();

    for (int i = 0; i < numSectors; i++) {
      Sector s = (Sector)grid.sectors.get(i);
      if (isInSector(s)) {
        // Do something about being in the sector
        print("found the sector\n");

        // Save it as the startSector
        startSector = s;
        break;
      }
    }
  }

  boolean isInSector(Sector s) {
    if (startPt.x >= s.topLeft.x && startPt.x < s.topRight.x && startPt.y >= s.topLeft.y && startPt.y < s.bottomLeft.y && startPt.x > s.borderThreshold && startPt.x < width - s.borderThreshold && startPt.y > s.borderThreshold && startPt.y < height - s.borderThreshold) {
      return true;
    } 
    else {
      return false;
    }
  }
}


class Grid {
  int w, h;
  float secWidth, secHeight;
  boolean showLines;

  boolean showSectors = true;

  ArrayList sectors           = new ArrayList();
  ArrayList unclaimedSectors  = new ArrayList();
  ArrayList claimedSectors    = new ArrayList();

  Grid(int tempW, int tempH, boolean show) {
    w = tempW;
    h = tempH;
    showLines = show;

    secWidth  = width  / (float)w;
    secHeight = height / (float)h;
    buildSectors();
  }

  boolean sectorWithPointIsAvailable(PVector thePoint) {
    boolean answer;
    Sector s = sectorWithPoint(thePoint);
    if (unclaimedSectors.contains(s)) {
      answer = true;
    } 
    else {
      answer = false;
    }
    return answer;
  }

  Sector sectorWithPoint(PVector thePoint) {
    Sector answer = null;
    for (int i = 0; i < sectors.size(); i++) {
      Sector s = (Sector)sectors.get(i);
      if (s.containsPoint(thePoint)) {
        answer = s;
        break;
      }
    }

    return answer;
  }

  void buildSectors() {
    for (int i = 0; i < h; i++) {
      for (int j = 0; j < w; j++) {
        PVector thisRegPoint = new PVector(j * secWidth, i * secHeight);
        Sector thisSector = new Sector(thisRegPoint, secWidth, secHeight, j, i);
        sectors.add(thisSector);
        unclaimedSectors.add(thisSector);
      }
    }
  }

  void display() {
    if (showLines) {
      drawLines();
    }
    if (showSectors) {
      drawSectors();
    }
  }

  void drawSectors() {

    // Iterate through all of the sectors
    for (int i = 0; i < sectors.size(); i++) {
      Sector thisSec = (Sector)sectors.get(i);
      thisSec.display();
    }
  }

  void drawLines() {
    stroke(#efefef);
    fill(#ffffff);

    for (int i = 0; i < w; i++) {
      float xPos = (i * secWidth) + secWidth;
      line(xPos, 0, xPos, height);
    }
    for (int i = 0; i < h; i++) {
      float yPos = (i * secHeight) + secHeight;
      line(0, yPos, width, yPos);
    }
  }
}


class Pattern {
  PVector origin;
  PVector[] linePoints = new PVector[0];

  float w, h;
  // 1. Single dot in center
  // 2. Medium density dots
  // 3. Dense dots
  // 4. Vertical lines
  // 5. Horizontal lines
  // 6. Upper-left to lower-right lines
  // 7. Upper-right to lower-left lines
  // 8. Hatching (combo 6 & 7)
  // 9. Circles
  // 10. Horizontal and Vertical Lines

  int type;

  int density; // Must be a multiple of 4

  boolean drawPoints = false;
  boolean linesAreSetup = false;

  color patternColor;

  Pattern () {
    type = 7;
    density = 20;
    patternColor = color(200, 200, 200);
  }

  Pattern(int theType, int theDensity) {
    type = theType;
    density = theDensity;
    patternColor = color(200, 200, 200);
  }

  void setOrigin(PVector theOrigin) {
    origin = theOrigin;
  }

  void setDimensions(float theW, float theH) {
    w = theW;
    h = theH;
  }

  void setType(int theType) {
    type = theType;
  }

  void setDensity(int theDensity) {
    density = theDensity;
  }

  void resetAnchorsForLines() {
    linesAreSetup = false;
  }

  void setColor(color incoming) {
    patternColor = incoming;
  }

  void display() {
    strokeWeight(1);
    // Prompts to set up the lines if needed
    linesAreSetup = false;

    switch (type) {
    case 1:
      drawSingleDot();
      break;
    case 2:
      drawDotsWithDensity((int)((float)density * 0.5), 1);
      break;
    case 3:
      drawDotsWithDensity(density, 1);
      break;
    case 4:
      setupLines();
      drawVerticalLines();
      break;
    case 5:
      setupLines();
      drawHorizontalLines();
      break;
    case 6:
      setupLines();
      drawDiagonalLToR();
      break;
    case 7:
      setupLines();
      drawDiagonalRToL();
      break;
    case 8:
      setupLines();
      drawDiagonalLToR();
      drawDiagonalRToL();
      break;
    case 9:
      drawDotsWithDensity(density, 2);
      break;
    case 10:
      setupLines();
      drawHorizontalLines();
      drawVerticalLines();
      break;
    }

    if (drawPoints) {
      // Test by drawing circles
      for (int i = 0; i < linePoints.length; i++) {
        smooth();
        stroke(patternColor);
        noFill();
        ellipse(linePoints[i].x, linePoints[i].y, 4, 4);
      }
    }
  }

  // CASE 1: DRAW SINGLE DOT
  void drawSingleDot() {
    noStroke();
    fill(patternColor);
    float dotDiameter = w / 5;
    float halfSize = w / 2;
    ellipseMode(CENTER);
    ellipse(origin.x + halfSize, origin.y + halfSize, dotDiameter, dotDiameter);
  }

  // CASE 2: DRAW MEDIUM DENSITY DOTS
  // CASE 3: DRAW HIGH DENSITY DOTS
  // CASE 9: DRAW CIRCLES
  void drawDotsWithDensity(int dotDensity, int theType) {
    switch (theType) {
    case 1:
      noStroke();
      fill(patternColor);
      break;
    case 2:
      stroke(patternColor);
      noFill();
      break;
    }

    float dotDiameter = w / (10 + dotDensity);
    float spacing = w / dotDensity;
    ellipseMode(CENTER);

    for (int i = 0; i < dotDensity - 1; i++) {
      for (int j = 0; j < dotDensity - 1; j++) {
        ellipse(origin.x + spacing + (i*spacing), origin.y + spacing + (j*spacing), dotDiameter, dotDiameter);
      }
    }
  }

  void setupLines() {
    if (!linesAreSetup) {
      createLinePoints();
      stroke(patternColor);
    }
  }

  // CASE 4: DRAW VERTICAL LINES
  void drawVerticalLines() {
    for (int i = 1; i < density / 4; i++) {
      int secondIndex = (int)((float)density * 0.75) - i;
      line(linePoints[i].x, linePoints[i].y, linePoints[secondIndex].x, linePoints[secondIndex].y);
    }
  }
  // CASE 5: DRAW HORIZONTAL LINES
  void drawHorizontalLines() {
    int startIndex = (int)((float)density * 0.25) + 1;
    int stopPoint  = (int)((float)density * 0.5);

    for (int i = startIndex; i < stopPoint; i++) {
      int offset = i - (int)((float)density * 0.25);
      int secondIndex =  linePoints.length - offset;
      line(linePoints[i].x, linePoints[i].y, linePoints[secondIndex].x, linePoints[secondIndex].y);
    }
  }

  // CASE 7: DRAW DIAGONAL RIGHT TO LEFT LINES
  void drawDiagonalRToL() {
    int topPointsMax = density / 4;
    int bottomLinesStart = (density / 2) + 1;
    int bottomLinesLast = (int)(((float)density * 0.75));

    // Draw top side starts
    for (int i = 1; i <= topPointsMax; i++) {
      int secondPointIndex = density - i;
      line(linePoints[i].x, linePoints[i].y, linePoints[secondPointIndex].x, linePoints[secondPointIndex].y);
    }

    // Draw bottom side starts
    for (int i = bottomLinesStart; i < bottomLinesLast; i++) {
      int offset = (int)(i - ((float)density * 0.5));
      int secondPointIndex = (int)((float)density * 0.5) - offset;
      line(linePoints[i].x, linePoints[i].y, linePoints[secondPointIndex].x, linePoints[secondPointIndex].y);
    }
  }

  // CASE 6: DRAW DIAGONAL LEFT TO RIGHT LINES
  void drawDiagonalLToR() {
    int topPointsMax = density / 4;
    int bottomLinesStart = (density / 2) + 1;
    int bottomLinesLast = (int)(((float)density * 0.75));

    // Draw top side starts
    for (int i = 0; i < topPointsMax; i++) {
      int secondPointIndex = (density / 2) - i;
      line(linePoints[i].x, linePoints[i].y, linePoints[secondPointIndex].x, linePoints[secondPointIndex].y);
    }

    // Draw bottom side starts
    for (int i = bottomLinesStart; i < bottomLinesLast; i++) {
      int offset = i - (density / 2);
      int secondPointIndex = density - offset;
      line(linePoints[i].x, linePoints[i].y, linePoints[secondPointIndex].x, linePoints[secondPointIndex].y);
    }
  }

  void createLinePoints() {
    // The origin is point 0
    linePoints = null;
    linePoints = new PVector[0];
    linePoints = (PVector[])append(linePoints, origin);

    int linesPerSide = density / 4;

    // Working with squares so h and w are equal; thus just making a single delta
    float delta = w / linesPerSide;

    // Top of the square
    for (int i = linePoints.length; i <= linesPerSide; i++) {
      // The next point is the previous point plus the witch
      PVector nextPoint = new PVector(linePoints[i-1].x + delta, linePoints[i-1].y);
      linePoints = (PVector[])append(linePoints, nextPoint);
    }

    // R side of square
    for (int i = linePoints.length; i <= linesPerSide * 2; i++) {
      // The next point is the previous point plus the witch
      PVector nextPoint = new PVector(linePoints[i-1].x, linePoints[i-1].y + delta);
      linePoints = (PVector[])append(linePoints, nextPoint);
    }

    // Bottom of square
    for (int i = linePoints.length; i <= linesPerSide * 3; i++) {
      // The next point is the previous point plus the witch
      PVector nextPoint = new PVector(linePoints[i-1].x - delta, linePoints[i-1].y);
      linePoints = (PVector[])append(linePoints, nextPoint);
    }

    // L side of square
    for (int i = linePoints.length; i < linesPerSide * 4; i++) {
      // The next point is the previous point plus the witch
      PVector nextPoint = new PVector(linePoints[i-1].x, linePoints[i-1].y - delta);
      linePoints = (PVector[])append(linePoints, nextPoint);
    }
  }
}


class Sector {
  PVector topLeft, topRight, bottomLeft, bottomRight; // registration point of top-left corner
  float secWidth, secHeight;
  int xCoord, yCoord;
  int borderThreshold = 5;
  Pattern p;

  boolean hoveredOver = false;
  
  boolean drawBorderTop   = false;
  boolean drawBorderRight = false;
  boolean drawBorderBottom  = false;
  boolean drawBorderLeft  = false;
  
  int sectorStrokeWeight = 1;

  Sector(PVector tempTopLeft, float tempSecWidth, float tempSecHeight, int tempXCoord, int tempYCoord) {
    topLeft   = tempTopLeft;
    secWidth  = tempSecWidth;
    secHeight = tempSecHeight;

    topRight    = new PVector(topLeft.x + secWidth, topLeft.y);
    bottomLeft  = new PVector(topLeft.x, topLeft.y + secHeight);
    bottomRight = new PVector(topLeft.x + secWidth, topLeft.y + secHeight);

    xCoord = tempXCoord;
    yCoord = tempYCoord;
    
    p = new Pattern();
    p.setOrigin(topLeft);
    p.setDimensions(secWidth,secHeight);
    p.setType(0);
  }

  void display() {
    p.display();
    
    strokeWeight(sectorStrokeWeight);
    stroke(0);
    
    if(drawBorderTop) {
     line(topLeft.x, topLeft.y, topRight.x, topRight.y);  
    }
    
    if(drawBorderRight) {
      line(topRight.x, topRight.y, bottomRight.x, bottomRight.y);   
    }
    
    if(drawBorderBottom) {
      line(bottomLeft.x, bottomLeft.y, bottomRight.x, bottomRight.y);   
    }
    
    if(drawBorderLeft) {
      line(topLeft.x, topLeft.y, bottomLeft.x, bottomLeft.y);   
    }
    
  }
  
  boolean containsPoint(PVector thePoint) {
    boolean answer;
    if(thePoint.x >= topLeft.x && thePoint.x < topRight.x && thePoint.y >= topLeft.y && thePoint.y < bottomLeft.y && thePoint.x > borderThreshold && thePoint.x < width - borderThreshold && thePoint.y > borderThreshold && thePoint.y < height - borderThreshold) {
      answer = true;
    } else {
      answer = false;
    }
    return answer;
  }
  
  void setUpPattern(int patternType, int patternDensity, color patternColor) {
    p.setType(patternType);
    p.setDensity(patternDensity);
    p.setColor(patternColor);
  }
  
  void establishPattern(int patternType, int patternDensity, color patternColor) {
    
  }
  
  PVector centerPoint() {
   PVector result = new PVector(topLeft.x + (secWidth * 0.5), topLeft.y + (secHeight * 0.5));
   return result;
  }
  
}

Advertisements

Written by Clay

February 21, 2012 at 19:29

Posted in Code, Processing

Tagged with ,

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: