• fullscreen
  • codex_processianus.pde
  • flow_map.pde
  • interaction.pde
  • 
      //////////////////////////////
      //                          //
      //                          //
      //    Codex Processianus    //
      //                          //
      //                          //
      //////////////////////////////
      
      // (c) Martin Schneider 2009
      
      // This sketch creates flow map based drawings.
      // A flow map is an image where hue indicates the direction of flow 
      // and brightness indicates the amount of flow at any given pixel
    
      
    // parameters
    
    int n = 1000;
    int rdodge = 20;
    int opacity = 9;
    int mapids = 5;
    
    boolean showmap, lifelong, brush, fine, dodge = true;
    int maxage, crayons, whirl, isolines, mapid, xhatch = 1;
    
    
    // array of palettes 
    
    color[][] palette =  {
      {#ffdd99, #882211}, // da vinci
      {#000000, #ffffff}, // blackboard
      {#ffffff, #000000,  #000099, #990099, #009999 }, // pencil crayons
      {#000000, #6666ff,  #aaaa66, #ff6666, #66ffff }, // fibreglass
      {#000000} // rainbow colors
    };
    
    int palettes = palette.length;
    int rainbow = 4;
    
    
    // global variables
    
    float[][] a = new float[n][2];
    int[] age = new int[n];
    float w, h;
    int c;
    
    
    
    void setup() {  
      size(500, 500);
      w = width/2;
      h = height/2;
      colorMode(HSB, TWO_PI, 2, 1);
      loadFlowmap(mapid);
      smooth();
      reset();
    }
    
    
    void draw() {
        
      if(showmap) {
        
        // show flow map
        background(0);
        image(ff, 0, 0);
      }
      
      else {
      
        // number of colors in the selected palette
        int colors = palette[crayons].length - 1;
        
        // select pen or brush
        strokeWeight(brush ? 3 : 1);
        
        // create new particles
        int np = n / maxage;
        for(int i=0; i<np & c<n; i++, c++) newp(c);
        
        // set detail
        int grain = fine ? 1 : 3;
        
        // draw particle traces 
        for(int i=0; i<c; i++) {
          
          // get particle from the array
          float[] p = a[i];
          
          // aging and rebirth
          if (age[i]++ > maxage) newp(i);  
          
          else {
                    
            // save the starting point       
            float p0 = p[0], p1 = p[1];
            
            // move through flow map for a small distance ( the grain )
            final int maxiter = 10;
            float d = 0, j = 0;
            
            while(d < grain & j++ < maxiter ) {
              
              // get the flow at the current point
              float[] f = f(p[0], p[1], whirl + 2 * (i % xhatch) );
              
              // update the point
              p[0] += f[0] * cos(f[1]);
              p[1] += f[0] * sin(f[1]);
              
              // calculate distance to the starting point
              d = dist(p0, p1, p[0], p[1]);
            }
            
            
            //prevent dot marks and allow for iso-stripe pattern
            if(d < grain + isolines * .15) continue;
            if(d < grain + isolines * .15) continue;
            
            // rainbow hue based on direction
            if (crayons == rainbow) stroke(atan2(p[0]-p0,p[1]-p1) + PI, 1, 1, opacity);
           
            // crayon hue based on cross-hatch direction
            else stroke(palette[crayons][1 + (i % xhatch) % colors], opacity);
            
            line(p0, p1, p[0], p[1]);
            
          }
        }
      }
    }
    
    
    void newp(int p) {
      
      if(dodge) {
        
        // particle inside a circle around the mouse position
        float r = random(rdodge), ang = random(TWO_PI);
        a[p] = new float[] { mouseX + r * cos(ang), mouseY + r *sin(ang) };
        
      } else { 
        
        // particle anywhere on screen
        a[p] = new float[] { random(width), random(height) };
        
      }
      age[p] = 0;
      
    }
    
    
    void reset() {
      
      // clear screen
      if(!showmap) background(palette[crayons][0]);
      
      // reset maxage and particle counter
      maxage = lifelong ? 20 : 10;
      c = 0;
      
    }
    
    
      ////                      ////
      //                          //
      //        flow map          //
      //                          //
      ////                      ////
      
    
    PImage ff0;
    PGraphics ff;
    
    float tx, ty;
    float zoom = 1;
    
    
    // load the selected flow map
    
    void loadFlowmap(int id) {
      ff = createGraphics(width, height, P2D);
      ff0 = loadImage("flowmap-" + (1+id) + ".jpg");
      updateFlowmap();
    }
    
    
    // rather than sampling the flow map directly we rescale it 
    // relying on the bilinear interpolation algorithm built into Processing ...
    // ( so we get smooth curves and borders, even for extreme zoom values )
    
    void updateFlowmap() {
      float fw = ff0.width / 2f;
      float fh = ff0.height / 2f;  
      float fzoom = zoom * max(w/fw, h/fh);
      ff.beginDraw();
      ff.background(0);
      ff.image(ff0, int(tx + w - fw*fzoom), int(ty + h - fh*fzoom), int(ff0.width * fzoom), int(ff0.height * fzoom));
      ff.endDraw();
    }
    
    
    // the flow map function
    
    float[] f(float x, float y, int w) {
      if ( x<0 | x>width | y<0 | y>width ) return new float[] {0, 0};
      else { 
        color c = ff.get((int)x, (int)y);
        float ang = hue(c) +  HALF_PI / xhatch * w;
        if(ang>TWO_PI) ang-=TWO_PI;
        float d = brightness(c);
        return new float[] { d, ang };
      }
    }
    
    
    // transformations
    
    void zoom(float z) {
       zoom *= z; 
       tx *= z; 
       ty *= z;
       updateFlowmap();
    }
    
    
    void move(int dx, int dy) {
      tx += dx; 
      ty += dy;
      updateFlowmap(); 
    }
    
    
    
      ////                      ////
      //                          //
      //       interaction        //
      //                          //
      ////                      ////
      
    
    void mousePressed() { 
      showmap = true; 
    }
    
    
    void mouseReleased() { 
      showmap = false; 
      reset(); 
    }
    
    
    void mouseDragged() {
      if(mouseButton == LEFT) 
        move(mouseX - pmouseX, mouseY - pmouseY);
     else 
        zoom(1 - (mouseY - pmouseY) / h);
    }
    
    
    void keyPressed() {
      
      switch(key) {
        
        case ' ' : mapid++; mapid %= mapids; loadFlowmap(mapid); break;
        
        case 'd' : dodge = !dodge; break;
        case 'b' : brush = !brush; break;
        case 'g' : fine =! fine; break;
        case 'l' : lifelong =! lifelong; break;
          
        case 'c' : crayons = ++crayons %  palettes; break;
        case 'i' : isolines = ++isolines % 4; break; 
        case 'w' : whirl = ++whirl % 4; break;
        
        case 'z' :; case 'y' : xhatch = max(--xhatch, 1) ; break;
        case 'x' : ++xhatch; break;
     
        case '0' : tx = 0; ty = 0; zoom(1/zoom); break;
        case 's' : saveFrame("codex-processianus-#####.png"); break;
        
        default: return;
        
      }
      
      reset();
      
    }
    
    
    

    code

    tweaks (0)

    about this sketch

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

    license

    advertisement

    bitcraft

    Codex Processianus

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


    This sketch creates flow map based drawings.
    A flow map is an image where hue indicates the direction of flow
    and brightness indicates the amount of flow at any given pixel.

    You can find some hires samples on flickr:
    http://www.flickr.com/photos/bitcraft/tags/codexprocessianus/

    Key Map

    [space] switch flow map
    [0] reset flow map position
    [s] save image to disk

    [d] dodge on/off
    [b] brush on/off
    [g] grain fine/rough
    [l] lines short/long

    [c] colors : daVinci, blackboard, crayons, fibreglass, rainbow
    [w] whirl direction
    [i] isolines

    [x] more x-hatch
    [z] less x-hatch

    Justin Pinkney
    13 Aug 2011
    Really nice sketch! Interacting with it without looking at the underlying flow map leads to a wonderfully mysterious drawing process. Thanks for the really well commented code too!
    jason stephens
    21 Apr 2012
    Loved this! Nicely done!

    You can see some results posted here: http://itp.nyu.edu/~js5346/jayblog/2012/04/21/flow-maps-for-the-flow-field-a-chemical-love-story/
    bitcraft
    24 Apr 2012
    Hey Jason - nice work!
    Seems like I really got you hooked on flow map based art :-)
    This is beautiful
    You need to login/register to comment.