Question

I'm having an issue with the animation I'm making. The principal idea is that 6 equilateral triangles revolve around a central point, while also rotating about their own selves.

When I run the code, each instance of a triangle uses the previous instance as a reference point, rather than the centre. This causes a cool spiral effect, but it's not what I'm after.

Code follows:

//Declare
tri myTri1;
tri myTri2;
tri myTri3;
tri myTri4;
tri myTri5;
tri myTri6;

void setup() {
  size(600, 600);
  smooth();

  //Initialise
  myTri1 = new tri();
  myTri2 = new tri();
  myTri3 = new tri();
  myTri4 = new tri();
  myTri5 = new tri();
  myTri6 = new tri();
}

void draw() {
  background(0);

  //Call Functions
  myTri1.run();
  translate(width/2,height/2);
  rotate(PI/3);
  translate(-width/2,-height/2);
  myTri2.run();
  translate(width/2,height/2);
  rotate(PI/3);
  translate(-width/2,-height/2);
  myTri3.run();
  translate(width/2,height/2);
  rotate(PI/3);
  translate(-width/2,-height/2);
  myTri4.run();
  translate(width/2,height/2);
  rotate(PI/3);
  translate(-width/2,-height/2);
  myTri5.run();
  translate(width/2,height/2);
  rotate(PI/3);
  translate(-width/2,-height/2);
  myTri6.run();
}

Second tab:

class tri {
  //Variables
  float ax, ay, bx, by, cx, cy; //triangle point coordinates
  float theta; //triangle angle
  float pi = PI; //pi reference

  //Construct
  tri() {
    theta = PI/6;
    ax = 0;
    ay = 0;
    bx = -50*(sin(theta));
    by = +50*(cos(theta));
    cx = +50*(sin(theta));
    cy = +50*(cos(theta));
  }

  //Functions
  void run() {

    translate(width/2, height/2);
    revolve(); //revolve triangle about centre
    spin(); //spin triangle about itself
    pulse(); //move triangle in/out
    display(); //show triangle
    translate(-width/2, -height/2);
  }

  void spin() {
    translate(0, by/2); //sets rotation axis to centre of triangle
    rotate(millis()*-0.0005*pi);
    translate(0, -by/2); //resets axis to centre of window
  }

  void revolve() {
    translate(-2*by, 0);
    ax = ax + 2*sin(millis()*0.005);
    ay = ay + 4*cos(millis()*0.005);
    bx = bx + 2*sin(millis()*0.005);
    by = by + 4*cos(millis()*0.005);
    cx = cx + 2*sin(millis()*0.005);
    cy = cy + 4*cos(millis()*0.005);
    translate(2*by, 0);
  }

  void pulse() {
    ay = ay + 5*sin(millis()*0.005);
    by = by + 5*sin(millis()*0.005);
    cy = cy + 5*sin(millis()*0.005);
  }

  void display() {
    fill(255);
    strokeWeight(0.8);
    triangle(ax, ay, bx, by, cx, cy);
  }
}

If anyone can point out where I'm going wrong with this it would be awesome, and if you can suggest any optimisations RE the formation of the hexagon (instead of the mess of translations) I would be incredibly happy.

Was it helpful?

Solution

Franchesca's suggestion good. You should have an idea of where the origin is and how the coordinate space transformations you apply affect that, at least until you get a feel for it and you're in complete control.

I also warmly recommend this Processing tutorial on 2d transformations

Now, back to your code :)

First thing you can improve is getting used to for loops and arrays. They may look scary at first, but once you get the hang of them they're quite easy. Wherever you can think of a situation where repetition is needed, you can use a for loop to make your life easier. In your case, generating the triangles and storing them can be done using loops and arrays.

For loop have the following syntax:

for keyword (3 elements: a start point,an end point(condition) and an increment,(separated by the ; character)

Let's say you want to move from a(0) to b(10) one step at a time:

for(int currentPos = 0 ; currentPos < 10; currentPos++){
  println("step: " + currentPos);
}

If you can walk, you can also skip :)

