3D Transformations

Packages:


In [ ]:
import plotly.offline as py
import plotly.graph_objs as go
py.init_notebook_mode(connected=True)
import numpy as np
import ipywidgets as widgets
import json

Utilities:


In [ ]:
def mesh2D(xlim, ylim, n=5): #Set up Meshgrid
    if isinstance(n, int):
        x = np.linspace(xlim[0],xlim[1],n)
        y = np.linspace(ylim[0],ylim[1],n)
    elif isinstance(n, list):
        x = np.linspace(xlim[0],xlim[1],n[0])
        y = np.linspace(ylim[0],ylim[1],n[1])
    else:
        raise Exception("Invalid Parameter")
        
    return np.meshgrid(x, y, sparse=True)

In [ ]:
def addOpacity(string, intensity="0.5"):
    strList = list(string)
    strList.pop()
    string = "".join(strList) + "," + intensity +")"
    return string

Objects:


In [ ]:
class Line: 
    def __init__(self, pointList):
        self.x = []
        self.y = []
        self.z = []
        
        for i in range(len(pointList)):
            self.x.append(pointList[i][0])
            self.y.append(pointList[i][1])
            self.z.append(pointList[i][2])
        
    def gObject(self, color="rgb(210,64,0)"):
        lineObject = go.Scatter3d(mode="lines",
                                  x=self.x,
                                  y=self.y,
                                  z=self.z,
                                  line=dict(color=(color),
                                            width=7)
                                 )
        return lineObject

In [ ]:
class Sphere:
    def __init__(self, radius=5, center=[0, 0, 0]):
        self.radius = radius
        self.center = center
        meshSize = 20
        theta = np.linspace(0,2*np.pi,meshSize)
        phi = np.linspace(0,np.pi,meshSize)
        self.x = radius*np.outer(np.cos(theta),np.sin(phi)) + center[0]
        self.y = radius*np.outer(np.sin(theta),np.sin(phi)) + center[1]
        self.z = radius*np.outer(np.ones(meshSize),np.cos(phi)) + center[2]
        
    def gObject(self, color=[[0.0, "rgb(0,62,116)"], [1.0, "rgb(255,255,255)"]]):
        sphere = go.Surface(x=self.x.tolist(),
                            y=self.y.tolist(),
                            z=self.z.tolist(),
                            showscale=False,
                            opacity=0.7,
                            colorscale=color
                           )       
        return sphere

In [ ]:
class Point:
    def __init__(self, position):
        self.position = np.array(position)
    
    def gObject(self, color="rgb(0,62,116)"):
        point = go.Scatter3d(mode="markers",
                             x=[self.position[0]],
                             y=[self.position[1]],
                             z=[self.position[2]],
                             marker=dict(color=color,
                                         size=7
                                        )
                            )
        return point

Orange: "rgb(210,64,0)"
Dark Green: "rgb(2,137,59)"
Imperial Blue: "rgb(0,62,116)"
Pool Blue: "rgb(2,161,205)"

3D Visualization

Unit Vectors and a Sphere

Frames


In [ ]:
theta1 = np.pi/2
t = np.linspace(0, theta1, 10)
initialPosition = np.matrix([[2], [2], [2]])

In [ ]:
def roXaxis(theta):
    M = np.matrix([[1, 0, 0],
                   [0, np.cos(theta), -np.sin(theta)], 
                   [0, np.sin(theta), np.cos(theta)]
                  ])
    return M

def roYaxis(theta):
    M = np.matrix([[np.cos(theta), 0, np.sin(theta)],
                   [0, 1, 0],
                   [-np.sin(theta), 0, np.cos(theta)]
                  ])
    return M

def roZaxis(theta):
    M = np.matrix([[np.cos(theta), -np.sin(theta), 0],
                   [np.sin(theta), np.cos(theta), 0],
                   [0, 0 ,1]
                  ])
    return M

In [ ]:
def computeFrames(rotation, theta, point, frames):
    vecPoint = np.matrix([[point[0]],
                          [point[1]],
                          [point[2]]
                         ])
    t = np.linspace(0, theta, frames)
    
    lineList = [point]
    output = []
    for i in t:
        newPoint = rotation(i)*vecPoint
        ptList = np.reshape(newPoint,(1,3)).tolist()[0]
        lineList.append(ptList)
        output.append([Point(ptList).gObject(), Line(lineList).gObject("rgb(210,64,0)")])
    return [output, ptList]

