• fullscreen
  • CylPanel.pde
  • UParametricForm01.pde
  • Util.pde
  • build.pde
  • shaper.pde
  • class CylPanel {
      float a,b,r1,r2,rD;
      float t,tD;
      UVec3 v1,v2;
      UVec3 v[];
      CylPanel next;
      
      CylPanel(float _a,float _b) {
        a=_a;
        b=_b;
    
        r1=(1/100f)*0.4+random(0.3)*(1/100f);
        if(random(100)>90) r1=(1/100f)*0.4+random(0.4,0.5)*(1/100f);    
        r2=r1*0.25f;
    
        if(r2>r1) {
          rD=r2;
          r2=r1;
          r1=rD;
        }
        rD=(r2-r1);
        
      }
        
      void init() {
        v1=new UVec3(1,0,0);
        v1.rotateY(b);
        v2=new UVec3(1,0,0);
        v2.rotateY(a);
        
        v=new UVec3[numSeg*2];
        
        for(int i=0; i<numSeg; i++) {
          float tt=(float)i/(float)(numSeg-1);
          
          v[i*2]=new UVec3(v1.x,(tt-0.5)*totalH,v1.z);
          v[i*2+1]=new UVec3(v2.x,(tt-0.5)*totalH,v2.z);
        }
      }
      
      void toModel(UGeometry g) {
        int rId=0;
        float rr=1;
        g.beginShape(QUAD_STRIP);
        
        for(int i=0; i<numSeg2; i+=2) {
          rr=(rD*shaper[rId++]+r1)*maxRad*100+minRad;
          g.vertex(v[i+1].x*rr, v[i+1].y, v[i+1].z*rr);
          g.vertex(v[i].x*rr, v[i].y, v[i].z*rr);
        }
        g.endShape();
        
        g.beginShape(QUAD_STRIP);
        rId=0;
        for(int i=0; i<numSeg2; i+=2) {
          rr=(rD*shaper[rId]+r1)*maxRad*100+minRad;
          g.vertex(v[i].x*rr,v[i].y,v[i].z*rr);
          rr=(next.rD*shaper[rId]+next.r1)*maxRad*100+minRad;
          g.vertex(
            next.v[i+1].x*rr,
            next.v[i+1].y,
            next.v[i+1].z*rr);
            
          rId++;
        }
        g.endShape();
      } 
    }
    
    // UParametricForm01.pde
    // unlekker.modelbuilder example
    // Marius Watz, 2011
    // http://workshop.evolutionzone.com
    //
    // Shared under Creative Commons "share-alike non-commercial use 
    // only" license.
    
    // Advanced parametric form example with GUI created using the
    // ControlP5 library by Andreas Schlegel. The library can be
    // downloaded here: http://sojamo.de/libraries/controlP5/
    //
    // This version has been modified to try to fix blurry fonts in P3D.
    // Please see the Modelbuilder distribution for the original example
    
    import controlP5.*;
    //import processing.opengl.*;
    
    import unlekker.util.*;
    import unlekker.modelbuilder.*;
    
    int num=20,numSeg=20;
    int numSeg2=numSeg*2;
    float totalH=100, maxRad=50,minRad=50;
    
    boolean doRebuild=false;
    UNav3D nav;
    
    CylPanel panel[];
    UGeometry model;
    
    PGraphics cp5;
    
    void setup() {
    //  size(600,600, OPENGL);
      size(600,600, P3D);
    
      initGUI();
      initShaper();
      rndForm();
    }
    
    void draw() {
      // web only hack 
      if(online) {
        beginRecord(cp5);
        fill(0,60,100);
        noStroke();
        rect(0,0,width,100);
        controlP5.draw();
        endRecord();
      }
      
      background(50);
    
      hint(ENABLE_DEPTH_TEST);
      pushMatrix();
      lights();
      nav.doTransforms();
    
      fill(255);
      stroke(0);
      model.draw(this);
      popMatrix();
      
      if(doRebuild) {
        buildModel();
        doRebuild=false;
      }
      
      // draw GUI
      if(!online) {
        fill(0,60,100);
        noStroke();
        rect(0,0,width,100);
        controlP5.draw();
    
        hint(DISABLE_DEPTH_TEST);
      }
      else image(cp5,0,0);
    
    }
    
    public void mouseDragged() {
      if(controlP5.window(this).isMouseOver()) return;
      nav.mouseDragged();
    }
    	
    public void keyPressed() {
      nav.keyPressed();
    }
    
    ControlP5 controlP5; // instance of the controlP5 library
    
    void initGUI() {
      nav=new UNav3D(this);
      nav.setTranslation(width/2,height/2,0);
      
      controlP5 = new ControlP5(this);
      controlP5.setColorActive(color(255,50,0));//200,0,80));
      controlP5.setColorForeground(color(0,40,60));//0,90,140));
      controlP5.setColorBackground(color(255));
      controlP5.setColorValue(color(255));
      controlP5.setColorLabel(color(255));
        controlP5.setAutoDraw(false);
        controlP5.setMoveable(false);
    
      controlP5.addSlider("num", // name, must match variable name
        10,72, // min and max values
        num, // the default value
        20,20, // X,Y position of slider
        80,13) // width and height of slider
        .setId(0); // set controller ID
        
      controlP5.addSlider("numSeg", // name, must match variable name
        10,100, // min and max values
        numSeg, // the default value
        20,40, // X,Y position of slider
        80,13) // width and height of slider
        .setId(1); // set controller ID
      numSeg2=numSeg*2;
    
      totalH=200;
      controlP5.addSlider("totalH", // name, must match variable name
        25,500, // min and max values
        totalH, // the default value
        150,20, // X,Y position of slider
        80,13). // width and height of slider
        setId(4);
    
      maxRad=200;
      controlP5.addSlider("maxRad", // name, must match variable name
        10,500, // min and max values
        maxRad, // the default value
        150,40, // X,Y position of slider
        80,13). // width and height of slider
        setId(8);
    
      shapeSlider=controlP5.addSlider("shapeStyle", // name, must match variable name
        1,shapeNames.length, // min and max values
        1, // the default value
        150,60, // X,Y position of slider
        80,13); // width and height of slider
      shapeSlider.setId(5);
    //  shapeSlider.setNumberOfTickMarks(shapeNames.length);
    
      controlP5.addSlider("minRad", // name, must match variable name
        10,100, // min and max values
        50, // the default value
        280,20, // X,Y position of slider
        80,13). // width and height of slider
        setId(6);
    
       // add "bang" button to randomize spheres
       controlP5.addBang("rndForm",
         20,60,13,13).
         setId(101);   
    
       // add "bang" button to randomize spheres
       controlP5.addBang("rndShaper",
         80,60,13,13).
         setId(102);   
         
       // add "bang" button to save STL
       controlP5.addBang("stlSave",
         420,20,13,13).
         setId(103);      
       
       // attempt at hacking P3D blurry fonts
       // http://forum.processing.org/topic/awful-looking-text-when-using-controlp5-in-p3d-mode
         cp5 = createGraphics(width,height,JAVA2D);
    
    }
    
    void controlEvent(ControlEvent theEvent) {
      boolean [] rebuildFlag={
        false,true,true,true,true,
        true,true,false,true,true};
        
      Controller c=theEvent.controller();
      int cid=c.id();
    //  println("\n"+theEvent.isController()+" "+
    //    c.id()+" "+c.label()+" "+c.value()+" type == "+theEvent.type());
      
      if(cid==0) num=(int)c.value();
      if(cid==1) {
        numSeg=(int)c.value();
        numSeg2=numSeg*2;
      }
    
      if(cid==4) totalH=(int)c.value();
      if(cid==5) { // shaper slider
        // don't call initShaper if it's the existing shaper style. this
        // prevents continuous calls to initShaper when the slider is
        // used rather than the rndShaper bang button.
        if((int)c.value()==oldShaper) return;
        shapeStyle=(int)c.value();
        initShaper();
      }
      if(cid==6) minRad=c.value();
      if(cid==8) maxRad=c.value();
    
      // flag that a rebuild is needed?
      if(cid<10 && rebuildFlag[cid]) {
        doRebuild=true;
      }   
    }
    
    void stlSave() {
      model.writeSTL(this, 
        UIO.getIncrementalFilename(
          this.getClass().getSimpleName()+
          "-####.stl",sketchPath));
    }
    
    // RANDOMIZE CYLINDER FORM
    void rndForm() {
      println("rndForm()");
      float D;
    
      if(shapeStyle==-1) rndShaper();
    
      panel=new CylPanel[num];
      D=TWO_PI/(float)num;
      for(int i=0; i<num; i++) 
        panel[i]=new CylPanel(
          D*(float)i,
          D*(float)(i+1));
    
      for(int i=0; i<num; i++) panel[i].next=panel[(i+1)%num];
      
      buildModel();
    }
    
    // BUILD MODEL ACCORDING TO CYLINDER SHAPE AND SHAPER FUNCTION
    void buildModel() {
      if(num!=panel.length) {
        println("Must generate new random form once number of ribs are changed.");
        return;
      }
      
      if(model!=null) model.reset();
      else model=new UGeometry();
      
      buildShapeProfile();
      for(int i=0; i<num; i++) panel[i].init();
      for(int i=0; i<num; i++) panel[i].toModel(model);
      
      // top cap
      float rr;
      
      model.beginShape(TRIANGLE_FAN);
      model.vertex(0,panel[0].v[numSeg2-1].y,0);
      for(int i=0; i<num; i++) {
        rr=(panel[i].rD*shaper[numSeg-1]+panel[i].r1)*maxRad*100+minRad;
        model.vertex(
          panel[i].v[numSeg2-1].x*rr,
          panel[i].v[numSeg2-1].y,
          panel[i].v[numSeg2-1].z*rr);
        model.vertex(
          panel[i].v[numSeg2-2].x*rr,
          panel[i].v[numSeg2-2].y,
          panel[i].v[numSeg2-2].z*rr);
      }
      model.endShape();
    
      // bottom cap
      model.beginShape(TRIANGLE_FAN);
      model.vertex(0,panel[0].v[0].y,0);
      for(int i=num-1; i>-1; i--) {
        rr=(panel[i].rD*shaper[0]+panel[i].r1)*maxRad*100+minRad;
        model.vertex(
          panel[i].v[0].x*rr,
          panel[i].v[0].y,
          panel[i].v[0].z*rr);
        model.vertex(
          panel[i].v[1].x*rr,
          panel[i].v[1].y,
          panel[i].v[1].z*rr);
      }
      model.endShape();
    }
    
    void vertex(UVec3 v) {
      vertex(v.x,v.y,v.z);
    }
    
    float bezCurve[]=new float[4];
    float shaper[];
    int shapeStyle=1,oldShaper=-1;
    
    String shapeNames[]= {
      "NORMAL", "SQ", "POW()",
      "SIN()","SQ(SIN())","WAVE","WAVE-STEPS",
      "BEZIER"};
    Slider shapeSlider;
    
    float shapeP1,shapeP2,shapeP3,shapePsteps;
    
    void rndShaper() {
      float rndval=random(8000)%8+1;
      shapeStyle=(int)rndval;
      println("shapeStyle "+shapeStyle+" "+rndval);
    
      // this next line will trigger a ControlP5.controlEvent()
      shapeSlider.setValue(shapeStyle); 
      
      initShaper();
    }
     
    void initShaper() { 
      oldShaper=shapeStyle;
      
      shapeP1=random(1);
      shapeP2=random(1);
      shapeP3=random(1);
      shapePsteps=(int)random(8,20);
      
      // initialize bezier shaper profile
      float maxBez=-100;
      for(int i=0; i<4; i++) {
        bezCurve[i]=random(1);
        maxBez=max(bezCurve[i],maxBez);
      }
      for(int i=0; i<4; i++) bezCurve[i]/=maxBez;
        
      shapeNames[2]="POW ( x, "+nf((1+shapeP1*3),0,2)+" )";
      shapeNames[3]="SIN ( x * "+nf(degrees(R*HALF_PI*shapeP1),0,2)+" )";
      shapeNames[4]="SQ ( SIN( "+nf(degrees(R*HALF_PI*shapeP1*2),0,2)+" )";
      
      shapeSlider.setCaptionLabel("Shaping function: "+shapeNames[shapeStyle-1]);
    }
    
    // PRE-CALCULATE SHAPER PROFILE VALUES
    void buildShapeProfile() { 
      float maxval=-1000;
      numSeg2=numSeg*2;
    
      shaper=new float[numSeg];
      for(int i=0; i<numSeg; i++) {
        float tt=(float)i/(float)(numSeg-1);
        shaper[i]=getShapeMod(1-tt);
        maxval=max(maxval,shaper[i]);
      }
      
      // NORMALIZE
    //  println("shaper[i]/="+maxval);
      for(int i=0; i<numSeg; i++) shaper[i]/=maxval;
    }
      
      
    // CALCULATES SELECTED SHAPER FUNCTION OF NATURE F(T)=X
    float getShapeMod(float R) {
      // SQUARE DECELERATION
      if(shapeStyle==2) return sq(R);
      
       // EXPONENTIAL
      if(shapeStyle==3) return pow(R,(1+shapeP1*3));  
      
       // SINE WAVE
      if(shapeStyle==4) return sin(R*HALF_PI*shapeP1);
      
      // SQUARE OF SINE
      if(shapeStyle==5) return sq(sin(R*PI*shapeP1*2)); 
      
      // MULTI SINE WAVE
      if(shapeStyle==6) { 
        float val=R*((float)((int)(1+shapeP1*12))*PI+HALF_PI)-HALF_PI;
        val=sin(val)*0.15+0.15+(0.5*R*R);
        return val;
      }
        
      // CHAOTIC SINE/COS WAVE
      if(shapeStyle==7) {
        float d1=(float)((int)(0.5+5*shapeP1))*PI;
        float d2=(float)((int)(0.5+6*shapeP2*1.5))*PI;
        float newR=R;
        
        float val=sin((1-R)*(d1+HALF_PI*shapeP3))*0.3+0.4;
        val+=cos((1-R)*d2)*0.1+0.1;
    //    val=0.2;
    //    val*=sin(PI*(1-R))*0.8+0.2;
        val=sq(R)*0.2+val*0.8;
    
    //    if(R>0.3) newR=1;
    //    else newR=sq(R/0.3);
    //    val=sq(R)*0.8+cos(HALF_PI*newR-HALF_PI)*0.2;
    
        return val;
      }
      if(shapeStyle==8) 
        return bezierPoint(bezCurve[0],bezCurve[1],bezCurve[2],bezCurve[3], R);
        
      // default when shapeStyle==1
      return R;
    }
    
    

    code

    tweaks (0)

    about this sketch

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

    license

    advertisement

    Marius Watz plus+

    UParametricForm01

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

    Parametric object generation with GUI control using Modelbuilder. After adjusting "num" slider you must click "rndForm" to create a new base form.

    Use shaping slider to apply different 3D shaping functions to the mesh.

    STL saving disabled online, works offline.

    ale plus+
    20 Jun 2012
    Really interesting... thanks for sharing!
    bitcraft
    29 Mar 2013
    Nice! But there seems to be a problem with controlP5, when running inside the browser.
    At times the GUI just doesn't work and I keep getting all kinds of IllegalAccessExceptions...
    Marius Watz plus+
    29 Mar 2013
    Strange, it works fine on my end. I have an updated version of it in any case, I'll post that later when I get a chance.
    You need to login/register to comment.