ANAR+ makes use of linear transformations such as rotations, translations and scaling. It is possible to declare them as variables or to call a function on a geometrical element to transform it. Here a rotation of 90 degrees around the Z axis on a Face object.
Face f; f.rotateZ(PI/2);
The advantage of declaring a transformation as variable is that it is possible to reuse it on several geometrical elements.
Face f,g; Transform t = new Transform(); t.rotateZ(PI/2); ... f.apply(t); g.apply(t);
If you modify the transformation after applying it to some element, the change will be propagated to the elements the transformation has been applied to.
Face f; Transform t = new Transform(); t.rotateZ(PI/2); ... f.apply(t); ... t.rotateY(PI/2);
Such transformation can be chained.
Face f; f.rotateZ(PI/2).scale(2).translate(100,-10,30);
Transform t = new Transform(); t.rotateZ(PI/2).scale(2).translate(100,-10,30);
Of course one can use parametric elements with transformations.
Param s = new Param(2); Pt p = Anar.Pt(1,10,23); Transform t = new Transform(); t.rotateZ(PI/2).scale(s).translate(p);
import anar.*; import processing.opengl.*; Obj myObj = new Obj(); public void setup(){ size(800,400,OPENGL); Anar.init(this); Anar.drawAxis(true); initForm(); } void initForm(){ //Create a new set of Transformations Transform myTransform = new Transform(); myTransform.translate(20,0,0); myTransform.rotateZ(.5f); myTransform.scale(1,1.09f,1.05f); //Create a box Obj box = new Box(20,20,10); for(int i=0; i<30; i++) { Obj myCopy = box.copy(); //Create an obj from box myCopy.apply(myTransform); //Apply the set of transformations to the copy myObj.add(myCopy); //Add myCopy obj to my main obj myObj box = myCopy; //Swap box <--> myCopy // myCopy is now the base for the next loop } Anar.camTarget(myObj); //Get the sliders from the set of transform // Note: here, we don't use an obj to create the sliders. Anar.sliders(myTransform); } public void draw(){ background(155); myObj.draw(); }
Another example with a 2-dimensional set of modules with random variations.
import anar.*; import processing.opengl.*; Obj myObj; void setup() { size(800, 400, OPENGL); Anar.init(this); initForm(); } //Create parameter array to store individual height values Sliders myParams = new Sliders(); int edge = 10; int Nx = 17; int Ny = 6; void initForm() { //Create a new Object to store our shape myObj = new Obj(); Obj module; for (int i=0; i<Nx; i++) { for (int j=0; j<Ny; j++) { Param p = new Param(random(20)); Transform t = new Transform(); t.translate(edge*i,0,0); t.translate(0,0,edge*j); module = new Box(edge,edge,1).translate(-edge/2,-edge/2).rotateX(PI/2).rotateZ(p); //Add the copy to myObj myObj.add(module.apply(t)); //Create sliders for the parameter array myParams.add(p); } } Anar.camTarget(myObj); } void draw() { background(150); myObj.draw(); // myParams.draw(); }
As seen already in the previous examples, parameters can be used for defining 3-dimensional linear transformations such as translations, scaling, rotations, etc. By default, the action of these transformations is centered at the origin, which means that a scaling of 2 will double the distance of a point to the origin.
As a result a scale transformation with different scaling values on every axis will distort the scaled element if it is not aligned with the axis. Here is an example.
import anar.*; import processing.opengl.*; Obj box; void setup() { size(800, 400, OPENGL); Anar.init(this); Anar.drawAxis(); Param p1 = new Param(1,0,10).addToSlidersMain(); Param p2 = new Param(2,0,10).addToSlidersMain(); Param p3 = new Param(3,0,10).addToSlidersMain(); box = new Box(30,20,10); box.rotateX(0.2); box.rotateY(0.3); box.rotateZ(0.4); box.scale(p1,p2,p3); } void draw() { background(150); box.draw(); }
Another example of the origin problem can be found in the Random Box Array code example. Note the difference when removing the translation(-edge/2,-edge/2) applied on the Box after its creation.
If this translation shows how to remedy a simple situation, one actually often needs a transformation centered and oriented with a local frame of reference. This is done in ANAR+ by specifying the local frame when initializing the transformation.
Pt p,q,r; ... Transform t = new Transform(p,q,r); t.rotateZ(PI/3);
In the code above the transformation
The subsequent rotation along the Z axis will thus correspond to a rotation on the pqr plane. Note that if pq and pr are not orthogonal to each other the X axis will not be aligned with direction pq, which means that one should use cautiously rotation around the X axis.
import anar.*; import processing.opengl.*; Obj box; void setup() { size(800, 400, OPENGL); Anar.init(this); Anar.drawAxis(); box = new Box(30,20,10); box.rotateX(0.2); box.rotateY(0.3); box.rotateZ(0.4); Face f = box.face(3).copy().orphaned(); Param p1 = new Param(1,0,10).addToSlidersMain(); Param p2 = new Param(2,0,10).addToSlidersMain(); Param p3 = new Param(3,0,10).addToSlidersMain(); Transform t = new Transform(f.pt(1),f.pt(0),f.pt(2)); t.scale(p1,p2,p3); box.apply(t); } void draw() { background(150); box.draw(); }
Folding is represented as rotations in the frame of reference of the precedent piece. Since the transformation is applied on the original piece, one only uses the transformation transporting the origin to the frame of reference.
Transform t = new Transform(p,q,r).postTransform();
One can also extract the transformation that puts the frame of reference at the origin aligned with the principal axis
Transform t = new Transform(p,q,r).preTransform();
import processing.opengl.*; import anar.*; /* * Example for Anar library by Guillaume LaBelle + Julien Nembrini * http://anar.ch */ void setup(){ size(800,400,OPENGL); Anar.init(this); Anar.drawAxis(true); initForm(); } void initForm(){ Anar.sliders.add(new Param(PI,PI,2*PI)); Transform tx = new TranslateX(10); Transform ty = new TranslateY(10); Anar.sliders.add(tx,ty); Pt a = Anar.Pt(0,0,0); Pt b = Anar.Pt(a,tx); Pt c = Anar.Pt(b,tx).apply(ty); Pt d = Anar.Pt(a,tx).apply(ty); Pt e = Anar.Pt(a,ty); Face shape = new Face(a,b,c,d,e); shape.rotateY(Anar.sliders.get(0)); shape.fill(255,0,0); Anar.add(shape); Face f = shape; for (int i = 0; i<25; i++) Anar.add(f = new Face(Anar.face(0),new Transform(f.pt(3),f.pt(1),f.pt(2)).postTransform()).fill(255,255,0)); for (int i = 0; i<15; i++) Anar.add(new Obj(Anar.objEnd()).apply(tx).apply(tx).apply(ty)); } void draw(){ background(155); Anar.draw(); if(frameCount%100==0) Anar.camTarget(Anar.main); }