fullscreen // 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;
}
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
CgRobot