Parametric Design with Processing
ModelBuilder2
While PVector is built into processing, UVertex is from Modelbuilder2, which is an external library.An Introduction to ModelBuilderMK2
- Import the library by clicking on Sketch>Import Library.
import the library import unlekker.mb2.geo.*; import unlekker.mb2.util.*; import ec.util.*;
- Add the following instance variables:
UVertex v, vDelta; float angleDelta;
- Create a setup() method. Inside the method:
-
Set the size
- Set v to be a new UVertex at the center of the sketch.
v=new UVertex(width/2, height/2);
- Set the vDelta variable to be a random UVertex point. To move at a higher speed, provide larger values:
vDelta=new UVertex (random(1,3), 0);
- Rotate the point 360°:
vDelta.rot(random(TWO_PI));
- Set angleDelta:
angleDelta=radians(random(0.5, 1.5));
-
Set the size
- Create a draw method:
void draw(){ }
import unlekker.mb2.geo.*; import unlekker.mb2.util.*; import ec.util.*; UVertex v, vDelta; float angleDelta; void setup() { size(600, 600); v=new UVertex(width/2, height/2); //to move at higher speed, provide larger values vDelta=new UVertex (random(1, 3), 0); vDelta.rot(random(TWO_PI)); angleDelta=radians(random(0.5, 1.5)); if(random(100)<50){ angleDelta=- angleDelta; } } void draw() { //add vDelta to v v.add(vDelta); vDelta.rot(angleDelta); ellipse(v.x,v.y,50,50); }
Complete program
import unlekker.mb2.geo.*; import unlekker.mb2.util.*; import ec.util.*; UVertex v, vDelta; float angleDelta; void setup() { size(600, 600); v=new UVertex(width/2, height/2); //to move at higher speed, provide larger values vDelta=new UVertex (random(1, 3), 0); vDelta.rot(random(TWO_PI)); angleDelta=radians(random(0.5, 1.5)); if(random(100)<50){ angleDelta=- angleDelta; } } void draw() { //add vDelta to v v.add(vDelta); vDelta.rot(angleDelta); ellipse(v.x,v.y,50,50); stroke(255,0,0); line(v.x,v.y, v.x+vDelta.x*20, v.y+vDelta.y*20); if(frameCount%100==0){ newRotation(); } if(v.x<0 ){ v.x=width; } if(v.x>width ){ v.x=0; } if(v.y<0 ){ v.y=height; } if(v.y>height ){ v.y=0; } } void newRotation(){ angleDelta=radians(random(0.5, 1.5)); if(random(100)<50){ angleDelta=- angleDelta; } vDelta.norm(random(1,3)); }
Using an array of UVertexLists
Tutorial derived from work by Marius Watz- Use the build() method to start building your model.
-
When you work in 3D you need a way to navigate. With ModelBuilderMK2 you use UNav3D
-
Create your models using OpenGL
-
UMB is the base class for all the functions in ModelBuilderMK2
this object allows you to set a reference to PApplet
- To draw a vertex list you can just call draw()
- Here is the basic skeleton:
import unlekker.mb2.geo.*; import unlekker.mb2.util.*; import ec.util.*; UVertexList vl,vl2; UNav3D nav; void setup(){ size(600,600,OPENGL); //initialize UMB with a reference to this sketch UMB.setPApplet(this); nav=new UNav3D(); build(); } void draw()(){ } void build(){ }
- ModelBuilderMK2 also supports Operator chaining
import unlekker.mb2.geo.*; import unlekker.mb2.util.*; import ec.util.*; UVertexList vl,vl2; UNav3D nav; void setup(){ size(600,600,OPENGL); //initialize UMB with a reference to this sketch UMB.setPApplet(this); nav=new UNav3D(); build(); } void draw(){ background(0); translate(width/2,height/2); nav.doTransforms(); stroke(255,255,0); noFill(); vl.draw(); stroke(0,0,255); vl2.draw(); } void build(){ vl=new UVertexList(); int n=36; for (int i=0;i<n;i++){ float a =map(i, 0, n, 0, TWO_PI); vl.add(new UVertex(100,0).rotZ(a)); } //adds a copy of the first vertex to the end of the list vl.close(); vl2=vl.copy().scale(2); }
- Adding a third dimension, some randomization and triangleFan
import unlekker.mb2.geo.*; import unlekker.mb2.util.*; import ec.util.*; UVertexList vl,vl2; UNav3D nav; UGeo fan, quadstrip; void setup(){ size(600,600,OPENGL); //initialize UMB with a reference to this sketch UMB.setPApplet(this); nav=new UNav3D(); build(); } void draw(){ background(0); translate(width/2,height/2); lights(); nav.doTransforms(); stroke(255,255,0); fill(255,25,25,100); // vl.draw(); fan.draw(); stroke(0,0,255); fill(25,25,255,100); // vl2.draw(); quadstrip.draw(); } void build(){ vl=new UVertexList(); int n=36; for (int i=0;i<n;i++){ float a =map(i, 0, n, 0, TWO_PI); vl.add(new UVertex(random(100,150),0).rotZ(a)); } //adds a copy of the first vertex to the end of the list vl.close(); vl2=vl.copy().scale(2).translate(0,0,100); //will calculater the centroid fan=new UGeo().triangleFan(vl); quadstrip=new UGeo().quadstrip(vl,vl2); }
Cylinders
- Creating a cylinder:
//create quadstrip between two vertex lists import unlekker.mb2.geo.*; import unlekker.mb2.util.*; import ec.util.*; UVertexList vl, vl2; UGeo geo; UNav3D nav; void setup() { size(600, 600, P3D); // initialize UMB UMB.setPApplet(this); nav=new UNav3D(); build(); } void draw(){ background(0); translate(width/2,height/2); lights(); nav.doTransforms();// do camera transformations // vl.draw(); //vl2.draw(); geo.draw(); } void build(){ int n=18; vl=new UVertexList(); for(int i=0;i<n;i++){ float deg=map(i, 0, n-1, 0, TWO_PI); vl.add(new UVertex(100,0,0).rotY(deg)); } vl2=vl.copy().translate(0,-200,0); vl.translate(0,200,0); //this will connect them geo=new UGeo().quadstrip(vl,vl2); }
-
Closing the form:
//create quadstrip between two vertex lists import unlekker.mb2.geo.*; import unlekker.mb2.util.*; import ec.util.*; UVertexList vl, vl2; UGeo geo; UNav3D nav; void setup() { size(600, 600, P3D); // initialize UMB UMB.setPApplet(this); nav=new UNav3D(); build(); } void draw(){ background(0); translate(width/2,height/2); lights(); nav.doTransforms();// do camera transformations // vl.draw(); //vl2.draw(); geo.draw(); } void build(){ int n=18; vl=new UVertexList(); for(int i=0;i<n;i++){ float deg=map(i, 0, n-1, 0, TWO_PI); vl.add(new UVertex(100,0,0).rotY(deg)); } vl2=vl.copy().translate(0,-200,0); vl.translate(0,200,0); //this will connect them geo=new UGeo().quadstrip(vl,vl2); geo.triangleFan(vl); geo.triangleFan(vl2); }
- Adding Normals
//create quadstrip between two vertex lists import unlekker.mb2.geo.*; import unlekker.mb2.util.*; import ec.util.*; UVertexList vl, vl2; UGeo geo; UNav3D nav; void setup() { size(600, 600, P3D); // initialize UMB UMB.setPApplet(this); nav=new UNav3D(); build(); } void draw(){ background(0); translate(width/2,height/2); lights(); nav.doTransforms();// do camera transformations // vl.draw(); //vl2.draw(); stroke(0); geo.draw(); stroke(255,0,0); geo.drawNormals(25); } void build(){ int n=18; vl=new UVertexList(); for(int i=0;i<n;i++){ float deg=-map(i, 0, n-1, 0, TWO_PI); vl.add(new UVertex(100,0,0).rotY(deg)); } vl2=vl.copy().translate(0,-200,0); vl.translate(0,200,0); //this will connect them geo=new UGeo().quadstrip(vl,vl2); //reverse geo.triangleFan(vl, true); geo.triangleFan(vl2); }
- Hiding and showing normals (and saving as an STL):
//create a more complex form //save model and picture of the models import unlekker.mb2.geo.*; import unlekker.mb2.util.*; import ec.util.*; ArrayList<UVertexList>stack; UGeo geo; UNav3D nav; void setup() { size(600, 600, P3D); // initialize UMB UMB.setPApplet(this); nav=new UNav3D(); build(); } void draw(){ background(0); translate(width/2,height/2); lights(); nav.doTransforms(); stroke(0); geo.draw(); stroke(255,0,0); if(mousePressed)geo.drawNormals(25); } void build() { int n=10; float h=400; stack=new ArrayList<UVertexList>(); int nPts=36; for (int i=0;i<n;i++) { UVertexList vl=new UVertexList(); for (int j=0;j<nPts;j++) { float deg=map(j, 0, nPts, 0, TWO_PI); //create a cylinder vl.add(new UVertex(100, 0).rotY(deg)); } float y=map(i, 0, n-1, -h*0.5, h*0.5); //adding the vl to stack, but translate it first and then close it //close adda a copy of first point stack.add(vl.translate(0, y, 0).close()); } geo =new UGeo().quadstrip(stack); geo.triangleFan(stack.get(stack.size()-1)); geo.triangleFan(stack.get(0), true); } void keyPressed(){ if(key=='s'||key=='S'){ //will give the name of the name of the sketch //reference to this sketch String filename=this.getClass().getSimpleName(); //path and prefix filename=UFile.nextFile(sketchPath,filename); saveFrame(filename.substring(0, filename.length()-4)+"-###.png"); geo.writeSTL(filename.substring(0, filename.length()-4)+"-###.stl"); } }
- Making a vase:
//create a more complex form //save model and picture of the models import unlekker.mb2.geo.*; import unlekker.mb2.util.*; import ec.util.*; ArrayList<UVertexList>stack; UGeo geo; UNav3D nav; void setup() { size(600, 600, P3D); // initialize UMB UMB.setPApplet(this); nav=new UNav3D(); build(); } void draw(){ background(0); translate(width/2,height/2); lights(); nav.doTransforms(); stroke(0); geo.draw(); stroke(255,0,0); if(mousePressed)geo.drawNormals(25); } void build() { int n=10; float h=400; stack=new ArrayList<UVertexList>(); int nPts=36; for (int i=0;i<n;i++) { UVertexList vl=new UVertexList(); float t=map(i,0, n-1, 0,1); //t=0 to 1 float m=shaper(t); for (int j=0;j<nPts;j++) { float deg=map(j, 0, nPts, 0, TWO_PI); //create a cylinder vl.add(new UVertex(m*100, 0).rotY(deg)); } float y=map(i, 0, n-1, -h*0.5, h*0.5); //adding the vl to stack, but translate it first and then close it //close adda a copy of first point stack.add(vl.translate(0, y, 0).close()); } geo =new UGeo().quadstrip(stack); geo.triangleFan(stack.get(stack.size()-1)); geo.triangleFan(stack.get(0), true); } float shaper(float t){ //takes a parameter and returns a multiploer for the shape //can use it in many functions //in this case we are using it to create a wavy outline return 1+0.5*sin(t*TWO_PI); } void keyPressed(){ if(key=='s'||key=='S'){ //will give the name of the name of the sketch //reference to this sketch String filename=this.getClass().getSimpleName(); //path and prefix filename=UFile.nextFile(sketchPath,filename); saveFrame(filename.substring(0, filename.length()-4)+"-###.png"); geo.writeSTL(filename.substring(0, filename.length()-4)+"-###.stl"); } }
- A more complex vase:
//create a more complex form //save model and picture of the models import unlekker.mb2.geo.*; import unlekker.mb2.util.*; import ec.util.*; ArrayList<UVertexList>stack; UGeo geo; UNav3D nav; void setup() { size(600, 600, P3D); // initialize UMB UMB.setPApplet(this); nav=new UNav3D(); build(); } void draw(){ background(0); translate(width/2,height/2); lights(); nav.doTransforms(); stroke(0); geo.draw(); stroke(255,0,0); if(mousePressed)geo.drawNormals(25); } void build() { int n=20; float h=400; stack=new ArrayList<UVertexList>(); int nPts=36; for (int i=0;i<n;i++) { UVertexList vl=new UVertexList(); float t=map(i,0, n-1, 0,1); //t=0 to 1 float m=shaper(t); for (int j=0;j<nPts;j++) { float deg=map(j, 0, nPts, 0, TWO_PI); //create a cylinder vl.add(new UVertex(m*100, 0).rotY(deg)); } float y=map(i, 0, n-1, -h*0.5, h*0.5); //adding the vl to stack, but translate it first and then close it //close adda a copy of first point stack.add(vl.translate(0, y, 0).close()); } geo =new UGeo().quadstrip(stack); geo.triangleFan(stack.get(stack.size()-1)); geo.triangleFan(stack.get(0), true); } //function to calculate outline float shaper(float t){ //takes a parameter and returns a multiploer for the shape //can use it in many functions //in this case we are using it to create a wavy outline //return 1+0.5*sin(t*TWO_PI); //0.5 to 1.5 //return random(0.5, 1); //return 1+random(0.3, 0.5)*-sin(t*TWO_PI); //more bulges return 1+random(0.3, 0.5)*-sin(random(1,2)*t*TWO_PI); } void keyPressed(){ if (key=='r'){ build(); }if(key=='s'||key=='S'){ //will give the name of the name of the sketch //reference to this sketch String filename=this.getClass().getSimpleName(); //path and prefix filename=UFile.nextFile(sketchPath,filename); saveFrame(filename.substring(0, filename.length()-4)+"-###.png"); geo.writeSTL(filename.substring(0, filename.length()-4)+"-###.stl"); } }
- Parametric:
import controlP5.*; import unlekker.mb2.geo.*; import unlekker.mb2.util.*; import ec.util.*; ArrayList<UVertexList> stack; UGeo geo; UNav3D nav; void setup() { size(600, 600, P3D); UMB.setPApplet(this); nav=new UNav3D(); initGUI(); build(); } void draw() { background(0); pushMatrix(); lights(); translate(width/2, height/2); nav.doTransforms(); stroke(0); geo.draw(); stroke(255, 0, 0); if (mousePressed) geo.drawNormals(25); popMatrix(); } void keyPressed() { if (key==' ') build(); if (key=='s') saveModel(); } float bez[]; void build() { // initialize a random 1D bezier bez=new float[] { a,b,c,d }; int n=steps; // number of stacked edges stack=new ArrayList<UVertexList>(); int npts=stepsCircle; // build the outside edges for(int i=0; i<n; i++) { UVertexList vl=new UVertexList(); float t=map(i, 0,n-1, 0,1); float m=shaper(t); for(int j=0; j<npts; j++) { float deg=map(j, 0,npts, 0,TWO_PI); // apply multiplier "m" to a constant radius // vl.add(new UVertex(rad*m,0).rotY(deg)); vl.add(new UVertex(random(0.5,1)*rad,0).rotY(deg)); } // calculate the Y translation float y=map(i, 0,n-1, -h*0.5,h*0.5); stack.add(vl.translate(0,y,0)); } // build the inside edges of our "vase" for(int i=n-1; i>0; i--) { float t=map(i, 0,n-1, 0,1); float m=shaper(t); UVertexList vl=stack.get(i).copy(); for(UVertex tmp : vl) { float vy=tmp.y; tmp.y=0; tmp.norm(tmp.mag()*0.5); tmp.y=vy; } stack.add(vl); } for(UVertexList tmp : stack) tmp.close(); // creates quadstrips for all vertex lists in stack geo=new UGeo().quadstrip(stack); // caps top and bottom with triangle fans geo.triangleFan(stack.get(stack.size()-1)); geo.triangleFan(stack.get(0),true); } // shaping function // takes a parameter "t" and returns a multiplier // for the shape float shaper(float t) { // calculate the point "t" on a 1D bezier return bezierPoint(bez[0],bez[1],bez[2],bez[3], t); // return 1+0.5*sin(t*TWO_PI); // return random(0.5,1.5); // return 1+random(0.3,0.5)*sin(t*4*TWO_PI); } float a,b,c,d; int steps; int stepsCircle; float rad; float h; ControlP5 cp; void initGUI() { cp=new ControlP5(this); cp.begin(5,5); // begin group of controllers at XY=5,5 cp.addSlider("steps").setRange(4,30).linebreak(); cp.addSlider("stepsCircle").setRange(4,30).linebreak(); cp.addSlider("a").setRange(0.5,3).linebreak(); cp.addSlider("b").setRange(0.2,3).linebreak(); cp.addSlider("c").setRange(0.2,3).linebreak(); cp.addSlider("d").setRange(0.5,3).linebreak(); cp.addSlider("rad").setRange(50,200).linebreak(); cp.addSlider("h").setRange(200,600).linebreak(); cp.addButton("rebuild"); cp.addButton("randomize").linebreak(); cp.addButton("saveModel"); cp.end(); } // rebuild() is called when the "rebuild" button is pressed void rebuild() { build(); } void randomize() { ((Slider)cp.controller("a")).setValue(random(0.5,3)); ((Slider)cp.controller("b")).setValue(random(0.5,3)); ((Slider)cp.controller("c")).setValue(random(0.5,3)); ((Slider)cp.controller("d")).setValue(random(0.5,3)); build(); } void saveModel() { String filename=this.getClass().getSimpleName(); // UFile.nextFilename requires a path, a prefix and an extension filename=UFile.nextFile(sketchPath, filename, "png"); saveFrame(filename); geo.writeSTL( filename.substring(0, filename.length()-4)+".stl"); }
- Open at both ends:
import controlP5.*; import unlekker.mb2.geo.*; import unlekker.mb2.util.*; import ec.util.*; ArrayList<UVertexList> stack; UGeo geo; UNav3D nav; void setup() { size(600, 600, P3D); UMB.setPApplet(this); nav=new UNav3D(); initGUI(); build(); } void draw() { background(0); pushMatrix(); lights(); translate(width/2, height/2); nav.doTransforms(); stroke(0); geo.draw(); stroke(255, 0, 0); if (mousePressed) geo.drawNormals(25); popMatrix(); } void keyPressed() { if (key==' ') build(); if (key=='s') saveModel(); } float bez[]; void build() { // initialize a random 1D bezier bez=new float[] { a,b,c,d }; int n=steps; // number of stacked edges stack=new ArrayList<UVertexList>(); int npts=stepsCircle; // build the outside edges for(int i=0; i<n; i++) { UVertexList vl=new UVertexList(); float t=map(i, 0,n-1, 0,1); float m=shaper(t); for(int j=0; j<npts; j++) { float deg=map(j, 0,npts, 0,TWO_PI); // apply multiplier "m" to a constant radius // vl.add(new UVertex(rad*m,0).rotY(deg)); vl.add(new UVertex(random(0.5,1)*rad,0).rotY(deg)); } // calculate the Y translation float y=map(i, 0,n-1, -h*0.5,h*0.5); stack.add(vl.translate(0,y,0)); } // build the inside edges of our "vase" for(int i=n-1; i>-1; i--) { float t=map(i, 0,n-1, 0,1); float m=shaper(t); UVertexList vl=stack.get(i).copy(); for(UVertex tmp : vl) { float vy=tmp.y; tmp.y=0; tmp.norm(tmp.mag()*0.5); tmp.y=vy; } stack.add(vl); } // add copy of the first vertex list to the end // so that it becomes a solid loop stack.add(stack.get(0)); for(UVertexList tmp : stack) tmp.close(); // creates quadstrips for all vertex lists in stack geo=new UGeo().quadstrip(stack); // caps top and bottom with triangle fans // geo.triangleFan(stack.get(stack.size()-1)); // geo.triangleFan(stack.get(0),true); } // shaping function // takes a parameter "t" and returns a multiplier // for the shape float shaper(float t) { // calculate the point "t" on a 1D bezier return bezierPoint(bez[0],bez[1],bez[2],bez[3], t); // return 1+0.5*sin(t*TWO_PI); // return random(0.5,1.5); // return 1+random(0.3,0.5)*sin(t*4*TWO_PI); }float a,b,c,d; int steps; int stepsCircle; float rad; float h; ControlP5 cp; void initGUI() { cp=new ControlP5(this); cp.begin(5,5); // begin group of controllers at XY=5,5 cp.addSlider("steps").setRange(4,30).linebreak(); cp.addSlider("stepsCircle").setRange(4,30).linebreak(); cp.addSlider("a").setRange(0.5,3).linebreak(); cp.addSlider("b").setRange(0.2,3).linebreak(); cp.addSlider("c").setRange(0.2,3).linebreak(); cp.addSlider("d").setRange(0.5,3).linebreak(); cp.addSlider("rad").setRange(50,200).linebreak(); cp.addSlider("h").setRange(200,600).linebreak(); cp.addButton("rebuild"); cp.addButton("randomize").linebreak(); cp.addButton("saveModel"); cp.end(); } // rebuild() is called when the "rebuild" button is pressed void rebuild() { build(); } void randomize() { ((Slider)cp.controller("a")).setValue(random(0.5,3)); ((Slider)cp.controller("b")).setValue(random(0.5,3)); ((Slider)cp.controller("c")).setValue(random(0.5,3)); ((Slider)cp.controller("d")).setValue(random(0.5,3)); build(); } void saveModel() { String filename=this.getClass().getSimpleName(); // UFile.nextFilename requires a path, a prefix and an extension filename=UFile.nextFile(sketchPath, filename, "png"); saveFrame(filename); geo.writeSTL( filename.substring(0, filename.length()-4)+".stl"); }
- A more complex form
import unlekker.mb2.geo.*; import unlekker.mb2.util.*; import ec.util.*; ArrayList<UVertexList>stack; UGeo geo; UNav3D nav; void setup() { size(600, 600, P3D); // initialize UMB UMB.setPApplet(this); nav=new UNav3D(); build(); } void draw(){ background(0); translate(width/2,height/2); lights(); nav.doTransforms(); stroke(0); geo.draw(); stroke(255,0,0); if(mousePressed)geo.drawNormals(25); } void build(){ int n=10; float h=100; stack=new ArrayList<UVertexList>(); int nPts=36; for(int i=0;i<n;i++){ UVertexList vl=new UVertexList(); for(int j=0;j<nPts;j++){ float deg=map(j,0, nPts,0,TWO_PI); vl.add(new UVertex(random(100,150), 0).rotY(deg)); } float y=map(i,0, n-1,-h, h); //adding the vl to stack, but translate it first and then close it //close adda a copy of first point stack.add(vl.translate(0,y,0).close()); } geo =new UGeo().quadstrip(stack); geo.triangleFan(stack.get(stack.size()-1)); geo.triangleFan(stack.get(0), true); } void keyPressed(){ if(key=='s'||key=='S'){ //will give the name of the name of the sketch //reference to this sketch String filename=this.getClass().getSimpleName(); //path and prefix filename=UFile.nextFile(sketchPath,filename); saveFrame(filename.substring(0, filename.length()-4)+"-###.png"); geo.writeSTL(filename.substring(0, filename.length()-4)+"-###.stl"); } }
import unlekker.mb2.geo.*; import unlekker.mb2.util.*; import ec.util.*; UVertexList pts; UGeo geo; UNav3D nav; void setup() { size(600,600,OPENGL); UMB.setPApplet(this); nav=new UNav3D(); build(); } void draw() { background(0); translate(width/2,height/2); lights(); nav.doTransforms(); // for(UVertex v:pts) UMB.pellipse(v, 10); stroke(255); if(mousePressed) { noStroke(); for(UFace f:geo.getF()) { UVertex v[]=f.getV(); beginShape(TRIANGLE); fill(v[0].col); UMB.pvertex(v[0]); fill(v[1].col); UMB.pvertex(v[1]); fill(v[2].col); UMB.pvertex(v[2]); endShape(); } } else { geo.draw(); } } void keyPressed() { if(key!=CODED) { build(); } } void build() { int n=(int)random(100,300); buildColor(); pts=new UVertexList(); while(pts.size()<n) { UVertex pt=new UVertex(random(50,300),0); pt.z=random(-100,100); pt.z=noise(pt.x*0.1,pt.y*0.1)*100; pts.add(pt.rot(random(TWO_PI))); } geo=new UGeo().triangulation(pts); for(UFace f : geo.getF()) f.setColor(randomColor()); geo.enable(UMB.COLORFACE); // assign colors to vertices for(UVertex v : geo.getV()) v.setColor(randomColor()); } ArrayList<Integer> colList; void buildColor() { colList=new ArrayList<Integer>(); int[] grad=new int[] { color(0,0,120), color(255,255,0), color(255), color(0,150,200), color(255), color(120,0,255) }; int ng=grad.length/2; for(int i=0; i<ng; i++) { int num=(int)random(3,9); for(int j=0; j<num; j++) { float t=map(j, 0,num-1, 0,1); int theCol=lerpColor(grad[i*2],grad[i*2+1], t); colList.add(theCol); } } println(colList.size()); for(int c:colList) println(UMB.hex(c)); } int randomColor() { return colList.get((int)random(colList.size())); } } int randomColor(){ return colList.get((int) }
-
Import Modelbuilder and OpenGL libraries.
- Create setup() and draw() methods.
-
Create an instance variable to hold an array of UVertexLists. This array variable will be of type UVertexList. Name the array vl
To create an array use this syntax:UVertexList[] vl;
- Add an instance variable of type UNav3D named nav.
- Add an instance variable of type UGeometry. Name the variable geo.
- Create a method named build(). This method will not return anything (void).
- Inside the method:
- Initialize the array of UVertexLists to hold 12 elements by setting the array variable to UVertexList.getVertexLists(12)
- You want to add 3 vertex points that will determine the x and y offsets
//give it an x and y offset vl[0].add(200,0,0); vl[0].add(random(50,200), random (10, 190),0); vl[0].add(200,200,0);
- Now you are going to iterate over the array of UVertexLists starting at 1 and continuing until the variable is less than or equal to vl.length-1
- Inside the loop. add this next line of code which copies the first 3 points into the list
vl[i]=new UVertexList(vl[0]);
- This next line of code moves the points all the way around the Y axis:
vl[i].rotateY(map(i, 0, vl.length-1, 0, TWO_PI));
- Outside of the loop initialize the geo instance by setting it to new UGeometry()
- In order to connect all the points, you need to call quadStrip(vl) on the geo instance.
- Initialize the array of UVertexLists to hold 12 elements by setting the array variable to UVertexList.getVertexLists(12)
- Navigate to the setup() method:
- Set the size of the sketch and the renderer you want to use.
- Call your build() method.
- Initialize nav by setting it to new UNav3D(this)
- You want to center the 3D space:
nav.setTranslation(width/2, height/2, 0);
- Set the size of the sketch and the renderer you want to use.
- Inside the draw() method:
- Update the background.
- Add lights.
- Set the stroke.
- Call doTransforms() on nav
- Set the fill color and pass in a low alpha value for transparency.
-
Call draw() on geo with a reference to self:
geo.draw(this);
- Update the background.
- Save and test.
-
Add another random point to the first array in the UVertexList array. This will create a line with four points.
- Save and test.
- Navigate back to build() and add this line of code to connect the last vertex list with the first.
geo.quadStrip(vl[vl.length-1], vl[0]);
- Now you need to add a top and a top to close the shape:
You'll use a
triangle fan and you can
extract a new vertex list from the vertex list array:
Outside of the loop:
//defines edge of the bottom UVertexList top=UVertexList.extractFromArray(0, vl);
- This is the method definition for UGeometry triangleFan(UVertexList vl,boolean useCentroid,boolean reverseOrder)
This code connects the vertex points, but doesn't close the mesh.
You may need to reverse the true and false parameters later, but try to create a triangleFan after the line that created the first quadStrip:geo.triangleFan(top, true, false);
- Add this line of code to close the vertex list by adding a copy of the first vertex.
bottom.close();
- Save and test.
- Extract a new vertex list from the vertex list array to define the bottom edge. The parameters this time are vl[0].n-1, vl
- Create a triangleFan for the bottom.
- Close the bottom by closing the vertex list by adding a copy of the first vertex.
-
Add geo.writeSTL(this, "test.stl"); at end of build() to create an stl file.
- Open NetFabb Studio Basic, Meshlab or an application of your choice and open the stl file to check the mesh.
- If you see something like this in NetFabb:
you'll need to go back to your sketch and switch the true and falses in either your geo.triangleFan(top or geo.triangleFan(bottom statement. You want something like this: - Save and test.
Adding a GUI
Tutorial derived from work by Marius Watz- Navigate to the line of code that creates an stl and comment it out.
- Import the controlP5 library. This library allow you to create GUI elements. The modelbuilder library has been written with convenience functions to make building your gui even easier.
- Create an instance variable of type USimpleGUI. Name it
gui.
- Create an instance variable of type int. Name it
meshRes. This variable will control the resolution of the mesh. In the original sketch this value was the number of elements in your UVertexList array or 12.
- Create an instance variables to hold your x and y vertices for the first, second, third and fourth points in your UVertexList. You could name them firstX, firstY
secondX,
secondY,
thirdX, thirdY, fourthX, fourthY for clarity.
- Create an instance variable of type boolean. Name it
doRebuild. This will help control when you want to update the sketch.
- Navigate to the draw() method and surround the doTransforms(), fill() and geo.draw(this) statements with calls to pushMatrix() and popMatrix().
- Create a conditional to test if doRebuild is true. If the condition is met, call build() and set doRebuild to false.
- Create a new method called initGUI(). This method does not return anything (void).
- Inside the initGUI() method
-
Initialize the instance of gui by setting it to new USimpleGUI(this).
- Set meshRes to 12.
- Set the other variables:
firstX=50; firstY=0; secondX=50; secondY=10; thirdX=50; thirdY=10; fourthX=50; fourthY=200;
- Add a slider to the gui instance by calling addSlider("Label", value, min, max) on the instance of gui. Set the Label to "meshRes" and set the value to meshRes. Set the min to 4 and the max to 50.
- Add another slider to the instance of gui. Call it "firstX" and set the value to firstX. Set the min to 50 and the max to 200
- Add another slider to the instance of gui. Call it "firstY" and set the value to firstY. Set the min to -150 and the max to 9
- Add another slider to the instance of gui. Call it "secondX" and set the value to secondX. Set the min to 50 and the max to 200
- Add another slider to the instance of gui. Call it "secondY" and set the value to secondY. Set the min to 10 and the max to 190
- Continue for all x and y points. Just remember that the fourthY should range between 191 and something above 200.
-
Initialize the instance of gui by setting it to new USimpleGUI(this).
- Create a new method called controlEvent. This method does not return anything, but takes ControlEvent ev as a parameter.
- Inside the method set doRebuild to true.
- Navigate to the build() method and replace the random numbers with the appropriate variables
- Navigate to the setup() method and add
- initGUI(); before build()
- nav.setGUI(gui);
- Navigate to the draw() method and call draw() on the instance of your gui object.
- Save and test
Saving STL
- Navigate to the initGUI() method. Inside the method add a button to the GUI. The syntax for adding a button is this:
Name your button saveSTL
gui.addButton("button/method_name);
- Create a saveSTL() method.
- Inside the method add this code so that every time you click on the button you will create an stl file with a unique name.
geo.writeSTL(this, UIO.getIncrementalFilename( this.getClass().getSimpleName()+" ###.stl", sketchPath) );
- Save and test.
- Add a fifth point to the UVertexList().
Terrain Builder
map()
- In the example below there are 100 random points of data that are drawn to the sketch:
While this works, it would be better to scale the data to the size you need. That's where map() comes in:
float val[]; void setup() { size(400, 400); val=new float[100]; //create the data for (int i=0;i<val.length;i++) { val[i]=random(100); } } void draw() { background(0); noFill(); stroke(255); //draw the data beginShape(); for (int i=0;i<val.length-1;i++) { vertex(i, val[i]); } endShape(); }
float val[]; void setup() { size(400, 400); val=new float[100]; for (int i=0;i<val.length;i++) { val[i]=random(100); } } void draw() { background(0); noFill(); stroke(255); //this is our data beginShape(); for (int i=0;i<val.length;i++) { //want to map to cover the entire screen float x=map(i, 0, val.length-1, 0, width-1); float y=map(val[i], 0, 100, 0, height-1); vertex(x, y); } endShape(); } void drawValues(float data[], float w, float h) { float maxVal=max(data); int n=data.length; beginShape(); for (int i=0;i<n;i++) { float x=map(i, 0, n-1, 0, w); float y=map(data[i], 0, maxVal, 0, h); vertex(x, y); } endShape(); }
- Now map the data so that the chart exists inside a shape that is 40 pixels from each edge.
- Add 2 horizontal lines. One at the top of the data and one at the bottom
- What if you want to see the data displayed radially?
float val[]; void setup() { size(400, 400); val=new float[100]; for (int i=0;i<val.length;i++) { val[i]=random(100); } } void draw() { background(0); noFill(); stroke(255, 255, 0); translate(width/2, height-200); drawValuesRadial(val, 80, 150); } void drawValuesRadial(float data[], float rInner, float rOuter) { float maxVal=max(data); int n=data.length; float x, y, angle, r; //x and y become angle and coordinates ellipse(0, 0, rInner*2, rInner*2); ellipse(0, 0, rOuter*2, rOuter*2); stroke(255, 255, 0); beginShape(); for (int i=0;i<n;i++) { angle=map(i, 0, n-1, 0, radians(360));//TWO_PI r=map(data[i], 0, maxVal, rInner, rOuter); x=cos(angle)*r; y=sin(angle)*r; vertex(x, y); line(x, y, x*0.5, y*0.5); } endShape(); }
- You can also bring in real data and describe it in 3D
Download this file and add it to your sketch.import unlekker.mb2.geo.*; import unlekker.mb2.util.*; import ec.util.*; UVertexList vl; UGeo geo; UNav3D nav; void setup(){ size(600,600,P3D); UMB.setPApplet(this); nav=new UNav3D(); importData("google2008.csv"); build(); } void draw(){ background(0); stroke(255); noFill(); translate(width/2, height/2); lights(); nav.doTransforms(); vl.draw(); } void build(){ //take csv values and map it to a circle vl=new UVertexList(); for(int i=0;i<val.length;i++){ float deg=map(i,0, val.length, 0, TWO_PI); vl.add(new UVertex(250,val[i]*450).rotY(deg)); } //geo=new UGeo(); } float maxVal; float val[]; void importData(String filename){ Table data=loadTable(filename, "header"); //get the float values from column 4 val=data.getFloatColumn(4); //get the max val maxVal=max(val); //normalize values for(int i=0; i<val.length;i++){ val[i]=val[i]/maxVal; } }
- Make it solid:
import unlekker.mb2.geo.*; import unlekker.mb2.util.*; import ec.util.*; UVertexList vl; UGeo geo; UNav3D nav; void setup(){ size(600,600,P3D); UMB.setPApplet(this); nav=new UNav3D(); importData("google2008.csv"); build(); } void draw(){ background(0); stroke(255); fill(255,255,0); translate(width/2, height/2); lights(); nav.doTransforms(); geo.draw(); } void build(){ //take csv values and map it to a circle vl=new UVertexList(); for(int i=0;i<val.length;i++){ float deg=map(i,0, val.length, 0, TWO_PI); vl.add(new UVertex(150,val[i]*150).rotY(deg)); } geo=new UGeo().quadstrip(vl, vl.copy().scale(1,0,1)); } float maxVal; float val[]; void importData(String filename){ Table data=loadTable(filename, "header"); //get the float values from column 4 val=data.getFloatColumn(4); //get the max val maxVal=max(val); //normalize values for(int i=0; i<val.length;i++){ val[i]=val[i]/maxVal; } }
- And then:
import unlekker.mb2.geo.*; import unlekker.mb2.util.*; import ec.util.*; UVertexList vl; UGeo geo; UNav3D nav; void setup(){ size(600,600,P3D); UMB.setPApplet(this); nav=new UNav3D(); importData("google2008.csv"); build(); } void draw(){ background(0); noStroke(); fill(255,255,0); translate(width/2, height/2); lights(); nav.doTransforms(); geo.draw(); } void build(){ //take csv values and map it to a circle vl=new UVertexList(); for(int i=0;i<val.length;i++){ float deg=map(i,0, val.length, 0, TWO_PI); vl.add(new UVertex(150,val[i]*150).rotY(deg)); } float m=0.7; geo=new UGeo().quadstrip(vl, vl.copy().scale(m,0,m)); } float maxVal; float val[]; void importData(String filename){ Table data=loadTable(filename, "header"); //get the float values from column 4 val=data.getFloatColumn(4); //get the max val maxVal=max(val); //normalize values for(int i=0; i<val.length;i++){ val[i]=val[i]/maxVal; } }
- And in 3D:
import unlekker.mb2.geo.*; import unlekker.mb2.util.*; import ec.util.*; UNav3D nav; ArrayList<Float> values; float minVal, maxVal; String filename="aapl2008.csv"; UGeo dataGeo; public void setup() { size(800, 600,P3D); // set PApplet reference UMB.setPApplet(this); nav=new UNav3D(); importData(); build(); } public void draw() { background(0); drawCredit(); lights(); translate(width/2, height/2); nav.doTransforms(); fill(255); noStroke(); dataGeo.draw(); } void build() { UVertexList vl=new UVertexList(); int cnt=0; float h=200; // create a vertex list representing the input data for(float theVal : values) { // map all values to radial XZ positions, with Y given by value vl.add(new UVertex(200,-h*theVal). rotY(map(cnt,0,values.size(), 0,TWO_PI))); cnt++; } // multipliers to create a conical shape float m=0.4f; float m2=0.6f; // vertex lists representing the inner and outer circles, // created by copying "vl" and scaling it in XZ by a multiplier. // by scaling Y by zero we constrain the vertices to the base plane. UVertexList circleInner=vl.copy().scale(m,0,m); UVertexList circleOuter=vl.copy().scale(m2,0,m2); // create quadstrips between the edges dataGeo=new UGeo().quadstrip(circleInner,vl); dataGeo.quadstrip(vl, circleOuter); dataGeo.quadstrip(circleOuter,circleInner); // add a single face at start and end of the three edges to // cap the geometry, making it solid dataGeo.addFace(circleOuter.first(),circleInner.first(), vl.first()); dataGeo.addFace(circleInner.last(),circleOuter.last(),vl.last()); // // reverse the last face to fix th // dataGeo.getF().get(dataGeo.sizeF()-1).reverse(); // export the STL dataGeo.writeSTL(sketchPath+"/"+UFile.noExt(filename)+".stl"); } void importData() { Table data; data=loadTable(filename, "header"); // get all the values from column id=4, the closing price float [] val=data.getFloatColumn(4); for (float theVal : val) maxVal=max(maxVal, theVal); minVal=min(val); maxVal=max(val); println(val.length+" "+minVal+" "+maxVal); // fill an ArrayList with normalized values values=new ArrayList<Float>(); for (float theVal : val) { values.add(theVal/maxVal); // normalize } } void keyPressed() { build(); } void drawCredit() { fill(255); textAlign(RIGHT); text(UMB.version(), width-5, 15); textAlign(LEFT); text(this.getClass().getSimpleName(), 5, 15); String dataStr=filename+" | "+ values.size()+" values ["+ nf(minVal,0,2)+" > "+nf(maxVal,0,2)+"]"; text(dataStr, 5, height-5); }
Terrain Builder
Tutorial derived from work by Marius Watz- Open Processing and create a new sketch.
-
Import Modelbuilder and OpenGL libraries.
- Create setup() and draw() methods.
-
Create an instance variable to hold an array of UVertexlists. This variable will be of type UVertexList. Name the array variable vl.
To create an array use this syntax:UVertexList[] vl;
- Add an instance variable of type UNav3D. Name the variable nav.
- Add an instance variable of type UGeometry. Name the variable geo.
- Navigate to the setup() method:
- Set the size of your sketch and select the renderer you want to use.
- Call your build() method.
- Initialize nav by setting it to new UNav3D(this).
- You want to center the 3D space:
nav.setTranslation(width/2, height/2, 0);
- Set the size of your sketch and select the renderer you want to use.
- Inside the draw() method:
- Update the background.
- Add lights.
- Set the stroke.
- Set the fill.
- Call doTransforms() on nav so you can manipulate 3D space.
- Call draw() on geo and pass the method
a reference to self:
geo.draw(this);
- Update the background.
- Create a method named build(). This method does not return anything (void)
- Inside the build() method:
- Initialize the array of UVertexLists to hold 20 elements. You can do this by setting the array variable to UVertexList.getVertexLists(20).
- Now you want to create a for loop to iterate over the UVertexList array.
- Inside the loop you just constructed, create a nested loop that iterates over the UVertexList array again.
- Inside the inner loop you want to add an x, y and z value to each element in the array (vl[i]).
The first point will be map(outer_loop_var, 0,vl.length-1, -1,1),
the second point will be a random number between 0.2 and 1
and the third point will be map(inner_loop_var, 0,vl.length-1, -1,1)
The x will take the outer loop variable and map it, changing the min of 0 and the max of the 11 to a new min of -1 and a new max of 1.
The y will be a random number between .2 and 1.
The z will take the inner loop variable and map it, changing the min of 0 and the max of the 11 to a new min of -1 and a new max of 1. - Initialize the geo instance by setting it to new UGeometry().
- In order to connect all the points, call quadStrip(vl) on the instance of geo.
- If you ran this now, you wouldn't see anything. Why?
- Call setDimensions(600) on the instance of geo. The parameter is the number in millimeters that you want width and length of the terrain to be.
- Initialize the array of UVertexLists to hold 20 elements. You can do this by setting the array variable to UVertexList.getVertexLists(20).
- Save and test.
Adding a GUI
Tutorial derived from work by Marius Watz- Open the terrain sketch.
- Import the controlP5 library.
- Create an instance variable of type USimpleGUI. Name it
gui.
- Create an instance variable of type int. Name it
meshRes. This variable will control the resolution of the mesh. In the original terrain sketch this value was the number of elements in your UVertexList array.
- Create an instance variable of type float. Name it
noiseStep. This will control the randomness of the form. Set it to 0.1.
- Create an instance variable of type boolean. Name it
doRebuild. This will help control when you want to update the sketch.
- Navigate to the draw() method and surround the doTransforms(), fill() and geo.draw(this) statements with calls to pushMatrix() and popMatrix().
- Create a conditional to test if doRebuild is true. If the condition is met, call build() and set doRebuild to false.
- Create a new method called initGUI(). This method does not return anything (void).
- Inside the initGUI() method
-
Initialize the instance of gui by setting it to new USimpleGUI(this).
- Set meshRes to 20.
- Add a slider to the gui instance by calling addSlider("Label", value, min, max) on the instance of gui. Set the Label to "meshRes" and set the value to meshRes. Set the min to 4 and the max to 50.
- Add another slider to the instance of gui. Call it "noiseStep" and set the value to noiseStep. Set the min to 0.001 and the max to 0.5
-
Initialize the instance of gui by setting it to new USimpleGUI(this).
- Create a new method called controlEvent. This method does not return anything, but takes ControlEvent ev as a parameter.
- Inside the method set doRebuild to true.
- Navigate to the setup() method and add
- initGUI();
- nav.setGUI(gui);
- Navigate to the build() method.
- Instead of hard-coding the number of elements in the array set this value to meshRes.
- Navigate to inside the inner loop of your nested loops and before you add points add this line:
float y=noise((float)outer_loop_var*noiseStep,(float)inner_loop_var*noiseStep);
- Add y to the second point.
- Navigate to the draw() method and call draw() on the instance of your gui object.
- Save and test.
Making the terrain solid
Tutorial derived from work by Marius Watz- Open the terrain sketch.
- Navigate to the setup() method and call setRotation() on the instance nav and pass it PI,0,0
- Move build() to the end of the draw() method.
- Navigate to the build() method
- You want to build the left edge of the terrain mesh. To do that, place this block of code before geo.setDimensions(600);:
UVertexList bottom=new UVertexList(vl[0]); for(int i=0; i<bottom.n; i++){ bottom.v[i].y=0; } // create edge as a quadstrip between bottom and // the first edge of the terrain geo.quadStrip(bottom, vl[0]);
- This will build the right edge:
// right edge bottom=new UVertexList(vl[meshRes-1]); for(int i=0; i<bottom.n; i++){ bottom.v[i].y=0; } // reverse order to get correct face normals geo.quadStrip(vl[meshRes-1], bottom);
- This will build the front wall:
// get edge using extractFromArray() to get // all the first points of the vertex lists. // this is the front edge UVertexList top=UVertexList.extractFromArray(0, vl); bottom=new UVertexList(top); for(int i=0; i<bottom.n; i++){ bottom.v[i].y=0; } geo.quadStrip(top, bottom);
- This will build the back wall:
// get edge using extractFromArray() to get // all the first points of the vertex lists. // this is the front edge top=UVertexList.extractFromArray(meshRes-1, vl); bottom=new UVertexList(top); for(int i=0; i<bottom.n; i++) { bottom.v[i].y=0; } geo.quadStrip(bottom, top);
- To create the ground plane you need to use UVec3:
// create ground plane as quad. // use new UVec3(vl[0].v[0]).scale(1,0,1) // to get a copy of the vertex with y=0 geo.beginShape(QUAD_STRIP); geo.vertex( new UVec3(vl[0].v[0]).mult(1,0,1)); geo.vertex( new UVec3(vl[0].v[meshRes-1]).mult(1,0,1)); geo.vertex( new UVec3(vl[meshRes-1].v[0]).mult(1,0,1)); // geo.vertex( new UVec3(vl[meshRes-1].v[meshRes-1]).mult(1,0,1)); geo.endShape();
- Navigate to the initGUI() method. Inside the method add a button to the GUI. The syntax for adding a button is this:
Name your button saveSTL
gui.addButton("button/method_name);
- Create a saveSTL() method.
- Inside the method add this code so that every time you click on the button you will create an stl file with a unique name.
geo.writeSTL(this, UIO.getIncrementalFilename( this.getClass().getSimpleName()+" ###.stl", sketchPath) );
- Save and test.
- You'll need to repair this mesh. I suggest using NetFabb
Images and Terrain Builder
Tutorial derived from work by Marius Watz- Open the Terrain Builder sketch from the previous tutorial.
- Find a suitable image to work with and add the file to your sketch.
- Create an instance variable of type float. Name the variable yScale
- Add another instance variable. This time of type PImage and name the variable img.
- Navigate to the initGUI() method.
- Inside the initGUI() method
- Comment out the slider and statements in the build() method that deal with noise.
- Set yScale to 0.05
- Add a slider to scale the Y axis. The syntax for adding a slider is this:
Name your slider yScale. Set the value to yScale, set min to 0.02 and max to .1.
gui.addSlider("slider name", value, min, max);
- Change meshRes to 50
- Change the meshRes slider values to range between 50 and 200.
- Comment out the slider and statements in the build() method that deal with noise.
- Navigate to the setup() method and load the image:
img=loadImage("name_of_image.png");
- Change the setRotation() to PI/2,0,0
- Navigate to the build() method
- Inside the nested loop comment out anything to do with noiseStep
- Now you are going calculate y as pixel brightness. Create a variable named y that is of type float. Set the variable to
1-(float)brightness(img.get( (int)map(i, 0,vl.length-1, 0,img.width-1), (int)map(j, 0,vl.length-1, img.height-1,0)))/255
- Set y to itself multiplied by yScale.
- Modify the statement where you add the points to the UVertexList:
vl[i].add( map(i, 0,vl.length-1, -1,1), y, map(j, 0,vl.length-1, -1,1) );
- Change the fill() to a gray , background() to black and stroke()
to noStroke().
- Save and test.
Standards
STANDARD 1Analysis, Inquiry, and Design: ENGINEERING DESIGN
Key Idea 1: details
Engineering design is an iterative process involving modeling and optimization (finding the best solution within given constraints); this process is used to develop technological solutions to problems within given constraints. (Note: The design process could apply to activities from simple investigations to long-term projects.)
STANDARD 5
Technology: Computer Technology
Key Idea 3: details
Computers, as tools for design, modeling, information processing, communication, and system control, have greatly increased human productivity and knowledge. Standard 2: Integrated Learning details Students will demonstrate how academic knowledge and skills are applied in the workplace and other settings.
Integrated learning encourages students to use essential academic concepts, facts, and procedures in applications related to life skills and the world of work. This approach allows students to see the usefulness of the concepts that they are being asked to learn and to understand their potential application in the world of work.
Saving STL files
This assumes that you have created a UGeometry geo object and a USimpleGUI gui object with a saveSTL button:- Create a saveSTL() method that does not return anything and does not take any arguments.
- Inside the method add this code so that every time you click on the button you will create an stl file with a unique name.
String filename=this.getClass().getSimpleName(); //path and prefix filename=UFile.nextFile(sketchPath,filename); saveFrame(filename.substring(0, filename.length()-4)+"-###.png"); geo.writeSTL(filename.substring(0, filename.length()-4)+"-###.stl");
Projects
This exercise introduces using a programming language as a tool that allows you to solve problems. It uses numbers, math and logic to solve a design problem.Algorithms
from Casey Reas's WORKSHOP 1: CONDITIONAL DRAWINGAlgorithms are the foundation of all programmed graphics, but of course algorithms exist outside of computer code. When applied to drawing, some algorithms are the basis for extraordinary interactions between people, pencils, and paper. Based on the Conditional Design Manifesto by Luna Maurer, Edo Paulus, Jonathan Puckey, and Roel Wouters, you'll explore a range of drawing systems and instructions and then invent one of our own.
Each member of the class should design a set of drawing instructions for one person to make a drawing. Design the instructions so that the drawing time will be approximately ten minutes.
Come to class with your instructions printed on a letter-size sheet of paper and two drawings that you've made using the instructions. Also bring four sheets of letter-size paper you want people to draw on and the drawing tools you'd like them to use. Your instructions must not exceed one page.
Toolbar
from Casey Reas's WORKSHOP 3: VARIABLES, RESPONSEUsing the ControlP5 library create a drawing tool that can be manipulated by the user.
Art & Code
from Casey Reas's WORKSHOP 2 & 3: DRAWING WITH CODE and VARIABLES, RESPONSESelect a crop of this Sarah Morris painting and draw it within Processing as a 640 x 360 pixel program. First select an interesting/ambitious crop, then draw it on graph paper and/or load it into a program such as Photoshop or Illustrator to read the color and coordinate data. Use integer values for coordinates and only use the following functions for geometry: line(), triangle(), quad(), rect(), ellipse(), arc(), beginShape(),,endShape()and vertex() . The goal is to translate the image into code.
Modify your code from above to make the image respond to the mouse. Do this by adding variables to your program and controlling your custom variables with the built-in Processing variables mouseX, mouseY, and mousePressed. Think about how the quality of motion from the mouse (direction and speed) should aesthetically affect the lines and shapes. The program should remain 640 x 360 pixels.
Add the 3rd dimension.
Encoding compositional logic into the generator
from Casey Reas's WORKSHOP 4: MEDIACreate a sketch so that elements have relationships. First, think about what this means and how you want to utilize the idea. (For example, if the photo of the moom is large, then the photo of the space station is small and vice versa.) Then, develop a precise plan for how your images relate and implement these relationships in code. Use only your own media or media that you have permission to use. Copy and modify the Art & Code sketch to start.
Objects
from Casey Reas's WORKSHOP 7: OBJECTSWrite a function to draw the simple object (cat, face, building,etc). Add parameters to the function so the object can be drawn differently based on the selected parameters. Draw three or more objects on the screen to show the differences possible by changing the parameters. Next, create a Object class using the code from your function as a foundation. Add methods to give the object motion (behavior). Draw three or more objects on the screen to show the differences possible by changing the parameters to the class and its methods.
Arrays
from Casey Reas's WORKSHOP 8: ARRAYSUse the pixels array of an image as data for something other than literally displaying the image. Use common techniques such as slitscanning and height mapping, or invent something new.
Reading List
- Getting Started with Processing
- Form+Code in Design, Art, and Architecture
- Processing: A Programming Handbook for Visual Designers and Artists
Saving STL files
This assumes that you have created a UGeometry geo object and a USimpleGUI gui object with a saveSTL button:- Create a saveSTL() method that does not return anything and does not take any arguments.
- Inside the method add this code so that every time you click on the button you will create an stl file with a unique name.
geo.writeSTL(this, UIO.getIncrementalFilename( this.getClass().getSimpleName()+" ###.stl", sketchPath) );