Your browser does not support the canvas tag.

Week 4

Big problems!
1. In normal pong the ball speeds up when it hits a paddle increasing the difficulty. We could just up the velocity.x and this works, but wouldn't it be nice if is sped up in the direction it was going, not just along one axis? (answer: "yes, Joe, it would. I love learning!")
2. What if you want your pong ball to be a rect or an image that "faces" the direction it's going?

Hooray! We can solve both of these problems by adding a little trig to our code! DON'T PANIC - the computer does all the maths for us. So the ball object needs two new variables, rotation that tells us which way it's headed and speed to tell us how fast it's going. Use sin and cos with the rotation and speed to get the x and y velocity. Then add the velocity to the location like we did last week. This complicates the wall bouncing slightly too. Instead of multiplying the velocity.x or velocity.y by -1 now you change the rotation variable. Fortunately this is pretty easy because our walls have a rotation of 0 or PI making the maths simple.

PVector ballLoc, velocity; 
float  r2 = .5; 
float speed = 4; 
void setup() {
  size (600, 400);
  smooth();
  ballLoc = new PVector(width/2, height/2);
  velocity = new PVector(0, 0);
  rectMode(CENTER);
}
void draw() {
  background(200);  
  velocity.x = cos(r2)*speed;  
  velocity.y = sin(r2)*speed;
  ballLoc.add(velocity); 
  pushMatrix(); // save current state
  translate(ballLoc.x, ballLoc.y); // make the "center" the new location
  rotate(r2); 
  line(0, 0, -40, 0); // draw the line and circle fromthis new center. 
  rect(0, 0, 10, 10);
  popMatrix(); // go back to current state. 
  if (ballLoc.x > width ||  ballLoc.x < 0) {
    r2 = PI - r2;
  }
  if (ballLoc.y > height || ballLoc.y < 0) {
    r2 = - r2;
  }
}

Complicated Bouncing

Okay, admittedly this get a little heady so if you want to ignore this part go ahead. But what if you want to bounce off something that's not perfectly flat or vertical? There's a couple issues. First is detecting the collision. There are a few ways to crack this nut, but one of the cleanest methods is to use two functions, one that checks to see if it has crossed the a line, the second to see if it's touching a line. These algorithms get a little tricky but it's mostly reapplying the Pythagorean theorem a couple times. The second problem is determining the angle the ball should leave at. First we calculate the angle it's coming in relative to an angle perpendicular to the angle of the line (angle of incidence). Then we calculate the angle it should go out by subtracting it from the perpendicular angle (angle of reflection). Physics!


 
PVector lineStart, lineEnd, ball, oldball, velocity; 
int balldiameter = 20; 
boolean linehit; 
color ballcolor = color(100, 200, 100); 
float rot = 2.8; 
float speedx, speedy; 
float speed = 6; 

void setup() {
  size(400, 400);
  smooth();
  lineStart = new PVector(200, 200); 
  lineEnd = new PVector(200, 200); 
  ball = new PVector(200, 200); 
  oldball = new PVector(200, 200); 
  velocity = new PVector(0, 0);
}
void draw() {
  background(200);  
  lineEnd.x = mouseX; 
  lineEnd.y = mouseY; 
  velocity.x = cos(rot)*(speed); 
  velocity.y = sin(rot)*(speed); 
  oldball.x = ball.x; 
  oldball.y = ball.y;
  ball.add(velocity); 
  if (ball.x < 0 || ball.x > width) { // side wall
    rot = PI - rot;
    linehit = false;
  }
  if (ball.y < 0 || ball.y > height) { // top & bottom
    rot = -rot; 
    linehit = false;
  }
  if (crossOverLine(ball.x, ball.y, oldball.x, oldball.y, lineStart.x, lineStart.y, lineEnd.x, lineEnd.y)) {
    bounce();
  } else  if (touchingLine(lineStart.x, lineStart.y, lineEnd.x, lineEnd.y, ball.x, ball.y, balldiameter/2)) {
    bounce();
  }
  stroke(0); 
  line (lineStart.x, lineStart.y, lineEnd.x, lineEnd.y); 
  fill(ballcolor); 
  ellipse (ball.x, ball.y, balldiameter, balldiameter);
  float ang  = atan2(lineStart.y-lineEnd.y, lineStart.x-lineEnd.x); 
  PVector crossEnd = new PVector(cos(ang + HALF_PI)*(20), sin(ang + HALF_PI)*(20)); 
  PVector crossStart = new PVector((lineEnd.x - lineStart.x)/2 + lineStart.x, (lineEnd.y - lineStart.y)/2 + lineStart.y); 
  fill(255, 0, 0); 
  ellipse (crossStart.x, crossStart.y, 6, 6); 
  line(crossStart.x, crossStart.y, crossStart.x + crossEnd.x, crossStart.y + crossEnd.y);
}

void bounce() {
  if (linehit == false) {
    linehit = true; 
    float ang  = atan2(lineStart.y-lineEnd.y, lineStart.x-lineEnd.x);
    float d = (ang + HALF_PI) - rot + PI; // get the angle between r and perpensicular
    rot =  d + (ang + HALF_PI) ; // add that angle to perpendicular
  }
}

