Trying to replicate a creation - stuck with laying out PVectors along a curve

Hello,

I am fairly new to Processing and coding in general. I have been trying to replicate a creation I found on the homepage of a studio called Yes. See www.yes.studio

I have got quite close, managing to create a number of sketches which, separately, kind of produce the required functionality. I am however struggling to put it all together.

My main stumbling block at the moment is arranging PVectors out in the required pattern - I’m also not sure exactly what the required layout is. I don’t think its concentric rings of points/ellipses. Almost looks like a spiral galaxy formation.

I have got a sketch working that does achieves the 3D rotation and shrinks the ellipses when the mouse is close (although this isn’t 100% correct).

Anyway, here is my code, I would really appreciate any suggestions towards how I might get closer to my goal.

The main sketch:

float angleX = 0;
float angleY = 0;
float angleZ = 0;

PVector[] points = new PVector[8];



void setup() {

  size(600, 600);

  points[0] = new PVector(-0.5, -0.5, 0);
  points[1] = new PVector(0.5, -0.5, 0);
  points[2] = new PVector(0.5, 0.5, 0);
  points[3] = new PVector(-0.5, 0.5, 0);
  points[4] = new PVector(-1, -1, 0);
  points[5] = new PVector(1, -1, 0);
  points[6] = new PVector(1, 1, 0);
  points[7] = new PVector(-1, 1, 0);
}

void draw() {

  background(0);
  translate(width/2, height/2);
  noStroke();
  fill(0, 255, 0);

  // Rotation matricies

  float[][] rotationZ = {
    { cos(angleZ), -sin(angleZ), 0}, 
    { sin(angleZ), cos(angleZ), 0}, 
    { 0, 0, 1}
  };

  float[][] rotationX = {
    { 1, 0, 0}, 
    { 0, cos(angleX), -sin(angleX)}, 
    { 0, sin(angleX), cos(angleX)}
  };

  float[][] rotationY = {
    { cos(angleY), 0, sin(angleY)}, 
    { 0, 1, 0}, 
    { -sin(angleY), 0, cos(angleY)}
  };

  for (PVector v : points) {

    //Compound rotation about the X and Y axis

    PVector rotated = matmul(rotationY, (matmul(rotationX, v)));
    float distance = 2;                                           //Projection distance
    float z = 1 / (distance - rotated.z);
    float[][] projection = {
      {z, 0, 0}, 
      {0, z, 0}
    };
    PVector projected2d  = matmul(projection, rotated);
    projected2d.mult(100);                                        //Scale up

    //As mouse gets closer to ellipse shrink it diameter

    float d = dist(projected2d.x, projected2d.y, mouseX, mouseY);
    float maxDist = dist(0, 0, width/2, height/2);
    float diameter  = map(d, 0, maxDist, 30, 0);
    ellipse(projected2d.x, projected2d.y, diameter, diameter);
  }

  //Calculate the angle to rotate about the Y axis

  float dY = dist(mouseX, 0, width/2, 0);
  float maxDistY = dist(0, 0, width/2, 0);
  float rotateItY;
  if (mouseX < width/2) {
    rotateItY = map(dY, 0, maxDistY, 0, -45);
  } else {
    rotateItY = map(dY, 0, maxDistY, 0, 45);
  }

  //Calculate the angle to rotate about the X axis

  float dX = dist(0, mouseY, 0, height/2);
  float maxDistX = dist(0, 0, 0, height/2);
  float rotateItX;
  if (mouseY < height/2) {
    rotateItX = map(dX, 0, maxDistX, 0, 45);
  } else {
    rotateItX = map(dX, 0, maxDistX, 0, -45);
  }
  angleY = radians(rotateItY);
  angleX = radians(rotateItX);
}

Shiffman’s 3D rotation matrix functions etc:

// Daniel Shiffman
// http://youtube.com/thecodingtrain
// http://codingtra.in

// Coding Challenge #112: 3D Rendering with Rotation and Projection
// https://youtu.be/p4Iz0XJY-Qk

// Matrix Multiplication
// https://youtu.be/tzsgS19RRc8

float[][] vecToMatrix(PVector v) {
  float[][] m = new float[3][1];
  m[0][0] = v.x;
  m[1][0] = v.y;
  m[2][0] = v.z;
  return m;
}

PVector matrixToVec(float[][] m) {
  PVector v = new PVector();
  v.x = m[0][0];
  v.y = m[1][0];
  if (m.length > 2) {
    v.z = m[2][0];
  }
  return v;
}

void logMatrix(float[][] m) {
  int cols = m[0].length;
  int rows = m.length;
  println(rows + "x" + cols);
  println("----------------");
  for (int i = 0; i < rows; i++) {
    for (int j = 0; j < cols; j++) {
      print(m[i][j] + " ");
    }
    println();
  }
  println();
}


PVector matmul(float[][] a, PVector b) {
  float[][] m = vecToMatrix(b);
  return matrixToVec(matmul(a,m));
}

float[][] matmul(float[][] a, float[][] b) {
  int colsA = a[0].length;
  int rowsA = a.length;
  int colsB = b[0].length;
  int rowsB = b.length;

  if (colsA != rowsB) {
    println("Columns of A must match rows of B");
    return null;
  }

  float result[][] = new float[rowsA][colsB];

  for (int i = 0; i < rowsA; i++) {
    for (int j = 0; j < colsB; j++) {
      float sum = 0;
      for (int k = 0; k < colsA; k++) {
        sum += a[i][k] * b[k][j];
      }
      result[i][j] = sum;
    }
  }
  return result;
}

Thanks

1 Like

