In [1]:
import plotly.offline as py
from plotly.graph_objs import *
import numpy as np
from numpy import pi, cos, sin, exp, log, sqrt
py.init_notebook_mode()
In [2]:
def curve(rfun,tmin=-2,tmax=2,tpts=200,color='black'):
    domain = np.linspace(tmin,tmax,tpts)
    r = [[rfun(t)[i] for t in domain] for i in range(3)]
    trace = Scatter3d(x=r[0],y=r[1],z=r[2],mode='lines',
                      line=Line(color=color,width=3))
    return(trace)
In [3]:
def dot(u,v):
    '''3D dot product'''
    return u[0]*v[0]+u[1]*v[1]+u[2]*v[2]
def mag(v):
    '''magnitude of 3D vector'''
    return sqrt(dot(v,v))
def scale(c,v):
    '''scalar multiple c*v'''
    return [c*x for x in v]
def plus(u,v):
    return [u[i]+v[i] for i in (0,1,2)]
def minus(u,v):
    return [u[i]-v[i] for i in (0,1,2)]
def unit(v):
    '''direction (unit vector) of v'''
    return scale(1/mag(v),v)
def cross(u,v):
    '''3D cross product'''
    return [u[1]*v[2]-u[2]*v[1],u[2]*v[0]-u[0]*v[2],u[0]*v[1]-u[1]*v[0]]
In [4]:
dt=1e-6
def D(u,t0,dt=dt):
    '''approximate du/dt for u 3D vector'''
    u1 = u(t0+dt)
    u2 = u(t0-dt)
    du = minus(u2,u1)
    return scale(1/(dt+dt),du)
In [5]:
def T(r,t0,dt=dt):
    '''unit tangent vector'''
    rp = D(r,t0,dt=dt)
    return unit(rp)
def Tp(r,t0,dt=dt):
    '''dT/dt'''
    fun = lambda t: T(r,t,dt=dt)
    return D(fun, t0, dt=dt)
def kappa(r,t0,dt=dt):
    '''curvature'''
    mTp = mag(Tp(r,t0,dt=dt))
    mrp = mag(D(r,t0,dt=dt))
    return mTp/mrp
def N(r,t0,dt=dt):
    '''unit normal vector'''
    return unit(Tp(r,t0,dt=dt))
In [61]:
r = lambda t: [2*t*cos(t),t*sin(t),3*t]
Tmin,Tmax=0,8*pi
helix=curve(r,color='red',tmin=Tmin,tmax=Tmax)
layout=Layout(scene=Scene(
              xaxis=XAxis(dict(range=[-60,60])),
              yaxis=YAxis(dict(range=[-30,30])),
              zaxis=ZAxis(dict(range=[0,100]))))
py.iplot(Figure(data=Data([helix]),layout=layout))
In [51]:
t0=12
tangent=T(r,t0)
normal=N(r,t0)
radius=1/kappa(r,t0)
print(tangent)
print(normal)
print(mag(tangent)) # should be 1
print(mag(normal)) # should be 1
print(dot(tangent,normal)) #should be 0
print(radius)
[-0.82313791528121882, -0.54194140512491351, -0.16953903927332906]
[-0.56191734593287368, 0.82041262298691697, 0.1056977974391245]
1.0
1.0
-2.33407182471e-11
18.591661304
In [52]:
def osc_circle(curve,t0,dt=dt):
    T0=T(curve,t0,dt=dt)
    N0=N(curve,t0,dt=dt)
    R=1/kappa(curve,t0,dt=dt)
    base=plus(curve(t0),scale(R,N0))
    arrow=lambda theta: plus(scale(cos(theta),T0),scale(sin(theta),N0))
    return(lambda theta: plus(base,scale(R,arrow(theta))))
In [62]:
#arc=curve(r,color='red',tmin=0,tmax=4)
circ=curve(osc_circle(r,t0),color='blue',tmin=0,tmax=2*pi)
py.iplot(Figure(data=Data([helix,circ]),layout=layout))
In [56]:
help(Layout)
Help on class Layout in module plotly.graph_objs.graph_objs:

