二维空间下的向量旋转

简介: 向量运算是计算机图形学的数学基础,而向量的旋转是向量的一种常见操作,本文将详细讲解向量在二维空间下的旋转原理。
在前端项目中,旋转一个元素我们会使用CSS的 rotate 函数,本文会让你对 rotate 有一个全新的认识。


向量


二维中间中的向量其实就是一个包含了两个数值的数组,一个是 x 坐标值,一个是 y 坐标值。

image.png

向量既可以表示一个 “点”(x, y),也可以表示一个从原点(0,0)到坐标点(x, y)的 “线” 称为 “向量”。


向量的旋转


向量的旋转指的是将一个已知向量旋转给定的弧度后得到一个旋转后的新向量,也就是说我们使用 “向量” 和 “弧度” 求出旋转后的“新向量”。我们通过一段伪代码来描述:

/**
 * v1: 原始向量
 * v2: 旋转后的新向量
 * rad: 旋转弧度
 */
const v2 = v1.rotate(rad);

若想实现一个 rotate 函数来旋转向量,“矩阵的线性变换” 可以帮助我们做到这一点。


 矩阵的线性变换


当一个矩阵和一个向量做乘法时,将产生一个新的向量(可以理解为:向量和矩阵做乘法描述了一个运动),矩阵描述了一个二维空间如何变换(旋转、拉伸等),向量相当于一个入参。


这与函数类似,当一个矩阵描述了二维空间逆时针旋转90°,那么任意一个向量与该矩阵做乘法时,都会得到一个逆时针旋转了90°的新向量。

image.png上面公式中,左侧的 2x2 的矩阵描述了二维空间如何变换,右侧的 (x, y) 是一个向量,整体计算过程给出了矩阵和向量通过乘法得到一个新的向量。


  • 基向量


为什么 2x2 的矩阵可以描述二维空间如何变换?这个问题很关键,因此这里单独用一个小节展开介绍一下。
空间中的任意线性变换都可以由“基向量”的变换来表示,而基向量的数量与维度保持一致,比如二维空间中,基向量就是两个被称为:i 帽和 j 帽的向量。这两个基向量都有各自的x坐标(1, 0)和y坐标(0, 1),因此加起来就是一个 2x2 的矩阵:


image.png

左列表示 i 帽,右列表示 j 帽,上面的数字为 x 坐标,下面的数字为 y 坐标,空间中的表示如下图所示:image.png

如上图所示,向量 v 是由基向量 i 帽和 j 帽相加得到的,因此当 i 帽和 j 帽的坐标发生变化时,向量 v 就会发生变化,比如逆时针旋转90°,那么矩阵值为:
此时 2x2 矩阵值为:

image.png

由于整个空间是线性变化的,因此基向量的变化等同于该空间下任意向量、坐标、图形的变化。

举个例子:空间中任意一个向量 v→(3, 2),与上面的矩阵相乘,将得到一个全新的逆时针旋转90°后的新向量:

image.png

  • 小结


回到我们的主题,就是说,当我们拥有矩阵和向量时,我们就可以得到变换后的新向量。向量是我们已知的,矩阵我们暂时还没有,但我们有一个弧度值,所以我们要做一件事,就是将弧度转化为矩阵。然后再用这个矩阵和向量做乘法得出变换后的新向量。


 将弧度值转化为矩阵


本节介绍一种通过弧度值来计算旋转后基向量 i 帽与 j 帽坐标的方法。

  • 基向量 i 帽旋转后的坐标



旋转前,基向量 i 帽的坐标为:(1, 0),旋转后 i 帽的坐标是:(cosθ, sinθ)。

image.png向量 a 在向量 b 上的投影等于两个向量夹角的余弦值乘以向量 a 的长度,反过来 ba 上的投影就是余弦值乘以向量 b 的长度。
在我们的场景里就是旋转后 i 帽和旋转前 i 帽夹角的余弦值乘以 i 帽的长度,而由于 i 帽的长度为 1,因此余弦值就是我们要的 i 帽旋转后在 x 轴的坐标。
至此,我们得到了旋转后 i 帽的 x 坐标:cosθ
旋转后的 i 帽在 y 轴的位置,我们可以用夹角的正弦值来计算。用夹角与边长来求出三角形的第三条边,而这第三条边的长度就是旋转后 i 帽在 y 轴的位置,如下图所示:


image.png

如上图所示,边 d 等于旋转后的 i 帽在 y 轴的位置等于 sinθ,我们用一个简单的推导步骤来证明:

sinθ = 对边 / 斜边
已知斜边边长为 1(i帽长度为1,旋转后长度不变)
sinθ = d / 1
d / 1 = sinθ
d = sinθ * 1
d = sinθ

至此,我们得出旋转后 i 帽的 y 坐标:sinθ,同时我们也得到了旋转后 i 帽的完整坐标:

image.png


向量旋转的代码实现

通过上一节详细的介绍,我们产出了一个公式:

image.png

最左侧的 2x2 矩阵是使用弧度计算出来的,与右边的向量做乘法,得出最后的一个新的向量,代码中直接使用最后一步生产出来的那个向量即可,代码如下:

/**
 * @param {[x, y]} v 原始向量
 * @param {number} rad 旋转弧度
 * @return {[x, y]} 旋转后的新向量
 */
function rotate(v, rad) {
  const c = Math.cos(rad);
  const s = Math.sin(rad);
  const [x, y] = v;
  return [
      x * c + y * -s,
      x * s + y * c
  ];
}



相关文章
|
存储 算法
​LeetCode刷题实战251:展开二维向量
算法的重要性,我就不多说了吧,想去大厂,就必须要经过基础知识和业务逻辑面试+算法面试。所以,为了提高大家的算法能力,这个公众号后续每天带大家做一道算法题,题目就从LeetCode上面选 !
140 0
[leetcode/lintcode 题解]国内大厂面试真题详解:摊平二维向量
[leetcode/lintcode 题解]国内大厂面试真题详解:摊平二维向量
[leetcode/lintcode 题解]国内大厂面试真题详解:摊平二维向量
|
18天前
|
Shell Android开发
Android系统 adb shell push/pull 禁止特定文件
Android系统 adb shell push/pull 禁止特定文件
23 1
|
4月前
|
Android开发 Python
Python封装ADB获取Android设备wifi地址的方法
Python封装ADB获取Android设备wifi地址的方法
64 0
|
8月前
|
开发工具 Android开发
Mac 安卓(Android) 配置adb路径
Mac 安卓(Android) 配置adb路径
228 0
|
4天前
|
Shell Android开发
Android Activity重写dump方法实现通过adb调试代码
Android Activity重写dump方法实现通过adb调试代码
10 0
|
5天前
|
Java Android开发
Android 对adb命令的拦截
Android 对adb命令的拦截
15 2
|
18天前
|
网络协议 Shell Android开发
Android 深入学习ADB调试原理(1)
Android 深入学习ADB调试原理(1)
26 1
|
18天前
|
存储 安全 Shell
Android系统 adb shell auth授权使用
Android系统 adb shell auth授权使用
29 2