求点 P(x1, y1) 绕点 Q(x2, y2) 逆时针旋转 θ 得到的点的坐标
先看绕原点旋转的情况:

如图所示点 v 绕 原点旋转 θ 角,得到点v’,假设 v点的坐标是(x, y) ,那么可以推导得到 v’点的坐标(x’, y’):
{x=r∗cosθy=r∗sinθ\left\{ \begin{aligned} x & = r * \cos\theta \\ y & = r * \sin\theta \\ \end{aligned} \right. {xy=r∗cosθ=r∗sinθ
{x′=r∗cos(ϕ+θ)y′=r∗sin(ϕ+θ)\left\{ \begin{aligned} x' & = r * \cos( \phi + \theta) \\ y' & = r * \sin( \phi + \theta) \\ \end{aligned} \right. {x′y′=r∗cos(ϕ+θ)=r∗sin(ϕ+θ)
展开得:
{x′=r∗cosϕ∗cosθ−r∗sinϕ∗sinθy′=r∗sinϕ∗cosθ+r∗cosϕ∗sinθ\left\{ \begin{aligned} x' & = r * \cos\phi * \cos\theta - r * \sin\phi * \sin\theta \\ y' & = r * \sin\phi * \cos\theta + r * \cos\phi * \sin\theta \\ \end{aligned} \right. {x′y′=r∗cosϕ∗cosθ−r∗sinϕ∗sinθ=r∗sinϕ∗cosθ+r∗cosϕ∗sinθ
带入 x 和 y 的表达式:
{x′=x∗cosθ−y∗sinθy′=y∗cosθ+x∗sinθ\left\{ \begin{aligned} x' & = x * \cos\theta - y * \sin\theta \\ y' & = y * \cos\theta + x* \sin\theta \\ \end{aligned} \right. {x′y′=x∗cosθ−y∗sinθ=y∗cosθ+x∗sinθ
写为矩阵:
[x′y′]=[cosθ−sinθsinθcosθ]∗[xy] \begin{bmatrix} x'\\ y'\\ \end{bmatrix}= \begin{bmatrix} \cos\theta&-\sin\theta\\ \sin\theta&\cos\theta\\ \end{bmatrix}* \begin{bmatrix} x\\ y\\ \end{bmatrix} [x′y′]=[cosθsinθ−sinθcosθ]∗[xy]
于是我们得到旋转变换矩阵 M:
M=[cosθ−sinθsinθcosθ] M = \begin{bmatrix} \cos\theta & -\sin\theta \\ \sin\theta & \cos\theta \end{bmatrix} M=[cosθsinθ−sinθcosθ]
设旋转后的到的坐标为 P’(x’, y’),类似于平面向量中将起点平移至原点的思想,我们可以将 P 点绕 Q 点旋转的过程看作 QP 向量旋转得到 QP’ 向量,于是根据 P 和 Q 坐标有:
[x′−x2y′−y2]=[cosθ−sinθsinθcosθ]∗[x1−x2y1−y2] \begin{bmatrix} x' - x_2\\ y' - y_2\\ \end{bmatrix}= \begin{bmatrix} \cos\theta&-\sin\theta\\ \sin\theta&\cos\theta\\ \end{bmatrix}* \begin{bmatrix} x_1 - x_2\\ y_1 - y_2\\ \end{bmatrix} [x′−x2y′−y2]=[cosθsinθ−sinθcosθ]∗[x1−x2y1−y2]
于是得到 P’ 坐标为:
[x′y′]=[cosθ−sinθsinθcosθ]∗[x1−x2y1−y2]+[x2y2] \begin{bmatrix} x'\\ y'\\ \end{bmatrix}= \begin{bmatrix} \cos\theta&-\sin\theta\\ \sin\theta&\cos\theta\\ \end{bmatrix}* \begin{bmatrix} x_1 - x_2\\ y_1 - y_2\\ \end{bmatrix} + \begin{bmatrix} x_2\\ y_2\\ \end{bmatrix} [x′y′]=[cosθsinθ−sinθcosθ]∗[x1−x2y1−y2]+[x2y2]
算法实现:
from collections import namedtuple
import math
Point = namedtuple("Point", ["x", 'y'])
def matrix_mul(M: list[list[float]], vec: list[float]) -> list[float]:
res = []
for row in M:
res.append(sum([a * b for (a, b) in zip(row, vec)]))
return res
def rotate(p: Point, q: Point, theta: float) -> Point:
M = [
[math.cos(theta), -math.sin(theta)],
[math.sin(theta), math.cos(theta)]
]
pq = [p.x - q.x, p.y - q.y]
t = matrix_mul(M, pq)
t[0] += q.x
t[1] += q.y
return Point(t[0], t[1])
def main():
print(rotate(Point(1.414, 0), Point(0, 0), math.pi / 4), "expect (1, 1)")
print(rotate(Point(1, 1), Point(0, 0), -math.pi / 4), "expect (1.414, 0)")
print(rotate(Point(2, 2), Point(1, 1), -math.pi / 4), "expect (2.414, 1)")
if __name__ == '__main__':
main()
4308

被折叠的 条评论
为什么被折叠?