class Layout(PlotlyDict)
 |  Valid attributes for 'layout' at path [] under parents ():
 |  
 |      ['angularaxis', 'annotations', 'autosize', 'bargap', 'bargroupgap',
 |      'barmode', 'barnorm', 'boxgap', 'boxgroupgap', 'boxmode', 'calendar',
 |      'direction', 'dragmode', 'font', 'geo', 'height', 'hiddenlabels',
 |      'hiddenlabelssrc', 'hidesources', 'hoverlabel', 'hovermode', 'images',
 |      'legend', 'mapbox', 'margin', 'orientation', 'paper_bgcolor',
 |      'plot_bgcolor', 'radialaxis', 'scene', 'separators', 'shapes',
 |      'showlegend', 'sliders', 'smith', 'ternary', 'title', 'titlefont',
 |      'updatemenus', 'width', 'xaxis', 'yaxis']
 |  
 |  Run `<layout-object>.help('attribute')` on any of the above.
 |  '<layout-object>' is the object at []
 |  
 |  Method resolution order:
 |      Layout
 |      PlotlyDict
 |      builtins.dict
 |      PlotlyBase
 |      builtins.object
 |  
 |  Methods inherited from PlotlyDict:
 |  
 |  __copy__(self)
 |  
 |  __deepcopy__(self, memodict={})
 |  
 |  __dir__(self)
 |      Dynamically return the existing and possible attributes.
 |  
 |  __getattr__(self, key)
 |      Python only calls this when key is missing!
 |  
 |  __getitem__(self, key)
 |      Calls __missing__ when key is not found. May mutate object.
 |  
 |  __init__(self, *args, **kwargs)
 |      Initialize self.  See help(type(self)) for accurate signature.
 |  
 |  __missing__(self, key)
 |      Mimics defaultdict. This is called from __getitem__ when key DNE.
 |  
 |  __setattr__(self, key, value)
 |      Maps __setattr__ onto __setitem__
 |  
 |  __setitem__(self, key, value, _raise=True)
 |      Validates/Converts values which should be Graph Objects.
 |  
 |  force_clean(self, **kwargs)
 |      Recursively remove empty/None values.
 |  
 |  get_data(self, flatten=False)
 |      Returns the JSON for the plot with non-data elements stripped.
 |  
 |  get_ordered(self, **kwargs)
 |      Return a predictable, OrderedDict version of self.
 |  
 |  help(self, attribute=None, return_help=False)
 |      Print help string for this object or an attribute of this object.
 |      
 |      :param (str) attribute: A valid attribute string for this object.
 |      :param (bool) return_help: Return help_string instead of printing it?
 |      :return: (None|str)
 |  
 |  strip_style(self)
 |      Recursively strip style from the current representation.
 |      
 |      All PlotlyDicts and PlotlyLists are guaranteed to survive the
 |      stripping process, though they made be left empty. This is allowable.
 |      
 |      Keys that will be stripped in this process are tagged with
 |      `'type': 'style'` in graph_objs_meta.json. Note that a key tagged as
 |      style, but with an array as a value may still be considered data.
 |  
 |  to_string(self, level=0, indent=4, eol='\n', pretty=True, max_chars=80)
 |      Returns a formatted string showing graph_obj constructors.
 |      
 |      :param (int) level: The number of indentations to start with.
 |      :param (int) indent: The indentation amount.
 |      :param (str) eol: The end of line character(s).
 |      :param (bool) pretty: Curtail long list output with a '..' ?
 |      :param (int) max_chars: The max characters per line.
 |      
 |      Example:
 |      
 |          print(obj.to_string())
 |  
 |  update(self, dict1=None, **dict2)
 |      Update current dict with dict1 and then dict2.
 |      
 |      This recursively updates the structure of the original dictionary-like
 |      object with the new entries in the second and third objects. This
 |      allows users to update with large, nested structures.
 |      
 |      Note, because the dict2 packs up all the keyword arguments, you can
 |      specify the changes as a list of keyword agruments.
 |      
 |      Examples:
 |      # update with dict
 |      obj = Layout(title='my title', xaxis=XAxis(range=[0,1], domain=[0,1]))
 |      update_dict = dict(title='new title', xaxis=dict(domain=[0,.8]))
 |      obj.update(update_dict)
 |      obj
 |      {'title': 'new title', 'xaxis': {'range': [0,1], 'domain': [0,.8]}}
 |      
 |      # update with list of keyword arguments
 |      obj = Layout(title='my title', xaxis=XAxis(range=[0,1], domain=[0,1]))
 |      obj.update(title='new title', xaxis=dict(domain=[0,.8]))
 |      obj
 |      {'title': 'new title', 'xaxis': {'range': [0,1], 'domain': [0,.8]}}
 |      
 |      This 'fully' supports duck-typing in that the call signature is
 |      identical, however this differs slightly from the normal update
 |      method provided by Python's dictionaries.
 |  
 |  ----------------------------------------------------------------------
 |  Data descriptors inherited from PlotlyDict:
 |  
 |  __dict__
 |      dictionary for instance variables (if defined)
 |  
 |  __weakref__
 |      list of weak references to the object (if defined)
 |  
 |  ----------------------------------------------------------------------
 |  Methods inherited from builtins.dict:
 |  
 |  __contains__(self, key, /)
 |      True if D has a key k, else False.
 |  
 |  __delitem__(self, key, /)
 |      Delete self[key].
 |  
 |  __eq__(self, value, /)
 |      Return self==value.
 |  
 |  __ge__(self, value, /)
 |      Return self>=value.
 |  
 |  __getattribute__(self, name, /)
 |      Return getattr(self, name).
 |  
 |  __gt__(self, value, /)
 |      Return self>value.
 |  
 |  __iter__(self, /)
 |      Implement iter(self).
 |  
 |  __le__(self, value, /)
 |      Return self<=value.
 |  
 |  __len__(self, /)
 |      Return len(self).
 |  
 |  __lt__(self, value, /)
 |      Return self<value.
 |  
 |  __ne__(self, value, /)
 |      Return self!=value.
 |  
 |  __new__(*args, **kwargs) from builtins.type
 |      Create and return a new object.  See help(type) for accurate signature.
 |  
 |  __repr__(self, /)
 |      Return repr(self).
 |  
 |  __sizeof__(...)
 |      D.__sizeof__() -> size of D in memory, in bytes
 |  
 |  clear(...)
 |      D.clear() -> None.  Remove all items from D.
 |  
 |  copy(...)
 |      D.copy() -> a shallow copy of D
 |  
 |  fromkeys(iterable, value=None, /) from builtins.type
 |      Returns a new dict with keys from iterable and values equal to value.
 |  
 |  get(...)
 |      D.get(k[,d]) -> D[k] if k in D, else d.  d defaults to None.
 |  
 |  items(...)
 |      D.items() -> a set-like object providing a view on D's items
 |  
 |  keys(...)
 |      D.keys() -> a set-like object providing a view on D's keys
 |  
 |  pop(...)
 |      D.pop(k[,d]) -> v, remove specified key and return the corresponding value.
 |      If key is not found, d is returned if given, otherwise KeyError is raised
 |  
 |  popitem(...)
 |      D.popitem() -> (k, v), remove and return some (key, value) pair as a
 |      2-tuple; but raise KeyError if D is empty.
 |  
 |  setdefault(...)
 |      D.setdefault(k[,d]) -> D.get(k,d), also set D[k]=d if k not in D
 |  
 |  values(...)
 |      D.values() -> an object providing a view on D's values
 |  
 |  ----------------------------------------------------------------------
 |  Data and other attributes inherited from builtins.dict:
 |  
 |  __hash__ = None
 |  
 |  ----------------------------------------------------------------------
 |  Methods inherited from PlotlyBase:
 |  
 |  to_graph_objs(self, **kwargs)
 |      Everything is cast into graph_objs. Here for backwards compat.
 |  
 |  validate(self)
 |      Everything is *always* validated now. keep for backwards compat.

In [ ]: