HW 8: UFO’s

The precursor to my final project: a little data viz about UFO sightings in the US. Each state produces a dot of a size proportional to the number of UFO sightings, and randomly places them on a canvas:

Untitled-1

Here’s my code:

String [] upload; //the raw text file uploaded
String [] states; //stores all the states
int [] counts; //stores all the counts as ints
float mcounts[]; //mapped counts, to make drawing easier
float rnum[][]; //array of random locations

void setup()
{

size(800, 800);

//load the data
upload=loadStrings(“Count_by_state.txt”);

//initialise all the strings
states= new String[upload.length];
counts=new int[upload.length];
mcounts=new float[upload.length];
rnum=new float[upload.length][2];

for (int i=0;i<upload.length;i++)
{
states[i]=splitTokens(upload[i])[0];
counts[i]=int(splitTokens(upload[i])[1]);
mcounts[i]=map(counts[i],min(counts), max(counts),3,75); //map values!
println(counts);

//fill the random location array
rnum[i][0]=random(0,width);
rnum[i][1]=random(0,height);
}

}

void draw()
{
background(0);
fill(255);

for (int j=0; j<states.length; j++)
ellipse(rnum[j][0],rnum[j][1],mcounts[j],mcounts[j]); //draw ellipse in a random location, size corresponds to the count

}

Advertisements

HW 7: Dripping Video

This program was was supposed to have a white screen with a blobs of paint dripping down as you click, with the trail of each blob revealing a parts of the video that your camera is capturing (that would be you, if you’re using a laptop with a built-in camera). Parts of it worked, but were super slow, but then the whole thing sort of failed as I added more to the program…

Here’s the code:

import toxi.physics2d.*;
import toxi.physics2d.behaviors.*;
import toxi.geom.*;
import toxi.math.*;
import processing.video.*;

//camera to capture image
Capture cam;

//droplet image;
PImage drop;

//stores all the paint blobs on screen
ArrayList bloblist;

//the number of particles that each paint blob will make
int blobsize;

//physics stuff
VerletPhysics2D physics;

//Canvas!
PGraphics canvas;

//boolean to see whether or not a photo has been taken
boolean firstTime = true;

void setup()
{
size(640, 480, P2D);

//take the photo
cam = new Capture(this, 640, 480);
cam.start();

//load the droplet image
drop=loadImage(“droplet.png”);

//instantiate physics and set bounding box
physics=new VerletPhysics2D();
physics.setWorldBounds(new Rect(0, 0, width, height));

//add gravity
physics.addBehavior(new GravityBehavior(new Vec2D(0, 0.5)));

//initialize variables and lists
bloblist=new ArrayList ();
blobsize=3;

//initialize canvas
canvas = createGraphics(width, height);

//load pixels to access later
loadPixels();
}

void draw()
{
//update the physics world and load pixels
physics.update();
loadPixels();
canvas.loadPixels();

//white background
background(255);
canvas.background(255);

//TEST
//image(cam,0,0);

//display the parts of the photo that should be revealed
/* color c= color(0, 0, 0);
for (int i=0;i
{
if (canvas.pixels[i]==c) //if canvas area is black, display image pixel
{
pixels[i]=cam.pixels[i];
}
}*/

/*for (int i=0;i
{
for (int count1=0;count1
{
for (int count2=0;count2
{
color c= color(0,0,0);
if (canvas.get(count1, count2)==c)
pixels[i]=cam.pixels[i];
}
}
}

//check each blob in bloblist and display [on the canvas, not to screen] each particle in each blob
if (!bloblist.isEmpty()) { //if bloblist is not empty
for (Blob b: bloblist)//diplay EACH blob in bloblist
{
if (!b.particles.isEmpty())//if the blob itself is not empty
for (Particle p: b.particles)//meaning EACH particle in each blob in bloblist
{
p.updateHistory();
p.display();
}
}
}

/* fill(0);
textSize(20);
text(int(frameRate),10,30);
text(“Total blobs: ” + bloblist.size(),10,60);
text(“Total particles: ” + physics.particles.size(),10,90);
int total = 0;
for (VerletParticle2D p : physics.particles) {
Particle pp = (Particle) p;
total += pp.history.size();
}
text(“Total ellipses: ” + total,10,120);
*/
updatePixels();
}

void mousePressed()
{
bloblist.add(new Blob (blobsize));
}

void captureEvent(Capture cam)
{
if (firstTime && millis() > 3000) {
cam.read();
firstTime = false;
}
}

// A blob is an ArrayList of dripping particles
class Blob
{
ArrayList<Particle> particles;

Blob(int n)
{
particles=new ArrayList<Particle>();
int tempposx=mouseX;
int tempposy=mouseY;
for (int i=0;i<n; i++)
{
Particle p1=new Particle(tempposx, tempposy);
particles.add(p1);
physics.addParticle(p1);//referencing the main here! Outside the scope of this function!

tempposx+=20;
tempposy+=20;
}
}
}

class Particle extends VerletParticle2D
{

ArrayList <Vec2D> history;

Particle(float x, float y)
{
super(x, y);
history=new ArrayList<Vec2D>();
}

void display()
{
/*//display the particle itself
pushStyle();
fill(0);
ellipse(x, y, 50, 50);
popStyle();*/

//display particle history history
if (!history.isEmpty())
for (Vec2D vec1:history)
{

/*pushStyle();
fill(0);
ellipse(vec1.x, vec1.y, 70, 70);
popStyle();*/
//reveal the image
//canvas.beginDraw(); //draw black blobs on cavas
image(drop,vec1.x, vec1.y); //calls the image from outside the scope!
//canvas.endDraw();
}
}

void updateHistory()
{

if (y<=height-1 && frameCount%3==0)
{

history.add(new Vec2D(x, y));
}
}
}

HW 5: Constellations

Catching up on some old homework posts:

A little program that randomly draws stars on the screen. You’re supposed to be able to draw constellations between the stars, and you could at one point, but that broke during some changes I made and I never got that functionality back…

Here’s the code:

