2

我正在做一个项目,我需要(作为 2d 点)绕着 3d 球体走动。我无法弄清楚如何在没有极性失真的情况下实现这一目标。基本上我想要前进、后退、左转和右转,以及左转和右转。我一直在尝试使用球坐标进行此操作,但我的功能似乎不正确。我该怎么做才能让它工作?(我正在使用 p5.js 库在 JavaScript 中制作这个)

目前,我正在尝试将 x 和 y 变量分别映射到球形空间的 phi 和 theta。但是,它似乎不起作用,而且我不确定正确实施该点是否会在大圈中移动。

我还使用角度变量将 x 和 y 移动 (cos(A), sin(A)),但我不确定这是否也有效。

我认为我需要做的是与大圆导航有关,但我不了解它背后的数学。

链接到我当前的版本:https ://editor.p5js.org/hpestock/sketches/FXtn82-0k

当前代码看起来像

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φ
}
4

2 回答 2

1

我不知道 javascript,但您可以实现以下函数(我在 python 中实现,希望您可以从中读取它们背后的数学/几何逻辑),允许您选择球体上的运动方向,移动沿着给定角度 ds 的步长的大圆,并改变运动方向。我假设运动在单位球面上(半径为 1)。如果没有,您需要将代码缩放到适当的半径。

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_
于 2021-11-12T04:17:48.127 回答
1

我不确定在极坐标中执行此操作的数学运算,但可以简单地通过跟踪位置和方向作为一对 3d 笛卡尔向量并使用Rodrigues 的旋转公式向前移动(通过旋转位置)和转动(通过旋转轴)。

// 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>

于 2021-11-08T19:54:05.000 回答