class Bucket { private float x, y, px, py, w, h, c, s; Bucket(float _x, float _y, float _w, float _h) { w = _w; h = _h; x = _x; y = _y; c = sqrt(h*h - w*w); s = 3; } void move(int d) { py = y; px = x; switch(d) { case 1: y -= s; break; case 2: x += s; break; case 3: y += s; break; case 4: x -= s; break; } } void collide(Pebble peb) { // distance to the focus float distToF = fociDistSum(peb.x(), peb.y()); // if the pebble was above the bucket in the previous frame and is now // inside the bucket, add it to pebs if ( peb.py() < y && peb.y() > y && distToF < 2*h) { peb.caught = true; return; } // then we want to calculate some values we'll need: // the closest a pebble is allowed to get to the edge of the bucket float ad = peb.radius() + 3; // distance to the center float distToC = dist(peb.x(), peb.y(), x, y); // distance to the edge, will be negative if peb is inside the bucket float distToE = abs(distToF - 2*h); // vector pointing at the center of the arc float moveDirX = (x - peb.x())/distToC; float moveDirY = (y - peb.y())/distToC; // vector pointing at the lower focus float bounceDirX = moveDirX*6; float bounceDirY = ((y+c) - peb.y())/dist(peb.x(), peb.y(), x, y+c); // change in the buckets position float cbx = x - px; if ( peb.caught ) { // untag peb if it's exited through the opening if ( peb.y() < y ) { peb.caught = false; return; } // if the pebble is overlapping the edge, move it so that it // is just touching the edge if ( distToE < ad ) { peb.moveBy(moveDirX * (ad - distToE), moveDirY * (ad - distToE)); if ( (peb.x() < x && cbx > 0) || (peb.x() > x && cbx < 0) ) peb.addVelocity(bounceDirX, bounceDirY); } // if the pebb is outside the bucket, move it back inside if ( distToF > 2*h ) { peb.moveBy(moveDirX * (ad + distToE), moveDirY * (ad + distToE)); peb.setVelocity(0, 0); } } else if ( peb.y() > y && peb.y() < y + h && peb.x() > x - w && peb.x() < x + w ) { // if peb is overlapping the edge, move it just touching the edge // and reverse it's x velocity if ( distToE < ad ) { peb.moveBy(-moveDirX * (ad - distToE), -moveDirY * (ad - distToE)); peb.setVelocity(-bounceDirX, -bounceDirY); } // if the pebble is inside the bucket, move it back outside if ( distToF < 2*h ) { peb.moveBy(-moveDirX * (ad + distToE), -moveDirY * (ad + distToE)); peb.setVelocity(-peb.vx(), peb.vy()); } } } private float fociDistSum(float _x, float _y) { return dist(_x, _y, x, y - c) + dist(_x, _y, x, y + c); } // returns (ex, ey), a point on the edge that is ALSO on the line // connecting (x, y) and (_x, _y) // i.e. the closest point on the edge of the bucket private float[] edgeXY(float _x, float _y) { float dx = _x - x; float dy = y - _y; // calculate the polar angle float theta = atan(dx/dy) + PI/2; // convert to parametric angle float t = atan( (w/h) * tan(theta) ); // this is the parametric equation for an ellipse float ex = w*cos(t); float ey = h*sin(t); // adjust the position on the arc if ( _x < x ) { ex *= -1; ey *= -1; } // translate to the bucket's location ex += x; ey += y; float[] xy = { ex, ey }; return xy; } void render() { fill(200, 51, 99, 64); stroke(0); //strokeWeight(3); arc(x, y, w, h, 0, PI); } }