boolean crossOverLine (float px, float py, float qx, float qy, float ax, float ay, float bx, float by) {
  float s, t, w1, w2, w3, w4, w5, w6;
  w1 = ax*(qy-py);
  w2 = (bx-ax)*(qy-py);
  w3 = px*(qy-py);
  w4= (qx-px)*ay;
  w5= (by-ay)*(qx-px);
  w6= py*(qx-px);
  s = (w3-w1+w4-w6)/(w2-w5);
  t = (ax+s*(bx-ax)-px)/(qx-px);
  if (s<0 || s>1 || qx==px || t<0 || t > 1 || w2==w5) {
    return false;
  } else {
    return true;
  }
}

boolean touchingLine (float px, float py, float qx, float qy, float cx, float cy, int rad ) {
  float dx, dy, t, rt;
  dx = qx-px; // get lenght of line
  dy = qy-py;
  t = -((px-cx)*dx+(py-cy)*dy)/((dx*dx)+(dy*dy)); 
  if (t<0.0) {
    t=0.0;
  } else if (t>1.0) {
    t = 1.0;
  }
  dx = (px+t*(qx-px))-cx;
  dy = (py +t*(qy-py))-cy;
  rt = (dx*dx) +(dy*dy);
  if (rt<(rad*rad)) {
    return true;
  } else {
    return false;
  }
}


laser cutter check list

If there's a fire
Q: "I know you didn't mention this material but is it okay if I cut n" - where n is all materials not mentioned.
A: Probably not and you'd 100% need to run it by Paul before you try. Some materials, when cut, create gasses which can kill you and worse, dammage the laser cutter.
Pong with score and arduino
import processing.serial.*; // accesses the serial library
import cc.arduino.*; // accesses the arduino library
Arduino arduino; // creates a variable called arduino

ArrayList<BallClass> ballslist = new ArrayList<BallClass>();
PVector player1, player2; 
int playerW = 15; // width of paddle
int playerH = 60; // hieght of paddle
int score1, score2; 
PFont font; 
long timer; // global
int interval = 1000; 
boolean startBall; 
boolean arduinoSwitch = true; 
void setup() {
  size(600, 400); 
  rectMode(CENTER); // draw rects from center
  ballslist = new ArrayList();
  if (arduinoSwitch) {
    for (int i = 0; i < Arduino.list ().length; i ++) {
      println(i + "  " + Arduino.list()[i]); // prints your USB bus
    }
    arduino = new Arduino(this, Arduino.list()[15], 57600); // sets our variable "arduino" to the actual arduino
  }

  font = createFont("helvetica", 48); 
  textFont(font); 
  textAlign(CENTER); 
  score1 = 0; 
  score2 = 0;
  restart();
}

void restart() {
  for (int i = ballslist.size()-1; i >= 0; i--) {
    ballslist.remove(i); // clear all the balls
  }
  ballslist.add(new BallClass());
  ballslist.add(new BallClass());
  player1 = new PVector(20, height/2); 
  player2 = new PVector(width -20, height/2); 
  timer = millis(); // in setup
  startBall = false;
}

void draw () {
  background (50);
  if (millis() - timer > interval) { //timer to pause the ball before it starts
    startBall = true;
  }

  text(score1, 40, 40); 
  text(score2, width-40, 40); 
  if(arduinoSwitch){
  player1.y = map(arduino.analogRead(0), 0, 1023, playerH/2, height - playerH/2); 
  player2.y = map(arduino.analogRead(1), 0, 1023, playerH/2, height - playerH/2); 
  }
  rect(player1.x, player1.y, playerW, playerH); 
  rect(player2.x, player2.y, playerW, playerH); 
  //println(arduino.analogRead(0)); 
  for (int i = ballslist.size()-1; i >= 0; i--) { 
    BallClass dball = ballslist.get(i); 
    if (startBall == true) {
      dball.move();
    }
    dball.display();
  }
}

class BallClass {
  PVector ballLoc; 
  int ballsize = 16; // width of ball
  float speedX, speedY; 
  BallClass() {
    ballLoc = new PVector(width/2, height/2);
    speedX = 4.2; 
    speedY = random(.1, .4);
    if (random(2) > 1) {
      speedX *= -1;
    }
    if (random(2) > 1) {
      speedY *= -1;
    }
  }

  void move() {
    ballLoc.x += speedX;
    ballLoc.y += speedY;
    //restart()
    if (ballLoc.x + ballsize/2> width) {
      score1 ++; 
      restart();
    }

    if (ballLoc.x - ballsize/2< 0) {
      score2 ++; 
      restart();
    }
    if (ballLoc.y + ballsize/2> height || ballLoc.y - ballsize/2< 0) {
      speedY *= -1;
    }
    paddle(player2); 
    paddle(player1);
  }
  void paddle(PVector pad) {
    if (ballLoc.x + ballsize/2> pad.x - playerW/2 &&
      ballLoc.x - ballsize/2< pad.x + playerW/2 &&
      ballLoc.y + ballsize/2 > pad.y - playerH/2 && 
      ballLoc.y - ballsize/2 < pad.y + playerH/2
      ) {
      speedX *= -1;
    }
  }
  void display() {
    ellipse(ballLoc.x, ballLoc.y, ballsize, ballsize);
  }
} 

This week's reading is from Olia Lialina.