for(int currentPos = 0 ; currentPos < 10; currentPos+=2){
  println("step: " + currentPos);
}

even backwards if you want:

for(int currentPos = 10 ; currentPos > 0; currentPos--){
  println("step: " + currentPos);
}

This is very useful when traversing all sort of data(triangles in a scene, vertices in a triangle, etc.)

How do you organize your data ? You place it in a list or array. An array contains elements of the same type and has a set length. The syntax to declare an array is like so:

ObjectType[] nameOfArray;

and you can initialize an empty array:

int[] fiveNumbers = new int[5];//new keyword then the data type and length in sq.brackets

or you can initialize the array with values:

String[] words = {"ini","mini","miny","moe"};

You access elements in an array using square brackets and the index of the object in the list you want to access. Arrays have a length property so you can easily count objects.

background(255);
String[] words = {"ini","mini","miny","moe"};
for(int i = 0 ; i < words.length; i++){
   fill(map(i,0,words.length, 0,255));
   text(words[i],10,10*(i+1));
}

Now back to your original question. Here is your main code simplified using for loops and arrays:

//Declare
int numTri = 6;//number of triangles
tri[] triangles = new tri[numTri];//a list/an array of tri objects (currently empty)
float angleIncrement = TWO_PI/numTri;
float radius = 100;
void setup() {
  size(600, 600);
  smooth();

  //Initialise
  for(int i = 0 ; i < numTri; i++){
    triangles[i] = new tri();//allocate/initialise each tri object into it's 'slot' in the list/array
  }
}

void draw() {
  background(0);
  translate(width * .5, height * .5);//move everything to the centre
  for(int i = 0 ; i < numTri; i++){
    pushMatrix();
      rotate(angleIncrement * i);//rotate from the last offset(centre)
      translate(radius,0);//move on (rotated) X axis away from the centre
      triangles[i].run();
    popMatrix();
  }
}
void drawAxes(int size){
  pushStyle();
  stroke(255,0,0);
  line(0,0,size,0);
  stroke(0,255,0);
  line(0,0,0,size);
  popStyle();
}

Notice I've indented the code within push/pop matrix calls. It's not necessary but I've added that so you can get a feel for how coordinate spaces nest. These call are very useful as they deal with the nitty gritty math part behind the scenes for you. Notice how I'm placing the symbols in a circle without using the polar to cartesian conversion formula (cos(angle) * radius, sin(angle) * radius).

You can test that with this code from your other tab:

class tri {
  //Variables
  float ax, ay, bx, by, cx, cy; //triangle point coordinates
  float theta; //triangle angle
  float pi = PI; //pi reference

  //Construct
  tri() {
    theta = PI/6;
    ax = 0;
    ay = 0;
    bx = -50*(sin(theta));
    by = +50*(cos(theta));
    cx = +50*(sin(theta));
    cy = +50*(cos(theta));
  }

  //Functions
  void run() {
    pushMatrix();
    revolve(); //revolve triangle about centre
//    pulse(); //move triangle in/out
    display(); //show triangle
    popMatrix();
  }

  void revolve() {
    translate(-2*by, 0);
    float angle = millis()*0.005;
    float cos = cos(angle);
    float sin = sin(angle);
    ax = ax + 2*sin;
    ay = ay + 4*cos;
    bx = bx + 2*sin;
    by = by + 4*cos;
    cx = cx + 2*sin;
    cy = cy + 4*cos;
    translate(2*by, 0);
  }

  void pulse() {
    ay = ay + 5*sin(millis()*0.005);
    by = by + 5*sin(millis()*0.005);
    cy = cy + 5*sin(millis()*0.005);
  }

  void display() {
    fill(255);
    strokeWeight(0.8);
    triangle(ax, ay, bx, by, cx, cy);
  }
}

Also notice I've added a drawAxes function. That's just a utility to make it easier to understand in what coordinate space your drawing.

Again, going back to arrays and for loops, here's a modified version of your code:

