Category: Assignment-05-VisualClock

Sunflower Time

Sooooo I fixed the green leaves! Sort of. I found out that, when creating arc shapes in Java, the fill turns out differently in Js. So I just used ellipses instead.

My inspiration for this was a pendulum. I wanted to do a cat tail, but I went with sunflower stems instead. Shoutout to Golan for teaching me the concatenated rotations!

Anyways, the stalks sway every second. The petals of the sunflowers appear every minute. (There are two rows of petals, one light yellow and one yellow orange. Each row holds 30 petals max.) The petals just appear. They don’t grow or anything. I need to work on that. The hours were also tricky, and could use a lot of room for improvement. My original plan was to have a flower pop up every hour, but I didn’t know how to do that with my weird for loops. So I decided to have the center of the sunflower have lines for every hour on a 0-24 hour clock.

Sooooo there you have it.

Here are some screenshots followed by my code:

Screen Shot 2013-09-26 at 5.05.01 PM

Screen Shot 2013-09-26 at 5.10.52 PM

Screen Shot 2013-09-26 at 5.13.11 PM

color[] blues={#D7F4F6};
color[] browns={#4A3000,#A64500};
color[] greens={#679B00,#89CB12,#DBE86A,#4F5F03,#C2E656};
color[] petals={#FFC700,#F79E00,#FFFF00,#FFCF00};
  
void setup(){
  size(500,250);
  frameRate(30);
}
  
void draw(){
  backdrop(blues[0],greens[2]);
  drawGroups(greens[1],-1,0,-10);
  drawGroups(greens[0],1,80,10);
}
  
void drawGroups(color stemCol,int direction,int shortness,int margin){
  float xSpacing=width/7.0;
    
  for(int i=1;i< =6;i++){
    pushMatrix();
    translate(xSpacing*i+margin,height+shortness);
    drawFlower(stemCol,direction);
    popMatrix();
  }
}
  
void drawFlower(color stemCol,int direction){
  float len=40;
  float speed=0.001;
  float t=map(sin(millis()*TWO_PI*speed),-1,1,-2,2);
    
  stroke(stemCol);
  fill(stemCol);
  pushMatrix();
  rotate(-HALF_PI);
  for(int i=0;i<12;i++){
    len*=0.83;
    rotate(radians(direction*t));
    rect(0,0,len+2,-7);
    translate(len,0);
    if(i<8){
      drawLeaves(stemCol,i);
    }
  }
  drawPetals(petals[3],20,45,30);
  drawPetals(petals[2],10,45,0);
  popMatrix();
}
  
void drawPetals(color c,int margin,int len,int time){
  float min=360/(minute()+1);
  for(int i=0;i<=minute()-time;i++){
    pushMatrix();
    rotate(radians(min*i));
    stroke(c,175);
    fill(c,175);
    ellipse(margin,0,len,7);
    popMatrix();
  }
  drawHead();
}
  
void drawHead(){
  float hr=360/(hour()+1);
  stroke(browns[0]);
  fill(browns[0]);
  ellipse(0,0,42,42);
  stroke(browns[1]);
  fill(browns[1]);
  for(int i=0;i<=hour();i++){
    pushMatrix();
    rotate(radians(hr*i));
    ellipse(11,0,5,0.75);
    popMatrix();
  }
}
  
void drawLeaves(color c,int num){
  fill(c,200);
  stroke(c,200);
    
  if(num%2==1){
    pushMatrix();
    rotate(radians(-55));
    ellipse(20,0,30,10);
    popMatrix();
    //arc(-31,-31,60,60,0,PI/2.0,OPEN);
    //arc(0,0,60,60,PI,3.0*PI/2.0,OPEN);
  }
  else{
    pushMatrix();
    rotate(radians(235));
    ellipse(-17,0,30,10);
    //arc(0, 0, 60, 60, 0, PI/2.0, OPEN);
    //arc(31,31,60,60,PI,3.0*PI/2.0,OPEN);
    popMatrix();
  }
}
  
void backdrop(color c1, color c2){
  noFill();
  for(int i=0;i<=height;i++){
      float inter=map(i,0,height,0,1);
      color c=lerpColor(c1,c2,inter);
      stroke(c);
      line(0,i,width,i);
  }
}

Ticha-Organclock

While the human body itself has made a number of useful contributions to humanity (e.g. procreation), it was never regarded as a tool that can be used for common, day-to-day purposes (e.g. telling time). Although for many people the word ‘tool’ is not the most desirable adjective to use when concerning the human body, it could be argued that it is not justified to be so offended by the notion while readily accepting the capitalization on other organisms’ bodies to make utilitarian and/or decorative objects. Bearskin rugs, cowhide shoes, and alligator leather watches are some examples of this.

Nevertheless, this clock is not meant to be an animal rights statement – but simply a means to offer a non-conventional perspective on the function of a human body. Additionally, it provides the juxtapositional image of using something as volatile and unpredictable as a body to construct a device that requires utmost precision.

I believe that this simple clock communicates my idea to some extent – but more attention should be given to the design/functionality for it to be even more effective. I do plan to possibly tweak the clock further to add more useful features (such as making the stomach glow during mealtimes) and do some touch-ups on the drawings.

EDIT: Shortened the code so that it’s not as repetitive (thanks for the suggestions Dave!) and played with sine waves to make the heart beat less regularly after talking to Golan. It’s still not behaving the way I would like it to, so if I were to make any future revisions I would definitely try to find some reliable equations that model heart beats. It would also be interesting to extend the project further by creating a ‘human clock’ of sorts, as a means to more strongly represent the biorhythmic nature of our bodies.

(A side note: It appears that the screen flashes white occasionally for some reason – not completely sure why. Also, part of the image will be cut off unless you zoom in.)

PImage heart;
PImage heart;
PImage lungs;
PImage bg;

ArrayList oxyleft;
ArrayList oxyright;

void setup() {
  size(692,804);
  heart = loadImage("heart.png");
  lungs = loadImage("lungs.png");
  bg = loadImage("organsBG.png");
  
  oxyleft = new ArrayList();
  oxyright = new ArrayList();
  
  smooth();
}

void draw() {
  background(0);
  image(bg, 0,0);
  
  for(int i = 0; i < hour(); i++) {
    oxyleft.add(new Oxy(56,288, 171,407));
  }
  
  for(int i = 0; i < minute(); i++) {
    oxyright.add(new Oxy(424,630, 160,430));
  }
  
  //beats once per second
  float x = millis()/1000.0;
  float c01 = cos(2*x);
  float c02 = cos(1+5*x);
  float c03 = 1+((c01+c02)/6);
  
  float heartPulse = pow(c03,5.0);
  
  float heartH = map(heartPulse, 0, 3,  320,345);
  
  image(heart, 200, 130, 279, heartH);
  
  for(int i = 0; i < hour(); i++) {
    Oxy l = (Oxy) oxyleft.get(i);
    
    l.run(288,56, 407,171);
    l.display();
  }
  
  for(int i = 0; i < minute(); i++) {
    Oxy l = (Oxy) oxyright.get(i);
    
    l.run(630,424, 430,160);
    l.display();
  }
  
  image(lungs,0,0);
  
}

//oxygen molecules
class Oxy {
  float posX;
  float posY;
  float size;
  float xspeed;
  float yspeed;
  
  Oxy(float loX, float hiX, float loY, float hiY) {
    posX = random(loX,hiX);
    posY = random(loY,hiY);
    xspeed = random(-1,1);
    yspeed = random(-2,0);
  
  }
  
  void run(float hiX, float loX, float hiY, float loY) {
    posX = posX + xspeed;
    
    if(hiX < posX || posX < loX) {
      xspeed *= -1;
    }
    
    posY = posY + yspeed;
    
    if(hiY < posY || posY < loY) {
      yspeed *= -1;
    }
  }
  
  void display() {
    noStroke();
    fill(#b10c0c, 220);
    ellipse(posX, posY, 5,5);
    
  }
  
}

Time Flowing By (Abstract Clock)

Processing Sketch

Code

Main

// Credit to Golan Levin for time boilerplate and Daniel Shiffman for Sine Wave Example

int prevSecond; 
int lastRolloverTime; 
int mils;

int h;
int m;
int s;

Wave secondsWave;
ArrayList minutesWaves;
ArrayList hoursWaves;
float baseWaveHeight;

void setup() {
  size(640, 360);
  baseWaveHeight = height/8;

  stroke(120);
  setupGlobalTimes();

  // initialize objects
  hoursWaves = createHoursWaves();
  minutesWaves = createMinutesWaves();
  secondsWave = createSecondsWave();

  lastRolloverTime = 0; 
}

void draw() {
  background(255); 

  setMils();
  setupGlobalTimes();
  // uncommen the line below to draw time as text for debugging
  // drawTime();

  pushMatrix();
  // int hShift = mils();
  translate(0, height/2); // work relative to horizontal venter at middle

  float periodInSeconds = 60.0; 
  float periodInMilliseconds = periodInSeconds * 1000.0; 
  float timeBasedSinusoidallyVaryingQuantity = sin(TWO_PI * millis()/periodInMilliseconds);

  /* calculate and update seconds waves */
  float secondsVShift = map(timeBasedSinusoidallyVaryingQuantity, -1, 1, height/2, (height/3));
  secondsWave.verticalShift = secondsVShift;

  float millisToCrossScreen = 1000.0;
  float secondsHShift = getCurrentHShift(millisToCrossScreen);

  secondsWave.horizontalShift = secondsHShift;
  secondsWave.update();

  if (m != 0) {
    Wave bottomMinutesWave = minutesWaves.get(0);
    float mergeDegree = millis()/periodInMilliseconds;
    secondsWave.mergeWaves(bottomMinutesWave, mergeDegree);
  }

  strokeWeight(2);
  secondsWave.display();

  /* calculate and update minutes wave */
  millisToCrossScreen = 3 * 1000.0;
  int minutesPerWave = 5;
  for (int i = 0; i < minutesWaves.size(); i+=minutesPerWave) {
    Wave minutesWave = minutesWaves.get(i);
    float minutesHShift = getCurrentHShift(millisToCrossScreen);
    minutesWave.horizontalShift = minutesHShift;
    minutesWave.update();
    strokeWeight(1.5);
    minutesWave.display();
  }

  /* calculate and update hours wave */ 
  millisToCrossScreen = 30.0 * 1000.0;
  float hoursHShift = getCurrentHShift(millisToCrossScreen);
  for (int i = 0; i < hoursWaves.size(); i++) {
    Wave hWave = hoursWaves.get(i);
    // hWave.horizontalShift = hoursHShift*(i/3);
    hWave.update();
    strokeWeight(1);
    hWave.display();
  }  
  popMatrix();
}

float getCurrentHShift(float millisToCrossScreen) {
  float hShift = millis()/millisToCrossScreen;
  return hShift;
}

Wave createSecondsWave() {
  float amp = baseWaveHeight*1.1;
  float freq = 0.75;
  // arguments for Wave constructor are (amplitude, frequency, horizontalShift, verticalShift, pointSpacing)
  Wave sWave = new Wave(amp, freq, 0, 0, 5);
  return sWave;
}

ArrayList createMinutesWaves() {
  ArrayList minutesWaves= new ArrayList();
  println("m " + m);

  if (m != 0) {
    for (int i=0; i < m; i++) {
      println("i " + i);
      float amplitude = baseWaveHeight/3;
      float frequency = 1.5;
      int pointSpacing = 5;
      float allShift = 1*(height/10);
      float perWaveOffset = i*(baseWaveHeight/30);
      float vShift = allShift + perWaveOffset;
      float hShift = i*(width/20);
      Wave mWave = new Wave(amplitude, frequency, hShift, vShift, pointSpacing);
      minutesWaves.add(mWave);
    }
  }
  return minutesWaves;
}

ArrayList createHoursWaves() {
  ArrayList hoursWaves= new ArrayList();
  // for every hour but the current one, draw a very slow moving wave at the top of the screen
  if (h != 0) {
    for (int i = 0; i < h; i++) {       // arguments for Wave constructor are (amplitude, frequency, horizontalShift, verticalShift, pointSpacing)       float amplitude = baseWaveHeight/4;       float frequency = 1;       float perWaveOffset = (i*(baseWaveHeight/4));       float allShiftUp = -1*(height/2);        float vShift = allShiftUp + perWaveOffset;       int pointSpacing = 5;       int hShift = 0; // (i/3)       Wave hWave = new Wave(amplitude, frequency, hShift, vShift, pointSpacing);       hoursWaves.add(hWave);       }   }   return hoursWaves; } void setupGlobalTimes() {   // Fetch the components of the time (hours, minutes, seconds, milliseconds).   // Incidentally, you can also get day(), month(), year(), etc.    h = 15;//hour();    m = 7;///minute();    s = 30;// second();  } void setMils(){   // The millis() are not synchronized with the clock time.    // Instead, the millis() represent the time since the program started. Grrr.    // To approximate the "clock millis", we have to check when the seconds roll over.    if (s != prevSecond){      lastRolloverTime = millis();    }   mils = millis() - lastRolloverTime;   prevSecond = s; } // just here to help develop and debug void drawTime() {   setMils();   //-------------------------------------------------   // Assemble a string to display the time conventionally.   String hourStr   = nf(((h > 12)? h-12:h), 2); // format String with 2 digits
  String minuteStr = nf(m, 2); // format String with 2 digits
  String secondStr = nf(s, 2); // format String with 2 digits
  String ampmStr   = (h < 12) ? "AM" : "PM"; 
  String milsStr   = nf(mils, 3); 
  String theTimeString = hourStr + ":" + minuteStr + ":" + secondStr; 
  theTimeString += "." + milsStr + " " + ampmStr; 

  fill (0); 
  text (theTimeString, width/5, 10); 
  noFill();
}

Wave Class

class Wave {
  float amplitude;
  float frequency;
  float horizontalShift;
  float verticalShift;
  int pointSpacing;
  ArrayList points;

  Wave(float thisAmplitude, float thisFrequency, float thisHorizontalShift, float thisVerticalShift, int thisPointSpacing) {
   amplitude = thisAmplitude;
   frequency = thisFrequency;
   horizontalShift = thisHorizontalShift;
   verticalShift = thisVerticalShift;
   pointSpacing = thisPointSpacing;
   points = new ArrayList();
  }

  // gets the y value for a given x position on this wave
  float getY(float xAngle) {
    float y = amplitude * sin((frequency * xAngle) - horizontalShift) + verticalShift;
    return y;
  }

  // calculate and store positions for each point that's a part of this wave
  void update() {
    points.clear();
    // for each x position across the window, get the y outputted by the wave function
    for (int x=0; x < width; x++) {
      float xAngle = map(x, 0, width, 0, TWO_PI);
      PVector xyPoint = new PVector(x, getY(xAngle));
      points.add(xyPoint);
    }
  }

  // take this wave and make it gradually more similar with another wave
  void mergeWaves(Wave anotherWave, float degree) {
    ArrayList newPoints = new ArrayList();
    // average points along this wave and the other wave's path to the degree
    // passed into the function
    for (int i = 0; i < points.size(); i++) {
      if (i < anotherWave.points.size()) {
        PVector thisPoint = points.get(i);
        PVector thatPoint = anotherWave.points.get(i);
        float mergeX = lerp(thisPoint.x, thatPoint.x, degree);
        float mergeY = lerp(thisPoint.y, thatPoint.y, degree);
        PVector mergePoint = new PVector(mergeX, mergeY);
        newPoints.add(mergePoint);
      }
    }
    points = newPoints;
  }

  void display() {
    // setup drawing settings
    smooth();
    noFill();
    // println("points.size() = " + points.size()); 

    // draw points that make up the curve
    beginShape(LINES);
    for (int pointNum = 0; pointNum < points.size(); pointNum++) {
      PVector point = points.get(pointNum);
      vertex(point.x, point.y);
    }
    endShape();
  }
}

Most up-to-date code: https://github.com/juliat/abstract-clock

Reflection

For this project I had two things in mind. First, I wanted to create a clock that I’d actually want to look at, something that I could have on my desk that would give me a sense of the time and also be pleasurable to look at every once in a while. Second, I wanted to create a clock that would say something about how time feels. For me, that meant creating a clock with waves flowing at different paces. Seconds pass quickly, like waves on the sand, while hours are more like the waves you see as you look at the horizon—almost solid, stately.

I like how this sketch has come out so far—the overall feel is going in the direction that I want it to. Still, there are many things I would like to improve and polish about it. For example:

  • Making the waves fade more smoothly into one another (a better version of what the seconds wave does with the minutes wave right now)
  • Explore readability of the clock—making it easier to actually tell the time using the clock.
  • Giving more attention to the transitions between hours and minutes over the course of a day. Right now, waves just appear or disappear suddenly. They should really fade or merge into one another or something.

All/Other Ideas for This Project

1. Water and Fishes

Abstract Clock Sketches Page 1

2. Abstract Waves, 3. Circles

Abstract Clock Sketch Page 2

Waves – Texture and Pattern Research

Circles
Color/circle grid inspiration.

4. Slices of Sun and Moon

Abstract Clock Sketches