Hi Henri,

Have a look at this video: https://www.youtube.com/watch?v=sj8Sg8qnjOg

1 Like

Thanks jb4x. I really enjoyed that video. I think whoever made the sketch I am referring too certainly used the golden ratio and the number phi.

For now I think I am going to put this challenge on hold as I think I am trying to run before I can walk. I have still yet to properly learn about vectors.

Don’t get discourage, I don’t think you are trying to run before you can walk, vectors are not that hard to understand and the more you’ll try to do things with them the better you will understand them!

Vectors are really just a box in which you can hold 2 or 3 data (depending on your application). Then you can do what you want with those data and give them the meaning you need for you application.

  • It can be used to keep track of a position, each value representing the x, y and z coordinates of a point. In this case imagine an arrow starting at the origin of your reference (0, 0, 0) and pointing at the point of coordinates (x, y, z).
  • It can also be used to keep track of change in position. Think of the vector as (deltaX, deltaY, deltaZ). In this case imagine an arrow starting at the original position of your point (x, y, z) and pointing towards the new position (x + deltaX, y + deltaY, z + deltaZ).
  • Etc.

In processing vectors are stored with PVector, and the good thing about this class is that there are plenty of useful methods that you can find here: https://processing.org/reference/PVector.html. It can make your ife super easy :slight_smile:

For your application you want to use vectors to keep track of the position of each seeds. Here an exemple of how to generate the pattern in 2D. I’ll let you transpose it for your code :wink:

void setup() {
  size(500, 500);
  background(20);
  
  fill(200);
  noStroke();
  
  final int nbOfSeeds = 400;        // Number of ellipses to draw
  final float r = 0.6180;           // The golden ratio -> used to get the seds spread uniformly
  final float rotStep = r * TWO_PI; // The amount of turn between each seeds
  
  translate(width / 2, height / 2); // Everything will be drawn around the (0, 0) point so this line is used to translate everything to the middle of the screen
  for (int i = 0; i < nbOfSeeds; i++) { // We loop for every seeds we want to draw
    PVector tempVec = new PVector(1, 0); // We create a unit vector pointing right
    tempVec.mult(4*pow(i, r)).rotate(i*rotStep); // We first mutliply the vector by 4*pow(i, r) - this can be tweaked - and then, we turn the vector by the proper angle
    ellipse(tempVec.x, tempVec.y, 5, 5); // We draw an ellipse where the vector we created is pointing
  }  
}
2 Likes

Thank you for that push in the right direction. I have managed to implement your code into my 3D rotation sketch:

Just a few more bits to add on:

  • I need to limit the rotation of the “seeds” to only when the mouse is within the “seed head”

  • Add the dilation of seeds when mouse is close

  • Add some colour

Thanks again for the encouragement!

1 Like

I am almost there. There is one more piece of functionality that would complete this challenge.

Here is where I am up to:

In the example www.yes.studio if the mouse goes off the seedhead it is as if the mouse slowly takes the shortest route back to the centre of the seedhead. In my sketch I the rotation and dilation does behave as if the mouse is at the centre of the seedhead but there isn’t the nice slow return. It happens in one frame.

How would be best go about achieving this? Can you animate the mouse back to the centre of the seedhead or is it better to try and achieve the movement by cycling through the rotations?

Another thing to note - when the mouse is back within the seedhead it appears as if the mouse comes from the centre to the edge where the mouse is re-entering the seedhead.

1 Like

Looking great so far!

If you look carefully at the example (btw, your link does not always shows the right example and you may need to refresh several time to get the right one), you may notice that the effect is happening no matter where the mouse is except the effect is a bit slower when the mouse leave the circle.

To achieve that you would need to differentiate the current position from a target position. In your case the target position is the current mouse (x, y) position (or the center of the figure if the mouse is outside) and the current position is the x and y values that produces the visual effect you see.

Now you have that the trick is to slowly blend between the current position and the targeted one. It can be done plenty of ways:

  • You move always at the same speed and so you add that speed to the current position until you’ve reach the target one
  • You move by half the distance each time so you move faster at the beginning than at the end
  • You always take the same time to reach the target so it goes faster the further away the target is from the current position
  • You use bezier curves to set a profil that tells how to make the transition from the current position to the target (the same idea as in video editing if you have done some or animation in css)
1 Like

Thanks!

I’m not sure I understood your last post.

I had a go at making the return rotation. This involved splitting the window into quadrants and making separate if statements for each quadrant. i.e if in top left X and Y rotations should increase or decrease as appropriate.

It seems like there must be a simpler way. Like just tracing back from the point where the mouse left the seedhead to the middle of the seed head.

Here is how it looks now:

This method also doesn’t animate for re-entry to the seedhead.

Thanks again for your support

1 Like

The idea is to do something like this:

PVector currentPos;

void setup() {
  size(600, 600);
  
  noStroke();
  fill(200);
  
  currentPos = new PVector(width /2, height / 2);
}


void draw() {
  background(20);  
  moveToward(mouseX, mouseY);  
  ellipse(currentPos.x, currentPos.y, 20, 20);
}

void moveToward(int x, int y) {
  PVector tempVec = new PVector(x - currentPos.x, y - currentPos.y);
  tempVec.mult(0.2);  
  currentPos.add(tempVec);
}

In this example, every frame, the ellipse will travel 20% of the distance to the cursor.

Now imagine that instead of using the mouse coordinate to control your shape, you are using the ellipse coordinate. Then you’ll get a smooth effect.

2 Likes

Hey thanks so much for this, I’m going to give it a go this weekend. I’ll let you know how it goes and post the results.

1 Like