如何围绕另一个旋转点?

Мак*_*зюк 0 math geometry trigonometry rotation

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

要围绕另一个点旋转一个点,我将点移动到坐标系的原点,因此要旋转的点将位于坐标系的原点(0,0,0),围绕Z,Y和Z旋转点轴,然后将其向后移。

示例:我需要绕点y(3,2,1)旋转点x(1,1,1),所以我从点x- (1 - 3,1 - 2,1 - 1), rotate point x绕x,y和z轴减去点y的坐标,然后返回x`将y坐标添加到正确的位置。能行吗 对不起,英语不好。

Bli*_*n67 10

了解轮换。

从2D开始。

2D坐标通过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;
Run Code Online (Sandbox Code Playgroud)

回转

这似乎是定位一个点的漫长道路。但是,当旋转坐标系时,实际上是在旋转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));
Run Code Online (Sandbox Code Playgroud)

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

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;
Run Code Online (Sandbox Code Playgroud)

有一些捷径。y轴与x轴成90度(除非偏斜)以将向量旋转90deg,我们交换了将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;
Run Code Online (Sandbox Code Playgroud)

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

pos.x = point.x * xAxis.x + point.y * yAxis.x;
pos.y = point.x * xAxis.y + point.y * yAxis.y;
Run Code Online (Sandbox Code Playgroud)

起源。

坐标系由原点以及描述轴的单位矢量定义。要围绕特定点旋转点,我们需要使旋转点成为原点。然后,我们移动坐标以相对于新原点旋转

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;
Run Code Online (Sandbox Code Playgroud)

现在我们可以应用旋转

rotatedPos.x = pos.x * xAxis.x + pos.y * yAxis.x;
rotatedPos.y = pos.x * xAxis.y + pos.y * yAxis.y;
Run Code Online (Sandbox Code Playgroud)

但是旋转点仍相对于原点,我们需要将其相对于原点移回原处。

rotatedPos.x = rotatedPos.x + origin.x;
rotatedPos.y = rotatedPos.y + origin.y;
Run Code Online (Sandbox Code Playgroud)

在计算机图形学中,我们通常会保持相对于其自身本地原点的相关坐标。它有一个坐标系统,我们称为局部坐标,以[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;
Run Code Online (Sandbox Code Playgroud)

规模

我们通常要缩放坐标,这可以通过更改代表每个轴的单位矢量的长度来实现。例如,如果要沿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;
Run Code Online (Sandbox Code Playgroud)

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.
Run Code Online (Sandbox Code Playgroud)

因此要将点移动到上述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
Run Code Online (Sandbox Code Playgroud)

或更紧凑

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
Run Code Online (Sandbox Code Playgroud)

矩阵

上述开始变得有点笨重,从而简化它我们可以转换上述目的pointxAxisyAxiszAxisorigin成一组阵列(称为矩阵)

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
*/
Run Code Online (Sandbox Code Playgroud)

可以简化表示法,在许多语言中,重载将使您可以直接以简写形式进行数学运算。

 pos = point * rotation + origin;    
Run Code Online (Sandbox Code Playgroud)

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
Run Code Online (Sandbox Code Playgroud)

或作为旋转矩阵

A = rotationZ = [cos(ang), sin(ang), 0, -sin(ang), cos(ang), 0, 0, 0, 1];
Run Code Online (Sandbox Code Playgroud)

然后我们想绕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
Run Code Online (Sandbox Code Playgroud)

或作为旋转矩阵

B = rotationY = [cos(ang), 0, sin(ang), 0, 1, 0, -sin(ang), 0, cos(ang)];
Run Code Online (Sandbox Code Playgroud)

然后,我们可以将每个轴沿z旋转旋转y旋转。

// 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;
Run Code Online (Sandbox Code Playgroud)

还是短手

rotationAB = rotationZ * rotationY;
Run Code Online (Sandbox Code Playgroud)

要么

AB = A * B;
Run Code Online (Sandbox Code Playgroud)

所得矩阵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)];
Run Code Online (Sandbox Code Playgroud)

最后的轮换是

ABC = A * B * C
Run Code Online (Sandbox Code Playgroud)

但是请记住,顺序很重要。

A * B * C != C * B * A; // order of multiplication is important.
Run Code Online (Sandbox Code Playgroud)

使用矩阵,向量库。

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

我一直认为,最好的学习方法是编写自己的库,但是在这种情况下,库是一个很好的起点,因为与旋转,缩放和平移相比,该主题的深度要大得多。

那里有数百个矩阵/矢量数学库,一个很好的起点是github


数学符号

在数学î中,x轴称为(发音为i hat),?y轴称为(j hat)。各轴由一个单位矢量定义,î = [1,0]? = [0,1] (对于3D我们使用三个三维矢量î = [1,0,0]? = [0,1,0]k hat = [0,0,1]抱歉,我不能找到在字符组k帽)


向量基础知识。

一个向量

向量是代表方向和距离的一组数字。在数学上,向量是矩阵。例如,v = [1,0]以代码作为结构/对象/类别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
Run Code Online (Sandbox Code Playgroud)

向量长度

您可以使用毕达哥拉斯来获取向量的长度。例如,向量{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;
Run Code Online (Sandbox Code Playgroud)

角度的单位向量

为了在特定方向上创建单位矢量,我们使用一些三角

 angle = 90; // in deg (normally this is radians)
 v90.x = cos(angle);
 v90.y = sin(angle);
Run Code Online (Sandbox Code Playgroud)