• fullscreen
  • BranchGrowth.pde
  • FlowerGrowth.pde
  • GrassGrowth.pde
  • Growth.pde
  • GrowthPattern.pde
  • GrowthSet.pde
  • MNode.pde
  • Nodes.pde
  • SimulGrowth.pde
  • SingleGrowth.pde
  • 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;
        }
    }
    

    code

    tweaks (0)

    about this sketch

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

    license

    advertisement

    Xiaohan Zhang

    Nodes v1

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

    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>

    Guigui plus+
    14 May 2009
    oooh, it doesn't work on my intel mac. Neither in Safari, nore in Firefox. And if I try the downloaded sketch in Processing I get 'The package "zhang" does not exist, You might be missing a library.' What a pity, it seemed so nice!
    I see this error in java console:
    Exception in thread "Animation Thread" java.lang.ClassCastException: zhang.Camera$MouseAdapterImpl
    at java.awt.AWTEventMulticaster.add(AWTEventMulticaster.java:635)
    at java.awt.Component.addMouseWheelListener(Component.java:4966)
    at zhang.Camera.registerScroll(Camera.java:101)
    at Nodes.setup(Nodes.java:53)

    I think c.registerScroll(true, 1.09f); doesn't work in browsers.
    Hey I checked it in Processing too, (I needed to add "zhang.jar" to the 'code' folder, etc..) but it didn't work anyways. 'Scroll' seems to be the problem on Macs. I wonder if people can see this in PC..?..
    Xiaohan Zhang
    14 May 2009
    blargh. i'll get rid of mouse-wheel zoom. it's useless in a web browser anyways
    Guigui plus+
    14 May 2009
    Yes, on my laptop there is no mouse-wheel. So, it's nice you let it down because this work truly worth a look 8^). An idea, maybe not so brilliantly crazy would be to random also the size, the shape & the color of the circles @@
    bejoscha
    13 Aug 2011
    I like the "brushlike" features of a several-growthstep model in "smooth".r
    You need to login/register to comment.