int NUM_VERTICES = 64; int HALF_VERTICES = 32; int NUM_CONSTRAINTS = NUM_VERTICES * 2 + HALF_VERTICES; int RADIUS = 70; Verlet[] vertices = new Verlet[ NUM_VERTICES ]; Constraint[] constraints; void setup() { size( 400, 400 ); background( 255 ); noStroke(); for ( int i = 0; i < NUM_VERTICES; i++ ) { float x = 110 + cos( PI * i / HALF_VERTICES ) * RADIUS; float y = height - 110 + sin( PI * i / HALF_VERTICES ) * RADIUS; vertices[ i ] = new Verlet( x, y ); } constraints = new Constraint[ NUM_CONSTRAINTS ]; int index = 0; for ( int i = 0; i < NUM_VERTICES; i++ ) { constraints[ index ] = new Constraint( vertices[ i ], vertices[ ( i + 1 ) % NUM_VERTICES ] ); index++; } for ( int i = 0; i < HALF_VERTICES; i++ ) { constraints[ index ] = new Constraint( vertices[ i ], vertices[ HALF_VERTICES + i ] ); index++; } for ( int i = 0; i < NUM_VERTICES; i++ ) { constraints[ index ] = new Constraint( vertices[ i ], vertices[ ( i + 4 ) % NUM_VERTICES ] ); vertices[ i ].setVx( 4 ); vertices[ i ].setVy( -9 ); index++; } } void draw() { fill( 255, 75 ); rect( 0, 0, width, height ); for ( int i = 0; i < NUM_VERTICES; i++ ) { vertices[ i ].y += .2; vertices[ i ].update(); vertices[ i ].constrain( 4, width - 4, 4, height - 4 ); } for ( int i = 0; i < NUM_CONSTRAINTS; i++ ) constraints[ i ].satisfyConstraints(); beginShape(); fill( 0xFFB2BFCF ); for ( int i = 0; i < NUM_VERTICES; i++ ) curveVertex( vertices[ i ].x, vertices[ i ].y ); endShape( CLOSE ); } void keyPressed() { if ( keyCode == UP ) { for ( int i = 0; i < NUM_VERTICES; i++ ) vertices[ i ].y -= 5; } if ( keyCode == LEFT ) { for ( int i = 0; i < NUM_VERTICES; i++ ) vertices[ i ].x -= .5; } else if ( keyCode == RIGHT ) { for ( int i = 0; i < NUM_VERTICES; i++ ) vertices[ i ].x += .5; } } class Verlet { public float x; public float y; float _ox; float _oy; public Verlet( float x, float y ) { this.x = _ox = x; this.y = _oy = y; } void update() { float tx = x; float ty = y; x += getVx(); y += getVy(); _ox = tx; _oy = ty; } float getVx() { return x - _ox; } float setVx( float value ) { return _ox = x - value; } float getVy() { return y - _oy; } float setVy( float value ) { return _oy = y - value; } void constrain( int minx, int maxx, int miny, int maxy) { x = max( minx, min( maxx, x ) ); y = max( miny, min( maxy, y ) ); } void render() { fill( 255, 255, 255 ); ellipse( x, y, 4, 4 ); } } class Constraint { float _length; Verlet _pa; Verlet _pb; public Constraint( Verlet pa, Verlet pb ) { _pa = pa; _pb = pb; float dx = _pa.x - _pb.x; float dy = _pa.y - _pb.y; _length = sqrt( dx * dx + dy * dy ); } void satisfyConstraints() { float dx = _pb.x - _pa.x; float dy = _pb.y - _pa.y; float dist = sqrt( dx * dx + dy * dy ); float diff = _length - dist; float ox = diff * dx / dist * .4; float oy = diff * dy / dist * .4; _pa.x -= ox; _pa.y -= oy; _pb.x += ox; _pb.y += oy; } void render() { stroke( 255 ); line( _pa.x, _pa.y, _pb.x, _pb.y ); } }