2

I am working on a project where I need to (as a 2d point) walk around a 3d sphere. I am having trouble figuring out how to achieve this without polar distortion. Basically I want to have Move Forward, Backward, Left, and Right, as well as Turn Left, and Turn Right. I've been trying to get this working with spherical coordinates, but my functions seem to be incorrect. What can I do to get this working? (I am making this in JavaScript, using the p5.js library)

Currently, I'm trying to map an x, and y variable to spherical space's phi, and theta respectively. It doesn't seem to be working, though, and I'm not sure whether, correctly implemented, the point would move in Great Circles.

I'm also using an angle variable, to move x, and y by (cos(A), sin(A)), but I'm not sure if this is working either.

I think what I need to do is related to Great Circle Navigation, but I don't understand the mathematics behind it.

Link to my current version: https://editor.p5js.org/hpestock/sketches/FXtn82-0k

Current code looks something like

var X,Y,Z;
X=0;
Y=0;
Z=0;

var A=0;

var scaler = 100;

var MOVE_FORWARD = true;
var MOVE_BACKWARD= false;
var MOVE_LEFT    = false;
var MOVE_RIGHT   = false;
var TURN_LEFT    = false;
var TURN_RIGHT   = false;

//var i=0;
var x = 0;
var y = 0;

function setup() {
  createCanvas(400, 400, WEBGL);
  x= 0;
  y= 0;
  A= 0;
  background(220);
}

function keyPressed(){
  if(key == "w"){
    MOVE_FORWARD = true;
  }else if(key == "ArrowLeft"){
    TURN_LEFT = true;
  }else if(key == "ArrowRight"){
    TURN_RIGHT = true;
  }
}

function keyReleased(){
  if(key == "w"){
    MOVE_FORWARD = false;
  }else if(key == "ArrowLeft"){
    TURN_LEFT = false;       
  }else if(key == "ArrowRight"){
    TURN_RIGHT = false;
  }
}

function draw() {
  
  if(MOVE_FORWARD){
    x+=0.005*cos(A);
    y+=0.005*sin(A);
  }
  if(TURN_LEFT){
    A+=PI/64;
  }
  if(TURN_RIGHT){
    A-=PI/64;
  }
  
  var xyz = Sph(1,y,x);
  X=xyz[0];
  Y=xyz[1];
  Z=xyz[2];
  background(220);
  sphere(scaler);
  push();
  translate(X*scaler,Y*scaler,Z*scaler);
  sphere(5);
  pop();
  
  /*i+=PI/32;
  if(i>2*PI){
     i=0;
     }*/
}

function Move(a,d){
  //
}

function Sph(p,t,h){
  //p = radius
  //t (theta) = 2d rotation
  //h (phi) = 3d roation
  return ([p*cos(h)*cos(t),p*cos(h)*sin(t),p*sin(h)]);
  
  //x=ρsinφcosθ,y=ρsinφsinθ, and z=ρcosφ
}
Joe Dimagio
  • 55
  • 1
  • 1
  • 6

2 Answers2

1

I'm not sure about the math for doing this in polar coordinates but it can be done simply by tracking position and orientation as a pair of 3d cartesian vectors and using Rodrigues' rotation formula to move forward (by rotating the position) and turn (by rotating the axis).

// position vector
let pos;
// axis of rotation
let axis;

let scaler = 150;

function setup() {
  createCanvas(400, 400, WEBGL);
  background(220);
  pos = createVector(1, 0, 0);
  axis = createVector(0, 1, 0);
  fill('red');
  noStroke();
}

function draw() {
  if (keyIsDown(87) || keyIsDown(UP_ARROW)) {
    // move "forward"
    rotateVector(pos, axis, PI / 64);
  }
  if (keyIsDown(LEFT_ARROW)) {
    // turn left
    rotateVector(axis, pos, -PI / 64);
  }
  if (keyIsDown(RIGHT_ARROW)) {
    // turn right
    rotateVector(axis, pos, PI / 64);
  }

  ambientLight(100);
  directionalLight(200, 200, 200, 1, 1, -1);

  push();
  translate(pos.x * scaler, pos.y * scaler, pos.z * scaler);
  sphere(5);
  pop();
}

function rotateVector(v, axis, angle) {
  // v * cos(θ) + (axis ✕ v) * sin + axis * (axis · v) * (1 - cos)
  return v.mult(cos(angle))
    .add(axis.copy().cross(v).mult(sin(angle)))
    .add(axis.copy().mult(axis.dot(v) * (1 - cos(angle))));
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.4.0/p5.js"></script>
Paul Wheeler
  • 18,988
  • 3
  • 28
  • 41
  • How would you go about moving left or right to your position? – Joe Dimagio Nov 08 '21 at 21:13
  • The cross product of two vectors is a vector that is perpendicular to both. Therefore you can use the cross product of the axis and position vectors as a secondary axis to rotate around for strafing left and right. – Paul Wheeler Nov 08 '21 at 21:38
  • Well what if I was planning to cast a bunch of rays in a certain angle? – Joe Dimagio Nov 08 '21 at 23:21
  • Also, there seems to be something wrong with the movement code. When I turn a lot, the vector seems to get messed up. Vector {p5: p5, x: -1.6917920338414268e-22, y: -1.5182659938399651e-21, z: 1.1775934845047779e-21, toString: ƒ p5VectorToString()…} – Joe Dimagio Nov 10 '21 at 00:12
1

I do not know javascript, but you could implement the following functions (which I implemented in python and hopefully you can read off from them the mathematical/geometric logic behind them) that allow you to select a direction of motion on the sphere, to move along a great circle along a step of given angle ds, and to change directions of motion. I am assuming the motion is on the unit sphere (of radius 1). If not, you need to scale the code to the appropriate radius.

import numpy as np
import math

def direct(r, a):
'''
given position-vector r on the unit sphere and initial angle a, 
measured from the meridian, i.e. direction north being a = 0,
the result is the unit vector t pointing in that direction 
'''
  e_z = np.array([0,0,1]) 
  u = np.cross(e_z, r)
  u = u / math.sqrt(u.dot(u))
  v = np.cross(r, u)
  t = math.cos(a) * v + math.sin(a) * u
  return r, t

def move(r, t, ds):
'''
given unit position-vector r and unit direction vector t on the unit sphere, 
make a step of arclength ds radians from point r in the direction of t along
the great circle that passes through r and tangent to t. The result is the
new position r_ and the new direction vector t_ still tangent to the same
great circle.
'''
  co = math.cos(ds)
  cs = math.sin(ds) 
  r_ =  co * r + si * t
  t_ = -si * r + cs * t
  return t_, t_

def redirect(r, t, da):

'''
given unit position-vector r and unit direction vector t on the unit sphere, 
rotate the vector t at an angle da. 
The result is the new direction vector t_

when da > 0 redirect right
when da < 0 redirect left
'''
  rot_axis = np.cross(r, t)
  t_ = math.cos(da) * t - math.sin(da) * rot_axis
  return r, t_
Futurologist
  • 1,874
  • 2
  • 7
  • 9
  • I don't understand vector math in the slightest, and I'm very bad at using vectors in general, is there any way you could write this with an x, y, z format? – Joe Dimagio Nov 15 '21 at 00:36