• fullscreen
  • Events.pde
  • GUI.pde
  • Seeker.pde
  • Worms04P3D.pde
  • public void dropEvent(DropEvent theDropEvent) {
    
      // si le drop est une image jpg
      // on charge l'adresse dans  mUrl.
      // if dropped file is an image, its url is stored in mUrl
      if (theDropEvent.isFile()) {
        mUrl = theDropEvent.filePath();
      }
      if (theDropEvent.isURL()==true) {
        if (theDropEvent.isImage()) {
          mUrl = theDropEvent.url().trim();
        }
      }
    }
    void rangeController(ControlEvent theControlEvent) {
      if (theControlEvent.isFrom("rangeController")) {
        // min and max values are stored in an array.
        // access this array with controller().arrayValue().
        // min is at index 0, max is at index 1.
        seuilBrillanceMini = int(theControlEvent.getController().getArrayValue(0));
        seuilBrillanceMaxi = int(theControlEvent.getController().getArrayValue(1));
      }
    }
    
    void Restart() {
      initialiser();
    }
    
    // color information from ColorPicker 'picker' are forwarded to the picker(int) function
    void picker(int col) {
      macouleur=col;
    }
    
    void controlEvent(ControlEvent theEvent) { 
      if (theEvent.isFrom(radio)) {
        modeCouleur = int(theEvent.group().value());    
      }
      if (theEvent.isFrom(shapeRadio)) {
        forme = int(theEvent.group().value());
        if(forme==2){
          strokeButton.setOn();
          fillButton.setOff();
          cp.setColorValue(color(100,1,1, 100));
        }
      }
      if(theEvent.isFrom(saveButton)){
        dessine=false;
        if(savePath==""){savePath=selectFolder("Choose a folder to save this applet captures:");}
        if(savePath!=null){saveFrame(savePath+"/worms-####.jpg");}
        dessine=true;
      }
    }
    
    
    /////////////////////////////////////////////////////////////////////
    //                          GUI                                    //
    /////////////////////////////////////////////////////////////////////
    import controlP5.*;
    
    //GUI controls
    public ControlP5 controlP5;
    public ControlWindow controlWindow;
    public Textlabel modeCouleurLabel;
    public Textlabel seuilMaxLabel;
    public Textlabel seuilMinLabel;
    public Textlabel pInfo;
    public Textlabel behaviourLabel;
    public RadioButton radio ;
    public RadioButton shapeRadio ;
    public Button strokeButton;
    public Button fillButton;
    public Button drawButton;
    public Button restartButton;
    public Button limitButton;
    public Button saveButton;
    public Slider s1;
    public Slider s2;
    public Slider s3;
    public Slider s4;
    public Slider s5;
    public Slider s6;
    public ColorPicker cp;
    public Range range;
    public Tooltip tt;
    String monInfo;
    
    void setupControls(){
      
      //***************************initialize controlP5*************************************
      controlP5 = new ControlP5(this);
      controlP5.setControlFont(new ControlFont(createFont("ArialUnicodeMS", 10, true), 12));
      //initialize controlWindow
      controlWindow =controlP5.addControlWindow("controlP5CW",310,hautI).setBackground(128).hideCoordinates();
      
      //***************************add colorpicker cp***************************
      cp = controlP5.addColorPicker("picker",10,10,280,12)
              .setColorValue(color(255,255,255, 255))
              .moveTo(controlWindow);
              
      // ***************************add horizontal sliders and labels for stroke***************************
      controlP5.addSlider("myweight", 1, 50, 3, 10, 100, 100, 15).setWindow(controlWindow);
      s2 = (Slider)controlP5.controller("myweight")
      .setLabel("Stroke width:");
      s2.setDecimalPrecision(0);
      
      //***************************add slider for maximum stroke length***************************
      controlP5.addSlider("maxstrokelength", 1, 600, 400, 10, 150, 150, 15).moveTo(controlWindow);
      s1 = (Slider)controlP5.controller("maxstrokelength")
      .setLabel("Max stroke length");
    
      //***************************add strokeButton and fillButton Buttons to control if the seeker draws strokes, fills or both***************************
      strokeButton=controlP5.addButton("Stroke", 0, 10, 200, 60, 15)
      .setSwitch(true)
      .moveTo(controlWindow);   
      fillButton=controlP5.addButton("Fill", 0, 150,200, 50, 15)
      .setSwitch(true)
      .setOn()
      .moveTo(controlWindow);
      
      // ***************************add radioButton for drawing shape***************************
      shapeRadio = controlP5.addRadioButton("shapeRadio", 10, 250)
      .moveTo(controlWindow)
      .setColorForeground(color(120))
      .setColorLabel(color(0))
      .setItemsPerRow(4)
      .setSpacingColumn(100)
      .addItem("Spheres", 1)
      .addItem("Lines", 2)
      .addItem("Boxes", 3)
      //shapeRadio.addItem("Triangle", 3);
      .activate("Spheres");
      
      // ***************************add radioButton for drawing modes***************************
      radio = controlP5.addRadioButton("radioButton", 10, 600)
      .moveTo(controlWindow)
      .setColorForeground(color(120))
      .setColorLabel(color(0))
      .setItemsPerRow(4)
      .setSpacingColumn(100)
      .addItem("Normal", 1)
      .addItem("Pic Colors", 2)
      .addItem("Growing", 3)
      .activate("Normal");
      
    // ***************************add horizontal sliders and labels for stroke***************************
      controlP5.addSlider("amplitudeTest", 1, 5, 2, 10, 650, 100, 15).setWindow(controlWindow);
      s6 = (Slider)controlP5.controller("amplitudeTest")
      .setLabel("Test amplitude:");
      s6.setDecimalPrecision(0);
      amplitudeTest=2;
      //***************************add switch button for limit to threshold***************************
      limitButton= controlP5.addButton("short_strokes",0, 10,350, 110, 15)
      .moveTo(controlWindow)  
     .setLabel("limit to value")
      .setSwitch(true).setOn();
      
      //***************************add drawButton***************************
      drawButton=controlP5.addButton("Draw", 0, 10, 800, 50, 15)
      .setSwitch(true)
      .moveTo(controlWindow);
      
      //***************************add saveButton***************************
      saveButton=controlP5.addButton("Save", 0, 100, 800, 50, 15)
      .moveTo(controlWindow); 
      
      //***************************add restartButton***************************
      restartButton=controlP5.addButton("Restart", 0, 190, 800, 70, 15)
      .moveTo(controlWindow);
     
      //***************************add slider for inertia***************************
      controlP5.addSlider("inertia", 1, 20, 8, 10, 480, 200, 15).moveTo(controlWindow);
      s3 = (Slider)controlP5.controller("inertia");
      s3.setLabel("Inertia:");
      
      //***************************add slider for devMax (acceleration limiter)***************************
      controlP5.addSlider("devMax", 0, 5, 2.2, 10, 500, 200, 15).moveTo(controlWindow);
      s4 = (Slider)controlP5.controller("devMax");
      s4.setLabel("Acceleration:");
      
      //***************************add slider for maxSpeed***************************
      controlP5.addSlider("maxSpeed", 0, 100, 40, 10, 520, 200, 15).moveTo(controlWindow);
      s5 = (Slider)controlP5.controller("maxSpeed");
      s5.setLabel("Max speed:");
      s5.setSliderMode(Slider.FIX);  
      behaviourLabel = controlP5.addTextlabel( "behaviourLabel", "Seekers' behaviour parameters", 30, 450).moveTo(controlWindow)
        .setColorValue(0);
        
      //***************************add Range control for value thresholds***************************
      range=controlP5.addRange("rangeController", 0, 255, 0, 128, 10, 400, 200, 15).moveTo(controlWindow)
      .setDecimalPrecision(0)
      .setCaptionLabel("Value range")
      .setSliderMode(Slider.FIX);
      seuilMaxLabel = controlP5.addTextlabel( "light", "light", 10, 380).moveTo(controlWindow)
        .setColorValue(0);
      seuilMinLabel = controlP5.addTextlabel( "dark", "dark", 170, 380).moveTo(controlWindow)
        .setColorValue(0);
      modeCouleurLabel =controlP5.addTextlabel("Drawing modes:", "Drawing modes:", 100, 560).moveTo(controlWindow)
        .setColorValue(0);
      pInfo=controlP5.addTextlabel( "pInfo", "You can drag and drop a picture (not too big!)"+"\n from your web or file browser...", 10, 700).moveTo(controlWindow);
      pInfo.setColorValue(0);
      
    //***************************add some tooltips***************************
      tt= controlP5.getTooltip();
      tt.setDelay(200);
      tt.register("s5","Maximum speed of the seeker.");
      tt.register("s2","Width of the strokes");
      tt.register("s1","Maximum length of a worm.");
      tt.register("s3","Inertia of the seeker");
      tt.register("s4","Acceleration of the seeker");
    }
    
    class Seeker {
      
      //index
      int index;
      //position
      PVector p=new PVector();
      //speed
      PVector v=new PVector();
      
      //worm's length
      float longueur;
      //worm's type
      boolean typeVer=true;
      //worm's limite
      int limiteDown;
      //stroke weight
      float weight;
      boolean deplace;
      color couleur;
      //variable speed
      float variationSpeed= 1+int(random(200));
      boolean on = true; // Turns the display on and off
    
      // Constructor
      Seeker(int Index,PVector P, PVector V) {
        index=Index;
        p=P;
        v=V;
        limiteDown=0;
        weight=myweight;
        longueur=0;
        couleur = macouleur;
        deplace=false;
      }
    
      void start(float xpos, float ypos) {
        
        on = true;
      }
     
      void display(float actuX,float actuY) {
        if (on == true) {
    // option "pic colors" selected
        if (modeCouleur==2) {
          macouleur=source.pixels[floor(mySeeker.p.x)+floor(mySeeker.p.y)*larg];
        }
    //option "growing" selected
        if (modeCouleur==3) {            
          float vert=200;//50+(100*(1-sin(mySeeker.longueur/200)));
          float rouge=50*(1-cos(mySeeker.longueur/300));
          float bleu=100*(1-sin(mySeeker.longueur/100));//100-vert-rouge;
    
          macouleur=color(rouge, vert, bleu);
        }
    
        //control the type of drawing, with or without strokes and/or fill
        //strokeWeight(1);
        if (strokeButton.isOn()) {
          if (fillButton.isOn()) {
            stroke(macouleur, -mySeeker.limiteDown+100/mySeeker.weight);
          }
          else {
            stroke(macouleur, -mySeeker.limiteDown+500/mySeeker.weight);
          }
        }
        else noStroke();
        if (fillButton.isOn()) {
          fill(macouleur);
        }
        else {
          noFill();
        }
     //shapes size = dia
        float dia=mySeeker.weight*(1-sin(mySeeker.longueur/mySeeker.variationSpeed));
          switch(forme) {
     // spheres
        case 1: 
          strokeWeight(1);
          pushMatrix();
          translate(this.p.x, this.p.y);
          sphereDetail(int(dia/2));
          sphere(dia);
          popMatrix(); 
          break;
     //lines
        case 2: 
          if (!this.deplace) {
            strokeWeight(this.weight);
            line(actuX, actuY,this.p.x, this.p.y);
          }
          break;
     // boxes
        default:
          strokeWeight(1);
          pushMatrix();
          translate(this.p.x, this.p.y);
          //rotate(float(mySeeker.longueur)/100);
          rotateX(this.longueur/100);
          rotateY(this.longueur/100);
          box(dia);  
          popMatrix();
          break;
        }
        }
      }
    }
    
    
    // Pierre MARZIN
    // Trying to learn Processing... First project, much inspired by "Mycelium" by Scloopy?
    // http://marzinp.free.fr/applets
    
    //GUI and Drag 'n drop: great libraries ! Thanks to Andreas Schlegel (http://www.sojamo.de/libraries/index.html)
    
    import sojamo.drop.*;
    //import processing.opengl.*;
    
    public SDrop drop;
    public String mUrl;
    public String savePath="";
    
    // Source image
    PImage source;      
    //worms parameters,
    //position
    PVector p;
    //speed
    PVector v;
    //aimed direction
    PVector vise=new PVector();
    //"seeking" vector
    PVector deviation=new PVector();
    //worm's length
    int longueurVer;
    //like pixels[], to keep track of how many times a point is reached by a worm
    int [] buffer;
    boolean deplace;
    int limiteDown;
    float seekerWeight;
    int variationspeed;
    //number of worms at the beginning
    int nbePoints;
    //seekers array
    Seeker [] seekersArray;
    //width and length of the drawing area
    int larg;
    int haut;
    int largI;
    int hautI;
    //brightness of the destination pixel
    float briMax;
    //minimum brightness threshold
    public int seuilBrillanceMini;
    //maximum brightness threshold
    public int seuilBrillanceMaxi;
    //location of the tested point in pixels[]
    int locTest;
    //around a point (worms position), how many pixels will we look at...
    int amplitudeTest;
    //constrain the aimed direction vector into an r radius circle
    int inertia;
    //constrain the acceleration vector into a devMax radius circle
    float devMax;
    //constrain the speed vector into a maxSpeed radius circle
    int maxSpeed;
    //stroke's weight (slider) or radius of ellipse used for drawing
    public float myweight;
    //stroke's maximum length
    public int maxstrokelength;
    //draw or not (button drawButton)
    public boolean dessine;
    //different drawing options
    public int modeCouleur;
    public int forme;
    float L1color=random(255);
    float L2color=random(255);
    float L3color=random(255);
    float L4color=random(255);
    float L5color=random(255);
    //fill/stroke color
    int macouleur;
    boolean limite;
    Seeker mySeeker;
    
    //************************************************************************
    //setup only sets up what won't change:GUI and window params
    //I use initialiser() to set up what has to be initialised
    //when you hit "RestartButton" and dessin() to set the drawing parameters
    void setup() {
      frameRate(100);
      larg=largI=1200;
      haut=hautI=850;
      //size(1200, 1000,OPENGL);
      size(1200, 850, P3D);
      limite=false;
      drop = new SDrop(this);
      source = loadImage("tiger.jpg");
      //maximize the source image without changing its aspect ratio
      if (hautI*source.width>largI*source.height) {
        larg=largI;
        haut=larg*source.height/source.width;
      }
      else {
        haut=hautI;
        larg=haut*source.width/source.height;
      }
      source.resize(larg, haut);
      source.loadPixels();
      setupControls();
      initialiser();
    }
    //************************************************************************
    //launched after setup and any time you hit the restartButton button
    void initialiser() { 
    
      background(255);
      L1color=random(255);
      L2color=random(255);
      L3color=random(255);
      L4color=random(255);
      L5color=random(255);
      nbePoints=6;
      buffer=new int[(1+haut)*(1+larg)];
      smooth();
      drawButton.setOn();
      dessine=true;
      p=new PVector();
      v=new PVector();
      seekersArray=new Seeker[nbePoints];
      maxstrokelength=int(s1.value());
      inertia=int(s3.value());
      myweight=s2.value();
      devMax=s4.value();
      maxSpeed=int(s5.value());
      seuilBrillanceMini = floor(range.getArrayValue(0));
      seuilBrillanceMaxi = floor(range.getArrayValue(1));
      forme=int(shapeRadio.value());
      for (int i=0;i<nbePoints;i++) {
    
        v=new PVector(3-random(6), 3-random(6));
        p=new PVector(random(larg), random(haut));
        while ( (brightness (source.pixels[floor (p.x)+floor(p.y)*larg])>seuilBrillanceMaxi)||(brightness(source.pixels[floor(p.x)+floor(p.y)*larg])<seuilBrillanceMini))
        {
          p.x=int(random(larg));
          p.y=int(random(haut));
        }
        seekersArray[i]=new Seeker(i, p, v);
      }
    }
    //************************************************************************
    void draw() {
    
      defineLights();
      dessine=drawButton.isOn();
      limite=limitButton.isOn();
    //If the source image changes, it has to be maximized and the sketch to be reinitialized
      if (mUrl!=null) {
        source=null;
        source=loadImage(mUrl);
        if (hautI*source.width>largI*source.height) {
          larg=largI;
          haut=larg*source.height/source.width;
        }
        else {
          haut=hautI;
          larg=haut*source.width/source.height;
        }
        source.resize(larg, haut);
        source.loadPixels();
        initialiser();
        mUrl=null;
      }
      
    //if drawButton is on, go drawing
      if (dessine) {
        dessin();
      }
      
    //needed to render controlP5 controls
      controlP5.draw();
    }
    //************************************************************************
    void dessin() {
      locTest=0;
      //for each "seeker" (worm's head)
      for (int t=0;t<seekersArray.length;t++) {//X.length
        //on va tester les pixels autour du mobile p[t] en direction de la vitesse du mobile
        //calcul du barycentre des points testes ponderes de la brillance (vise.x, vise.y)
        //for each seeker, we gonna test pixels around the seeker's position and calculate their barycenter, loaded by pixels values (0/255 dark/light)
        mySeeker=seekersArray[t];
        //barycenter's coordinates
        vise.x=0;
        vise.y=0;
    //avoid looking for mySeeker.p.x for every pixels
        float seekX=mySeeker.p.x;
        float seekY=mySeeker.p.y;
        int actuX=floor(mySeeker.p.x);
        int actuY=floor(mySeeker.p.y);
        
    //barycenter calculation
        for (int i=-amplitudeTest;i<amplitudeTest+1;i++) {///rdessin
          for (int j=-amplitudeTest;j<amplitudeTest+1;j++) {
              locTest=floor(seekX)+i+(floor(seekY)+j)*larg; 
              //does the point belongs to the source image?
              if ((seekX+i>0)&& (seekY+j>0)&&(seekX+i<larg-1)&&(seekY+j<haut-1)) {
                vise.x+=i*(255-brightness(source.pixels[locTest]));
                vise.y+=j*(255-brightness(source.pixels[locTest]));
              }
          }
        }
         
    //coeur du comportement de seeker:   
    // core of the behaviour of the seeker (http://www.shiffman.net/ see wanderer's code)
        vise.normalize();
        vise.mult(100/inertia);   
        //vise.limit(100/inertia);       
        mySeeker.v.add(new PVector(vise.x, vise.y)); //+hasard-random(2*hasard)  
        deviation=mySeeker.v.get();
        deviation.normalize();
        deviation.mult(devMax);
        //deviation.limit(devMax);
        mySeeker.v.normalize();
        mySeeker.v.mult(maxSpeed); 
        //mySeeker.v.limit(maxSpeed);
        mySeeker.p.x+=deviation.x;
        mySeeker.p.y+=deviation.y;
        
    //******************different cases that lead to move the seeker to another random place**************
    //outside window
        if ((mySeeker.p.x<1)|| (mySeeker.p.y<0)||(mySeeker.p.x>larg-1)||(mySeeker.p.y>haut-1))//||
          {
            deplacePoint(mySeeker);
          }
          seekX=mySeeker.p.x;
          seekY=mySeeker.p.y;
      //buffer est une copie vide de l'image. on l'augmente pour chaque point parcouru
      //buffer is an empty copy of the source image. It's increased every time a point is reached.
          buffer[floor(seekX)+floor(seekY)*larg]++;
        //si on est passe plus de 10 fois on demenage le point
         // If a point is reached 10 times, seeker is moved
          if (buffer[floor(seekX)+floor(seekY)*larg]>10) {
          deplacePoint(mySeeker);
        } 
        //on compte les segments de chaque ver 
        //worm's length is increased
        mySeeker.longueur++;
        //si c'est trop long on demenage
        //seeker's moved if worm's too long
        if (mySeeker.longueur>maxstrokelength) {         
          deplacePoint(mySeeker);
        }
      
      //inside window, limite on and inside value range
          if ((limite)&&((brightness(source.pixels[floor(seekX)+floor(seekY)*larg])<=seuilBrillanceMaxi)&&(brightness(source.pixels[floor(seekX)+floor(seekY)*larg])>=seuilBrillanceMini)))
          {
            if (mySeeker.limiteDown!=0) {
              mySeeker.limiteDown-=2;
            }
          }
      //limite on and outside value range    
          if ((limite)&&((brightness(source.pixels[floor(seekX)+floor(seekY)*larg])>seuilBrillanceMaxi)||(brightness(source.pixels[floor(seekX)+floor(seekY)*larg])<seuilBrillanceMini)))
          {
            if (mySeeker.limiteDown==0) {
              mySeeker.limiteDown=2;
            }
            mySeeker.limiteDown+=4;//print(mySeeker.limiteDown+" ");
            if (mySeeker.limiteDown>=152/myweight) {
              mySeeker.limiteDown=0;          
              deplacePoint(mySeeker);
            }
          }
       //null deviation    
          if ((deviation.x==0)&&(deviation.y==0))
          {
            mySeeker.limiteDown=0;
            deplacePoint(mySeeker);
          }
          else {
            briMax=brightness(source.pixels[floor(seekX)+floor(seekY)*larg]);
          }
    // go draw the seeker's shape
        mySeeker.display(actuX,actuY);
        
    //on cree un nouveau vers de temps en temps (on pourrait tester selon la brilance de la zone...)
    // from times to times a new worm is created
        if (random(1)>1-(255-briMax)/80000) 
        {seekersArray=(Seeker[])append(seekersArray, new Seeker(seekersArray.length,new PVector(seekX, seekY), new PVector(mySeeker.v.x*(5+random(6)), mySeeker.v.x*(5+random(6)))));
        }
      }
    }
    //*****************move the seeker function***************************
    void deplacePoint(Seeker seeker) {
            mySeeker.longueur=0;
      seeker.deplace=true;
      seeker.p.x=1+int(random(larg-1));
      seeker.p.y=1+int(random(haut-1));
      while ( (brightness (source.pixels[floor (seeker.p.x)+floor(seeker.p.y)*larg])>seuilBrillanceMaxi)||(brightness(source.pixels[floor(seeker.p.x)+floor(seeker.p.y)*larg])<seuilBrillanceMini))
      {
        seeker.p.x=1+int(random(larg-1));
        seeker.p.y=1+int(random(haut-1));
      }
    }
    
    //*******************set the lights function***************************
    public void defineLights() {
      // Orange point light on the right
      pointLight(L4color, 0, L5color, // Color
      200, -100, 800); // Position
    
      // Blue directional light from the left
      directionalLight(0, L1color,L2color, // Color
      1, 0, -2);    // The x-, y-, z-axis direction
    
      // Yellow spotlight from the front
      spotLight(255, L3color, 50, // Color
      400, 440, 800, // Position
      0, 0, -1, // Direction
      PI/2, 1);     // Angle, concentration
    }
    
    

    code

    tweaks (0)

    about this sketch

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

    license

    advertisement

    Pierre MARZIN

    Worms04P3D

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

    Latest development of my animated "filter" worms... Drag and drop seems to work! Save shouldn't work here. Play with the settings while the image is drawn.

    You need to login/register to comment.