class tri {
  //Variables
  float ai = TWO_PI/3;//angle increment
  float r  = 50;
  float sr = r * 1.5;//spin radius
  float vt = 5;//vertical translation(for pulse)
  PVector[] verts = new PVector[3];

  boolean rotateAroundCentre = true;
  boolean translateAroundCentre = false;
  boolean translateVertically = false;
  //Construct
  tri() {
    for(int i = 0 ; i < 3; i++){
      verts[i] = new PVector(cos(ai * i) * r,sin(ai * i) * r);
    }
  }

  //Functions
  void run() {
    pushMatrix();
      float angle = millis()*0.0005;
      if(rotateAroundCentre) rotate(angle);
      if(translateVertically) translate(sin(angle)*vt,0);
      if(translateAroundCentre){
//        translate(cos(angle) * sr,sin(angle) * r);
//        or          
          rotate(angle);
          translate(sr,0);   
      }
    display(); //show triangle
    popMatrix();
  }
  void display() {
    fill(255);
    strokeWeight(0.8);
    triangle(verts[0].x, verts[0].y, verts[1].x, verts[1].y, verts[2].x, verts[2].y);
    drawAxes(10);
  }
}

Feel free to play with the boolean rotateAroundCentre,translateAroundCentre,translateVertically variables and have fun playing with coordinates and geometry :)

For example here's a version of the sketch that you can toggle the 3 options above using the 1/2/3 keys on your keyboard:

//Declare
int numTri = 6;//number of triangles
tri[] triangles = new tri[numTri];//a list/an array of tri objects (currently empty)
float angleIncrement = TWO_PI/numTri;
float radius = 100;
boolean[] options = {false,false,false}; 
void setup() {
  size(600, 600);
  smooth();

  //Initialise
  for(int i = 0 ; i < numTri; i++){
    triangles[i] = new tri();//allocate/initialise each tri object into it's 'slot' in the list/array
  }
}

void draw() {
  background(0);
  translate(width * .5, height * .5);//move everything to the centre
  for(int i = 0 ; i < numTri; i++){
    pushMatrix();
      rotate(angleIncrement * i);//rotate from the last offset(centre)
      translate(radius,0);//move on (rotated) X axis away from the centre
      drawAxes(20);
      triangles[i].run();
    popMatrix();
  }
}
void drawAxes(int size){
  pushStyle();
  stroke(255,0,0);
  line(0,0,size,0);
  stroke(0,255,0);
  line(0,0,0,size);
  popStyle();
}
void keyReleased(){
  for(int i = 0 ; i < 3; i++) if(key == (49+i)) options[i] = !options[i];//quick'n'dirty option toggling
  for(int i = 0; i < numTri; i++) {
    triangles[i].rotateAroundCentre = options[0];
    triangles[i].translateAroundCentre = options[1];
    triangles[i].translateVertically = options[2];
  }
}
class tri {
  //Variables
  float ai = TWO_PI/3;//angle increment
  float r  = 50;
  float sr = r * 1.5;//spin radius
  float vt = 5;//vertical translation(for pulse)
  PVector[] verts = new PVector[3];

  boolean rotateAroundCentre = false;
  boolean translateAroundCentre = false;
  boolean translateVertically = false;
  //Construct
  tri() {
    for(int i = 0 ; i < 3; i++){
      verts[i] = new PVector(cos(ai * i) * r,sin(ai * i) * r);
    }
  }

  //Functions
  void run() {
    pushMatrix();
      float angle = millis()*0.0005;
      if(rotateAroundCentre) rotate(angle);
      drawAxes(30);

      if(translateVertically) translate(sin(angle)*vt,0);
      drawAxes(40);

      if(translateAroundCentre){
//        translate(cos(angle) * sr,sin(angle) * r);
//        or          
          rotate(angle);
          drawAxes(40);

          translate(sr,0);   
      }
    display(); //show triangle
    popMatrix();
  }
  void display() {
    fill(255);
    strokeWeight(0.8);
    triangle(verts[0].x, verts[0].y, verts[1].x, verts[1].y, verts[2].x, verts[2].y);
    drawAxes(10);
  }
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top