fullscreen class BranchGrowth extends SingleGrowth {
public int num;
public float angOffset, len;
public BranchGrowth() {
}
public String getText() {
return "num="+num+", angOffset="+angOffset+", len="+len;
}
public BranchGrowth(int numChild, float angOffset, float len) {
this.num = numChild;
this.angOffset = angOffset;
this.len = len;
}
public int getGrowNum() {
return num;
}
public BranchGrowth createRandom() {
return new BranchGrowth((int)random(1, 5), random(180), random(25, 200));
}
public Set getGrowth(MNode parent) {
float angOffset = radians(this.angOffset);
Set set = new HashSet();
float ang = parent.ang - angOffset / 2;
for (int k = 0; k < num; k++) {
set.add(new MNode(parent.getX() + len * cos(ang), parent.getY() + len * sin(ang), parent.size * .8, parent.lineScale, ang, parent.col));
ang += angOffset;
}
return set;
// println(id+": "+getChildren().size());
}
}
class FlowerGrowth extends BranchGrowth {
public FlowerGrowth() {
}
public FlowerGrowth createRandom() {
return new FlowerGrowth((int)random(1, 6), random(25, 200));
}
public FlowerGrowth(int num, float radius) {
super(num, 360f/num, radius);
}
}
class GrassGrowth extends SingleGrowth {
public int num;
public float radius, len, angOffset;
public GrassGrowth() {
}
public String getText() {
return "num="+num+", radius="+radius+", len="+len+", angOffset="+angOffset;
}
public int getGrowNum() {
return num;
}
public GrassGrowth(int num, float radius, float dist, float angOffset) {
this.num = num;
this.radius = radius;
this.len = dist;
this.angOffset = angOffset;
}
public Growth createRandom() {
return new GrassGrowth((int)random(1, 5), random(45), random(100, 200), random(360));
}
public Set getGrowth(MNode parent) {
float angOffset = radians(this.angOffset);
Set set = new HashSet();
float dx = parent.getX() + len * cos(parent.ang),
dy = parent.getY() + len * sin(parent.ang);
for(int x = 0; x < num; x++) {
float ang = map(x, 0, num, 0, TWO_PI) + angOffset + parent.ang;
float mx = dx + radius * cos(ang),
my = dy + radius * sin(ang);
set.add(new MNode(mx, my,
parent.size * .8, parent.lineScale,
atan2(my - parent.getY(), mx - parent.getX()),
parent.col));
}
return set;
}
}
interface Growth {
public Set grow(MNode parent);
public Growth createRandom();
public int getGrowNum();
public void show();
}
class GrowthPattern extends GrowthSet {
public GrowthPattern() {
super(new LinkedList());
}
public GrowthPattern createRandom() {
GrowthPattern pat = new GrowthPattern();
int num = (int) random(1, 4);
for (int k = 0; k < num; k++) {
pat.addGrowth(getRandomGrowth());
}
return pat;
}
public int getGrowNum() {
int k = 1;
Iterator i = grows.iterator();
while(i.hasNext()) {
Growth g = (Growth) i.next();
k *= g.getGrowNum();
}
return k;
}
public Set grow(Set parents) {
Iterator growIterator = grows.iterator();
while(growIterator.hasNext()) {
Growth g = (Growth) growIterator.next();
Set children = new HashSet();
Iterator setIterator = parents.iterator();
while(setIterator.hasNext()) {
MNode n = (MNode)setIterator.next();
children.addAll(g.grow(n));
}
// println("finisehd growing for "+g);
parents = children;
}
// println("done growing");
return parents;
}
public Set grow(MNode parent) {
Set set = new HashSet();
set.add(parent);
return grow(set);
}
}
abstract class GrowthSet implements Growth {
Collection grows;
public GrowthSet(Collection grows) {
this.grows = grows;
}
public void addGrowth(Growth g) {
grows.add(g);
}
public void removeGrowth(Growth g) {
grows.remove(g);
}
public void show() {
text(getClass().getSimpleName()+" [num="+grows.size()+"]", 0, 0);
pushStyle();
translate(15, lineHeight);
stroke(0);
strokeWeight(1);
Iterator gr = grows.iterator();
while(gr.hasNext()) {
Growth g = (Growth) gr.next();
//vertical
line(0, 0, 0, lineHeight);
//horizontal
line(0, lineHeight, lineHeight * .7f, lineHeight);
translate(lineHeight, lineHeight / 2);
g.show();
translate(-lineHeight, lineHeight / 2);
// translate(0, lineHeight);
}
translate(-15, 0);
popStyle();
}
}
private static int counter = 1;
class MNode {
float x, y;
Set children;
final static float rad = 3;
float mx, my, ang, size, lineScale;
protected int frameCount;
int id = counter++;
int col;
public MNode(float x, float y, float size, float lineScale, float ang, int col) {
this.x = x;
this.y = y;
children = new HashSet();
nodes.add(this);
this.mx = x;
this.my = y;
this.size = size;
this.lineScale = lineScale;
this.ang = ang;
this.col = col;
}
Vec2 getLoc() {
return new Vec2(getX(), getY());
}
private void align() {
float ang = radians(frameCount * 2 + getX() + getY());
setX(mx + rad * cos(ang));
setY(my + rad * sin(ang));
frameCount++;
}
float angleTo(MNode n) {
return atan2(n.y - y, n.x - x);
}
public Set getChildren() {
return children;
}
public void add(MNode c) {
children.add(c);
}
public void remove(MNode c) {
children.remove(c);
}
public boolean contains(MNode c) {
return children.contains(c);
}
public float getX() {
return x;
}
public void setX(float x) {
this.x = x;
}
public float getY() {
return y;
}
public void setY(float y) {
this.y = y;
}
public void setLoc(float x, float y) {
mx = x;
my = y;
align();
}
void setLoc(Vec2 loc) {
setLoc(loc.x, loc.y);
}
void act() {
align();
}
void drawConnects() {
pushStyle();
pushMatrix();
strokeWeight(size * lineScale);
stroke(0);
noFill();
Iterator i = getChildren().iterator();
// println(id+": "+getChildren().size());
while(i.hasNext()) {
MNode b = (MNode) i.next();
switch(lineMode) {
case 0:
break;
case LINE:
line(getX(), getY(), b.getX(), b.getY());
break;
case CURVE:
// float x1 = ctlPt(this, 1) * width;
// float y1 = ctlPt(this, 2) * height;
// float x2 = ctlPt(b, 1) * width;
// float y2 = ctlPt(b, 2) * height;
// curve(x1, y1, getX(), getY(), b.getX(), b.getY(), x2, y2);
float[] pt = ctlPts(this, b);
curve(pt[0], pt[1], getX(), getY(), b.getX(), b.getY(), pt[2], pt[3]);
break;
case BEZIER:
// float x3 = ctlPt(this, 1) * width;
// float y3 = ctlPt(this, 2) * height;
// float x4 = ctlPt(b, 1) * width;
// float y4 = ctlPt(b, 2) * height;
// bezier(getX(), getY(), x3, y3, x4, y4, b.getX(), b.getY());
pt = ctlPts(this, b);
bezier(getX(), getY(), pt[0], pt[1], pt[2], pt[3], b.getX(), b.getY());
break;
}
}
popMatrix();
popStyle();
}
void drawEllipses() {
pushStyle();
pushMatrix();
strokeWeight(size * lineScale / 5);
stroke(0);
// noFill();
// ellipse(getX(), getY(), size, size);
// fill(m.color / 2);
// ellipse(m.getX(), m.getY(), size * 0.8F, size * 0.8F);
fill(col);
ellipse(getX(), getY(), size / 2, size / 2);
popMatrix();
popStyle();
}
}
private float[] ctlPts(MNode a, MNode b) {
float[] ret = new float[4];
float len = dist(a.x, a.y, b.x, b.y);
ret[0] = a.x + ctlPt(a, 1) * len;
ret[1] = a.y + ctlPt(a, 2) * len;
ret[2] = b.x + ctlPt(b, 1) * len;
ret[3] = b.y + ctlPt(b, 2) * len;
return ret;
}
private float ctlPt(float x, float y, float z) {
return noise(x * 0.002, y * 0.002, z);
}
private float ctlPt(MNode m, float z) {
return ctlPt(m.getX(), m.getY(), z);
}
import zhang.*;
Set nodes;
Camera c;
final Growth[] growths = new Growth[] {
new GrassGrowth(), new GrowthPattern(), new FlowerGrowth(), new BranchGrowth(), new SimulGrowth() };
int lineMode = CURVE;
final static int NONE = 0,
LINE = 1,
CURVE = 2,
BEZIER = 3;
// ELLIPSE = 11,
// SQ = 12, //square
// TRIANGLE = 13;
GrowthPattern p;
MNode main;
Set leaves;
boolean displayPattern = true;
boolean dangerous = false;
boolean smooth = false;
public final static int DANGER_THRESH = 1500;
float lineHeight;
boolean first = true;
void setup() {
// Dimension d = Toolkit.getDefaultToolkit().getScreenSize();
// size(d.width, d.height);
size(800, 600);
c = new Camera(this);
c.registerScroll(false, 1.09f);
// smooth();
// println(PFont.list());
textFont(loadFont("EuphemiaCAS.vlw"));
textAlign(LEFT, CENTER);
fill(0);
noStroke();
text("wasd or arrows to move the screen\n"+
"'-'/'=' to zoom in/out\n"+
"' ' (space) to grow\n"+
"'p' to toggle text display\n"+
"'o' to toggle smooth\n"+
"'r' to reset\n", width/2 - 100, height/2);
lineHeight = textAscent() + textDescent();
textAlign(LEFT, TOP);
//create a growth pattern
reset();
// Set set = p.grow(main);
// set = p.grow(set);
}
void reset() {
nodes = new HashSet();
do{
p = (GrowthPattern) createRandomInstance(growths[1]);
println(p.getGrowNum());
}
while(p.getGrowNum() > 4000);
main = new MNode( width / 2, height * .9f - 32, 32, .09f, -PI/2, color(random(255), random(255), random(255)));
leaves = new HashSet();
leaves.add(main);
dangerous = false;
grow();
}
void grow() {
if(leaves.size() * p.getGrowNum() > 100000) return;
leaves = p.grow(leaves);
if(leaves.size() * p.getGrowNum() > DANGER_THRESH) {
dangerous = true;
}
// println("Grew "+(leaves.size()-num + 1)+", a total of "+nodes.size()+" nodes!");
}
public void mousePressed() {
first = false;
}
public void keyPressed() {
first = false;
if(key == 'p') {
displayPattern = !displayPattern;
}
else if(key == 'o') {
if(smooth) {
smooth = false;
noSmooth();
}
else {
smooth = true;
smooth();
}
}
else if(key == ' ') {
grow();
}
else if(key == 'r') {
reset();
}
}
void draw() {
// println(frameRate);
if(first) {
return;
}
background(204);
if(displayPattern) {
fill(0);
p.show();
resetMatrix();
}
if(dangerous) {
fill(255, 0, 0);
textAlign(RIGHT, TOP);
text("!!!!!!!!\n" +
"!!!!!!!!\n" +
"!!!!!!!!\n" +
"!!!!!!!!\n" +
"!!!!!!!!\n" +
"!!!!!!!!\n" +
"!!!!!!!!\n" +
"!!!!!!!!\n" +
"!!!!!!!!", width-10, 0);
textAlign(LEFT, TOP);
}
// text(String.valueOf(frameRate), 0, 0);
c.scroll(10);
c.apply();
Iterator i = nodes.iterator();
while(i.hasNext()) {
MNode n = (MNode) i.next();
n.act();
}
i = nodes.iterator();
while(i.hasNext()) {
MNode n = (MNode) i.next();
n.drawConnects();
}
i = nodes.iterator();
while(i.hasNext()) {
MNode n = (MNode) i.next();
n.drawEllipses();
}
// println(frameRate);
}
public Growth getRandomGrowth() {
return createRandomInstance(growths[(int)random(growths.length)]);
}
public Growth createRandomInstance(Growth g) {
// println("Creating a random "+g.getClass().getSimpleName());
return g.createRandom();
}
class SimulGrowth extends GrowthSet {
public SimulGrowth() {
super(new HashSet());
}
public int getGrowNum() {
int k = 0;
Iterator i = grows.iterator();
while(i.hasNext()) {
Growth g = (Growth) i.next();
k += g.getGrowNum();
}
return k;
}
public SimulGrowth createRandom() {
SimulGrowth s = new SimulGrowth();
int k = (int)random(1, 4);
for(int x = 0; x < k; x++) {
s.grows.add(getRandomGrowth());
}
return s;
}
public Set grow(MNode parent) {
Set set = new HashSet();
Iterator i = grows.iterator();
while(i.hasNext()) {
Growth g = (Growth) i.next();
set.addAll(g.grow(parent));
}
return set;
}
}
abstract class SingleGrowth implements Growth {
public SingleGrowth() {
}
public abstract String getText();
public void show() {
text(getClass().getSimpleName() + "["+getText()+"]", 0, 0);
}
protected abstract Set getGrowth(MNode parent);
public Set grow(MNode parent) {
Set set = getGrowth(parent);
parent.getChildren().addAll(set);
return set;
}
}
Generates a random rule for growth. Please post any ideas for new rules, I'd love some crazy ideas!<br>
Future versions may include 3D, relative growth, generative movement.
wasd or arrows to move the screen<br>
'-'/'=' to zoom in/out<br>
' ' (space) to grow<br>
'p' to toggle text display<br>
'o' to toggle smooth<br>
'r' to reset<br>