• fullscreen
  • Circle.pde
  • Oscillator.pde
  • QuadTree.pde
  • simulatedAnnealingWithCircleOverlaps.pde
  • class Circle {
      PVector loc;
      float rad;
      color col;
      PVector target = new PVector(width/2, height/2);
      
      Circle(float x, float y, float rad) {
        this.loc = new PVector(x, y);
        this.rad = rad;
        this.col = color(220);
      }
      
      void draw() {
        stroke(col);
        noFill();
        ellipse(loc.x, loc.y, rad, rad);
      }
      
      void nudge(float temperature) {
        PVector origLoc = loc.get();
        
        int tries = 100;
        while (tries > 0) {
          tries -= 1;
          PVector d = getNudgeVector(temperature);
          float dist = 1000.0 * random(temperature) / (rad * rad);
          d.mult(dist);
          
          loc.x = constrain(loc.x + d.x, 0, width);
          loc.y = constrain(loc.y + d.y, 0, height);
          
          if (!anyOverlaps(this, circles)) {
            return;
          }
          else {
            loc = origLoc.get();
          }
        }
      }
      
      PVector getNudgeVector(float temperature) {
        PVector dir = PVector.sub(target, loc);
        float angle = atan2(dir.y, dir.x);
        
        float dAngle = 1.5 * PI * temperature * (random(1) - 0.5);
        angle += dAngle;
        
        return new PVector(cos(angle), sin(angle));
      }
      
      PVector getRandomNudgeVector(float dist) {
        // Random vector
        float dir = random(TWO_PI);
        float dx = dist * cos(dir);
        float dy = dist * sin(dir);
        return new PVector(dx, dy);
      }
      
      boolean overlaps(Circle c) {
        return PVector.dist(loc, c.loc) < (rad + c.rad) * 0.5;
      }
    }
    
    class Oscillator {
      private float theta = 0;
      private float dTheta = 0.03;
      
      float value() {
        //theta = (theta + dTheta) % TWO_PI;
        theta += dTheta;
        return sin(theta);
      }
    }
    
    /*
    
    
    class QuadTree<T> {
      ArrayList<T> items;
      ArrayList<QuadTree<T>> kids;
      int left, top, right, bottom;
      
      QuadTree<T>(int left, int top, int right, int bottom) {
        this.left = left;
        this.top = top;
        this.right = right;
        this.bottom = bottom;
      }
      
      void add(T item) {
        ;
      }
    }
    
    
    class CirclesMap {
      
      ArrayList<Circle> circles = new ArrayList<Circle>();
      TreeSet<Circle> byX = new TreeSet<Circle>(new ByX());
      TreeSet<Circle> byY = new TreeSet<Circle>(new ByY());
      
      void add(Circle c) {
        circles.add(c);
        byX.add(c);
        byY.add(c);
      }
      
      ArrayList<Circle> bbSearch(float x, float y, float dist) {
        HashSet<Circle> xMatches = new HashSet<Circle>();
        
        Iterator<Circle> iter = byX.iterator();
        while (iter.hasNext()) {
          if (abs(iter.next().x - x) < dist) {
            
        
        HashSet<Circle> yMatches = new HashSet<Circle>();
        
        return new ArrayList<Circle>(xMatches.
      }
    }
    
    class ByX implements Comparator<Circle> {
      int compare(Circle a, Circle b) {
        return a.x.compareTo(b.x);
      }
    }
    
    class ByY implements Comparator<Circle> {
      int compare(Circle a, Circle b) {
        return a.y.compareTo(b.y);
      }
    }
    
    */
    
    int n = 200;
    int minRadius = 10;
    int maxRadius = 30;
    float temperature = 1.0;
    
    ArrayList<Circle> circles;
    
    void setup() {
      size(600, 600);
      colorMode(HSB);
      ellipseMode(CENTER);
      background(30);
      smooth();
      makeRandomCircles();
    }
    
    void makeRandomCircles() {
      circles = new ArrayList<Circle>();
      addRandomCircles(n);
    }
    
    Circle makeRandomCircle() {
      Circle rand = new Circle(random(width), random(height), random(minRadius, maxRadius));
      while (anyOverlaps(rand, circles)) {
        rand = new Circle(random(width), random(height), random(minRadius, maxRadius));
      }
      
      float target = random(1);
      rand.target = new PVector(target * width, target * width);
      rand.col = color(target * 128, 255, 255);
      
      return rand;
    }
    
    boolean anyOverlaps(Circle c, ArrayList<Circle> circles) {
      for (int i = 0; i < circles.size(); i++) {
        if (c != circles.get(i) && c.overlaps(circles.get(i))) {
          return true;
        }
      }
      return false;
    }
    
    void addRandomCircles(int num) {
      for (int i = 0; i < num; i++) {
        circles.add(makeRandomCircle());
      }
    }
    
    Oscillator tempOsc = new Oscillator();
    void draw() {
      background(30);
      
      //temperature = map(mouseY, height, 0, 0, 1);
      //temperature *= 0.999;
      //temperature = constrain(temperature - 0.005, 0, 1);
      temperature = map(tempOsc.value(), -1, 1, 0.4, 1);
      
      nudgeCircles(temperature);
      drawCircles();
      drawTemperature(temperature);
    }
    
    void mouseClicked() {
      makeRandomCircles();
      temperature = 1;
    }
    
    void keyPressed() {
      addRandomCircles(floor(n * 0.5));
    }
    
    void drawTemperature(float temperature) {
      pushStyle();
      fill(0, 0, 255, 50);
      noStroke();
      float barHeight = temperature * height;
      rect(0, height - barHeight, 30, barHeight);
      popStyle();
    }
    
    void nudgeCircles(float temperature) {
      for (int i = 0; i < circles.size(); i++) {
        circles.get(i).nudge(temperature);
      }
    }
    
    void nudgeOverlappingCircles(float temperature) {
      for (int i = 0; i < circles.size(); i++) {
        circles.get(i).col = color(220);
      }
      
      for (int i = 0; i < circles.size()-1; i++) {
        Circle a = circles.get(i);
        for (int j = i+1; j < circles.size(); j++) {
          Circle b = circles.get(j);
          if (a.overlaps(b)) {
            b.nudge(temperature);
            a.col = b.col = color(0, 220, 220);
          }
        }
      }
    }
    
    void drawCircles() {
      for (int i = 0; i < circles.size(); i++) {
        circles.get(i).draw();
      }
    }
    

    code

    tweaks (0)

    about this sketch

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

    license

    advertisement

    Dan Bernier

    Arrange circles with simulated annealing, #1

    Add to Faves Me Likey@!
    You must login/register to add this sketch to your favorites.

    This sketch arranges circles along a diagonal - top-left (red) to bottom-right (blue).

    It arranges them with simulated annealing. Each circle moves around a bit - more, if the temperature is high, and less, if the temperature is low. Smaller circles can move faster. The temperature is displayed as a transparent bar on the left, and currently oscillates between 0.4 and 1.0.

    Controls:
    - click the mouse to reset
    - type a key to add more circles (too many will slow it down though)

    You need to login/register to comment.