Week 07: Programming practice

Topics:

  • Practice with the concepts introduced so far in the course

  • Implementing more complex programs

  • Box intersection testing

Resources:

Draw vs. Loops

Recall the following program from lab 05

week07 circles

int numCircles = 10;
float radius = 200;

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

  float delta = 2*pi/numCircles;
  for (float angle = 0; angle < 2*PI; angle += delta) {
    float x = 250 + radius * cos(angle);
    float y = 250 + radius * sin(angle);
    ellipse(x, y, 50, 50);
  }
}

Let’s modify this program to draw the same image using draw() instead of a loop

int numCircles = 100;
float radius = 200;
float delta = 2*PI/numCircles;

// approach: turn loop variable into accumulator variable
float angle = 0;

void setup() {
  size(500,500);
}

void draw() {
  float x = 250 + radius * cos(angle);
  float y = 250 + radius * sin(angle);
  ellipse(x, y, 50, 50);
  angle += delta;
}

What are the differences between the two approaches?

  • With a loop, the code executes a finite number of steps (10) and we only see the result after it’s complete

  • With draw(), the code executes forever (unless we add an if statement to stop further drawing). Note that we do not call background to clear the screen.

Now modify the above program to animate one circle so it moves in a circle.

week07 circleMove

float radius = 200;
float delta = 0.1; // rate of change
float angle = 0;

void setup() {
  size(500,500);
}

void draw() {
  background(200);
  float x = 250 + radius * cos(angle);
  float y = 250 + radius * sin(angle);
  ellipse(x, y, 50, 50);
  angle += delta;
}

Array pallet

Write a program that creates 200 random rectangles.

  • The position and sizes of the rectangles should be random

  • The colors of the rectangle should be chosen randomly from a pallet of colors

    • For example, the image below was created with: #01315e, #92266c, #ff7979, #ffc174, #fff972

  • For fun, check out these sites for pallet ideas

week07 pallet

