3D Rotations: Rotating around the screen axes

I wanted a sphere to rotate in a way that the point closest to the viewer always rotates up when the cursor is moved upwards and always left and right when the cursor is moved left and right.
I had problems because after the transformation matrix caused the rotation axis to no longer correspond to the screen axis when using e.g. rotateY();

In this version of the sketch I managed to make it work along one axis, but I feel like I need some more complicated trigonometry to subtract out the rotation along both axes.

function setup() {
	createCanvas(windowWidth, windowHeight, WEBGL);
	smooth();
	noFill();
}

function windowResized() {
	resizeCanvas(windowWidth, windowHeight, WEBGL);
}


function draw() {
	background(255);

	let x = map(mouseX, 0, width, -PI ,PI);
	let y = map(mouseY, 0, height, -PI, PI);

	rotateY(x);
	rotate(-y, [cos(x), 0, sin(x)]);

	ellipse(0, 0, height / 2, height / 2, 50);
	rotateX(HALF_PI);
	ellipse(0, 0, height / 2, height / 2, 50);
	rotateY(HALF_PI);
	ellipse(0, 0, height / 2, height / 2, 50);
}
3 Likes

do you mean this?

void setup() {
  size(1200, 800, P3D);
  smooth();
  noFill();
}

void draw() {
  background(255);

  translate(width/2, height/2); 
  float x = map(mouseX, 0, width, -PI, PI);
  float y = map(mouseY, 0, height, -PI, PI);

  rotateY(x);
  rotateX(-y);//[cos(x), 0, sin(x)]);

  ellipse(0, 0, 50, 50);

  pushMatrix();
  translate(40, 0);
  rotateX(HALF_PI);
  ellipse( 0, 0, 50, 50);
  popMatrix(); 

  pushMatrix();
  translate(0, 40);
  rotateY(HALF_PI);
  ellipse( 0, 0, 50, 50);
  popMatrix();
}
1 Like

This does not achieve the effect I tried to describe.
The three ellipses are supposed to form a sphere.
Now, on this sphere, the imagine the point that is closest to the viewer, which is always in the center of the canvas.
This point should always rotate in the direction the mouse is moving in.


I already implemented this for one axis, but cant make it work in both x and y direction.
Would appreciate it a lot if someone took another look at this, because i feel like this is doable but you need some trigonometry formula i can’t quite figure out.

1 Like

Chrisir has a good example for you to work with.
A little tweaking and you can achieve what you want; I did.

Hint:

:slight_smile:

2nd example…

void setup() {
  size(1200, 800, P3D);
  smooth();
  noFill();
}

void draw() {
  background(255);
  lights();

  translate(width/2, height/2);

  // these are your ANGLES: 
  float x = map(mouseX, 0, width, -PI, PI);
  float y = map(mouseY, 0, height, -PI, PI);

  //x=0;
  //y=0;

  text(nf(x, 1, 2)+","+nf(y, 1, 2), 
    50, -50);

  noFill(); 
  stroke(0); 

  rotateY(x);
  rotateX(-y);//[cos(x), 0, sin(x)]);

  ellipse(0, 0, 50, 50);

  pushMatrix();
  translate(0, 0);
  rotateX(HALF_PI);
  ellipse( 0, 0, 50, 50);
  popMatrix(); 

  pushMatrix();
  translate(0, 0);
  rotateY(HALF_PI);
  ellipse( 0, 0, 50, 50);
  popMatrix();

  // red sphere 
  pushMatrix();
  translate(0, 0, 25);
  rotateY(HALF_PI);
  noStroke();
  fill(255, 0, 0); 
  sphere( 5 );
  popMatrix();
}
2 Likes

All I did was add color to @Chrisir example:

void setup() {
size(1200, 800, P3D);
smooth();
noFill();
strokeWeight(3);
}

void draw() {
background(255);

translate(width/2, height/2);
rotateY(-TAU/32);

float x = map(mouseX, 0, width, -PI, PI);
float y = map(mouseY, 0, height, -PI, PI);

rotateY(x);
rotateX(-y);//[cos(x), 0, sin(x)]);

stroke(0, 255, 0);
ellipse(0, 0, 150, 150);

pushMatrix();
translate(40, 0);
rotateX(HALF_PI);
rotateY(x);
stroke(255, 0, 0);
ellipse( 0, 0, 150, 150);
popMatrix();

pushMatrix();
translate(0, 40);
rotateY(HALF_PI);
rotateX(-y);
stroke(0, 0, 255);
ellipse( 0, 0, 150, 150);
popMatrix();
}