In [ ]:
def computeCompositeFrames(rotation1, rotation2, theta, point, frames, color1="rgb(210,64,0)", color2="rgb(210,64,0)"):
    vecPoint = np.matrix([[point[0]],
                          [point[1]],
                          [point[2]]
                         ])
    t = np.linspace(0, theta, frames)
    
    lineList = [point]
    output = []
    for i in t:
        newPoint = rotation1(i)*vecPoint
        ptList = np.reshape(newPoint,(1,3)).tolist()[0]
        lineList.append(ptList)
        output.append([Point(ptList).gObject(),
                       Line(lineList).gObject(color1),
                       Line([[0., 0., 0.], [0., 0., 0.]]).gObject()
                      ])
    lineList1 = [ptList]
    for j in t[1:]:
        newPoint2 = rotation2(j)*newPoint
        ptList = np.reshape(newPoint2,(1,3)).tolist()[0]
        lineList1.append(ptList)
        output.append([Point(ptList).gObject(),
                       Line(lineList1).gObject(color2),
                       Line(lineList).gObject(addOpacity(color1, "0.7"))
                      ])
        
    return output

In [ ]:
data = []
for i in range(8):
    line = Line([[0., 0., 0.], [0., 0., 0.]]) #Add dummy lines for transformations
    data.append(line.gObject("rgb(0,0,0)"))
        
radius = 2*np.sqrt(3)
sphere = Sphere(radius)
data.append(sphere.gObject())
    
initialPoint = [2., 2., 2.]
    
data.append(Point(initialPoint).gObject("rgb(0,0,0)"))

for i in range(3):
    uvec = [0., 0., 0.]
    uvec[i] = 1
    line = Line([[0., 0., 0.], uvec])
    data.append(line.gObject("rgb(0,0,0)"))

frames = []    
frameSize = 20
frameList1 = computeCompositeFrames(roYaxis,roZaxis,
                                    np.pi/2,
                                    initialPoint, 
                                    frameSize,
                                    "rgb(255,0,0)",
                                    "rgb(0,0,255)"
                                   )
frameList2 = computeCompositeFrames(roZaxis,roYaxis,
                                    np.pi/2,
                                    initialPoint, 
                                    frameSize,
                                    "rgb(0,0,255)",
                                    "rgb(255,0,0)"
                                   )
for i in range(len(frameList1)):
    frames.append(dict(data=[frameList1[i][0],
                             frameList1[i][1],
                             frameList1[i][2],
                             frameList2[i][0],
                             frameList2[i][1],
                             frameList2[i][2]
                            ], 
                       name="frame %i" %i
                      )
                 )

In [ ]:
steps=[]
for i in range(0,frameSize,1):
    step = dict(label='R1',
                method='animate',
                args=[["frame %i" %i],
                      dict(mode="immediate",
                           transition=dict(duration=300
                                          ),
                           frame=dict(duration=300,
                                      redraw=False
                                     )
                          )
                     ]
               )
    steps.append(step)
    
for i in range(0,frameSize-1,1):
    step = dict(label='R2',
                method='animate',
                args=[["frame %i" %(i + frameSize)],
                      dict(mode="immediate",
                           transition=dict(duration=300
                                          ),
                           frame=dict(duration=300,
                                      redraw=False
                                     )
                          )
                     ]
               )
    steps.append(step)

sliders= [dict(
    active=0,
    pad={'t':2*frameSize},
    steps=steps
)]

layout=dict(width=1000, height=500,
            title='Non-Communativeness of 3D Rotations', 
            hovermode='closest',
            updatemenus=[dict(x=-0.05,
                              y=0.15,
                              yanchor="top",
                              xanchor="right",
                              showactive=False,
                              type="buttons",
                              pad={"t": 87, "r": 10},
                              buttons=[dict(method="animate",
                                            args=[None,
                                                  dict(fromcurrent=True,
                                                       transition=dict(duration=50,
                                                                       easing="quadratic-in-out"
                                                                      ),
                                                       frame=dict(duration=50,
                                                                  redraw=False
                                                                 )
                                                      )
                                                 ],
                                            label="Play"
                                           ),
                                       dict(method="animate",
                                           args=[[None],
                                                 dict(mode="immediate",
                                                      transition=dict(duration=0
                                                                     ),
                                                      frame=dict(duration=0,
                                                                 redraw=False
                                                                )
                                                     )
                                                ],
                                            label="Pause"
                                           )
                                      ]
                             )
                        ],
            showlegend=False,#This can be used to isolate individual trace
            sliders=sliders)
figure = dict(data=data, frames=frames, layout=layout)
py.plot(figure)

In [ ]:
# import json
# with open("data_3d.json", 'w') as test_file:
#     json.dump(figure, test_file, ensure_ascii=False)