如何围绕另一个旋转点?

How to rotate point around another one?

我在围绕另一个点旋转时遇到问题。 我不擅长三角学,所以请帮助我并纠正我的解决方案。

要围绕另一个点旋转一个点,我将点移动到坐标系的原点,这样我要旋转的点将位于坐标系 (0,0,0) 的原点,旋转点Z、Y、Z轴再往回移

示例:我需要围绕点 y(3,2,1) 旋转点 x(1,1,1),所以我从点 x​​ 减去点 y 的坐标 - x(1 - 3,1 - 2,1 - 1), rotate point x 围绕 x , y 和 z 轴,然后 return x`` 通过添加 y 坐标到正确的位置。它会工作吗? 对不起英语不好。

你的做法是正确的。它可以应用于不同的操作,如旋转和缩放。如果你仅限于一个几何函数的编程开发环境,其中心点仅以 (0,0,0) 为中心,那么你的步骤可以应用于执行相对于中心点的操作:

1) Apply a vector to the point to rotate that would move the center point to the origin by:
   a) Determine the vector offset that would move the center point (for example point 3,2,1) to the origin.
      In this case, it is vector <-3,-2,-1>.
   b) Then apply it to the point (in this case  1,1,1 => [1 - 3,1 - 2,1 - 1];
2) Apply the operation (in this case a rotation), and probably a transformation matrix;
3) Apply the reverse of the vector in "1a" to the point determined in step "b" above.

第一个答案的类似解决方案: Rotation of Point in 3D Space

理解旋转。

从二维开始。

二维坐标通过 2 个值 x 和 y 定义位置。 x 是沿 x 轴的距离,y 是沿 y 轴的距离。按照惯例,计算机图形通常具有从左到右定义的 x 轴和从上到下的 y 轴。

注意此答案中的代码是伪代码,不代表任何特定语言。

我们可以将一个轴表示为一个向量,比如x轴是x方向1个单位,向下0个单位,y轴是x方向0个单位,向下1个单位。

