Skip to content

Instantly share code, notes, and snippets.

@LyleScott
Last active October 7, 2024 08:05
Show Gist options
  • Save LyleScott/e36e08bfb23b1f87af68c9051f985302 to your computer and use it in GitHub Desktop.
Save LyleScott/e36e08bfb23b1f87af68c9051f985302 to your computer and use it in GitHub Desktop.
Rotate X,Y (2D) coordinates around a point or origin in Python
"""
Lyle Scott, III // [email protected]
Multiple ways to rotate a 2D point around the origin / a point.
Timer benchmark results @ https://gist.github.com/LyleScott/d17e9d314fbe6fc29767d8c5c029c362
"""
from __future__ import print_function
import math
import numpy as np
def rotate_via_numpy(xy, radians):
"""Use numpy to build a rotation matrix and take the dot product."""
x, y = xy
c, s = np.cos(radians), np.sin(radians)
j = np.matrix([[c, s], [-s, c]])
m = np.dot(j, [x, y])
return float(m.T[0]), float(m.T[1])
def rotate_origin_only(xy, radians):
"""Only rotate a point around the origin (0, 0)."""
x, y = xy
xx = x * math.cos(radians) + y * math.sin(radians)
yy = -x * math.sin(radians) + y * math.cos(radians)
return xx, yy
def rotate_around_point_lowperf(point, radians, origin=(0, 0)):
"""Rotate a point around a given point.
I call this the "low performance" version since it's recalculating
the same values more than once [cos(radians), sin(radians), x-ox, y-oy).
It's more readable than the next function, though.
"""
x, y = point
ox, oy = origin
qx = ox + math.cos(radians) * (x - ox) + math.sin(radians) * (y - oy)
qy = oy + -math.sin(radians) * (x - ox) + math.cos(radians) * (y - oy)
return qx, qy
def rotate_around_point_highperf(xy, radians, origin=(0, 0)):
"""Rotate a point around a given point.
I call this the "high performance" version since we're caching some
values that are needed >1 time. It's less readable than the previous
function but it's faster.
"""
x, y = xy
offset_x, offset_y = origin
adjusted_x = (x - offset_x)
adjusted_y = (y - offset_y)
cos_rad = math.cos(radians)
sin_rad = math.sin(radians)
qx = offset_x + cos_rad * adjusted_x + sin_rad * adjusted_y
qy = offset_y + -sin_rad * adjusted_x + cos_rad * adjusted_y
return qx, qy
def _main():
theta = math.radians(90)
point = (5, -11)
print(rotate_via_numpy(point, theta))
print(rotate_origin_only(point, theta))
print(rotate_around_point_lowperf(point, theta))
print(rotate_around_point_highperf(point, theta))
if __name__ == '__main__':
_main()
@Ajk4
Copy link

Ajk4 commented Feb 25, 2020

AFAIK signs in

    qx = offset_x + cos_rad * adjusted_x + sin_rad * adjusted_y
    qy = offset_y + -sin_rad * adjusted_x + cos_rad * adjusted_y

are incorrect. Please refer to rotation matrix in https://en.wikipedia.org/wiki/Rotation_matrix

@NicolasCaous
Copy link

NicolasCaous commented Mar 25, 2020

@Ajk4
They are not incorrect if you are rotating clockwise. But, if the intention is to rotate in the counterclockwise direction, you are right.

@tigercoding56
Copy link

tigercoding56 commented Dec 19, 2022

thank you :D
i always forget how to rotate things
so having this is very handy

@rabbl
Copy link

rabbl commented Nov 2, 2023

Thank you! The best and most insightful source in the web! I will print and frame it.

@lukasrohatsch
Copy link

Hi,
thank you for this code!
In the meantime return float(m.T[0]), float(m.T[1]) throws a DeprecationWarning.

Using return m.T[0].item(), m.T[1].item() instead solves it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment