• fullscreen
  • Boule.pde
  • Sonoboules.pde
  • class Boule {
      float posX, posY;
      float velX, velY;
      float radius = BOULE_RADIUS;
      float strength = 0;
      int multi;
      Oscillator osc;
      
      Boule(float x, float y, int multi) {
        this.posX = x;
        this.posY = y;
        this.multi = multi;
      }
      
      void setVelocity(float speed, float direction) {
        velX = speed * cos(direction);
        velY = speed * sin(direction);
      }
    
      float getSpeed() {
        return sqrt(velX * velX + velY * velY);
      }
      
      float getDirection() {
        return atan2(velY, velX);
      }
      
      float getTransparency(float r) {
        return (r < radius) ? 0 : (r < radius + 1) ? r - radius : 1;
      }
      
      float speedFactor(float s, float theta) {
        return cos(getDirection() - theta) * getSpeed() / s;
      }
      
      boolean collidesWith(Boule other) {
        return dist(this.posX, this.posY, other.posX, other.posY) <= this.radius + other.radius;
      }
      
      void update(float dt) {
    
        // Update position
        posX += velX * dt;
        posY += velY * dt;
    
        // Check for collision with left/right edge
        if (posX < radius) {
          posX = 2 * radius - posX;
          velX = -velX;
        }
        else if (posX >= width - radius) {
          posX = 2 * (width - radius) - posX;
          velX = -velX;
        }
    
        // Check for collision with top/bottom edge
        if (posY < radius) {
          posY = 2 * radius - posY;
          velY = -velY;
        }
        else if (posY >= height - radius) {
          posY = 2 * (height - radius) - posY;
          velY = -velY;
        }
    
        // Recalculate strength
        float decay = BOULE_ATTEN * strength * exp(-dt);
        if (strength > decay)
          strength -= decay;
        else
          strength = 0;
    
        // Update oscillator amplitude
        osc.setAmp(VOLUME * strength / multi);
      }
    }
    
    import ddf.minim.*;
    import ddf.minim.signals.*;
    
    static final float VOLUME = 0.05;
    static final float BRIGHTNESS = 0.1;
    
    static float BOULE_RADIUS = 24;
    static float BOULE_SPEED = 120;
    static float BOULE_ATTEN = 0.05;
    
    static final float F = 110; // Fundamental frequency
    
    int[] multis = { 3, 4, 6, 8, 9, 12 };
    Boule[] boules = new Boule[multis.length];
    
    float t; // Time of last update, in seconds
    
    Minim minim;
    AudioOutput out;
      
    void setup() {
      size(640, 480);
    
      minim = new Minim(this);
      out = minim.getLineOut(Minim.STEREO);
    
      for (int i = 0; i < boules.length; i++) {
        boules[i] = new Boule(random(width), random(height), multis[i]);
        boules[i].setVelocity(BOULE_SPEED, random(TWO_PI));
        boules[i].osc = new SineWave(F * multis[i], 0, out.sampleRate());
        out.addSignal(boules[i].osc);
      }
    
      t = millis() * 1e-3;
    }
    
    void draw() {
      
      // Apply motion update
      float tt = millis() * 1e-3;
      for (int i = 0; i < boules.length; i++)
        boules[i].update(tt - t);
      t = tt;
      
      // Check for collisions
      for (int i = 0; i < boules.length; i++) {
        for (int j = i + 1; j < boules.length; j++) {
          if (boules[i].collidesWith(boules[j])) {
            // Calculate offset
            float dx = boules[j].posX - boules[i].posX;
            float dy = boules[j].posY - boules[i].posY;
            // Calculate distance and angle
            float s = sqrt(dx * dx + dy * dy);
            float theta = atan2(dy, dx);
            // Calculate speed factors
            float p = boules[j].speedFactor(s, theta) - boules[i].speedFactor(s, theta);
            // Update speeds
            boules[i].velX += p * dx;
            boules[i].velY += p * dy;
            boules[j].velX -= p * dx;
            boules[j].velY -= p * dy;
            // Adjust strength
            boules[i].strength += abs(p);
            boules[j].strength += abs(p);
            // Shift first object away from second, so they don't keep colliding
            float q = (boules[i].radius + boules[j].radius) / s - 1;
            boules[i].posX -= q * dx;
            boules[i].posY -= q * dy;
          }
        }
      }
      
      // Draw display
      loadPixels();
      for (int y = 0; y < height; y++) {
        int y_offset = y * width;
        for (int x = 0; x < width; x++) {
          float vis = 1;
          float value = 0;
          for (int i = 0; i < boules.length; i++) {
            float r = dist(x, y, boules[i].posX, boules[i].posY);
            value += BRIGHTNESS * sq(boules[i].radius / r)  * boules[i].strength;
            float z = r - boules[i].radius;
            vis *= (z < 0) ? 0 : (z < 1) ? z : 1;
          }
          pixels[y_offset + x] = color(0xff * vis * value);
        }
      }
      updatePixels();
    }
    
    void stop() {
      out.close();
      minim.stop();
      super.stop();
    }
    

    code

    tweaks (0)

    about this sketch

    This sketch is running as Java applet, exported from Processing.

    license

    advertisement

    Michael Groufsky
    CgRobot
    14 Nov 2012
    This is really cool! Nice job.
    Very nice one, thanks for sharing.
    ale plus+
    6 Aug 2013
    Really nice!
    You need to login/register to comment.