对于代码,我们将向量定义为(例如 x 轴 xAxis.x = 1yAxis.y = 0

轴有一个重要的特性,即它们总是 1 个单位长。见下文单位向量

因此,在定义了轴和一个点后,我们可以通过先沿 x 轴然后沿 y 轴移动来找到它的位置。

xAxis = { x : 1, y : 0 }; // define the x axis
yAxis = { x : 0, y : 1 }; // define the y axis
point = { x : 10, y : 10 };

// position the point first move along the x axis by distance x
pos.x = point.x * xAxis.x;
pos.y = point.x * xAxis.y;

// then move along the y axis by distance y
pos.x = pos.x + point.y * yAxis.x;
pos.y = pos.y + point.y * yAxis.y;

旋转

这似乎是定位点的漫长方法。但是,当您旋转坐标系时,您实际上是在旋转 x 轴和 y 轴。因此,要获得旋转坐标系中的坐标,您需要 2 个旋转轴。见下文来自角度的单位向量

x轴旋转旋转量,y轴与x轴成90度。

rotation = PI / 4; // using radians rotate clockwise 45 deg
// get the x axis at 45 deg
xAxis.x = cos(rotation);
xAxis.y = sin(rotation);

// get the y axis at 90 deg (PI / 2) from the x axis
yAxis.x = cos(rotation + (PI / 2));
yAxis.y = sin(rotation + (PI / 2));

我们现在可以在旋转坐标系中移动点

point = { x : 10, y : 10 };

// position the point first move along the x axis by distance x
pos.x = point.x * xAxis.x;
pos.y = point.x * xAxis.y;

// then move along the y axis by distance y
pos.x = pos.x + point.y * yAxis.x;
pos.y = pos.y + point.y * yAxis.y;

有一些捷径。 y 轴与 x 轴成 90 度(除非倾斜)以将向量旋转 90 度,我们交换取反 y

的分量
// get the x axis at 45 deg
xAxis.x = cos(rotation);
xAxis.y = sin(rotation);

// get y at 90 deg from x
yAxis.x = -xAxis.y;
yAxis.y = xAxis.x;

同样沿轴移动,每个分量相互独立,因此可以在一行中完成计算。

pos.x = point.x * xAxis.x + point.y * yAxis.x;
pos.y = point.x * xAxis.y + point.y * yAxis.y;

起源。

坐标系由原点和描述轴的单位向量定义。要围绕特定点旋转一个点,我们需要将旋转点作为原点。我们再移动坐标相对于新原点旋转

point = { x : 10, y : 10 };
origin = { x : 5 , y : 4 };

// move point relative to the origin

pos.x = point.x - origin.x;
pos.y = point.y - origin.y;

现在我们可以应用旋转

rotatedPos.x = pos.x * xAxis.x + pos.y * yAxis.x;
rotatedPos.y = pos.x * xAxis.y + pos.y * yAxis.y;

但是旋转后的点仍然是相对于原点的,我们需要将它相对于原点移回原点。

rotatedPos.x = rotatedPos.x + origin.x;
rotatedPos.y = rotatedPos.y + origin.y;

在计算机图形学中,我们通常会保留相对于其本地原点的相关坐标。它有一个我们称之为局部坐标系的坐标系,以[0,0]或3d中的[0,0,0]为旋转点。这意味着我们可以跳过相对于旋转点移动点的计算部分。

然后我们可以为每个轴在一行中进行旋转和定位

pos.x = point.x * xAxis.x + point.y * yAxis.x + origin.x;
pos.y = point.x * xAxis.y + point.y * yAxis.y + origin.y;

规模

我们想要缩放坐标也是很常见的,这可以通过改变代表每个轴的单位向量的长度来实现。例如,如果我们想沿 x 轴缩放坐标 2 倍,我们将 x 轴的长度设为 2 倍

point = {x : 5, y : 6}; // point in local coordinates.
xAxis = {x : 1, y : 0}; // normalised x axis
xAxis.x = xAxis.x * 2;  // scale x axis
xAxis.y = xAxis.y * 2;

// apply transformation.
pos.x = point.x * xAxis.x + point.y * yAxis.x + origin.x;
pos.y = point.x * xAxis.y + point.y * yAxis.y + origin.y;

3D

对于 3D,一切都大同小异,但多了一个轴和组件

xAxis = {x : 1, y : 0, z : 0}; // direction and scale of x axis
yAxis = {x : 0, y : 1, z : 0}; // direction and scale of y axis
zAxis = {x : 0, y : 0, z : 1}; // direction and scale of z axis
origin = {x : 0, y : 0, z : 0}; // position of origin.

因此要将点移动到上述 3D 坐标系中

point = {x : 5, y : 6, z : 4}; // point in local coordinates.
// move point.x distances along x axis
pos.x = point.x * xAxis.x
pos.y = point.x * xAxis.y
pos.z = point.x * xAxis.z

// move point.y distances along y axis
pos.x += point.y * yAxis.x
pos.y += point.y * yAxis.y
pos.z += point.y * yAxis.z

// move point.y distances along y axis
pos.x += point.z * zAxis.x
pos.y += point.z * zAxis.y
pos.z += point.z * zAxis.z

// then relative to the origin
pos.x += origin.x
pos.y += origin.y
pos.z += origin.z

或更紧凑

pos.x = point.x * xAxis.x + point.y * yAxis.x + point.z * zAxis.x + origin.x
pos.y = point.x * xAxis.y + point.y * yAxis.y + point.z * zAxis.y + origin.y
pos.z = point.x * xAxis.z + point.y * yAxis.z + point.z * zAxis.z + origin.z

矩阵

上面开始变得有点笨拙所以为了简化它我们可以转换上面的对象point, xAxis, yAxis, zAxis, origin 到一组数组(称为矩阵)

point = [5,6,4]; // vector as array  [x,y,z]
origin = [0,0,0]; // origin as array [x,y,z]
rotation = [1,0,0,0,1,0,0,0,1]; // 3 axis [x.x,x.y,x.z, y.x,y.y,y.z, z.x,z.y,z.z]

// rotation /*
    [x.x,x.y,x.z,  // x axis
     y.x,y.y,y.z,  // y axis
     z.x,z.y,z.z]  // z axis
*/

符号可以简化,在许多语言中,重载允许您直接以 shorthand 形式进行数学计算。

 pos = point * rotation + origin;    

3D 旋转

在 2D 中,我们通常只围绕一个虚轴(屏幕内外的 z 轴)旋转,在 3D 中,我们围绕 3 个轴(x、y 或 z 轴)之一旋转。

我们旋转的顺序也会影响最终旋转的位置。围绕 z 旋转 5 度然后围绕 y 旋转 10 度不同于围绕 y 旋转 10 度然后围绕 z 旋转 5 度。

由于每个轴本身就是一个可以旋转的向量,我们可以通过一个旋转矩阵来旋转每个轴。结果是一个组合了许多旋转的矩阵。

假设我们想绕 z 轴旋转 10 度,我们创建了 3 个旋转轴

ang = 10; // in deg
xAxisA = [cos(ang) ,sin(ang),0];  // 1 unit long
yAxisA = [-sin(ang),cos(ang),0];  // 1 unit long
zAxisA = [0        ,0       ,1];  // 1 unit long

或者作为旋转矩阵

A = rotationZ = [cos(ang), sin(ang), 0, -sin(ang), cos(ang), 0, 0, 0, 1];

然后我们想绕 y

旋转
xAxisB = [cos(ang) ,0 , sin(ang)];  // 1 unit long
yAxisB = [0,        1 , 0 ];        // 1 unit long
zAxisB = [-sin(ang),0, cos(ang)];   // 1 unit long

或者作为旋转矩阵

B = rotationY = [cos(ang), 0, sin(ang), 0, 1, 0, -sin(ang), 0, cos(ang)];

然后我们可以通过 y 旋转旋转 z 旋转中的每个轴。

// rotate each rotation axis by the second rotation axis.
xAxisAB.x = xAxisA.x * xAxisB.x + xAxisA.y * yAxisB.x + xAxisA.z * zAxisB.x;
xAxisAB.y = xAxisA.x * xAxisB.y + xAxisA.y * yAxisB.y + xAxisA.z * zAxisB.y;
xAxisAB.z = xAxisA.x * xAxisB.z + xAxisA.y * yAxisB.z + xAxisA.z * zAxisB.z;
yAxisAB.x = yAxisA.x * xAxisB.x + yAxisA.y * yAxisB.x + yAxisA.z * zAxisB.x;
yAxisAB.y = yAxisA.x * xAxisB.y + yAxisA.y * yAxisB.y + yAxisA.z * zAxisB.y;
yAxisAB.z = yAxisA.x * xAxisB.z + yAxisA.y * yAxisB.z + yAxisA.z * zAxisB.z;
zAxisAB.x = zAxisA.x * xAxisB.x + zAxisA.y * yAxisB.x + zAxisA.z * zAxisB.x;
zAxisAB.y = zAxisA.x * xAxisB.y + zAxisA.y * yAxisB.y + zAxisA.z * zAxisB.y;
zAxisAB.z = zAxisA.x * xAxisB.z + zAxisA.y * yAxisB.z + zAxisA.z * zAxisB.z;

或空手

rotationAB = rotationZ * rotationY;

AB = A * B;

生成的矩阵 AB 是 z 旋转和 y 旋转的组合旋转。你可以继续前进,围绕 x

旋转
// rotate about the x Axis
xAxisC = [1,        0 , 0 ];        // 1 unit long
yAxisC = [0, cos(ang) , sin(ang)];  // 1 unit long
zAxisC = [0, -sin(ang), cos(ang)];  // 1 unit long

C = rotationX =[1, 0, 0, 0, cos(ang), sin(ang), 0, -sin(ang), cos(ang)];

最终轮换为

ABC = A * B * C

但请记住顺序很重要。

A * B * C != C * B * A; // order of multiplication is important.

使用矩阵、向量库。

以上是大学一年级计算机科学课程几周内将涵盖的内容,但假设您对矢量数学有很好的理解。编写代码可能会变得非常重复,并且由于问题的性质,很难阅读和发现错误。

我一直认为最好的学习方法是编写自己的库,但在这种情况下,库是一个很好的起点,因为这个主题比旋转、缩放和平移更深入。

那里有数百个 matrix/vector 数学库,一个好的起点是 github


数学符号

在数学中,x 轴称为 î(发音为 i hat),y 轴称为 ĵ(发音为 j hat)。每个轴由一个单位向量 î = [1,0]ĵ = [0,1] 定义(对于 3D,我们使用三个 3D 向量 î = [1,0,0]ĵ = [0,1,0]k hat = [0,0,1] 对不起,我找不到k 帽子在字符集中)


矢量基础知识。

一个向量

向量是一组表示方向和距离的数字。在数学中,向量是矩阵。例如 v = [1,0] 在代码中为 structure/object/class v = { x : 1, y : 0} 矢量也可以描述为距离和方向,例如 10 公里东南。从一种类型转换为另一种类型很容易(见下文)

单位向量。

单位向量是长度为 1 个单位的向量。 (1,0) 是一个单位向量,它的长度是 1 个单位长。您可以乘以单位向量以找到原点 n 个单位。

n = 5;
axis = { x : 1, y : 0 };
point.x = axis.x * n;   // 5 * 1 = 5
point.y = axis.y * n;   // 5 * 0 = 0

矢量长度

您可以使用毕达哥拉斯来获取向量的长度。例如,向量 {x : 3, y : 4} 的长度等于 sqrt( 3 * 3 + 4 * 4 ) = sqrt( 9 + 16 ) = sqrt( 25 ) = 5 五个单位长,显然不是单位向量

标准化向量(标准化美式拼写)

向量归一化过程会将向量转换为单位向量。它是通过将向量的分量除以向量长度来完成的。

vec = { x : 3, y : 4 };
length = sqrt( vec.x * vec.x + vec.y * vec.y ); // length = 5
// nVec is the normalised vector vec
nVec.x = vec.x / length;   // 3/5 = 0.6;
nVec.y = vec.y / length;   // 4/5 = 0.8;

来自角度的单位向量

为了在特定方向创建单位向量,我们使用了一点三角函数

 angle = 90; // in deg (normally this is radians)
 v90.x = cos(angle);
 v90.y = sin(angle);