color col_red = color(240,0,0); color col_green = color(50,250,50); class OrbNode { PVector pos; PVector vel; PVector tickAccel; float psize; //diameter color fillCol = color(255); OrbNode next, prev; static final float SPRING_LEN = 50; // "resting" length static final float SPRING_CONST = 0.005; static final float DAMPING = 0.995 ; OrbNode(int x, int y) { pos = new PVector(x,y); vel = new PVector(0,0); tickAccel = new PVector(0,0); psize = 20; } void display() { stroke(0); fill(fillCol); if(checkXBound() || checkYBound()) {fill(col_red);} circle(pos.x, pos.y, psize); // draw a line for velocity //float VEL_SCALE=5; //PVector velEnd = PVector.add(pos, PVector.mult(vel, VEL_SCALE)); //stroke(col_red); //line(pos.x,pos.y, velEnd.x, velEnd.y); //draw lines to next and previous float OFFSET = 3; if (next != null) { stroke(col_green); line(pos.x + OFFSET, pos.y + OFFSET, next.pos.x + OFFSET, next.pos.y + OFFSET); } if (prev != null) { stroke(col_red); line(pos.x - OFFSET, pos.y - OFFSET, prev.pos.x - OFFSET, prev.pos.y - OFFSET); } } void applyForce(PVector force) { //assuming mass is 1 tickAccel.add(force); } void run() { //all tickAccel gets applied to velocity vel.add(tickAccel); tickAccel.set(0,0); //damp velocity vel.mult(DAMPING); //bounce if(checkXBound()) { vel.x *= -1; } if(checkYBound()) { vel.y *= -1; } pos.add(vel); } boolean checkXBound() { // don't check current position, but check next position (pos + vel) // to help prevent glitching out of bounds (i.e. we'll bounce before we're ever in the wall) return (pos.x + vel.x + psize/2f) > width || (pos.x + vel.x - psize/2f) < 0; } boolean checkYBound() { return (pos.y + vel.y + psize/2f) > height || (pos.y + vel.y - psize/2f) < 0; } void applySpringForce() { if(next != null) { applyForce(calculateSpringForce(next)); } if(prev != null) { applyForce(calculateSpringForce(prev)); } } PVector calculateSpringForce(OrbNode other) { PVector toOther = PVector.sub(other.pos, this.pos); //This is the pixel offset from this, to other float distToOther = toOther.mag(); // positive is stretched > len, negative is compressed < len float stringStretch = distToOther - SPRING_LEN; //hooke's law //stretch positive -> stretched -> spring is pulling TOWARDS other -> pos force //stretch negative -> compressed -> pushing AWAY from other -> reversed force float forceTowardsOther = SPRING_CONST * stringStretch; //need to turn that into a vector //toOther should now have correct magnitude //if forceTowards is negative, setMag will flip the vector toOther.setMag(forceTowardsOther); //toOther now points in the right direction, and has //magnitude equal to the amount of force we calculated return toOther; }//calculateSpringForce }