When I first worked with translations and rotations I created labeled axes to understand these.

I went a little overboard with this example but enjoyed the ride:

This worked on PC and my Android (I moved the scrollbars to bottom later):

And later this:

Scrollbars are in Processing examples; mouseX and mouseY are often enough.

:slight_smile:

3 Likes

The “red dot” might help to clarify what I mean.
The point on the sphere that moves in the way the mouse does, is not supposed to be a fixed point on the sphere. Rather, in every rotated form of the sphere, the point i want to move is the point of the sphere in the exact center of the sketch.
This is my problem, because, after a rotation of e.g. 90 degrees around the y axis, moving the mouse upwards doesn’t move the point in the middle upwards, but instead the red point which was fixed in the beginning.
To accomplish what I what, I think, you have to calculate the y axis, which represents the y axis of my screen and not the y axis of the currently rotated coordinate system. Hence why I started messing around with trigonometry as above. Though there an upward movement behaves as I want with a rotation along the y axis already applied, it does not the other way around.

I don’t understand

please elaborate

Chrisir

Are you referring to my second example when you say red dot?

Yes, referring to your second example.
In it, after a 90 degree rotation around y, the red dot travels up the side of the sphere when you move the mouse upward.
What I want is, with the same setup, that the red dot stays in its place and the point of the sphere in the center of the sketch, moves upward. This is no longer achievable by rotateX(), because a rotation matrix has already been applied.
I should note that I actually switched to p5js because it allows me to rotate around a specified axis, instead of just x, y and z.

rotate(-y, [cos(x), 0, sin(x)]);

This is not possible in Java Processing afaik.

I can’t help you with that

Take a look here:

:slight_smile:

Example of rotate(float angle, float v0, float v1, float v2):

float angle;

void setup()
{
size(500, 500, P3D);
}

void draw()
{
background(255);
translate(width/2, height/2);

strokeWeight(3);
stroke(128);
line(-100, -100, 0, 100, 100, 0);

rotate(angle, 100, 100, 0);

pushMatrix();
shape();
popMatrix();

angle += TAU/1000;
}

void shape()
{
stroke(128);
strokeWeight(3);
line(0, 0, 0, 100  *cos(TAU/8), -100*  cos(TAU/8), 0);
strokeWeight(10);
point(100  *cos(TAU/8), -100*  cos(TAU/8), 0);
}

Example code I provided rotating around line\axes;

Rotating around line\axes with ellipses:

:slight_smile:

Thank you for your effort, but that only gets me to the point of where I quit in p5.
I tried my best to understand some of the maths in the medium.com article, but I still can’t figure out how to calculate the “screen axis” that is needed to turn the rotated sphere as intended.

I ported your code for Java Processing:

void setup() 
  {
  size(500, 500, P3D);
  smooth();
  noFill();
  // ortho();
  }

void draw() 
  {
  background(255);

  float x = map(mouseX, 0, width, -PI ,PI);
  float y = map(mouseY, 0, height, -PI, PI);

  translate(width/2, height/2);

  rotateY(x);
  rotateX(-y);
  
  box(2*40, 2*10, 2*90);

  ellipse(0, 0, height/2, height/2);
  rotateX(HALF_PI);
  ellipse(0, 0, height/2, height/2);
  rotateY(HALF_PI);
  ellipse(0, 0, height/2, height/2);
  }

Is that what you are trying to achieve?

Change the ranges (smaller) for the mapping to get a better sense of rotations.
Using ortho() sometimes helps; default is perspective().

:slight_smile:

References:


actually i think there is view problem,
while for a full 3D view the mouse interpretation like

mouseX, 0, width, -PI ,PI 

is optimal, it looks bad.

a much more natural view i feel with

mouseX, 0, width, -PI/2, PI/2

anyhow the whole concept with this mouse requires the thinking
that NULL is when the mouse points at the CENTER of the canvas,
( a other approach would be to use delta values…)

to repair the processing y axis points down,
can use a (-1)*…

