// Exhaust by Ryan Alexander // Created with Processing 69 on September 1 2004 Puff puffs[] = new Puff[12]; int npuffs; Puff inflating; int newtimeout = 30; int newtimer = 0; color bg; boolean mouseInside = false; void setup() { size(280,400); bg = color(0,0,0); framerate(30); smooth(); } void draw() { if(inflating==null) { if(newtimer==0) { if(npuffs=0;) { puffs[i].react(0.04, 0.2, 0.1, 0.05); // Push from the bottom of the screen if(puffs[i].segment==puffs[i].maxseg) for(int j=puffs[i].n; --j>=0;) puffs[i].v[j].avoid(puffs[i].v[j].x,height,height/2,0.01); // Avoid other Puffs for(int j=npuffs; --j>=0;) { if(j!=i) puffs[i].avoid(puffs[j], puffs[i].maxseg*2, 0.4); } // Remove if off the screen int noff = 0; for(int j=puffs[i].n; --j>=0;) if(puffs[i].v[j].y<0) noff++; if(noff==puffs[i].n) removePuff(i); } // Mouse if(mouseInside && get(mouseX,mouseY)==bg) for(int i=npuffs; --i>=0;) puffs[i].avoid(mouseX,mouseY,80,0.06); background(bg); fill(255); noStroke(); for(int i=npuffs; --i>=0;) { puffs[i].update(0.75); puffs[i].contain(0,-200,width-1,height, 0.6); } if(mousePressed) { stroke(255); noFill(); for(int i=npuffs; --i>=0;) puffs[i].displayLoop(); } else { fill(255); noStroke(); for(int i=npuffs; --i>=0;) puffs[i].displayPoly(); } if(inflating!=null && inflating.segment==inflating.maxseg) inflating = null; } void removePuff(int i) { if(i < npuffs-1) System.arraycopy(puffs,i+1,puffs,i,npuffs-1-i); npuffs--; } public void mouseEntered(MouseEvent e) { mouseInside = true; } public void mouseExited(MouseEvent e) { mouseInside = false; } // Puff ////////////////////////////////////////////////////// class Puff extends SoftLine { float radius, maxseg, basex, basey, baserad; Puff(float x, float y, float ms, float br, int nv) { super(x-br, y, x+br, y, nv); maxseg = ms; basex = x; basey = y; baserad = br; for(int i=0; i maxseg) { segment = maxseg; } else { for(int i=n-1; --i>=1;) v[i].yv += vel*((cos(i/(n-1.0)*TWO_PI-PI)+1)*0.5); segment += amount; } radius = 0.5*segment*(1/sin(PI/n)); } } // Generic Classes ////////////////////////////////////////////////////// class SoftLine { Particle v[]; int n; float segment; SoftLine(float x1, float y1, float x2, float y2, int nv) { n = nv; segment = dist(x1,y1,x2,y2)/(n-1); v = new Particle[n]; for(int i=n; --i>=0;) v[i] = new Particle(x1+(float)i/(n-1)*(x2-x1), y1+(float)i/(n-1)*(y2-y1), 0,0); } SoftLine(float x, float y, int nv, float angle, float sg) { n = nv; segment = sg; v = new Particle[n]; for(int i=n; --i>=0;) v[i] = new Particle(x+rotatedX(segment*i,angle), y+rotatedY(segment*i,angle), 0,0); } void spring(int spread, float amount) { for(int i=n; --i>=0;) { for(int j=1; j <= spread; j++) { if(i <= n-j-1) v[i].spring(v[i+j].x, v[i+j].y, segment*j, (float)spread/j*amount); if(i >= j) v[i].spring(v[i-j].x, v[i-j].y, segment*j, (float)spread/j*amount); } } } void springLoop(int spread, float amount) { int index; for(int i=n; --i>=0;) { for(int j=1; j <= spread; j++) { index = i+j; if(index >= n) index -= n; v[i].spring(v[index].x, v[index].y, segment*j, (float)spread/j*amount); index = i-j; if(index < 0) index = n+index; v[i].spring(v[index].x, v[index].y, segment*j, (float)spread/j*amount); } } } void avoid(float x, float y, float r, float amount) { for(int i=n; --i>=0;) if(v[i].touching(x, y, r)) v[i].spring(x, y, r, amount); } void avoid(SoftLine sl, float r, float amount) { for(int i=n; --i>=0;) { for(int j=sl.n; --j>=0;) { if(v[i].touching(sl.v[j].x, sl.v[j].y, r) && sl.v[j]!=v[i]) v[i].spring(sl.v[j].x, sl.v[j].y, r, amount); } } } void avoidSelf(float r, float amount) { for(int i=n; --i>=0;) { for(int j=n; --j>=0;) { if(v[i].touching(v[j].x, v[j].y, r) && j!=i && segment*abs(i-j)>r) v[i].spring(v[j].x, v[j].y, r, amount); } } } void avoidSelfLoop(float r, float amount) { float diff, lpdiff; // Difference between the two vertices for(int i=n; --i>=0;) { for(int j=n; --j>=0;) { diff = abs(i-j); lpdiff = n-1-diff; if(v[i].touching(v[j].x, v[j].y, r) && j!=i && segment*min(diff,lpdiff)>r) v[i].spring(v[j].x, v[j].y, r, amount); } } } void impulse(float xi, float yi) { for(int i=n; --i>=0;) { v[i].xv += xi; v[i].yv += yi; } } void update(float damping) { for(int i=n; --i>=0;) v[i].update(damping); } void update(int start, int end, float damping) { for(int i=end; --i>=start;) v[i].update(damping); } void contain(float l, float t, float r, float b, float bouncy) { for(int i=n; --i>=0;) v[i].contain(l,t,r,b,bouncy); } void display() { beginShape(LINE_STRIP); for(int i=n; --i>=0;) vertex(v[i].x, v[i].y); endShape(); } void displayLoop() { beginShape(LINE_LOOP); for(int i=n; --i>=0;) vertex(v[i].x, v[i].y); endShape(); } void displayPoly() { beginShape(POLYGON); for(int i=n; --i>=0;) vertex(v[i].x, v[i].y); endShape(); } float averageX() { float ax = 0; for(int i=n; --i>=0;) ax += v[i].x; return ax / n; } float averageY() { float ay = 0; for(int i=n; --i>=0;) ay += v[i].y; return ay / n; } } class Particle { float x, y, xv, yv; color c; Particle(float ix, float iy, float ixv, float iyv) { x = ix; y = iy; xv = ixv; yv = iyv; } void update() { x += xv; y += yv; } void update(float d) { xv *= d; yv *= d; x += xv; y += yv; } void spring(float sx, float sy, float rest, float amount) { float dx = sx - x; float dy = sy - y; if(dx != 0 || dy != 0) { // Prevent / by zero float mag = sqrt(dx*dx + dy*dy); float ext = mag - rest; xv += (dx / mag * ext) * amount; yv += (dy / mag * ext) * amount; } if(keyPressed){stroke(255,0,0,64); line(x,y,sx,sy);} } void goal(float gx, float gy, float amount) { if(x != gx) xv += (gx - x) * amount; if(y != gy) yv += (gy - y) * amount; } void avoid(float x, float y, float r, float amount) { if(touching(x, y, r)) spring(x, y, r, amount); } void bounce(float hitx, float hity, float angle, float amount) { // Rotate a new vector to the angle float ax = cos(angle); float ay = sin(angle); float dotp = dot(xv, yv, ax, ay); xv = (ax * dotp - xv)*amount+ax; yv = (ay * dotp - yv)*amount+ay; x = hitx; y = hity; } void contain(float l, float t, float r, float b, float bouncy) { if(x < l) { x = l; xv *= -bouncy; } if(x > r) { x = r; xv *= -bouncy; } if(y < t) { y = t; yv *= -bouncy; } if(y > b) { y = b; yv *= -bouncy; } } boolean touching(float jx, float jy, float radii) { float dx = jx-x; float dy = jy-y; return dx*dx+dy*dy < radii*radii; } } // Helpers float rotatedX(float v,float angle) { return v*cos(angle); } float rotatedY(float v,float angle) { return v*sin(angle); } float rotatedX(float x,float y,float angle) { return x*cos(angle) - y*sin(angle); } float rotatedY(float x,float y,float angle) { return x*sin(angle) + y*cos(angle); } float dot(float x1, float y1, float x2, float y2) { return x1*x2 + y1*y2; } // generic linear interpolation int mix(int a,int b, float f) { return (int)(a+(b-a)*f); } float mix(float a, float b, float f) { return a+(b-a)*f; } // blend 2 colours int blend(int a, int b, float f) { return mix(a>>16,b>>16,f)<<16 | mix(a>>8&0xff,b>>8&0xff,f)<<8 | mix(a&0xff,b&0xff,f); }