Axis rotation in 3D with python - python

I am trying to rotate a triad of orthogonal axis, which origin is located on the surface of a sphere. The aim would be to rotate them in order to pass from Cartesian coordinates, to radial and transverse component of the sphere. I tried to find a python function but I haven't find anything good for my case so I built it by myself.
First I computed the three angles:
#Center of the sphere
xc = 7500
yc = 7500
zc = 8960
#File where the coordinates are located
data = pd.read_csv('file_test', header = None,delimiter =' ')
xa = np.array(data.iloc[1:,1])
ya = np.array(data.iloc[1:,2])
za = np.array(data.iloc[1:,0])
#Angles definition
theta = np.arctan2((ya-yc),(xa-xc))
phi = np.arctan2((za-zc),(xa-xc))
gamma = np.arctan2((za-zc),(ya-yc))
Then I rotate the vector x,y,z in this way:
def rotate_receivers(x, y, z, a, b, c):
x1 = x * np.cos(b) * np.cos(c) - y * (np.sin(c) * np.cos(b)) + z * (np.sin(b))
y1= x * (np.sin(a) * np.sin(b) *np.cos(c) + np.cos(a) * np.sin(c)) + y * ( - np.sin(b) * np.sin(a) * np.sin(c) + np.cos(c) * np.cos(a)) - z * (np.sin(a) * np.cos(b))
z1 = x * ( - np.cos(a) * np.sin(b) * np.cos(c) + np.sin(a) * np.sin(c)) + y * ( np.sin(b) * np.sin(c) * np.cos(a) + np.sin(a) * np.cos(c)) + z * np.cos(a) * np.cos(b)
return x1, y1, z1
Now I am expecting to rotate them in this way:
x1,y1,z1 = rotate_receivers(x[i],y[i],z[i],gamma[i],phi[i],theta[i])
Now the rotation works only for some angles and I don't understand why. It has to be something wrong maybe in the way I defined the angles but I don't understand what.
Thanks!

Related

Rotate a line by a specific angle

I have a line with lenght of l and p1 and p2 and i want to rotate it by angle.
I was using this matrix but it doesn't work.
matrix
I have this code in python:
def rotate (point, point2, angel):
x1 = (cos(radians(angel)) * point1.x) +( sin(radians(angel)) * point1.y)
y1 = (-1 * sin(radians(angel)) * point1.x )+ (cos(radians(angel)) * point1.y)
x2 = (cos(radians(angel)) * point2.x) + (sin(radians(angel)) * point2.y)
y2 = (-1 * sin(radians(angel)) * point2.x) + (cos(radians(angel)) * point2.y)
return [[x1, y1], [x2, y2]]
You first need to specify an origin point, the rotation will be created around that point. You can adapt your code and use something like this:
def rotate(origin, point, angle):
"""
Rotate a point counterclockwise by a given angle around a given origin.
The angle should be given in radians.
"""
ox, oy = origin.x, origin.y
px, py = point.x, point.y
qx = ox + math.cos(angle) * (px - ox) - math.sin(angle) * (py - oy)
qy = oy + math.sin(angle) * (px - ox) + math.cos(angle) * (py - oy)
return Point(qx, qy)
You can then plot some point and see the results with:
x1 = Point(0, 0)
x2 = Point(5, 5)
mid_point = Point((x1.x + x2.x) / 2, (x1.y + x2.y) / 2)
plt.plot([x1.x, x2.x], [x1.y, x2.y], c='red')
x1 = rotate(mid_point, x1, math.radians(30))
x2 = rotate(mid_point, x2, math.radians(30))
plt.plot([x1.x, x2.x], [x1.y, x2.y], c='blue')
plt.show()
Where you can clearly see the rotation around the middle point between the two lines.

Find the domain of x and y from the intersection line of two surfaces

I have two point cloud and I use scipy.optimize.curve_fit to get the equations representing two surfaces:
z1 = F1(x, y) = (A1 * x ** 2) + (B1 * y ** 2) + (C1 * x * y) + (D1 * x) + (E1 * y) + F1
z2 = F2(x, y) = (A2 * x ** 2) + (B2 * y ** 2) + (C2 * x * y) + (D2 * x) + (E2 * y) + F2
And the equation of their intersection line is:
MY GOAL: Find the domain of x and y
For example: if x0 is in the domain of the intersection line, what is the range of y corresponding to x0?
I have tried some methods with sympy or scipy, but cannot get the domian.

How can I stop pyplot lines on another line?

Hello! Does anyone know to achieve something similar to what the graph shows, using matpltolib? I need to draw some lines, such as CC' and DD', that do not cross over a given line AB. Is there any way to achieve this?
To draw a line from a point C, perpendicular to a line AB and stopping at AB, a projection point P can be calculated. P should be on AB, so P = t*A + (1-t)*B. This equation on points means it should hold both for the x and for the y coordinates: xp = t * xa + (1 - t) * xb and yp = t * ya + (1 - t) * yb) for some t. P should also be on a line containing C, and having the perpendicular vector as its direction. The perpendicular vector is yb - ya, - (xb - xa). So, P = C + u * Perp for some u.
Sympy, Python's symbolic math library can be used to solve these equations:
from sympy import symbols, Eq, solve
t, u, xa, ya, xb, yb, xc, yc = symbols('t u xa ya xb yb xc yc ')
eqpx = Eq(xc + u * (yb - ya), t * xa + (1 - t) * xb)
eqpy = Eq(yc + u * (xa - xb), t * ya + (1 - t) * yb)
solution = solve([eqpx, eqpy])
This finds:
{t: -(xa*xb - xa*xc - xb**2 + xb*xc + ya*yb - ya*yc - yb**2 + yb*yc)/(xa**2 - 2*xa*xb + xb**2 + ya**2 - 2*ya*yb + yb**2),
u: (xa*yb - xa*yc - xb*ya + xb*yc + xc*ya - xc*yb)/(xa**2 - 2*xa*xb + xb**2 + ya**2 - 2*ya*yb + yb**2)}
The solution can be used to define a function to project a point C onto a line AB. Here is an example:
from matplotlib import pyplot as plt
def project_on_line(xc, yc, xa, ya, xb, yb):
t = -(xa * xb - xa * xc - xb ** 2 + xb * xc + ya * yb - ya * yc - yb ** 2 + yb * yc) / (
xa ** 2 - 2 * xa * xb + xb ** 2 + ya ** 2 - 2 * ya * yb + yb ** 2)
return t * xa + (1 - t) * xb, t * ya + (1 - t) * yb
def show_point(x, y, name):
plt.text(x, y, f' {name}', ha='left', va='top')
xa, ya = 0, 2
xb, yb = 6, 5
xc, yc = 5, 1
xd, yd = 7, 0
xcp, ycp = project_on_line(xc, yc, xa, ya, xb, yb)
xdp, ydp = project_on_line(xd, yd, xa, ya, xb, yb)
show_point(xa, ya, "A")
show_point(xb, yb, "B")
show_point(xc, yc, "C")
show_point(xcp, ycp, "C'")
show_point(xd, yd, "D")
show_point(xdp, ydp, "D'")
plt.plot([xa, xb], [ya, yb], color='dodgerblue')
plt.plot([xc, xcp], [yc, ycp], color='dodgerblue')
plt.plot([xd, xdp], [yd, ydp], color='dodgerblue')
plt.axis('equal')
plt.show()

Rotate a square by an angle in degree

I have a square with center x0, y0. I wish to rotate the vertex of this square for a given angle (theta) expressed in degree and return the new rotated vertex in clockwise direction. I am using this approach to rotate a single point applied for each vertex
rotate point (px, py) around point (x0, y0) by angle theta you'll get:
p'x = cos(theta) * (px-x0) - sin(theta) * (py-y0) + x0
p'y = sin(theta) * (px-x0) + cos(theta) * (py-y0) + y0
where:
px, py = coordinate of the point
y0, x0, = centre of rotation
theta = angle of rotation
I wrote a function in Python where the parameters are: x, y (=centre of the square), side of the square, and theta_degree (angle of rotation in degree) but the returns is in anticlockwise direction
from math import cos, sin
def get_square_plot(x, y, side, theta_degree=0):
theta = theta_degree * pi/180
xa = x-side/2
ya = y+side/2
xb = x+side/2
yb = y+side/2
xc = x+side/2
yc = y-side/2
xd = x-side/2
yd = y-side/2
xa_new = cos(theta) * (xa - x) - sin(theta) * (ya - y) + x
ya_new = sin(theta) * (xa - x) - cos(theta) * (ya - y) + y
xb_new = cos(theta) * (xb - x) - sin(theta) * (yb - y) + x
yb_new = sin(theta) * (xb - x) - cos(theta) * (yb - y) + y
xc_new = cos(theta) * (xc - x) - sin(theta) * (yc - y) + x
yc_new = sin(theta) * (xc - x) - cos(theta) * (yc - y) + y
xd_new = cos(theta) * (xd - x) - sin(theta) * (yd - y) + x
yd_new = sin(theta) * (xd - x) - cos(theta) * (yd - y) + y
return [(xa_new, ya_new),(xb_new, yb_new),(xc_new, yc_new),(xd_new, yd_new)]
get_square_plot(0, 0, 10, 0)
[(-5.0, -5.0), (5.0, -5.0), (5.0, 5.0), (-5.0, 5.0)]
instead of
[(-5.0, 5.0), (5.0, 5.0), (5.0, -5.0), (-5.0, -5.0)]
It's such a simple thing -- you've got the formula wrong for all of your y-values.
It should be:
ya_new = sin(theta) * (xa - x) + cos(theta) * (ya - y) + y
addition instead of subtraction.
Don't forget about the geometry module, either. It can deal with a variety of basic shapes and handle translation, rotation, etc...
A square can be constructed with RegularPolygon. It does so by locating vertices a given radius from the center; to get a square with a given side length, divide by sqrt(2). Here is a function to rotate the diamond-orientation so sides are parallel to the axes and then rotate the desired angle, a:
>>> Square = lambda c, r, a: RegularPolygon(c, r/sqrt(2), 4, -rad(a) - pi/4)
>>> Square((0,0),10,0).vertices
[Point(5, -5), Point(5, 5), Point(-5, 5), Point(-5, -5)]
>>> [w.n(2) for w in Square((0,0),10,1).vertices]
[Point(4.9, -5.1), Point(5.1, 4.9), Point(-4.9, 5.1), Point(-5.1, -4.9)]
Note that the slight CW rotation of 1 degree (-rad(1)) puts the first vertex a little closer to the y-axis and a little lower as we expect. You can also enter a symbol for the angle:
>>> from sympy.utilities.misc import filldedent
>>> print filldedent(Square((0,0),10,a).vertices)
[Point(5*sqrt(2)*cos(pi*a/180 + pi/4), -5*sqrt(2)*sin(pi*a/180 +
pi/4)), Point(5*sqrt(2)*sin(pi*a/180 + pi/4), 5*sqrt(2)*cos(pi*a/180 +
pi/4)), Point(-5*sqrt(2)*cos(pi*a/180 + pi/4), 5*sqrt(2)*sin(pi*a/180
+ pi/4)), Point(-5*sqrt(2)*sin(pi*a/180 + pi/4),
-5*sqrt(2)*cos(pi*a/180 + pi/4))]
You can also check your point rotation formula by rotating a point -theta (for CW):
>>> var('px py theta x0 y0')
(px, py, theta, x0, y0)
>>> R = Point(px,py).rotate(-theta, Point(x0,y0))
>>> R.x
x0 + (px - x0)*cos(theta) + (py - y0)*sin(theta)
>>> R.y
y0 + (-px + x0)*sin(theta) + (py - y0)*cos(theta)

changing the color of matplotlib sphere and adjusting shape python

Edit
The only part that I would still like a solution for is getting the spheres to look spherical. If I scale the axis the same, the spheres are too small but spherical. Is there a way to then clip the unused portion off so it is zoomed in? Or can we need set the axis so big and get the desired effect?
End Edit
From plotting a sphere in python, I was able to two add a spheres to my plot.
However, I can't get them to look spherical no matter how I adjust the parameters. Additionally, adding 'g' or any other color to the end of there plots doesn't change there color.
I also tried color='g' but that didn't work either.
How can I get a spherical look (ax autoscale) and change the color?
#!/usr/bin/env python
# This porgram numerically solves the trajectory from the Earth to the moon
# with the specified flight path, true anomaly, and initial conditions.
import numpy as np
from scipy.integrate import odeint
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
from numpy import linspace
me = 5.974 * 10 ** (24) # mass of the earth
mm = 7.348 * 10 ** (22) # mass of the moon
G = 6.67259 * 10 ** (-20) # gravitational parameter
re = 6378.0 # radius of the earth in km
rm = 1737.0 # radius of the moon in km
r12 = 384400.0 # distance between the CoM of the earth and moon
M = me + mm
pi1 = me / M
pi2 = mm / M
mue = 398600.0 # gravitational parameter of earth km^3/sec^2
mum = G * mm # grav param of the moon
mu = mue + mum
omega = np.sqrt(mu / r12 ** 3)
nu = -134.979 * np.pi / 180 # true anomaly angle in radian
x = 327156.0 - 4671
# x location where the moon's SOI effects the spacecraft with the offset of the
# Earth not being at (0,0) in the Earth-Moon system
y = 33050.0 # y location
vbo = 10.85 # velocity at burnout
gamma = 0 * np.pi / 180 # angle in radians of the flight path
vx = vbo * (np.sin(gamma) * np.cos(nu) - np.cos(gamma) * np.sin(nu))
# velocity of the bo in the x direction
vy = vbo * (np.sin(gamma) * np.sin(nu) + np.cos(gamma) * np.cos(nu))
# velocity of the bo in the y direction
xrel = (re + 300.0) * np.cos(nu) - pi2 * r12
yrel = (re + 300.0) * np.sin(nu)
# r0 = [xrel, yrel, 0]
# v0 = [vx, vy, 0]
u0 = [xrel, yrel, 0, vx, vy, 0]
def deriv(u, dt):
n1 = -((mue * (u[0] + pi2 * r12) / np.sqrt((u[0] + pi2 * r12) ** 2
+ u[1] ** 2) ** 3)
- (mum * (u[0] - pi1 * r12) / np.sqrt((u[0] - pi1 * r12) ** 2
+ u[1] ** 2) ** 3))
n2 = -((mue * u[1] / np.sqrt((u[0] + pi2 * r12) ** 2 + u[1] ** 2) ** 3)
- (mum * u[1] / np.sqrt((u[0] - pi1 * r12) ** 2 + u[1] ** 2) ** 3))
return [u[3], # dotu[0] = u[3]
u[4], # dotu[1] = u[4]
u[5], # dotu[2] = u[5]
2 * omega * u[4] + omega ** 2 * u[0] + n1, # dotu[3] = that
omega ** 2 * u[1] - 2 * omega * u[3] + n2, # dotu[4] = that
0] # dotu[5] = 0
dt = np.linspace(0.0, 320000.0, 1000000.0) # 200000 secs to run the simulation
u = odeint(deriv, u0, dt)
x, y, z, x2, y2, z2 = u.T
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
ax.plot(x, y, z)
# adding the moon
phi = np.linspace(0, 2 * np.pi, 100)
theta = np.linspace(0, np.pi, 100)
xm = rm * np.outer(np.cos(phi), np.sin(theta)) + r12
ym = rm * np.outer(np.sin(phi), np.sin(theta))
zm = rm * np.outer(np.ones(np.size(phi)), np.cos(theta))
ax.plot_surface(xm, ym, zm)
ax.auto_scale_xyz([-50000, 400000], [0, 160000], [-130000, 130000])
# adding the earth
xe = re * np.outer(np.cos(phi), np.sin(theta)) - 4671
ye = re * np.outer(np.sin(phi), np.sin(theta))
ze = re * np.outer(np.ones(np.size(phi)), np.cos(theta))
ax.plot_surface(xe, ye, ze, 'g')
ax.auto_scale_xyz([-50000, 400000], [0, 160000], [-130000, 130000])
#
plt.show()
# The code below finds the distance between path and the moon
my_x, my_y, my_z = (384400,0,0)
delta_x = x - my_x
delta_y = y - my_y
delta_z = z - my_z
distance = np.array([np.sqrt(delta_x ** 2 + delta_y ** 2 + delta_z ** 2)])
minimum = np.amin(distance)
print(minimum)

Categories

Resources