//ICM HW 5 “Constellations” by Sharang Biswas

ArrayList starList; //holds all the stars that will be displayed onscreen
ArrayList constellationList; //holds all the previously drawn constellations
///the colours of the stars
color c1= color(71, 60, 139); //slateblue4
color c2=color(85, 26, 139);//purple 4
color c3=color(0, 0, 255);//blue
color c4=color(25, 25, 112);// midnight blue
color c5=color(99, 184, 255);// steelblue1
color c6=color(58, 95, 205);// royalblue3
color[] colourList;

//counter to slow down colour change
int countCol=0;

// to change size
int countSize=0; //counter
int rSize=0; //holds the random size

//Currently clicked Star
Star currentStar;

//Currently Drawn Constellation
Constellation constel;

/////////////////////////////////////////////////////////
void setup()
{
//screen size
size(800, 800);

//initialise the arrays;
starList=new ArrayList();
constellationList=new ArrayList<Constellation>();
colourList=new color[] {
c1, c2, c3, c4, c5
};

//initialise Constellation and currentStar
constel=new Constellation();
currentStar=new Star();
//need to make a random number of stars placed randomly
int r=int(random(500, width)); //a random number of stars
for (int i=0; i
{
int x=int(random(0, width)); //random position of stars
int y=int(random(0, height));
starList.add(new Star(x, y));
}
}
///////////////////////////////////////////////////////////
void draw()
{
//black background
background(0, 0, 0);

// initialise the sky: place all the stars from the array into it!
//Also, pick a random colour (so stars change colour)
//and make it a random size!
pushStyle();
noStroke();
for (Star s: starList)
{
if (countCol%7==0) //pick random colour, with a delay
fill(s.randomColour(colourList));

if (countSize%3000==0) //pick random size, with a delay
rSize=s.randomSize(countSize);

ellipse(s.xpos, s.ypos, rSize, rSize);

countSize++;
}
popStyle();

//draw the constellation
constel.scribeConstellation();
//incrememnt the colour-change-speed counter
countCol++;
if (countCol>10000)
countCol=0;
}
////////////////////////////////////////////////////////////

void mousePressed()
{
for (Star s:starList)
{
if (s.inBounds(mouseX, mouseY))
// ASK AND ADD jiggle function
{

//the star clicked on becomes the current star, which is then activated
currentStar=s;
currentStar.activate();

// add a new element to the constellation
constel.addStar(currentStar);
/*TEST*/ println(constel.getSize());

// if the newest star added is the first star added, OR if right clicked, then the constellation is now complete
if (currentStar.isEqual(constel.path.get(0)) || mouseButton==RIGHT)
{
constellationList.add(constel); //add constel to the list of old constellations
constel=new Constellation();//empty constel
currentStar=new Star(); //empty the new star
//ALSO DO SOMETHING TO THE CONSTELLATION
}
}
}
}
/////////////////////////////////////////////////////////////

/////////////////////////////////////////////////////////////

class Constellation
{

ArrayList path;

Constellation()

{
path=new ArrayList<Star>();
}

void addStar(Star s)
{
path.add(s);
}

int getSize()
{
return path.size();
}

void scribeConstellation()
{
if (path.size() !=0)
{
pushStyle();

//loop and draw lines between all the stars in the constellation
for (int i=1; i
{
fill(255, 0, 0);
strokeJoin(ROUND);
strokeWeight(3);
line(path.get(i).xpos, path.get(i).ypos, path.get(i-1).xpos, path.get(i-1).ypos);
}

popStyle();
}
}
}

class Star
{

int size; //radius of a circular star
color clr;
int xpos;
int ypos;
boolean active; //whether or not the star is clicked on

Star()
{
size=0;
clr=color(0, 0, 0);
xpos=0;
ypos=0;
active=false;
}

Star(int x, int y)
{
size=5;
clr=color(0, 0, 255); //blue
xpos=x;
ypos=y;
active=false;
}

boolean inBounds(int x, int y) //mouseX and mouseY are passed in as x and y
{
return dist(x, y, xpos, ypos)<size; //if the x and y are within the circle, return true
}

void jiggle()
{
//put something in here
}

color randomColour(color [] arr)
{
int r =int(random(0, arr.length)); //random colour from colourList
return arr[r];
}
int randomSize(int a)
{
return int(map(noise(a), 0, 1, 5, 10));
}

boolean isEqual(Star s)
{
return s.xpos==xpos && s.ypos==ypos;
}

boolean isBlank()
{
return size==0;
}

void activate()
{
active=true;
}
}

////////////////////////////////////////////////////////////////

Final Project: “Unidentified”

cropped photoMy final project for the class is a data-visualization about UFO-sightings in the United States, accompanied by narratives of some of the reports. It was showcased in the ITP Winter showcase.

You can check out a demo video here.

Hovering over a state gives you info about the number of UFO sightings:

Screenshot-1

Clicking on  a state plays an audio excerpt of one of the user submissions to the database, and pulls up information about that particular sighting:

Screenshot-2

Here’s the code (it’s a little messy and full of notes and things):

import ddf.minim.*;

//The arrays 
String[] countbystate; //uploaded data for countsbystate.txt
String [] descriptions; // uploaded data from descriptions.txt
ArrayList  statelist; //the list of all states

//The voices and sounds
Minim minim;

//Whether to show title screen, credits and description
boolean showtitles;
boolean showcredits;
boolean showdescription; //included text description and audio narrative

//The current state, whose description we're showing
State currentstate;

//Font used
PFont font1; //small font
PFont font2; //large font

//Map
PImage map;//main map
PImage map2; //background map

//cursor
PImage clickcursor;

//Background for the second map
PGraphics canvas;

//buttons
Button titlesexit; //the button on the title screen to exit the titles and view map
Button creditsexit; // button on the credits screen to exit credits and view map
Button descriptionexit;// button to quit a description when
Button titlesshowbutton; //button on the map screen to show the titles again
Button creditsshowbutton; //the button on the map screen to show the credits

////////////////////////////////////////
void setup()
{
  size(displayWidth, displayHeight);

  //load the main map
  map = loadImage("USATestMap3GreenBlackDataColours.png");
  map.resize(width, height); //resize the image to fit the screen

  //load cursors
  clickcursor=loadImage("clickicon.png");
  clickcursor.resize(32, 32);

  //set the canvas as map2
  canvas = createGraphics(width, height);
  map2=loadImage("USATestMap3GreenBlackColours.png");
  map2.resize(width, height); //resize the image to fit the screen
  canvas.beginDraw();
  canvas.background(map2);
  canvas.endDraw();

  //create fonts
  font1=createFont("x-files", 30);
  font2=createFont("x-files", 45);

  //upload data
  countbystate=loadStrings("Count_by_state.txt");
  descriptions=loadStrings("descriptions.txt");

  //initilaise the arraylist to hold states
  statelist=new ArrayList ();

  //use countbystate to get info for each state
  for (int i=0;idraw()

{
  loadPixels();

  //Set the background to be the US Map
  background(map);

  //change cursor if moves on the map
  if (canvas.get(mouseX, mouseY)!=0)
  {
    cursor(HAND);
  }
  else
  {
    cursor(ARROW);
  }

  //println(canvas.get(mouseX,mouseY));

  ///show the count floating 
  //Show the count when you hover
  for (State s: statelist)
  {
    if (canvas.pixels[mouseX+mouseY*width]==s.mapcolour && showcredits==false && showtitles==false)
    {
      s.showFlicker(20, 700);
    }
  }

  //buttons to show credits and titles
  //titlesshowbutton.displayButton();
  //creditsshowbutton.displayButton();

  //The Heading
  pushStyle();
  textAlign(CENTER);
  fill(255);
  textFont(font1);
  text("UFO Sightings in Mainland USA", width/2, 35);

  popStyle();

  //titles
  if (showtitles)
  {
    displayTitles();
  }

  //credits
  if (showcredits)
  {
    displayCredits();
  }

  //descpriptions and narratives
  if (showdescription)
  {
    //currentstate.narrative.rewind();
    displayDescription();
    int temp_m=millis();
    playNarrative();

    if (currentstate.narrative.length()<=millis()-temp_m) //to try and get rid of that buzzing
    {
      //currentstate.narrative.mute();
    }
  }
  else if (currentstate!=null) // turn if sounds if want to prematurely exit
  {
    currentstate.narrative.mute();
  }

  //update colours
  /*for (State s: statelist) 
   for (int i=0; imousePressed()
{

  //button to show titles
  if (titlesshowbutton.isInside() && showtitles==false)
    showtitles=true;

  //button to show credits
  if (creditsshowbutton.isInside() && showcredits==false)
    showcredits=true;

  //click on a State. Show the description.
  if (showtitles==false && showcredits==false  && showdescription==false)
  {
    for (State s: statelist)
    {
      if (canvas.get(mouseX, mouseY)==s.mapcolour)
      {
        showdescription=true;
        currentstate=s;
        currentstate.narrative.rewind();
      }
    }
  } 
  //**************************************IMPTNT! This prevents the activation of a new state upon clicking on it to get rid of the description screen.
  else if (showdescription) {
    showdescription = false;
  }
}
////////////////////////////////////////////////
////////////////////////////////////////////////
////////////////////////////////////////////////

void keyPressed()
{
  /*//exit titles
   if (titlesexit.isInside() && showtitles==true) //if you're inside the button and the titles aren't showing
   {
   showtitles=false;
   delaytimer=second();
   }
   //if (showtitles==true)
   //showtitles=false;

   //exit button for credits
   if (showcredits==true)
   {
   showcredits=false;
   delaytimer2=second();
   }

   //exit description
   if (descriptionexit.isInside())
   showdescription=false;*/

  //exit titles
  if (showtitles==true) //if you're inside the button and the titles aren't showing
    showtitles=false;

  //exit button for credits
  if (showcredits==true)
    showcredits=false;

  //exit description
  if (showdescription=true)
    showdescription=false;

  //reload all the sound

}

////////////////////////////////////////////////
////////////////////////////////////////////////
////////////////////////////////////////////////
void stop() //closing minim and stuff
{
  for (State s:statelist)
    s.narrative.close();  
  minim.stop();
  super.stop();
}
class Button //basic, rectangular (round coerner), coloured button
{
  //positions using a CENTER rectmode
  float xpos;
  float ypos;
  float len; //length
  float high; //width
  color clr;
  String text;

  Button(float x, float y, float l, float h, color c, String txt)
  {
    xpos=x;
    ypos=y;
    len=l;
    high=h;
    clr=c;
    text=txt;
  }

  void displayButton()
  {
    pushStyle();
    rectMode(CENTER);
    noFill();
    stroke(clr);
    strokeWeight(7);
    rect(xpos, ypos-10, len, high, 10);
    fill(255);
    textAlign(CENTER);
    textFont(font1);
    text(text,xpos,ypos);
    popStyle();
  }

  boolean isInside() //checks if MOUSE is in the button
  {
    return mouseX<xpos+len &&="" mousex="">xpos-len && mouseY<ypos+high &&="" mousey="">ypos-high;
  }
}

class State
{
  String name; //name of the state
  float xpos; // the abscissa of the centre
  float ypos; //the ordinate of the centre
  int count; //the total number of UFO sightings in the state
  color mapcolour; //the color of the underlying, invisible map, for selection purposes
  color datacolour; //the color of the map using the data
  String description;
  AudioPlayer narrative;

  State (String n, int c, int cl, float x, float y)
  {
    name=n;
    xpos=x;
    ypos=y;
    count=c;
    mapcolour=cl;
    datacolour=0;
    description="";
  }

  //Add the narrative voice
  void addNarr(AudioPlayer ap)
  {
    narrative=ap;
  }

  //causes the count to appear on screen, flickering
  void showFlicker(float x, float y)
  { 
    pushStyle();
    textFont(font2);
    fill(255);

    String countstr=String.valueOf(count); //convert the count to a string first

      //flicker only happens sometimes, so check that
    float temp1=random(10);
    float temp2=random(10);

    if (temp1<9)  
      text(countstr+" UFO SIGHTINGS", x, y);

    if (temp2<9)
      text(name+":", x, y-40);

    popStyle();
  }
}

HW 6: Wiggly Doll

This week we were supposed to use a physics engine to make something cool. I made a doll whose hands and feet you can move about: the rest of the body will follow! Woot!

doll 2

Doll

 

Here’s the code:

import toxi.physics2d.*;
import toxi.physics2d.behaviors.*;
import toxi.geom.*;

// Initialise the physics world
VerletPhysics2D physics;

//body parts
//lefthand-->leftforearm-->leftelbow-->leftshoulder-->neck
//leftfoot-->leftshin--->leftknee-->leftleg--->crotch
//shin and forearm are stiff
EndChain lefthand;
Spring2D leftshoulder;
Spring2D leftforearm;

EndChain righthand;
Spring2D rightshoulder;
Spring2D rightforearm;

EndChain leftfoot;
Spring2D leftleg;
Spring2D leftshin;

EndChain rightfoot;
Spring2D rightleg;
Spring2D rightshin;

Particle crotch;
JoinParticle leftelbow;
JoinParticle rightelbow;
JoinParticle leftknee;
JoinParticle rightknee;

Spring2D body;
EndChain neck;

void setup()
{
  size(700, 700);

  //instantiate physics and set bounding box
  physics=new VerletPhysics2D();
  physics.setWorldBounds(new Rect(0, 0, width, height));

  //add gravity
  physics.addBehavior(new GravityBehavior(new Vec2D(0, 0.5)));

  //instantiate other stuff
  leftfoot=new EndChain(10, 20, 0.8, 10);
  leftknee=new JoinParticle(random(0,width/2), random(height/2,height));
  leftshin=new Spring2D(leftfoot.head, leftknee, 100, 0.5);
  crotch=new JoinParticle(width, random(height/2,height));
  leftleg=new Spring2D(leftknee, crotch, 100, 0.5);

  rightfoot=new EndChain(10, 20, 0.8, 10);
  rightknee=new JoinParticle(random(width/2,width), random(height/2,height));
  rightshin=new Spring2D(rightfoot.head, rightknee, 100, 0.5);
  rightleg=new Spring2D(rightknee, crotch, 100, 0.5);

  lefthand=new EndChain(10, 20, 0.8, 10);
  leftelbow=new JoinParticle(random(0,width/2), random(0,height/2));
  leftforearm=new Spring2D(lefthand.head, leftelbow, 100, 0.5);
  neck=new EndChain(30, 20, 0.8, 25);
  leftshoulder=new Spring2D(leftelbow, neck.head, 100, 0.5);

  righthand=new EndChain(10, 20, 0.8, 10);
  rightelbow=new JoinParticle(random(width/2,width), random(0,height/2));
  rightforearm=new Spring2D(righthand.head, rightelbow, 100, 0.5);
  rightshoulder=new Spring2D(rightelbow, neck.head, 100, 0.5);

  body=new Spring2D(crotch, neck.head, 200, 0.8);

  //Addd stuff to physics that wasn't added in constructors
  physics.addSpring(leftforearm);
  physics.addSpring(rightforearm);
  physics.addSpring(rightshoulder);
  physics.addSpring(leftshoulder);
  physics.addSpring(leftshin);
  physics.addSpring(rightshin);
  physics.addSpring(rightleg);
  physics.addSpring(leftleg);
  physics.addSpring(rightleg);
  physics.addSpring(body);

  physics.addParticle(crotch);
  physics.addParticle(leftknee);
  physics.addParticle(rightknee);
  physics.addParticle(leftelbow);
  physics.addParticle(rightelbow);
}

void draw()
{
  //white background
  background(255);

  //update the physics world!
  physics.update();

  //update tail locations
  leftfoot.updateTail(mouseX, mouseY);
  rightfoot.updateTail(mouseX, mouseY);
  lefthand.updateTail(mouseX, mouseY);
  righthand.updateTail(mouseX, mouseY);
  neck.updateTail(mouseX, mouseY);

  leftfoot.display();
  leftshin.display();
  leftknee.display();
  leftleg.display();

  rightfoot.display();
  rightshin.display();
  rightknee.display();
  rightleg.display();

  lefthand.display();
  leftforearm.display();
  leftelbow.display();
  leftshoulder.display();

  righthand.display();
  rightforearm.display();
  rightelbow.display();
  rightshoulder.display();

  neck.display();
  crotch.display();
  body.display();
}

void mousePressed() {
  // Check to see if we're grabbing anything
  leftfoot.contains(mouseX, mouseY);
  rightfoot.contains(mouseX, mouseY);
  lefthand.contains(mouseX, mouseY);
  righthand.contains(mouseX, mouseY);
  neck.contains(mouseX, mouseY);
}

void mouseReleased() {
  // Release the stuff
  leftfoot.release();
  rightfoot.release();
  lefthand.release();
  righthand.release();
  neck.release();
}
// adapted from SoftStringPendulum, in Nature of Code by Dan Shiffman

class Chain {

  // String properties
  float totalLength;  // How long
  int numPoints;      // How many points
  float strength;     // Strength of springs

  // list of particles in the chain
  ArrayList<Particle> particles;

  // an extra reference to the tail and head particles; last and first particles in the arraylist
  Particle tail;
  Particle head;

  // Chain constructor

  Chain()
  {
  }

  Chain(float l, int n, float s) 
  {
    particles = new ArrayList<Particle>();

    totalLength = l;
    numPoints = n;
    strength = s;

    float len = totalLength / numPoints;

    // Here is the real work, go through and add particles to the chain itself
    for (int i=0; i < numPoints; i++) {
      // Make a new particle with an initial starting location
      Particle particle=new Particle(random(0,width), i*len);

      // Redundancy, we put the particles both in physics and in our own ArrayList
      physics.addParticle(particle);
      particles.add(particle);

      // Connect the particles with a Spring (except for the head)
      if (i != 0) {
        Particle previous = particles.get(i-1);
        VerletSpring2D spring = new VerletSpring2D(particle, previous, len, strength);
        // Add the spring to the physics world
        physics.addSpring(spring);
      }
    }

    // Store the head reference
    head=particles.get(0);

    // Store reference to the tail
    tail = particles.get(numPoints-1);
  }

  // Draw the chain
  void display() 
  {
    // Draw line connecting all points
    pushStyle();
    beginShape();
    stroke(0);
    strokeWeight(2);
    noFill();
    for (Particle p : particles) 
    {
      vertex(p.x, p.y);
    }
    endShape();
    popStyle();
  }
}

///////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////

class EndChain extends Chain
{

  // variables for mouse dragging
  PVector offset = new PVector();
  boolean dragged = false;
  float radius;       // Radius of ball at tail

  // Constructors
  EndChain()
  {
    super();
  }

  EndChain(float l, int n, float s, float r) 
  {
    super(l, n, s);
    //radius
    radius=r;
    tail.radius = radius;
    tail.lock();
  }

  //sets radius of the ball at the end, also lock the tail (which is ALWAYS locked)
  /*void setRadius(float r)
   {
   radius=r;
   tail.radius = radius;
   tail.lock();
   }
   */

  // Check if a point is within the ball at the end of the chain
  // If so, set dragged = true;
  void contains(int x, int y) 
  {
    float d = dist(x, y, tail.x, tail.y);
    if (d < radius) 
    {
      offset.x = tail.x - x;
      offset.y = tail.y - y;
      dragged = true;
    }
  }

  // Release the ball
  void release() 
  {
    dragged = false;
  }

  // Update tail location if being dragged
  void updateTail(int x, int y) 
  {
    if (dragged) 
    {
      tail.set(x+offset.x, y+offset.y);
    }
  }

  void display() 
  {
    // Draw line connecting all points
    pushStyle();
    beginShape();
    stroke(0);
    strokeWeight(2);
    noFill();
    for (Particle p : particles) 
    {
      vertex(p.x, p.y);
    }
    endShape();
    tail.display(); // tail displays only in the endchain subclass
    popStyle();
  }
}

// adapted from SoftStringPendulum, in Nature of Code by Dan Shiffman

class Particle extends VerletParticle2D 
{
  float radius;  // Adding a radius for each particle

  Particle(float x, float y) 
  {
    super(x, y);
    radius=4;
  }

  //display particle
  void display() 
  {
    fill(255);
    stroke(0);
    strokeWeight(2);
    ellipse(x, y, radius*2, radius*2);
  }
}

/////////////////////////////////////
///////////////////////////////////////
/////////////////////////////////////
class JoinParticle extends Particle
{

  JoinParticle(float x, float y) 
  {
    super(x, y);
    radius=5;
  }

    void display() 
  {
    fill(0);
    stroke(0);
    strokeWeight(1);
    ellipse(x, y, radius*2, radius*2);
  }

}
class Spring2D extends VerletSpring2D
{

  Spring2D(Particle p1, Particle p2, float l, float s)
  {
    super(p1, p2, l, s);
  }

  void display()
  {
    pushStyle();

    beginShape();
    stroke(0);
    strokeWeight(5);
    noFill();
    vertex(a.x, a.y);
    vertex(b.x, b.y);
    endShape();

    /*fill(0);
     strokeWeight(3);
     stroke(a.x, a.y, b.x, b.y);*/
    popStyle();
  }
}

HW Week 4: More Classes and Objects: “Bacteria Builder”

This week, Dan Shiffman asked us to try and incorporate more complex class-mechanics (that sounds so D&D…) into our programs. I had am ambitious idea of making a microbiology game in a couple of days… Needless to say, I decided to turn this into a multi-week project! A glance at the syllabus shows that I’ll probably need to use material from future weeks in this project, so all is well!

The game (which I may call “Bacteria Builder”) puts you in the shoes of a genetic engineer tasked with building different bacteria to suit certain needs. You’ll be able to upgrade certain organelles (such as a gain a sturdy cell-wall for protection, or a flagellum for motion etc.) but you’ll only have a limited number of upgrades to pick from and you’ll have to decide which ones are right for the task.

Here’s the code I have so far:

// ICM HW Bacteriology 
//by Sharang Biswas
// I got the loading screen and basic functionality to work!
//next week: a) fill in other organelles and buttons. b)Do the "Game" part (do you win or not, calculations etc.)
//c) add some basic animation (wiggling cilia or something...

//Note the difference between "show" and "display". "Show" readies the object for display,while "display" actually sends it to the screen 

int h, w;  //scren height and width

//fonts>>> Remember to set these!
PFont f1; //Font used on intro screen and instructions
PFont f2; //other font

//define rectangle and round, for ease of use. Used for the shaper of the interactable.
int rectangle=-1;
int round=1;

//define bacillus and coccus variables, same as above;
int bacillus=0;
int coccus=1;

//Instantiate the interactables used, and their colours.
Interactable screen1;
Interactable submit;
Interactable inter_bac;
Interactable inter_coc;

//list of all interactable elements in the game. 
ArrayList<Interactable> ilist;

//instantiate the organelles
Membrane memb; //cell-membrane
Organelle nucl; //nucleus
Organelle wall; //cell wall

///////////////////////////////////////////////////////////////////////
void setup()
{
  h=800;
  w=800;
  size(h, w);
  background(255, 255, 255);

  //setup the fonts
  f1=createFont("Garamond Bold", 20);
  f2=createFont("Garamond", 14);

  //initialise all the interactables; define their colours
  color c1=color(0, 0, 125); 
  //the start screen
  screen1=new Interactable("Welcome famed Gentic Engineer! \nIt's your task to build bacteria to fulfill mankind's needs.\nChoose the organelles you think will contribute to a task and we can test them out in our simulation!\nPress any key to continue", w/2, h/2, h-10, w-10, c1, rectangle);
  //the submit button
  submit=new Interactable("SUBMIT", w/2, h-10, 160, 50, c1, rectangle);
  //Membrane picker: bacillus or cocci
  inter_bac=new Interactable("Bacillus", 10, 3*h/4, 160, 50, c1, rectangle);
  inter_coc=new Interactable("Coccus", 180, 3*h/4, 160, 50, c1, rectangle);

  //initialise the organelles
  memb=new Membrane(0);

  //add the interactables (except screen1 to the array)
  ilist=new ArrayList<Interactable>();
  ilist.add(submit);//**************************NEED TO ADD THIS**************************//
  ilist.add(inter_bac);
  ilist.add(inter_coc);

  //show the starting screen
  screen1.show();

  //show all the interactables
  for (int i=0; i<ilist.size(); i++)
  {
    // disply all interactables. Remember, only the ones marked true for the "shown"
    // variable will actually display
    ilist.get(i).show();
  }
}
/////////////////////////////////////////////////////////////////////

void draw()
{
  background(255, 255, 255); //white background

  // disply all interactables. Remember, only the ones marked true for the "shown"
  // variable will actually display
  for (int i=0; i<ilist.size(); i++)
  {
    ilist.get(i).display(f1);
  }

  //draw the membrane
  memb.display();

  // display the start screen ON TOP OF everything
  screen1.display(f2);
}

////////////////////////////////////////////////////////////////////////

void keyPressed()
{
  //Deactivate the Intro Screen
  if (screen1.shown==true)
  {
    screen1.hide();
    println(screen1.shown);
  }
}

void mousePressed()
{

  //choosing mebrane
  if (inter_bac.isInBounds(mouseX, mouseY) || inter_coc.isInBounds(mouseX, mouseY))
  { 
    inter_coc.deactivate();
    inter_bac.deactivate();

    if (inter_bac.isInBounds(mouseX, mouseY))
      inter_bac.activate();

    else if (inter_coc.isInBounds(mouseX, mouseY))
      inter_coc.activate();

    memb.change(inter_bac, inter_coc);
  }
}
/////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////
class Interactable
{
  String label;
  int x, y; //position on screen
  int w, h; // width and height, or horizontal radius and vertical radius
  color fillcolour; 
  boolean active; // whether or not the element is active
  boolean shown;
  int shape;

  Interactable(String s, int x1, int y1, int w1, int h1, color c, int shape1)
  {
    label=s;
    x=x1;
    y=y1;
    h=h1;
    w=w1;
    fillcolour=c;
    active=false;
    shown=false;
    shape=shape1;
  }

  void display(PFont ftemp) //actually displays the interactable. Needs to take in font!
  {
    pushMatrix();
    // use centre of everything
    rectMode(CENTER);

    //choose rectange or round interactable
    if (shown)
    {
      fill(fillcolour);
      if (shape==rectangle)
      {
        rect(x, y, w, h);
      }

      else
      {
        ellipse(x, y, w, h);
      }

      //display the text on the button (regardless of shape)
      fill(0, 0, 0); //text is black
      textFont(ftemp);
      text(label, x, y);
    }

    popMatrix();
  }

  void show() // changes the "shown" variable to true. 
  {
    shown=true;
  }

  void hide()//changes the shown variable to false
  {
    shown=false;
  }

  void activate () //changes the active variable to true. Takes in MouseX, MouseY and checks if its within the button's bounds.
  {
    active=true;
  }

  void deactivate() // changes the active variable to false
  {
    active=false;
  }

  boolean isInBounds (int mx, int my)
  {
    boolean answer;
    if (mx<x+w/2 && mx>x-w/2 && my<y+h/2 && my>y-h/2)
    {
      return true;
    }

    return false;
  }
}
//////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////
class Organelle
{

  int cost;

  Organelle()
  {
    cost=0;
  }

  Organelle(int x)
  {
    cost=x;
  }
}

/////////////////////////////////////////////////////

class Nucleus extends Organelle
{
  Nucleus()
  {
    super();
  }
}

//////////////////////////////////////////////////////

class Membrane extends Organelle
{
  int memshape;
  Membrane( int x)
  {
    super();
    memshape=0; //default to bacilli=0, coccus =1
  }

  void change(Interactable bac, Interactable coc) //changes the shape of the membrane, based on button states put in MUST be this order
  {
    if (bac.active)
    {
      memshape=0;
    }
    else if (coc.active)
    {
      memshape=1;
    }
  }

  void display() // displays the membrane
  {
    pushMatrix();
    fill(255, 255, 255);
    if (memshape==0)
    {
      rectMode(CENTER);
      rect(w/2, h/2-100, 400, 200, 13); //the last number's the curvature of the corners
    }
    else if (memshape==1)
    {
      ellipseMode(CENTER);
      ellipse(w/2, h/2-100, 400, 200);
    } 
    popMatrix();
  }

  ///////////////////////////////////////////////////////

  class CellWall extends Organelle
  {
    CellWall()
    {
      super();
    }
  }
}
///////////////////////////////////////////////////////

HW Week 3: Classes and Objects “Runic Spells”

This week, we were asked to incorporate object-oriented programming. I wanted to make something more interactive and game-like, something that random users could have some fun with. I also like incorporating fantasy elements into my work, so I decided create a little spell-casting app!

An few hours of research and several hours of coding produced a clickable, modified Armanen rune system. Hovering over each rune brings up a larger image, its name and its associations, while clicking on it also “casts” a spell and generates a description of its effect. Each spell has more than one description!Runes_1

Runes_2

//Runic Spells by Sharang Biswas
//ICM Wk 3

PImage fa_img, fa_img2, ur_img, ur_img2, thorn_img, thorn_img2, os_img, os_img2, rit_img, rit_img2, ka_img, ka_img2, hagal_img, hagal_img2, not_img, not_img2, is_img, is_img2, ar_img, ar_img2, sol_img, sol_img2, tyr_img, tyr_img2, 
bar_img, bar_img2, laf_img, laf_img2, man_img, man_img2, yr_img, yr_img2, eh_img, eh_img2, ge_img, ge_img2;
PFont f1, f2;
int screenh=855; //screen parameters
int screenl=800; 

//instantiate all the runes 
Rune fa, ur, thorn, os, rit, ka, hagal, not, is, ar, sol, tyr, bar, laf, man, yr, eh, ge;

int posx=170;
int posy=90; //start positions for the images

//array to hold the runes
Rune [] runelist;

void setup()
{
  size(screenh, screenl); //screen size

  //load images, resize larger versions
  fa_img=loadImage("Fa.gif"); 
  fa_img2=loadImage("Fa.gif");
  fa_img2.resize(80, 80);

  ur_img=loadImage("Ur.gif");
  ur_img2=loadImage("Ur.gif");
  ur_img2.resize(80, 80);

  thorn_img=loadImage("Thorn.gif");
  thorn_img2=loadImage("Thorn.gif");
  thorn_img2.resize(80, 80);

  os_img=loadImage("Os.gif");
  os_img2=loadImage("Os.gif");
  os_img2.resize(80, 80);

  rit_img=loadImage("Rit.gif");
  rit_img2=loadImage("Rit.gif");
  rit_img2.resize(80, 80);

  ka_img=loadImage("Ka.gif");
  ka_img2=loadImage("Ka.gif");
  ka_img2.resize(80, 80);

  hagal_img=loadImage("Hagal.gif");
  hagal_img2=loadImage("Hagal.gif");
  hagal_img2.resize(80, 80);

  not_img=loadImage("Not.gif");
  not_img2=loadImage("Not.gif");
  not_img2.resize(80, 80);

  is_img=loadImage("Is.gif");
  is_img2=loadImage("Is.gif");
  is_img2.resize(80, 80);

  ar_img=loadImage("Ar.gif");
  ar_img2=loadImage("Ar.gif");
  ar_img2.resize(80, 80);

  sol_img=loadImage("Sol.gif");
  sol_img2=loadImage("Sol.gif");
  sol_img2.resize(80, 80);

  tyr_img=loadImage("Tyr.gif");
  tyr_img2=loadImage("Tyr.gif");
  tyr_img2.resize(80, 80);

  bar_img=loadImage("Bar.gif");
  bar_img2=loadImage("Bar.gif");
  bar_img2.resize(80, 80);

  laf_img=loadImage("Laf.gif");
  laf_img2=loadImage("Laf.gif");
  laf_img2.resize(80, 80);

  man_img=loadImage("Man.gif");
  man_img2=loadImage("Man.gif");
  man_img2.resize(80, 80);

  yr_img=loadImage("Yr.gif");
  yr_img2=loadImage("Yr.gif");
  yr_img2.resize(80, 80);

  eh_img=loadImage("Eh.gif");
  eh_img2=loadImage("Eh.gif");
  eh_img2.resize(80, 80);

  ge_img=loadImage("Ge.gif");
  ge_img2=loadImage("Ge.gif");
  ge_img2.resize(80, 80);

  f1=createFont("Garamond", 30);//lead font used
  f2=createFont("Garamond", 20);

  //initialise my runes;
  fa=new Rune("Fa", "Fire and Transformation", "Ouch! You blister your fingers.", "Uh-oh: yous house has caught fire!", "With a swirl of fiery light, you feel yourself transform. You are now a stoat. Live with it.", fa_img, fa_img2, posx, posy);
  ur=new Rune("Ur", "Resurrection and Eternity", "Grandma? Is that you?", "Ooh, your acne's all cleared up!", "You transform into a incorporeal, eternal watcher. Kinda boring...", ur_img, ur_img2, posx+50, posy);
  thorn=new Rune("Thorn", "Lighnting and the Phallus", "Boom! Flash! Sizzle...", "Oh wow! You...err...grew...", "Dust and dirt fly towards you. You are now statically charged FOREVER!", thorn_img, thorn_img2, posx+100, posy);
  os=new Rune("Os", "Speech and Freedom", "You can't stop talking! Oh no!", "You can transmit your voice over great distances. You are now a human telephone. Whoopee.", "You no longer have to go to work tomorrow.", os_img, os_img2, posx+149, posy);
  rit=new Rune("Rit", "Law and Ritual", "Poof! you're a politician. Now go make yourself useful", "You did the spell correctly! Nothing really happens, you can just bask in the glory of being right.", "The laws of the universe unravel. Thanks a bunch.", rit_img, rit_img2, posx+199, posy);
  ka=new Rune("Ka", "The World Tree and the Feminine", "Man! I feel like a woman!", "You feel emboldened. Time to go kick some ass!", "A giant tree appears before you. Now there's a hole in the ceiling.", ka_img, ka_img2, posx+246, posy);
  hagal=new Rune("Hagal", "Enclosure and Hail", " It's hailing. Way to mess up the climate.", "Damn: you're trapped in a labyrnth. And you don't even have any yarn...", "You can't move. It's like Neville with Hermione and her Petrificus totalus. Bummer.", hagal_img, hagal_img2, posx+294, posy);
  not=new Rune("Not", "Need and Fate", "You sudenly NEED to go to the bathroom. NOW!", "You change the future. Pity you can't see the future to admire your results.", "Your fate has been sealed. No, I don't know what it is: it's sealed!", not_img, not_img2, posx+344, posy);
  is=new Rune("Is", "Ice and Iron", "An arctic blasts fills the room. You now have frostbite on your nose. Fun.", "You magically know where North is.", "Your arm turns to steel. You could defeat any foe with it, if only you could lift it...", is_img, is_img2, posx+394, posy);  
  ar=new Rune("Ar", "Nobility and the Sun", "A puff of smoke and a crown appears on your head. Pretty!", "You get a bad case of sunburn.", "You feel warmer and start to sweat. Now you smell.", ar_img, ar_img2, posx+443, posy);
  sol=new Rune ("Sol", "Victory and Salvation", "You win at life. Keep it up!", "You're saved! Now go sin as much as you want!", "A gold medal appears around your neck. Shiny!", sol_img, sol_img2, posx+492, posy);
  tyr=new Rune("Tyr", "Animals and Generation", "A horde of kittens fills the room. Revel in the cuteness!", "You grow a third arm. Now you can do your homework EVEN faster!", "your homework magically doubles. Don't worry: it's character-building.", tyr_img, tyr_img2, posx+100, posy+100);
  bar=new Rune("Bar", "Song and Birth", "Your singing ability now surpasses famous opera singers. Congratulations! Go join American Idol!", "Congratulations: you're pregnant! With twins!", "You can't get 'Never Gonna Give You Up' out of your head!", bar_img, bar_img2, posx+150, posy+100);
  laf=new Rune("Laf", "Water and Defeat", "You lose the game", "You're suddenly soaking wet. Brrrr...", "A constant raincloud will now follow you everywhere. What a downer.", laf_img, laf_img2, posx+200, posy+100);
  man=new Rune("Man", "Noon and Masculinity", "You cause a partial penumbral lunar eclipse. Yay?", "MANLINESS!!! ROAR!!!!!!", "Floods. Floods all over. Way to go. Not.", man_img, man_img2, posx+250, posy+100);
  yr=new Rune ("Yr", "Anger and Rainbows", "Double Rainbow! What does it mean??", "You are now attracted to all genders. Variety is the spice of life!", "FURY COURSES THROUGH YOUR VEINS!", yr_img, yr_img2, posx+300, posy+100);
  eh=new Rune("Eh", "Marriage and Horses", "You now have your very own pony!", "A wedding ring appears on your finger. Now you need to figure out who you're married to...", "Your kingdom is now for a horse", eh_img, eh_img2, posx+350, posy+100);
  ge=new Rune("Ge", "Death and Gifts", "A brand new coffin, just for you!", "You die. GAME OVER", "A cat arrives in a box in the mail. It is both dead and alive until you open the box.", ge_img, ge_img2, posx+400, posy+100);

}

void draw()
{

  background(255, 255, 255); //white background
  fill(192, 192, 192); //colour of font (black)
  imageMode(CENTER);

  //display all the runes on-screen
  textFont(f2);
  fa.display();
  ur.display();
  thorn.display();
  os.display();
  rit.display();
  ka.display();
  hagal.display();
  not.display();
  is.display();
  ar.display();
  sol.display();
  tyr.display();
  bar.display();
  laf.display();
  man.display();
  yr.display();
  eh.display();
  ge.display();

  fa.hover();
  ur.hover();
  thorn.hover();
  os.hover();
  rit.hover();
  ka.hover();
  hagal.hover();
  not.hover();
  is.hover();
  ar.hover();
  sol.hover();
  tyr.hover();
  bar.hover();
  laf.hover();
  man.hover();
  yr.hover();
  eh.hover();
  ge.hover();

  // write intro text
  textFont(f1); //use the font previously loaded
  text("Click on a rune to cast a spell and see its effect:", screenh/2-275, 30);
}

void mousePressed() {
  int a=mouseX;
  int b=mouseY;

  fa.uncast();
  ur.uncast();
  thorn.uncast();
  os.uncast();
  rit.uncast();
  ka.uncast();
  hagal.uncast();
  not.uncast();
  is.uncast();
  ar.uncast();
  sol.uncast();
  tyr.uncast();
  bar.uncast();
  laf.uncast();
  man.uncast();
  yr.uncast();
  eh.uncast();
  ge.uncast();

  fa.cast(a, b);
  ur.cast(a, b);
  thorn.cast(a, b);
  os.cast(a, b);
  rit.cast(a, b);
  ka.cast(a, b);
  hagal.cast(a, b);
  not.cast(a, b);
  is.cast(a, b);
  ar.cast(a, b);
  sol.cast(a, b);
  tyr.cast(a, b);
  bar.cast(a, b);
  laf.cast(a, b);
  man.cast(a, b);
  yr.cast(a, b);
  eh.cast(a, b);
  ge.cast(a, b);
}
class Rune
{

  String name;
  String keyw;
  String [] effect;
  PImage picture;
  PImage lpicture; //larger version
  int x, y; //position on screen, center of each rune

  boolean active = false;
  int r=-1; 

  Rune(String name1, String keyw1, String effect0, String effect1, String effect2, PImage picture1, PImage picture2, int x1, int y1)
  {
    name=name1;
    keyw=keyw1;
    effect=new String[3];
    effect[0]=effect0;
    effect[1]=effect1;
    effect[2]=effect2;
    picture=picture1;
    lpicture=picture2;
    r=0;

    x=x1;
    y=y1;
  }

  void resize(int w, int h) {
    picture.resize(w, h);
  }

  void display() //displays the image on the screen
  { 

    imageMode(CENTER);
    image(picture, x, y);

if (active) 
{
  image(lpicture, 400, 400);
  PFont f=createFont("Garamond Bold", 35);
  textFont(f);
  text(name, 375, 470); 
  PFont f1= createFont("Garamond Italic", 30);
  textFont(f1);
  text(keyw, 330, 495);
  PFont f2=createFont("Garamond Bold", 20);
  textFont(f2);
  fill(0, 0, 0);
  text("\""+effect[r]+"\"", 10, 545);
  fill(192, 192, 192);
}
}

void cast(float px, float py) {
  //action when rune is clicked
  if (px<x+25 && px>x-25 && py<y+25 && py>y-25)
  {
    active = true;
    r=int(random(0, 3));
  }
}

void hover()
{
  if (mouseX<x+25 && mouseX>x-25 && mouseY<y+25 && mouseY>y-25)
  {
    PFont smallfont=createFont("Garamond Bold", 18);
    textFont(smallfont);
    text(name, x-15, y+55);
    PFont smallerfont=createFont("Garamond Italic", 15);
    textFont(smallerfont);
    text(keyw, x-55, y+68);
    image(lpicture, x, y);
  }
}

void uncast()
{
  active=false;
}
}