如何计算垂直于矢量的圆?

时间:2016-04-21 05:54:22

标签: javascript math 3d trigonometry

在XYZ空间中给定两个点(P1和P2),创建一个具有给定半径的管。为了做到这一点,我需要计算围绕两个点中的每个点的圆的点,使得圆垂直于P1→P2(并且彼此平行)。一个圆圈的dx / dy / dz可用于制作其他圆圈。代码的形式如下:

function circle(radius, segments, P1, P2) {
  // 3D circle around the origin, perpendicular to P1P2
  var circle = [];  
  var Q = [P2[0] - P1[0], P2[1] - P1[1], P2[2] - P1[2]];
  for (var i = 0; i < segments; i++) {
    var theta = 2*Math.PI*segment/i;
    var dx = mysteryFunctionX(Q, theta, radius);
    var dy = mysteryFunctionY(Q, theta, radius);
    var dz = mysteryFunctionZ(Q, theta, radius);
    circle.push([dx, dy, dz]);
    }
  return circle;
  }

每个神秘功能需要什么计算?

3 个答案:

答案 0 :(得分:1)

正如Ed的帖子中的链接所指出的那样,如果你的向量u和v垂直于你的轴Q,并且彼此相对,并且长度为1,那么每个点

 P + cos(theta)*u + sin(theta)*v
当θ在0和2pi之间时,

是圆心上的点,中心P在垂直于Q的平面上。

考虑到Q,找出你和v应该是什么,这可能有点棘手。一种方法是使用Householder反射器。很容易找到一个将(1,0,0)映射到Q的倍数的反射器。如果我们将这个反射器应用于(0,1,0)和(0,0,1),我们将得到向量u和v按上述要求。代数有点乏味但下面的C代码可以完成这项任务:

static  void    make_basis( const double* Q, double* u, double* v)
{
double  L = hypot( Q[0], hypot( Q[1], Q[2])); // length of Q
double  sigma = (Q[0]>0.0) ? L : -L;    // copysign( l, Q[0]) if you have it
double  h = Q[0] + sigma;   // first component of householder vector
double  beta = -1.0/(sigma*h);  // householder scale
    // apply to (0,1,0)'
double  f = beta*Q[1];
    u[0] = f*h;
    u[1] = 1.0+f*Q[1];
    u[2] = f*Q[2];
    // apply to (0,0,1)'
double  g = beta*Q[2];
    v[0] = g*h;
    v[1] = g*Q[1];
    v[2] = 1.0+g*Q[2];
}

答案 1 :(得分:0)

我发现这个link您可能会发现这个video

如果您正在处理角度,那么看起来您正在进行参数化路线。

答案 2 :(得分:0)

谢谢你转发Ed和dmuir - 这有帮助。这是我制作的代码似乎有效:

function addTube(radius, segments, P1, P2) {
  // Q = P1→P2 moved to origin
  var Qx = P2[0] - P1[0];
  var Qy = P2[1] - P1[1];
  var Qz = P2[2] - P1[2];
  // Create vectors U and V that are (1) mutually perpendicular and (2) perpendicular to Q
  if (Qx != 0) {  // create a perpendicular vector on the XY plane
    // there are an infinite number of potential vectors; arbitrarily select y = 1
    var Ux = -Qy/Qx;
    var Uy = 1;
    var Uz = 0;
    // to prove U is perpendicular:
    // (Qx, Qy, Qz)·(Ux, Uy, Uz) = Qx·Ux + Qy·Uy + Qz·Uz = Qx·-Qy/Qx + Qy·1 + Qz·0 = -Qy + Qy + 0 = 0
    }
  else if (Qy != 0) {  // create a perpendicular vector on the YZ plane
    var Ux = 0;
    var Uy = -Qz/Qy;
    var Uz = 1;
    }
  else {  // assume Qz != 0; create a perpendicular vector on the XZ plane
    var Ux = 1;
    var Uy = 0;
    var Uz = -Qx/Qz;
    }
  // The cross product of two vectors is perpendicular to both, so to find V:
  // (Vx, Vy, Vz) = (Qx, Qy, Qz)×(Ux, Uy, Uz) = (Qy×Uz - Qz×Uy, Qz×Ux - Qx×Uz, Qx×Uy - Qy×Ux)
  var Vx = Qy*Uz - Qz*Uy;
  var Vy = Qz*Ux - Qx*Uz;
  var Vz = Qx*Uy - Qy*Ux;
  // normalize U and V:
  var Ulength = Math.sqrt(Math.pow(Ux, 2) + Math.pow(Uy, 2) + Math.pow(Uz, 2));
  var Vlength = Math.sqrt(Math.pow(Vx, 2) + Math.pow(Vy, 2) + Math.pow(Vz, 2));
  Ux /= Ulength;
  Uy /= Ulength;
  Uz /= Ulength;
  Vx /= Vlength;
  Vy /= Vlength;
  Vz /= Vlength;
  for (var i = 0; i < segments; i++) {
    var θ = 2*Math.PI*i/segments;  // theta
    var dx = radius*(Math.cos(θ)*Ux + Math.sin(θ)*Vx);
    var dy = radius*(Math.cos(θ)*Uy + Math.sin(θ)*Vy);
    var dz = radius*(Math.cos(θ)*Uz + Math.sin(θ)*Vz);
    drawLine(P1[0] + dx, P1[1] + dy, P1[2] + dz,  // point on circle around P1
             P2[0] + dx, P2[1] + dy, P2[2] + dz)  // point on circle around P2 
    }
  }

我确信有很多方法可以缩短代码并提高效率。我在http://mvjantzen.com/tools/webgl/cylinder.html

使用Three.JS在线创建了一个简短的视觉演示