• fullscreen
  • colors.pde
  • crack.pde
  • Poly.pde
  • UCracking02A.pde
  • UColorTool colors;
    float hueOffs;
    
    void initColors() {
      colors=new UColorTool();
      
      // start by creating lots of colors
      colors.addGradient(5,10,"FF0099","FF0033");
      colors.addGradient(5,10,"FFFF00","FF3300");
      colors.addGradient(5,10,"00FFFF","00496A");
      colors.addGradient(5,10,"293236","00496A");
      colors.addGradient(5,10,"166A00","44FC12");
      colors.addGradient(5,10,"FFFFFF","CCFFFF");
      colors.generateColors(10,40);
      
      // force a limited selection from that large palette
      int cc[]=new int[6];//min(colors.n,(int)random(6,20))];
      for(int i=1; i<cc.length-1; i++) {
        cc[i]=colors.getRandomColor();
    //    cc[i]=colors.adjustHue(cc[i],hueOffs);
      }
      cc[0]=color(255);
      
      // set colors to that limited palette
      colors.colors=cc;
      colors.n=cc.length;
      
      doRecolor=true;  
    }
    
    
    // initializes triangle surface in one of two configurations
    public void reinit() {
      initColors();
      
      f=new ArrayList<Poly>();
      ((Toggle)gui.cp.controller("doCrack")).setState(false);
    
      // n = # of points around circumference
      // vl.length = # of "rings"
      float n=(int)random(3,9)*6;
      UVertexList vl[]=UVertexList.getVertexLists((int)random(3,9));
    
      // radial offset controlled by a sin() function
      float sineOffs=random(0.05,0.1)*(float)height;
      
      // # of phases the sin() offset goes through
      float sinePhases=(int)random(2,10);
    //  n+=sinePhases*3;
    
      // calculate concentric vertex lists, with a sin() offset
      for(int i=0; i<n; i++) {
        for(int j=0; j<vl.length; j++) {
          float a=map(i,0,n-1,0,TWO_PI);
          float h=map(j,0,vl.length-1,0.2,1);
          h*=height;
          h+=(sin(a*sinePhases)*sineOffs);
          
          vl[j].add(new UVec3(h,0,0).
            rotate(a).add(width/2,height/2));
          if(i==n-1) vl[j].close();
        }    
      }
      
      UGeometry geo=new UGeometry();
      geo.noDuplicates();
      
      // center triangle fan
      geo.triangleFan(vl[0],true,false);
      
      // quadstrip all the rings
      geo.quadStrip(vl);
        
      // create Poly instances from all the resulting UFaces
      for(int i=0; i<geo.faceNum; i++) {
        UVec3 vv[]=geo.face[i].v;
        f.add(new Poly(vv[0],vv[1],vv[2]));
      }
    }
    
    void crack() {
      ArrayList<Poly> ff=new ArrayList<Poly>();
    
      Poly pp;
      randomCrack=
        ((Toggle)gui.cp.controller("randomCrack")).getState();
      
      if(randomCrack) {
        int id=(int)(sq(random(1))*(float)f.size());
        if(random(100)>60) id=(int)random(f.size()/4);
        pp=f.get(id);
      }
      else  // crack in sequence, oldest first
        pp=f.get(0);
      
      // subdivide triangle to produce new triangles
      Poly p[]=pp.subdivide();
      
      // remove old triangle from arraylist
      f.remove(pp);
      
      // add the new triangles to arraylist
      for (int i=0; i<p.length; i++) {    
        UVec3 pv[]=p[i].v;
        int outCnt=0;
        
        // check if all three vertices are outside canvas
        for(int j=0; j<3; j++)
          if((pv[j].x<-50 || pv[j].x>width+50)
            || (pv[j].y<-50 || pv[j].y>height+50)) outCnt++;
          
        if(outCnt<3) f.add(p[i]);
        else println("Poly rejected: "+UUtil.toString(pv));
      }
    }
    
      
    
    class Poly {
      UVec3 v[];
      int col;
    
      Poly(UVec3 v1, UVec3 v2, UVec3 v3) {
        v=new UVec3[3];
        v[0]=v1;
        v[1]=v2;
        v[2]=v3;
    
        initColors();
      }
    
      public void initColors() {
        col=colors.getRandomColor();
        
    //    if(random(100)>75) 
          col=colors.adjustBrightness(col,
            (random(100)>66 ? random(0.5,0.8) : random(1.5,2)));
      }
      
      public void draw() {
        fill(col);
        if(brightness(col)<100) stroke(255,200);
        else stroke(0,200);
        
        noStroke();
        beginShape(TRIANGLES);
        vertex(v[0].x, v[0].y);
        vertex(v[1].x, v[1].y);
        vertex(v[2].x, v[2].y);
        endShape();
      }
    
      // calculate random point in triangle, tending towards the
      // center of the triangle surface
      public UVec3 calcRandomPoint() {
        UVec3 vv=UVec3.interpolate(v[1], v[2], UUtil.rnd.random(0.33f, 0.66f));
        vv=UVec3.interpolate(v[0], vv, UUtil.rnd.random(0.33f, 0.66f));
        return vv;
      }
    
      // subdivide Poly by calculating a random point in the triangle
      // face and using it to produce three new Poly instances
      public Poly[] subdivide() {
        UVec3 vv,vv2,vv3;
        Poly p[];
        
        // random center point
        if(subdiv==0) {
          p=new Poly[3];
          vv=calcRandomPoint();
          p[0]=new Poly(v[0], vv, v[1]);
          p[1]=new Poly(v[1], v[2], vv);
          p[2]=new Poly(v[2], v[0], vv);
          return p;
        }
    
        // mid-edge subdivision rule
        if(subdiv==1) {
          p=new Poly[4];
          int id=0;
          float randOffs=0.1;
          vv=UVec3.interpolate(v[id],v[((id++)+1)%3],
            random(-1,1)*randOffs+0.5); // mid-point offset
          vv2=UVec3.interpolate(v[id],v[((id++)+1)%3],
            random(-1,1)*randOffs+0.5); // mid-point offset
          vv3=UVec3.interpolate(v[id],v[(id+1)%3],
            random(-1,1)*randOffs+0.5); // mid-point offset
          p[0]=new Poly(v[0], vv, vv3);
          p[1]=new Poly(vv,v[1], vv2);
          p[2]=new Poly(v[2], vv2, vv3);
          p[3]=new Poly(vv, vv2, vv3);
          return p;
        }
        
        // halving strategy
        int id=(int)random(3);
        vv=UVec3.interpolate(v[id],v[(id+1)%3],0.5);
        p=new Poly[2];
        p[0]=new Poly(v[id], vv, v[(id+2)%3]);
        p[1]=new Poly(v[(id+1)%3], v[(id+2)%3], vv);
    //    p[2]=new Poly(v[2], v[0], vv);
        return p;
      }
    }
    
    
    import processing.pdf.*;
    
    /**
     * UCracking01.pde - Marius Watz, 2012
     * http://workshop.evolutionzone.com
     * 
     * Cracking (aka subdivision) of triangular geometry. Uses 
     * ArrayList to efficiently handle storage of new triangles 
     * while removing old ones after they've been subdivided. 
     *
     * The starting shape is built using a UGeometry instance
     * built from an array of vertex lists. UFace instances are
     * extracted and used to initialize the custom Poly class,
     * allowing us to construct a complex starting form.
     */
     
    
    import controlP5.*;
    
    import java.util.ArrayList;
    
    import unlekker.modelbuilder.*;
    import unlekker.util.*;
    
    public ArrayList<Poly> f;
    public boolean doCrack,doSave;
    public boolean randomCrack=false,doRecolor;
    public USimpleGUI gui;
    String filename;
    public int subdiv=0;
    
    public void setup() {
      size(600, 600,P2D);
      
      gui=new USimpleGUI(this);
      gui.addButton("reinit");
      gui.addRadioButton("subdivType",
        new String[] {"type 1","type 2","type 3"},100);
      gui.addButton("initColors");
      
      gui.addToggle("doCrack",doCrack); // toggle continuous cracking
      gui.addToggle("randomCrack",randomCrack); // crack at random?
      
      reinit();
      smooth();
    }
    
    public void draw() {
      background(0);
    
      if (doCrack) 
        // crack 20 times if randomCrac==false, 40 times if true
        for(int i=0; i<20+(randomCrack ? 20 : 0); i++) crack();
      
      // handle recoloring
      if(doRecolor && f!=null) {
        for(Poly pp:f) pp.initColors();
        doRecolor=false;
      }
      
      for (Poly pp:f) pp.draw();
    
      if(doSave) {
        saveFrame(
        UIO.getIncrementalFilename(
          this.getClass().getSimpleName()+" ###.png",
          sketchPath));
        doSave=false;
      }
      else     
        gui.draw();
    }
    
    
    void controlEvent(ControlEvent ev) {
      // handle ControlGroup event for the radio buttons
      if(ev.isGroup()) {
        for(int i=0;i<ev.group().arrayValue().length;i++) {
          if(ev.group().arrayValue()[i]>0) subdiv=i;
        }
      }
    }
    
    public void keyPressed() {
      // CTRL-A == initColors();
      if(keyEvent.isControlDown() && keyCode==KeyEvent.VK_A) initColors();
        if(!online && key=='s') doSave=true;
    
    }
    
    public void mousePressed() {
      if(!gui.isMouseOver()) crack();
    }
    
    

    code

    tweaks (0)

    about this sketch

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

    license

    advertisement

    Marius Watz plus+

    UCracking02A

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

    Cracking (aka subdivision) of triangular geometry. Uses ArrayList to efficiently handle storage of new triangles while removing old ones after they've been subdivided.

    This version builds starting shape using a UGeometry instance built from an array of vertex lists. UFace instances are extracted and used to initialize the custom Poly class, allowing us to construct a complex starting form.

    Other subdivision implementations:
    http://ncodescripting.blogspot.be/2010/06/subdivision.html
    http://formativecomplexity.blogspot.com/2012/02/aranda-lasch-seattle-public-library.html

    Ale
    26 Jun 2012
    So beautiful and inspiring! Thanks for sharing! I'll be tweaking this soon... :-)
    Regards!
    Jake
    10 Jan 2013
    Hey i really love this, am new to processing, would love to experiment with tweaking this but when i download the sketch i get the error "Cannot find anything named KeyEvent.VK_A" Any ideas?
    Marius Watz plus+
    11 Jan 2013
    Sorry about that, it's related to changes in Processing 2.0. Just change it to:

    if(key=='a') initColors();

    That line just provides a keyboard shortcut for initColors(), so it's not really integral to the sketch.
    Marius Watz plus+
    11 Jan 2013
    To clarify: Replace the following line -

    if(keyEvent.isControlDown() && keyCode==KeyEvent.VK_A) initColors();

    - with this line:

    if(key=='a') initColors();
    Jake
    12 Jan 2013
    thanks so much for the promt response man! got it working straight away, i think this stuff would look awesome on t-shirts! :
    http://img534.imageshack.us/img534/5899/geometrictests2.jpg
    Jake
    12 Jan 2013
    would be awesome to somehow morph this with your sketch UParametricForm01, to give it some 3 dimensional kick...
    You need to login/register to comment.