P5.js camera movement control with keys, how to assign limits to it?

Hello people,
I’m trying to set up a fully functional camera in my code that responds to movements assigned by the computer keys, that I’ve accomplished. Now I’m trying to assign a limit to those movements, for example: in the code there are two torus objects, I want the camera movement (pan) to stop moving along the X axis when the torus touches the edge of the canvas.
I have tried setting up if statements calling out the p5.camera orientation properties (eyeX, eyeY, eyeZ, centerX, centerY, centerZ, upX, upY, upZ) but have not succeeded.
What is your opinion on how to get this working ?
Thannks,

// Left Arrow ====> Rotate camera left 
// Right Arrow ====> Rotate camera right 
// Up Arrow ====> Move the camera upward 
// Down Arrow ====> Move the camera downward
// "+ - key ====> Move forward 
// "-" - Key ====> Move backward 

//50x50 red ellipse moves left, right, up and down with arrow presses and is limited by the canvas size 
//keyIsDown example 0

// set up rotations speed values
let x = 0.01;
let y = 0.01;
let zMove = 0.8;

let eyeX;
let eyeY; 
let eyeZ;
let centerX;
let centerY; 
let centerZ;
let upX;
let upY;
let upZ;

let cam;

function setup() {
  
  createCanvas(300, 300, WEBGL);
  perspective(PI/3.0, width/height, ((height/2.0) / tan(PI * (60.0/360.0))) / 100, ((height/2.0) / tan(PI * (60.0/360.0))) * 100);
  perspective(PI/3.0, width/height, ((height/2.0) / tan(PI * (60.0/360.0))) / 10, ((height/2.0) / tan(PI * (60.0/360.0))) * 10);
  cam = createCamera();

  cam.setPosition(0, 0, (height/2.0) / tan(PI*30.0 / 180.0)); // (eyeX, eyeY, eyeZ, centerX, centerY, centerZ, upX, upY, upZ);
  
  cam.pan(0);
  cam.tilt(0);
  cam.move(0, 0, 0);
  fill(255, 0, 0);
  
}

function draw() {
  
  if (keyIsDown(LEFT_ARROW)) {
    x = (0.01) * 1.0;// this regulates the speed of the movement
  }

  else if (keyIsDown(RIGHT_ARROW)) {
    x = (0.01) * -1.0;
  }
  
  else if (keyIsDown(UP_ARROW)) {
    y = (0.01) * 1.0;
  }

  else if (keyIsDown(DOWN_ARROW)) {
    y = (0.01) * -1.0;
  }
  
  else if (keyIsDown(107) || keyIsDown(187)) { // +
    zMove = (0.8) * -1.0;
  }

  else if (keyIsDown(109) || keyIsDown(189)) { // -
    zMove = (0.8) * 1.0;
  }
  
  else {
    x = 0.0;
    y = 0.0;
    zMove = 0.0;
  }
  
  //This is what I tried to do but didn't work
  //if (eyeX, centerX, upX == 0){
  //  x = 0.0;
  //}
  
  // if (eyeY, centerY, upY == 300){
  //  y = 0.0;
  //}
  
  //// if (eyeZ, centerZ, upZ == 100){
  ////  zMove = 0.0;
  ////}
  
  
  
  cam.pan(x);
  cam.tilt(y);
  cam.move(0, 0, zMove);
  
  
  background(200);
  clear();
 
  push();
  translate (-30,0,0);
  torus(30,15);
  pop();
  push();
  translate (95, 0,0);
  torus(30,15);
  pop();
}

Hi,

Welcome to the forum! :slight_smile:

Please make sure to correctly format your code by using the </> button when editing a message on the forum or using backticks around your code like this : ``` code ``` → code

When first running your code, I get this warning in the p5 web editor :

pop() was called without matching push()

You can read the documentation of the read() function here.

The push() function saves the current drawing style settings and transformations, while pop() restores these settings. Note that these functions are always used together.

Hello joseph,

