Мак*_*зюк 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坐标通过x和y两个值定义位置。x是沿x轴的距离,y是沿y轴的距离。按照惯例,计算机图形通常具有从左到右定义的x轴,从上到下定义的y轴。
此答案中的注释代码是伪代码,不代表任何特定语言。
我们可以将轴表示为矢量,例如,x轴在x方向上为1单位,向下为0单位,y轴在x方向上为0单位,向下为1单位。
对于代码,我们将向量定义为(例如x轴xAxis.x = 1
,yAxis.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,一切都差不多,但有一个附加的轴和组件
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)
上述开始变得有点笨重,从而简化它我们可以转换上述目的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
*/
Run Code Online (Sandbox Code Playgroud)
可以简化表示法,在许多语言中,重载将使您可以直接以简写形式进行数学运算。
pos = point * rotation + origin;
Run Code Online (Sandbox Code Playgroud)
在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)