as we play here 3D view, what is that without a ZOOM
use mouseWheel.

add i try to separate the

  • view code
  • axis code
  • user circles code
float x=0,y=0,zmag = 1;
float Y = -1;  // to invert processing y axis

void setup() {
  size(500, 500, P3D);
  noSmooth();
  noFill();
  //ortho();
  strokeWeight(2);
  println("use: mouse X Y, wheel for zoom");
}

void draw() {
  background(200, 200, 0);
  my_Camera();
  my_Drawing();
}

void my_Drawing() {
  axis();
  spheres();
}

void axis() {
  stroke(200, 0, 0);
  line(0, 0, 0, height/4, 0, 0);
  sphereAt(height/4, 0, 0, 6);
  stroke(0, 200, 0);
  line(0, 0, 0, 0, Y*height/4, 0);
  sphereAt(0, Y*height/4, 0, 6);
  stroke(0, 0, 200);
  line(0, 0, 0, 0,0,height/4);
  sphereAt(0,0,height/4, 6);
}

void spheres() {
  push();
  stroke(0, 200, 0);
  ellipse(0, 0, height/2, height/2);
  rotateX(HALF_PI);
  stroke(200, 0, 0);
  ellipse(0, 0, height/2, height/2);
  rotateY(HALF_PI);
  stroke(0, 0, 200);
  ellipse(0, 0, height/2, height/2);
  pop();
}

void my_Camera() {                                    // limit from +- PI to +- PI/2 like more natural view??
  x = map(mouseX, 0, width, -PI/2, PI/2);
  y = Y*map(mouseY, 0, height, -PI/2, PI/2);   // invert processing Y
  translate(width/2, height/2);
  scale(zmag);
  rotateY(x);
  rotateX(y);  
  // here the view is axis correct if mouse is middle/center of canvas!!!
  // right hand rule: thumb X RIGHT (RED), point finger Y UP(GREEN), middle finger Z FRONT (BLUE)( points to you )
}

void sphereAt(float x, float y, float z, float diam ) {
  push();
  translate(x, y, z);
  sphere(diam);
  pop();
}

void mouseWheel(MouseEvent event) {
  float e = event.getCount(); //println(e);
  zmag += e*0.01;
}

p5.js version :slight_smile:

does that operation feel better?

I very much appreciate all these attempts to help me, but none of these examples behave the way I tried to describe.

The rotation commands to achieve this cannot be as simple as rotateY(x) and rotateX(y), because the second rotation is executed in an already rotated system, and therefore do not rotate around the axes “of my monitor” that never change.

In your example, compare the behaviour of

  rotateY(x);
  rotateX(y);

to

  rotateY(x);
  rotate(y, cos(x), 0, sin(x));

my problem is achieving the effect rotate(y, cos(x), 0, sin(x)) has, but for both axes.

3 Likes

Keep working at it!

rts_00

I only used rotateY() and rotateX() to achieve this:

  • order matters; try changing the order of rotations and see what happens!
  • change the ranges (smaller) for the mapping to get a better sense of rotations. I already suggested this!
  • I added grids, fills, etc. to better visualize my work and the rotations; there were some interesting optical illusions otherwise.
  • the row of sphere() shapes were added along x-axis
  • point() is easy to plot along x-axis
  • switch between ortho() and perspective(); this can sometimes help in the development

I am not posting my “solution” since this is achievable and can be done by you.

I achieved the same results with:

Or I do not clearly understand what you are trying to achieve…

Please post what you have done so far.

Is this an academic assignment?

:slight_smile:

2 Likes

What is to be seen in your gif is the behaviour of my original post, although with the axes flipped.
Thank you for adding grid and lines, they might help describe my problem:

This is exactly what I meant:
yes

This is what I am trying to get rid of:
no

As you can see, the sphere rotates around the yellow x-axis.
What it should rotate around is the not yet drawn stationary “x-axis” that lies on the grid.
Just as the y-rotation in your sketch always happens around a not drawn y-axis on the grid.

This is not an assignment and I am not trying to bait you into doing it for me :wink:
This is a random sketch idea I had (further problems here lol P5js: 3D Typography Problems/Bugs?) where something I thought would be trivial apparently turned out to be kind of tricky and I’m almost certain there’s some clever math involved in solving this.