• fullscreen
  • aggregation.pde
  • aggregation_web.pde
  • classAggregation.pde
  • classAggregationPoint.pde
  • classTarget.pde
  • lookForTarget.pde
  • // Part of this code is based on: 
    
    // P_2_2_4_01.pde
    //
    // Generative Gestaltung, ISBN: 978-3-87439-759-9
    // First Edition, Hermann Schmidt, Mainz, 2009
    // Hartmut Bohnacker, Benedikt Gross, Julia Laub, Claudius Lazzeroni
    // Copyright 2009 Hartmut Bohnacker, Benedikt Gross, Julia Laub, Claudius Lazzeroni
    //
    // http://www.generative-gestaltung.de
    //
    // Licensed under the Apache License, Version 2.0 (the "License");
    // you may not use this file except in compliance with the License.
    // You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
    // Unless required by applicable law or agreed to in writing, software
    // distributed under the License is distributed on an "AS IS" BASIS,
    // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    // See the License for the specific language governing permissions and
    // limitations under the License.
    
    /**
     * limited diffusion aggregation
     *
     */
    
    void doAggregation ()
    {
      //println (ag.index);
      // create a radom set of parameters
      float newR = random(1, 2.5);
      float [] [] target = findTargets (1, input);
    
      float newX = random(0+newR, width-newR);
      float newY = random(0+newR, height-newR);
      newX = target [0] [0];
      newY = target [0] [1];
    
    
      float oldX = 0;
      float oldY = 0;
      float oldR = 0;
    
      float closestDist = 100000000;
      int id = 0, motherIndex = 0;
      // which circle is the closest?
      for (int i=0; i < ag.ap.length; i++) 
      {
    
        Target [] pos = ag.ap[i].getPos();
        //println ("pos.length: " + pos.length);
        for (int j = 0; j < pos.length; j++)
        {
          float newDist = dist(newX, newY, pos[j].x, pos[j].y);
          if (newDist < closestDist) {
            closestDist = newDist;
            motherIndex = ag.ap[i].id;
            id = pos[j].id;
            oldX = pos[j].x;
            oldY = pos[j].y;
            oldR = pos[j].r;
          }
        }
      }
    
      float angle = atan2(newY-oldY, newX-oldX);
      newX = oldX + cos (angle) * (oldR+newR);
      newY = oldY + sin (angle) * (oldR+newR);
    
      int index = (int) newY * input.width + (int) newX;
      ag.addAggregationPoint (newX, newY, newR, motherIndex, id, input.pixels [constrain (index, 0, input.pixels.length-1) ]);
    }
    
    Aggregation ag;
    PImage input, over;
    int branchesNum = 2, imageIndex = 1;
    float [] [] target;
    PImage [] images;
    color backgroundColor = color (230/*121, 195, 229*/);
    boolean showImage = false;
    
    void setup ()
    {
      size (675, 450);
      smooth();
    
      frameRate (30);
    
      images = new PImage [2];
      images[0] = loadImage ("bird.jpg");
      images[1] =loadImage ("fuzzy.jpg");
      input = images [imageIndex];
      
      over = createImage (1, 1, RGB);
      for (int i = 0; i < over.pixels.length; i++) over.pixels [i] = backgroundColor;
      over.updatePixels();
      
      initBranches(target, input);
    }
    
    void draw ()
    {
      background (255);
      ag.draw();
      for (int i = 0; i < 30; i++) doAggregation();
      blend (over, 0, 0, width, height, 0, 0, width, height, MULTIPLY);
      if (showImage) image (input, 10, 10, input.width / 4, input.height / 4);
    }
    
    
    void mousePressed ()
    {
      initBranches(target, input);
    }
    
    
    
    void initBranches (float [] [] target, PImage img)
    {
      target = (float[] []) findTargets (branchesNum, img);
    
      int [] c = new int [target.length];
      float [] r = new float [target.length];
      int index = 0;
      for (int i = 0; i < r.length; i++) 
      {
        r[i] = random (1, 2);
    
        index = (int) target [i] [1] *img.width + (int) target [i] [0];
        c [i] = img.pixels[index];
      }
      ag = new Aggregation (target, r, c);
    }
    
    void keyPressed ()
    {
      if (keyCode == KeyEvent.VK_1) 
      {
        branchesNum = 1;
        initBranches(target, input);
      }
      if (keyCode == KeyEvent.VK_2) 
      {
        branchesNum = 2;
        initBranches(target, input);
      }
      if (keyCode == KeyEvent.VK_3) 
      {
        branchesNum = 3;
        initBranches(target, input);
      }
      if (keyCode == KeyEvent.VK_4) 
      {
        branchesNum = 4;
        initBranches(target, input);
      }
      if (keyCode == KeyEvent.VK_5) 
      {
        branchesNum = 5;
        initBranches(target, input);
      }
      if (keyCode == KeyEvent.VK_6) 
      {
        branchesNum = 6;
        initBranches(target, input);
      }
      if (keyCode == KeyEvent.VK_7) 
      {
        branchesNum = 7;
        initBranches(target, input);
      }
      if (keyCode == KeyEvent.VK_8) 
      {
        branchesNum = 8;
        initBranches(target, input);
      }
      if (keyCode == KeyEvent.VK_9) 
      {
        branchesNum = 9;
        initBranches(target, input);
      }
      
      if (keyCode == KeyEvent.VK_I) showImage = !showImage;
    
      if (keyCode == KeyEvent.VK_SPACE) 
      {
        imageIndex++;
        if (imageIndex > images.length-1) imageIndex = 0;
        input = images [imageIndex];
        initBranches(target, input);
      }
    }
    
    
    class Aggregation
    {
      AggregationPoint [] ap;
      int index;
      Aggregation (float [] [] initPoints, float [] r, int [] c)
      {
        index = 0;
        ap = new AggregationPoint [initPoints.length];
        for (int i = 0; i < ap.length; i++) 
        {
          ap [i] = new AggregationPoint (initPoints [i] [0], initPoints [i] [1], r [i], index, c [i], 1.5);
          index++;
        }
      }
      
      Aggregation (float [] xInitPoints, float [] yInitPoints, float [] r)
      {
        index = 0;
        ap = new AggregationPoint [xInitPoints.length];
        for (int i = 0; i < ap.length; i++) 
        {
          ap [i] = new AggregationPoint (xInitPoints [i], yInitPoints [i], r[i], index, color (random (255), random (255), random (255)), 1.5);
          index++;
        }
      }
      
      void addAggregationPoint (float x, float y, float r, int motherIndex, int lookup, int c)
      {
        
        if (motherIndex == lookup)
        {
          ap [motherIndex].addChild (x, y, r, index, c);
          index++;
        }
        else{
          ap [motherIndex].addChild (x, y, r, lookup, index, c);
          index++;
        }
      }
      
      void draw ()
      {
        for (int i = 0; i < ap.length; i++) ap [i].draw();
      }
      
    }
    
    class AggregationPoint
    {
      int id, c;
      float x, y, r, sw;
      ArrayList <AggregationPoint> children;
    
      AggregationPoint (float x, float y, float r, int id, int c, float sw)
      {
        this.c = c;
        this.sw = sw;
        if (sw < 0.25) this.sw = 0.25;
        this.id = id;
        this.x = x;
        this.y = y;
        this.r = r;
      }
    
    
      void addChild (float x, float y, float r, int id, int c)
      {
        if (children == null) children = new ArrayList();
    
        children.add (new AggregationPoint (x, y, r, id, c, map (brightness (c), 0, 255, 1.5, 0.25)));
      }
    
      void addChild (float x, float y, float r, int lookup, int newId, int c)
      {
    
        if (this.id == lookup) addChild ( x, y, r, newId, lerpColor (c, this.c, 0.5));
        else 
        {
          if (children != null && children.size() > 0)
          {
            for (int i = 0; i < children.size(); i++)
            {
              children.get (i).addChild  (x, y, r, lookup, newId, c);
            }
          }
        }
      }
    
      Target [] getPos ()
      {  
    
        Target [] pos = {
          new Target (x, y, r, id)
          };
    
    
          if (children != null && children.size() > 0)
          {
            for (int i = 0; i < children.size(); i++)
            {
              Target [] childTargets = children.get(i).getPos();
              pos = (Target []) concat (pos, childTargets);
            }
          }  
        return pos;
      }
    
    
      void draw ()
      {
        stroke (0, 150);
        strokeWeight (sw);
        if (children != null && children.size() > 0)
        {
          if (children.size() > 1)
          {
            noStroke();
            fill (0, 80);
            ellipse (x, y, sw*2, sw*2);
            noFill();
            stroke (0, 80);
          }
          for (int i = 0; i < children.size(); i++)
          {
    
            line (x, y, children.get(i).x, children.get(i).y);
    
            children.get(i).draw();
          }
        }
      }
    }
    
    
    class Target
    {
      float x, y, r;
      int id;
      Target (float x, float y, float r, int id)
      {
        this.r = r;
        this.x = x;
        this.y = y;
        this.id = id;
      }
    }
    
    float [] [] findTargets (int n, PImage img)
    {
      img.loadPixels();
     // println ("find target");
      float [] [] target = new float [n] [2];
    
      PVector pos;
      for (int i = 0; i < target.length; i++)
      {
        //println (i);
        pos = target (img.pixels, (int) random (img.width), (int) random (img.height), img.width, img.height, 0);
    
        target [i] [0] = pos.x;
        target [i] [1] = pos.y;
      }
    
      return target;
    }
    
    PVector target (int [] colors, int x, int y, int W, int H, int depth)
    {
      PVector pos = new PVector (0, 0);
      int index = y*W+x;
      color c = colors [index];
    
      // println ("c: " + red (c) + ", " + green (c) + ", " + blue (c));
      if (depth == 15 || isValidTarget (brightness (c)))
      {
        pos.x = x;
        pos.y = y;
    
        // print (pos);
      } 
      else 
      {
        pos = target (colors, (int) random (W), (int) random (H), W, H, depth++);
      }
      
        return pos;
    }
    
    boolean isValidTarget (float fbrightness)
    {
      if (fbrightness > 210) return false;
      float value = map (fbrightness, 0, 255, 1, 100);
      // println (value);
    
      float iRandom = random (0, value);
      //println (value + ", " + iRandom);
      if (iRandom < 1) return true;
      else return false;
    }
    
    

    code

    tweaks (0)

    license

    advertisement

    Diana Lange

    Image based aggregation

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

    This code will add more aggregation points to "darker positions" than to "lighter positions". Works best with high-contrast input (graphics / photos).

    Controls:
    Mouse click == start over
    Space == toggle Image
    I == show / hide image input
    Keys 1-9 == Number of starting branches

    Video:
    https://vimeo.com/54021795

    ale plus+
    21 Nov 2012
    Excellent!
    CgRobot
    21 Nov 2012
    A little slow for my taste, but I do still like it.

    *Gasp* Two comments in one day?! How did you do it?!?
    Brendan Flynn
    27 Nov 2012
    Beautiful as usual.
    Diana Lange
    27 Nov 2012
    Small updates: you can choose now between two images (and show the image input)
    bitcraft
    27 Nov 2012
    I can't even express how much I like this.
    Your portraits remind me a lot of what happens when you get struck by lightning:
    http://twistedsifter.com/2012/03/lichtenberg-figures-lightning-strike-scars/
    Diana Lange
    28 Nov 2012
    OMG!
    jacques maire
    7 Dec 2012
    superbe idée!
    Dani Baer
    14 Mar 2013
    Hallo Diana,
    das ist SUPER!

    Ich habs mir mal runtergeladen, aber Processing findet KeyEvent.VK_1 nicht..
    Weißt du was da schief läuft ;)?

    lg Dani
    Diana Lange
    28 Mar 2013
    Du kannst statt dessen auch einfach " key == '1' " schreiben. (http://processing.org/reference/key.html)
    Kevin Dean
    8 May 2013
    This is great. I did a similar thing with random walkers to build an image in grayscale, but the direction you took is more visually interesting. Well done.
    dave5972
    4 Oct 2013
    Diana your work is fantastic. I am new to processing and wanted to experiment with your code. I have downloaded your code and opened it up, but when I play the code I get an error saying Cannot find anything named "KeyEvent.VK_1"

    I have searched online to see what I am doing wrong but cannot find an answer.

    Could you possibly tell me what I am doing wrong, or maybe point me in the right direction.

    Thanks.
    Diana Lange
    4 Oct 2013
    Use Processing 1.5 or if (key == '1') instead of if (keyCode == KeyEvent.VK_1), It's the last time I answer that question
    claire kang
    29 Oct 2013
    amazing
    Eduardo Obieta
    6 Apr 2014
    dave5972
    If you need to activate "KeyEvent.VK_1" put this line in your code, run in processing 2.1...

    import java.awt.event.KeyEvent;
    You need to login/register to comment.