# Non-orthogonal Collision with Multiple Ground Segments

Non-orthogonal Collision with Multiple Ground Segments by Ira Greenberg.

Based on Keith Peter’s Solution in Foundation Actionscript Animation: Making Things Move!

```
Orb orb;

PVector gravity = new PVector(0,0.05);
// The ground is an array of "Ground" objects
int segments = 40;
Ground[] ground = new Ground[segments];

void setup(){
size(640, 360);
// An orb object that will fall and bounce around
orb = new Orb(50, 50, 3);

// Calculate ground peak heights
float[] peakHeights = new float[segments+1];
for (int i=0; i<peakHeights.length; i++){
peakHeights[i] = random(height-40, height-30);
}

/* Float value required for segment width (segs)
calculations so the ground spans the entire
display window, regardless of segment number. */
float segs = segments;
for (int i=0; i<segments; i++){
ground[i]  = new Ground(width/segs*i, peakHeights[i], width/segs*(i+1), peakHeights[i+1]);
}
}

void draw(){
// Background
noStroke();
fill(0, 15);
rect(0, 0, width, height);

// Move and display the orb
orb.move();
orb.display();
// Check walls
orb.checkWallCollision();

// Check against all the ground segments
for (int i=0; i<segments; i++){
orb.checkGroundCollision(ground[i]);
}

// Draw ground
fill(127);
beginShape();
for (int i=0; i<segments; i++){
vertex(ground[i].x1, ground[i].y1);
vertex(ground[i].x2, ground[i].y2);
}
vertex(ground[segments-1].x2, height);
vertex(ground.x1, height);
endShape(CLOSE);

}

class Orb {
// Orb has positio and velocity
PVector position;
PVector velocity;
float r;
// A damping of 80% slows it down when it hits the ground
float damping = 0.8;

Orb(float x, float y, float r_) {
position = new PVector(x, y);
velocity = new PVector(.5, 0);
r = r_;
}

void move() {
// Move orb
velocity.add(gravity);
position.add(velocity);
}

void display() {
// Draw orb
noStroke();
fill(200);
ellipse(position.x, position.y, r*2, r*2);
}

// Check boundaries of window
void checkWallCollision() {
if (position.x > width-r) {
position.x = width-r;
velocity.x *= -damping;
}
else if (position.x < r) {
position.x = r;
velocity.x *= -damping;
}
}

void checkGroundCollision(Ground groundSegment) {

// Get difference between orb and ground
float deltaX = position.x - groundSegment.x;
float deltaY = position.y - groundSegment.y;

// Precalculate trig values
float cosine = cos(groundSegment.rot);
float sine = sin(groundSegment.rot);

/* Rotate ground and velocity to allow
orthogonal collision calculations */
float groundXTemp = cosine * deltaX + sine * deltaY;
float groundYTemp = cosine * deltaY - sine * deltaX;
float velocityXTemp = cosine * velocity.x + sine * velocity.y;
float velocityYTemp = cosine * velocity.y - sine * velocity.x;

/* Ground collision - check for surface
collision and also that orb is within
left/rights bounds of ground segment */
if (groundYTemp > -r &&
position.x > groundSegment.x1 &&
position.x < groundSegment.x2 ) {
// keep orb from going into ground
groundYTemp = -r;
// bounce and slow down orb
velocityYTemp *= -1.0;
velocityYTemp *= damping;
}

// Reset ground, velocity and orb
deltaX = cosine * groundXTemp - sine * groundYTemp;
deltaY = cosine * groundYTemp + sine * groundXTemp;
velocity.x = cosine * velocityXTemp - sine * velocityYTemp;
velocity.y = cosine * velocityYTemp + sine * velocityXTemp;
position.x = groundSegment.x + deltaX;
position.y = groundSegment.y + deltaY;
}
}

class Ground {
float x1, y1, x2, y2;
float x, y, len, rot;

// Constructor
Ground(float x1, float y1, float x2, float y2) {
this.x1 = x1;
this.y1 = y1;
this.x2 = x2;
this.y2 = y2;
x = (x1+x2)/2;
y = (y1+y2)/2;
len = dist(x1, y1, x2, y2);
rot = atan2((y2-y1), (x2-x1));
}
}
```

## Functions Used

ellipse()

Draws an ellipse (oval) to the screen

Learn More
fill()

Sets the color used to fill shapes

Learn More
size()

Defines the dimension of the display window width and height in units of pixels

Learn More
rect()

Draws a rectangle to the screen

Learn More
cos()

Calculates the cosine of an angle

Learn More
random()

Generates random numbers

Learn More
vertex()

All shapes are constructed by connecting a series of vertices

Learn More
sin()

Calculates the sine of an angle

Learn More
beginShape()

Using the beginShape() and endShape() functions allow creating more complex forms

Learn More
setup()

The setup() function is run once, when the program starts

Learn More
draw()

Called directly after setup(), the draw() function continuously executes the lines of code contained inside its block until the program is stopped or noLoop() is called

Learn More
endShape()

The endShape() function is the companion to beginShape() and may only be called after beginShape()

Learn More
atan2()

Calculates the angle (in radians) from a specified point to the coordinate origin as measured from the positive x-axis

Learn More
noStroke()

Disables drawing the stroke (outline)

Learn More
PVector

A class to describe a two or three dimensional vector, specifically a Euclidean (also known as geometric) vector

Learn More
dist()

Calculates the distance between two points

Learn More