Thanks for observing that I was missing the push() function and also for letting me know how to properly include my code.
I’m wondering if you have any feedback on how to assign limitations to the camera movements along the axis ?

1 Like

Hi again @camilabianchi ,

You are welcome! :wink:

When you say :

Can you try to formulate this sentence in more logic terms?
Can you see which programming structures you are going to need to achieve that?

I ask you this because you can think of the computer as being a dumb machine : it doesn’t know what a limitation is and “along the axis” means. Instead the computer sees numbers and values.

Try to think like a computer :yum:

I see where you come from.
I am new to this language so it’s a process to formulate my ideas like that.
What I’m trying to achieve is to assign the rotation speed, wether it is with the pan or tilt methods, to be zero when in the camera field of vision the object torus touches the edge of the canvas.
I’m also aware that what I am moving is the camera not the torus object.
For example in my code there is a commented part where I tried to call the camera object properties eyeX, centerX and upX in an if statement that says when the camera location within the X axis equals to zero then the motion speed equals to zero.
However it did not work, I am not sure why.

Yes you are right, it takes time to get the proper logic! :wink:

In your code you are doing that :

if (eyeX, centerX, upX == 0){
  x = 0.0;
}

But you need to know that JavaScript is a programming language with strange edge cases and syntax tricks that are sometimes hard to notice and debug.

This is not exactly how we write an if condition, in your case it’s doing this :

https://stackoverflow.com/questions/5347995/why-does-javascript-accept-commas-in-if-statements

Quoting :

The comma operator chains multiple expressions together, and the result of the operation is the value of the last operand. The only real use for it is when you need multiple side effects to occur, such as assignment or function calls.

So it means that the if statement with comma separated statements only see the last value so it’s only checking upX == 0

I suppose that you also want to check if eyeX and centerX are also equal to zero.

You could be tempted to write :

if (eyeX == centerX == upX == 0) {
  // block
}

Which would work but in this special case :yum:

Check this (evaluated in the JavaScript console, press F12 to get it) :

0 == 0 == 0 // -> false

0 == 0 == 0 == 0 // -> true

This is because it’s because there’s operator associativity (left to right) and it’s evaluating in this order :

((0 == 0) == 0) // -> (true == 0) -> false

(((0 == 0) == 0) == 0) // -> ((true == 0) == 0) -> (false == 0) -> true 

In the last example, you might wonder why false == 0 ? It’s because JavaScript again! :grinning:

Anyway you can see that it gets quite messy.

I don’t know what you want to check but you can write it like this using the Boolean operator AND (&&) :

if (eyeX == 0 && centerX == 0 && upX == 0){
  x = 0.0;
}

But this condition is not limiting the movement of your camera because it’s only executed when both three variables are equal to 0 which never happens…

3 Likes

Oh I see !
Thanks for pointing that out, I understand it now.
I did try to refer to each of them separately though and didn’t get any functioning result…

if (eyeX == 0){
 x = 0.0;
}
if (centerX == 0){
 x = 0.0;
}
if (upX == 0){
 x = 0.0;
}

I’m starting to realize that I might be trying to use the wrong method to control the camera position.
I did try to use the setPosition function and got nothing from it. Maybe the perspective function will be the one.

Yes, with the above conditions you are going to reset the x variable that is driving the pan of your camera but setting it to reset the camera pan.

Also note the the eyeX and eyeY variables are not defined in your code so by default in JavaScript they are undefined :

console.log(eyeX, eyeY); // -> undefined undefined

So anyway you are in fact checking undefined == 0 which evaluate to false.

So you have two solutions :

  • Either limit the pan and tilt values with predefined values (for example 45° angle…) (easy)

  • You need to mathematically determine if the torus is visible in the field of view of the camera, which implies doing some fancy math with matrices and culling :

https://stackoverflow.com/questions/20138879/determine-if-3d-coordinate-is-inside-frustum

But the last solution might be too complicated for you right now :yum: