Created
May 31, 2018 05:51
-
-
Save mpilosov/2af9eb6bf8dd0259c99bb0306ffa84f9 to your computer and use it in GitHub Desktop.
Three-Dimensional Projections (with Linear Algebra)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import numpy as np | |
from matplotlib import pyplot as plt | |
## DEFINE RE-USABLE METHODS | |
def rotX(theta): # build a rotation matrix around the y-axis | |
R = np.eye(3) | |
R[1,1] = np.cos(theta) | |
R[1,2] = -np.sin(theta) | |
R[2,1] = np.sin(theta) | |
R[2,2] = np.cos(theta) | |
return R | |
def rotY(theta): # build a rotation matrix around the y-axis | |
R = np.eye(3) | |
R[0,0] = np.cos(theta) | |
R[0,2] = np.sin(theta) | |
R[2,0] = -np.sin(theta) | |
R[2,2] = np.cos(theta) | |
return R | |
def rotZ(theta): # build a rotation matrix around the y-axis | |
R = np.eye(3) | |
R[0,0] = np.cos(theta) | |
R[0,1] = -np.sin(theta) | |
R[1,0] = np.sin(theta) | |
R[1,1] = np.cos(theta) | |
return R | |
class plane(object): # make a child of the base class `object` | |
def __init__(self, num_points_sides = 30, num_points_back = 20): | |
# these define our frame in the xy-plane | |
self.tail = [0, -20, 0] | |
self.nose = [0, 50, 0] | |
self.leftwing = [-20, -20, 0] | |
self.rightwing = [20, -20, 0] | |
self.points = [] | |
# we fit a parabola on the back | |
xwidth = self.rightwing[0] - self.leftwing[0] | |
for x in np.linspace(self.leftwing[0], self.rightwing[0], num_points_back): # x values from wing to wing | |
self.points.append( [x, -(1.5/xwidth)*(x-self.rightwing[0])*(x-self.leftwing[0]) + self.leftwing[1], 0 ] ) | |
# we can add a parabola as a spine | |
ywidth = self.nose[1] - self.tail[1] | |
for y in np.linspace(self.tail[1], self.nose[1], num_points_back): # x values from wing to wing | |
self.points.append( [0, y, -(0.5/ywidth)*(y-self.nose[1])*(y-self.tail[1]) ] ) | |
# and lines on the sides | |
slope_left = (self.nose[1] - self.leftwing[1])/(self.nose[0] - self.leftwing[0] ) | |
slope_right = (self.nose[1] - self.rightwing[1])/(self.nose[0] - self.rightwing[0]) | |
for x in np.linspace(self.leftwing[0], self.nose[0], num_points_sides): # x values from left wing to center | |
self.points.append( [x, slope_left*x + self.nose[1], 0] ) | |
for x in np.linspace(self.nose[0], self.rightwing[0], num_points_sides): # x values from center to right wing | |
self.points.append( [x, slope_right*x + self.nose[1], 0] ) | |
# you can add more points to the geometry of our airplane, for now we leave it as a triangle. | |
def update(self, R): # this is the utility function that is used by the others. | |
l = [] | |
for p in self.points: | |
l.append(np.dot(R, p)) | |
self.points = l | |
pass | |
def pitch(self, theta=np.pi/16): | |
R = rotX(theta) # get rotation matrix | |
self.update(R) # call the update function. | |
pass | |
def roll(self, theta=np.pi/16): | |
R = rotY(theta) # get rotation matrix | |
self.update(R) # call the update function. | |
pass | |
def yaw(self, theta=np.pi/16): | |
R = rotZ(theta) # get rotation matrix | |
self.update(R) # call the update function. | |
pass | |
def plot(self, min_dot_size = 10, scale_factor = 40): | |
plt.cla() # clear our axes | |
N = np.array(self.points).transpose() | |
m = np.max(N[:,2]) - np.min(N[:,2]) | |
# alpha=0.1+0.9*(p[2]-np.min(N[:,2]))/m, | |
for p in self.points: | |
plt.scatter(p[0], p[1], c='k', s=min_dot_size +scale_factor*(p[2]-np.min(N[:,2]))/m ) | |
# plt.xlim([-50, 50]) | |
# plt.ylim([-20, 70]) | |
plt.axis('equal') | |
plt.show() | |
### HANDLER FUNCTION. | |
def render(roll_angle, pitch_angle, yaw_angle): | |
try: | |
P = plane() # instantiate our class. | |
P.roll(roll_angle) | |
P.pitch(pitch_angle) | |
P.yaw(yaw_angle) | |
P.plot() | |
except RuntimeWarning: | |
pass | |
return None | |
##### OPTIONAL SLIDERS | |
# from ipywidgets import widgets | |
# from ipywidgets import interactive | |
# sliders = [widgets.FloatSlider(val = 0, min=-np.pi/2, max=np.pi/2, step=np.pi/50, continuous_update=False) | |
# for _ in range(3)] | |
# interactive(render, roll_angle=sliders[0], pitch_angle=sliders[1], yaw_angle=sliders[2]) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
USAGE
Jupyter
paste into a python notebook with widgets enabled (for instructions, see my writeup ), then uncomment the code at the bottom.
Command-Line (iPython) or through plain old
.py
files