• fullscreen
  • interaction.pde
  • julibrot_explorer.pde
  • ///// DIVING MODE //////
    
    void flyThrough() {
    
      rotateMouse();
      mouseButtons();
    
      if (mousePressed) { 
        if (julia && shiftPressed()) {
    
          // scale julia param
          if (mouseButton == LEFT) { 
            jx +=  map(rmouseX,0, width, -mr, +mr) / fps;  
            jy +=  map(rmouseY,0, height, -mr, +mr) / fps; 
          }
    
          // scale julia param in opposite direction
          if (mouseButton == RIGHT) { 
            jx -=  map(rmouseX,0, width, -mr, +mr) / fps;
            jy -=  map(rmouseY,0, height, -mr, +mr) / fps;
          }
    
          // rotate and scale julia param
          if (mouseButton == CENTER) {
            float ang = map(mouseX, 0, width, -PI/fps, PI/fps);
            float dr = pow(.70, 1f/fps);
            float d = map(mouseY, 0, height, dr, 1/dr);
            jx = d * (jx * cos(ang) - jy * sin(ang));
            jy = d * (jx * sin(ang) + jy * cos(ang));     
            println(dist(0,0,jx,jy));
          }
    
        } 
        else {
    
          // zoom in and move
          if (mouseButton == LEFT) { 
            mx +=  map(rmouseX,0, width, -mr, +mr) / fps;  // maximum horizontal speed: one window width per second
            my +=  map(rmouseY,0, height, -mr, +mr) / fps; 
            mr *= pow(.70, 1f/fps);                        
          } 
    
          // zoom out and move
          if (mouseButton == RIGHT) { 
            mx -=  map(rmouseX,0, width, -mr, +mr) / fps;
            my -=  map(rmouseY,0, height, -mr, +mr) / fps;
            mr /=  pow(.70, 1f/fps);  
          } 
    
          // zoom and rotate      
          if (mouseButton == CENTER) { 
            theta += map(mouseX, 0, width, -PI/fps, PI/fps);
            float dr = pow(.70, 1f/fps);
            mr *= map(mouseY, 0, height, dr, 1/dr);
          } 
    
        }
      }
    }
    
    
    ///// DRAG MODE //////
    
    
    void mouseDragged() {
      if(flying) return; 
      
      mouseButtons();
      rotateMouse();
    
      if(julia && shiftPressed()) {
        // drag julia params
        cursor(CROSS);
        switch(mouseButton) {
        case LEFT: // move 
          jx -= map(rmouseX - prmouseX, 0, width, 0, mr);
          jy -= map(rmouseY - prmouseY, 0, height, 0, mr);
          break;
        case RIGHT: // scale julia params
          float d = float(rmouseY - prmouseY) / height;
          jx *= 1 + d;
          jy *= 1 + d;
          break;
        case CENTER: // rotate julia params
          float ang = 2 * (atan2(mouseX - cx, mouseY - cy) - atan2(pmouseX - cx, pmouseY - cy)); 
          float temp = jx * cos(ang) + jy * sin(ang);
          jy = jx * -sin(ang) + jy * cos(ang);
          jx = temp;
          break;
        }
    
      } 
      else {
    
        // drag sampling window
        cursor(HAND);
        switch(mouseButton) {
        case LEFT: // move
          mx -= map(rmouseX - prmouseX, 0, cx, 0, mr);
          my -= map(rmouseY - prmouseY, 0, cy, 0, mr);
          break;
        case RIGHT: // zoom
          float d = float(mouseY - pmouseY) / height;
          mr *= 1 + d;
          break;
        case CENTER: // rotate 
          theta += atan2(rmouseX - cx, rmouseY - cy) - atan2(prmouseX - cx, prmouseY - cy) ;   
          break;
        } 
      }
    }
    
    void mouseReleased() {
      if(!flying) cursor(ARROW); 
    }
    
    
    /// MOUSE HACKS ///
    
    // add a mousewheel listener ...
    void useMouseWheel() {
      addMouseWheelListener(new java.awt.event.MouseWheelListener() { 
        public void mouseWheelMoved(java.awt.event.MouseWheelEvent evt) { 
          mouseWheel(evt.getWheelRotation());
        }
      }
      ); 
    }
    
    // use the mousewheel for rotation
    void mouseWheel(int delta) {
    
      float ang = PI / 24 * delta;
    
      if(julia && shiftPressed()) {
        float temp = jx * cos(ang) + jy * sin(ang);
        jy = jx * -sin(ang) + jy * cos(ang);
        jx = temp;
      } 
      else {
        theta -= ang;
      }
    }
    
    
    // map mouse motions to our rotated coordinate system
    void rotateMouse() {
      rmouseX = cx + int(cosinus * (mouseX-cx) - sinus * (mouseY-cy));
      rmouseY = cy + int(sinus * (mouseX-cx) + cosinus * (mouseY-cy));
      prmouseX = cx + int(cosinus * (pmouseX-cx) - sinus * (pmouseY-cy));
      prmouseY = cy + int(sinus * (pmouseX-cx) + cosinus * (pmouseY-cy));
    }
    
    // make mac users happy ( mapping modifier keys to mouse buttons )
    void mouseButtons() {
      // we have to access the keyEvent directly since processing does not allow to 
      // check for multiple modifier keys at once ...
      if (keyPressed && (keyEvent.getModifiers() & KeyEvent.CTRL_MASK) > 0) mouseButton = CENTER;
      if (keyPressed && (keyEvent.getModifiers() & KeyEvent.ALT_MASK) > 0) mouseButton = RIGHT;
    }
    
    boolean shiftPressed() {
      return keyPressed && ((keyEvent.getModifiers() & KeyEvent.SHIFT_MASK) > 0);
    }
    
    
    /// KEYBOARD INTERACTION ///
    
    void keyPressed() {
      switch(key) {
      case 'a': // animate the palette
        startcolor += maxcolors-1; 
        break;
      case 'A':
        startcolor++; 
        break;
      case 's': // smooth colors
        createPalette(maxcolors + 1);
        break;
      case 'S':
        createPalette(maxcolors - 1);
        break;
      case 'd': // deep thought
        maxiterations++ ; 
        break;
      case 'D': 
        maxiterations-- ; 
        break;
      case 'f': // browsing mode 
        flying =! flying; 
        if(flying) noCursor(); else cursor(ARROW);
        break;
      case 'g':
      case 'h':
        goHome();
        break;
      case 'j': // julia mode
        julia =! julia;
        break;
      case 'c': // capture
        saveFrame(screenshotName);
        break;
      case 'C': // capture hires
        saveScreenshot(screenshotName);
        break;
      case 'R': // start recording
        recording = true;
        break;
      case 'r': // stop recording
        recording = false;
        break;
      }
        
    
    }
    
    
    
     ///////////////////////////////////////////////
     //                                           //
     //                                           //
     //       Yet Another Julibrot Explorer       //
     //                                           //
     //                                           //
     ///////////////////////////////////////////////
    
     // (c) Martin Schneider 2009
    
    
     ////////////////// Key Map ////////////////////
     
     // [j] julia/mandelbrot switch 
     // [f] flying mode on/off
     // [g][h] go home!
     
     // [a] [A] animate colors 
     // [s] [S] smooth colors (change the number of colors)
     // [d] [D] deep thought (change the number of iterations)
     
     // [c] capture screenshot
     // [R] start recording a movie
     // [r] stop recording
     
    
     
     //////////////// Mouse Map ////////////////////
     
     
     ///// Dragging Mode /////
     
     // drag the mouse to change the mandelbrot params
     // shift-drag it to change juliaset parameters
     
     // [left] drag  
     // [right] zoom  
     // [center] rotate
     
     
     ///// Flying Mode /////
     
     // hold and move the mouse to dive in.
     // shift-move it to fly through the space of julia sets
     
     // [left] zoom in & move
     // [right] zoom out & move
     // [center] zoom & rotate
     
     // Note: You can press [ctrl] and [alt] while using the mouse 
     // to emulate middle and right mouse buttons ...
      
      
    
    // mandelbrot params
    float mx, my, mr;
    float mx0 = 0; // x origin 
    float my0 = 0; // y origin
    float mr0 = 3; // radius 
    
    // julia set params
    float jx = -.6;
    float jy = .6;
    
    // sampling params
    int maxiterations = 32;
    
    // rotation
    float theta;
    float sinus, cosinus;
    int rmouseX, rmouseY;
    int prmouseX, prmouseY;
    int cx, cy;
    
    // colors
    int startcolor = 0;
    int maxcolors = 32;
    color[] palette;
    
    // toggles
    boolean flying = false; // navigation mode
    boolean julia = false;  // julia mode
    boolean recording;      // recording animations
    
    // saving
    int hires = 2000;
    String screenshotName = "fractal-screenshot-####.png";
    
    // recording
    String frameName = "fractal-animation-######.jpg";
    float recordingFps = 60;
    
    // animation
    float fps;
    
    // julibrot transition
    float J, M;
    int tween;
    int jbmillis = 2000;
    
    void setup() {
      
      size(500, 500);
      createPalette(maxcolors);
      cx = width / 2;
      cy = height / 2;
      useMouseWheel();
      goHome();
      
    }
    
    
    void draw() { 
      
      // adaptive framerate for live browsing
      // fixed framerate for recording ...
      fps = recording ? recordingFps : frameRate; 
      
      // morph the julibrot intersection plane between mandelbrot and julia set 
      morphing();
      
      // do the mandelbrot calculations ...
      render(g);
      
      // save the result if we are in recording mode
      if(recording) saveFrame(frameName);
      
      // frame-based flying mode interaction 
      if(flying) flyThrough();
    }
    
    
    void render(PImage img) {
    
      // access image pixels
      img.loadPixels();
      
      // boundaries for sampling
      float xmin = mx - mr;
      float xmax = mx + mr;
      float ymin = my - mr;
      float ymax = my + mr;
      
      // sampling density
      float dx = (xmax - xmin) / img.width ;
      float dy = (ymax - ymin) / img.height;
      
      // rotation angles
      sinus = sin(theta);
      cosinus = cos(theta);
        
      // go sample!
      for(int j = 0; j < img.height; j++) {
      float y = ymin + j * dy; 
        for(int i = 0;  i < img.width; i++) {
        float x = xmin + i * dx;  
          
          // rotation
          float xr = mx + cosinus * (x-mx) - sinus * (y-my);
          float yr = my + sinus * (x-mx) + cosinus * (y-my);
          
          // calculation
          int n = julibrot(J*jx + M*xr, J*jy + M*yr, J*xr, J*yr, maxiterations);
          
          // paint the pixel
          if ( n == maxiterations) {
            img.pixels[i+j*img.width] = color(#ffffff); 
            
          } else {
             img.pixels[i+j*img.width] = palette[(startcolor + n) % maxcolors];   
          }
        }
      }
      img.updatePixels();
      
    }
    
    
    //// FRACTAL FUN
    
    int julibrot(float x, float y, float a, float b, int maxiterations) {
       int n = 0; 
        for(; n < maxiterations; n++) {
        float aa = a * a;
        float bb = b * b;
        b = 2 * a * b + y;
        a = aa - bb + x;
        // bail out if the distance to the origin is > 2
        if(aa + bb > 4) break;    
      }
      return n;
    }
    
    
    void morphing() {
      
     // set the target for julibrot transition
      int target = julia ? 1 : 0;
      
      // lerpstep is the amount of lerping needed
      // to reach 90% of the target within jbmillis
      float lerpstep =  2.3f /fps * 1000 / jbmillis; 
      J = lerp(J, target, lerpstep);
      
      // stop lerping after 99.9 % 
      float lerpstop = .001;                         
      if( target==1 && J > target-lerpstop) J=1; 
      if( target==0 && J < lerpstop) J=0;
       
      M = 1-J;  
      
    }
    
    
    void goHome() {
      mx = mx0; 
      my = my0;
      mr = mr0;
    }
    
    
    //// HELPER FUNCTIONS
    
    void createPalette(int s) {
      // readjust startcolor
      startcolor = int(startcolor * float(s) / maxcolors); 
      
      maxcolors = max(1, s);
      colorMode(HSB, maxcolors , 255, 255);
      palette = new color[maxcolors];
      for(int i=0; i<maxcolors; i++) {
        palette[i] = color(i, 128, 255);
      }
    }
    
    void saveScreenshot(String name) {  
      PGraphics tmp = g;
      PGraphics img =  createGraphics(hires, hires, P2D);
      render(img);
      g = img;
      saveFrame(name); 
      g = tmp;
    }
    
    
    
    

    code

    tweaks (0)

    about this sketch

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

    license

    advertisement

    bitcraft

    Julibrot Explorer

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


    Yet Another Julia Set / Mandelbrot Explorer ...

    special features:
    - smooth transition between mandelbrot and julia planes
    - rotation, baby!
    - switch between drag and fly mode
    - hires screenshots
    - saving animations
    - color controls

    Key Map

    [j] julia/mandelbrot switch
    [f] flying mode on/off
    [g][h] go home!

    [a] [A] animate colors
    [s] [S] smooth colors (change the number of colors)
    [d] [D] deep thought (change the number of iterations)

    [c] capture screenshot
    [R] start recording a movie
    [r] stop recording

    bitcraft
    4 Dec 2009
    Mouse Map

    Dragging Mode:
    drag the mouse to change the mandelbrot params, shift-drag it to change juliaset parameters

    [left] drag
    [right] zoom
    [center] rotate

    Flying Mode

    hold and move the mouse to dive in. shift-move it to fly through the space of julia sets

    [left] zoom in & move
    [right] zoom out & move
    [center] zoom & rotate

    Note: You can press [ctrl] and [alt] while using the mouse to emulate middle and right mouse buttons ...
    dotlassie
    4 Dec 2009
    The Julia/Mandelbrot switch animation is awesome!
    Impressively smooth!
    Felix Woitzel
    7 Mar 2010
    the julia/mandelbrot transition is truly spectacular!
    Amazing!
    You need to login/register to comment.