• 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
    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.