void drawRectangles() {
  color[] colors = {#01315e, #92266c, #ff7979, #ffc174, #fff972};
  for (int i = 0; i < 200; i++) {
    int idx = floor(random(0, colors.length));
    fill(colors[idx]);
    float x = random(0, width);
    float y = random(0, height);
    float w = random(0, width);
    float h = random(0, height);
    rect(x,y,w,h);
  }
}

void setup() {
  size(500,500);
  drawRectangles();
}

Keyboard controls

Write a program that supports the following controls for a rectangle character:

  • Pressing 'd' moves right

  • Pressing 'a' moves left

  • Pressing 'w' moves up

  • Pressing 's' moves down

week07 controls

float xPos = 0;
float yPos = 0;

void setup() {
  size(500,500);
}

void draw() {
  background(200);

  if (keyPressed) {
    if (key == 'a') {
      xPos = xPos - 10;
    }
    else if (key == 'd') {
      xPos = xPos + 10;
    }
    else if (key == 'w') {
      yPos = yPos - 10;
    }
    else if (key =='s') {
      yPos = yPos + 10;
    }
  }

  // Put in edge conditions
  // wrap or clamp
  rect(xPos, yPos, 100, 100);
}

How could we restrict the character’s movement to stay inside the canvas?

  • Clamp

  // clamp
  if (xPos > width-100) {
    xPos = width-100;
  }
  else if (xPos < 0) {
    xPos = 0;
  }
  if (yPos > height-100) {
    yPos = height-100;
  }
  else if (yPos < 0) {
    yPos = 0;
  }
  • Wrap

  if (xPos > width) {
    xPos = 0;
  }
  else if (xPos < 0) {
    xPos = width;
  }
  if (yPos > height) {
    yPos = 0;
  }
  else if (yPos < 0) {
    yPos = height;
  }

Hover selection

Recall the text box from Lab 06. Let’s modify it to create square buttons that supports a mouse hover highlight.

week07 buttons

// TODO: Implement insideTextBox
// Input: message (String) the text inside the box; Use this to get the box width/height
// Input: x (float) the X position of the text
// Input: y (float) the Y position of the text
// Returns boolean: true if the point mouseX,mouseY is inside the box; false otherwise
boolean insideTextBox(String message, float x, float y) {
  float tw = textWidth(message);
  float th = textAscent() + textDescent();

  float topLeftX = x - tw * 0.5;
  float topLeftY = y - th * 0.75;
  if (mouseX >= topLeftX && mouseX <= topLeftX+tw &&
      mouseY >= topLeftY && mouseY <= topLeftY+th) {
    return true;
  }

  return false;
}

void drawTextBox(String message, float x, float y) {
  float tw = textWidth(message);
  float th = textAscent() + textDescent();

  // TODO: Call insideTextBox to add highlight behavior
  fill(255);
  rect(x, y - th * 0.25, tw, th);

  fill(0);
  text(message, x, y);
}

void setup() {
  size(500,500);
  fill(0);
  textSize(64);
  textAlign(CENTER);
  rectMode(CENTER);
}

void draw() {
  background(200);
  drawTextBox("Yes", 150, 250);
  drawTextBox("No", 350, 250);
}

void mousePressed() {
  if (box1.inside(mouseX, mouseY)) {
    println("CLICK BOX1");
  }
  else if (box2.inside(mouseX, mouseY)) {
    println("CLICK BOX2");
  }
  else {
    println("CLICK NOTHING");
  }
}

Collisions

week07 collisions

Write a program that says "BAM!!" whenever two boxes collide. We will use classes for this program.

// Bryn Mawr 2020
// The main program
Box box1 = new Box(0,0,100,130, #8232E0);
Box box2 = new Box(300,400, 130, 100, #54832A);

void setup() {
  size(500,500);
  textSize(128);
  textAlign(CENTER);
}

void draw() {
  background(200);

  box1.update();
  box2.update();
  box1.draw();
  box2.draw();

  if (box1.intersects(box2)) {
    fill(0);
    push();
    translate(250,250);
    rotate(-PI/6);
    text("BAM!", 0, 0);
    pop();
  }
}

The Box class. Note that the boxes in this program use CORNER mode, not CENTER! Our Box class already has member variables for the position, size, and color. We’ve also implemented the constructor and accessor functions for you.

Now, let’s implement:

  • update

  • intersects

  • inside

class Box {

  // Uses corner mode!
  float mX = 0;
  float mY = 0;
  float mWidth = 100;
  float mHeight = 100;
  float mVelX = 0;
  float mVelY = 0;
  color mColor = #000000;

  Box(float x, float y, float w, float h, color c) {
    mX = x;
    mY = y;
    mWidth = w;
    mHeight = h;
    mVelX = random(-10,10);
    mVelY = random(-10,10);
    mColor = c;
  }

  // "setters"
  // mutator functions
  void setX(float x) {
    mX = x;
  }

  // accessor functions
  // "getters"
  float x() {
    return mX;
  }

  float y() {
    return mY;
  }

  float width() {
    return mWidth;
  }

  float height() {
    return mHeight;
  }

  boolean inside(float x, float y) {
      // This is a function stub.
      // (x,y) are the coordinates for a point
      // This function should return true if the point is inside this box; false otherwise
      if (x >= mX && x <= mX + mWidth &&
          y >= mY && y <= mY + mHeight) {
        return true;
      }
      return false;
  }

  boolean intersects(Box other) {
      // This is a function stub.
      // This function should return true if this Box intersects 'other'
      // To stay DRY, this function should call 'inside'

      // Approach: get coordinates for each corner of other
      // if any of the points of inside "me", then return true
      // otherwise, return false
      float x = other.x();
      float y = other.y();
      float w = other.width();
      float h = other.height();

      boolean topLeftTest = inside(x, y);
      boolean topRightTest = inside(x+w,y);
      boolean bottomLeftTest = inside(x, y+h);
      boolean bottomRightTest = inside(x+w,y+h);

      return topLeftTest || topRightTest ||
             bottomLeftTest || bottomRightTest;
  }

  void draw() {
    // Uses corner mode!
    fill(mColor);
    rect(mX, mY, mWidth, mHeight);
  }

  void update() {
      // Function stub
      // This function should implement a box that bounces inside the canvas
      mX += mVelX;
      mY += mVelY;

      if (mX < 0) {
        mVelX = -mVelX;
      }
      else if (mX > width-mWidth) {
        mVelX = -mVelX;
      }

      if (mY < 0) {
        mVelY = -mVelY;
      }
      else if (mY > height-mHeight) {
        mVelY = -mVelY;
      }
  }
}