In [2]:
import numpy as np
from __future__ import division
Sphere parameterization:
In [3]:
def sphere(theta, phi):
x=np.cos(phi)*np.cos(theta)
y=np.cos(phi)*np.sin(theta)
z=np.sin(phi)
return x,y,z
In [4]:
theta=np.linspace(0,2*np.pi,40)
phi=np.linspace(-np.pi/2, np.pi/2, 30)
theta,phi=np.meshgrid(theta,phi)
x,y,z=sphere(theta,phi)
Choose three non-collinear points in the parametric plane, i.e. give the $\theta, \varphi$ coordinates for these points:
In [5]:
A=np.array([0, np.pi/12])
B=np.array([np.pi/3, np.pi/12])
C=np.array([np.pi/4, 7*np.pi/16])
In [6]:
T=[A,B,C]# Parametric triangle of vertices A,B,C
We define a triangulation of the triangle $T$ giving first the barycentric coordinates of the triangulation points, and then calculating their cartesian coordinates:
In [7]:
def barycentric(n):
return [(i/n,j/n, 1-(i+j)/n) for i in range(n,-1, -1) for j in range(n-i, -1, -1)]
In [8]:
cartesian_coords=lambda T, w: w[0]*T[0]+w[1]*T[1]+w[2]*T[2]
The following function selects successively three consecutive points from the list of the triangulation points, and creates a list of sub-triangles:
In [9]:
def triangles(n, point):
triplets=[]
i=0
j=1
for nr in range(1,n+1):
for k in range(nr):
triplets.append([point[i], point[j], point[j+1]])
i+=1
j+=1
j+=1
return triplets
In order to plot the triangular wireframe on the sphere we are setting $n=8$, as being the number of points on each side of the triangle $T$:
In [10]:
n=8
bar=barycentric(n)
pts_tri=[cartesian_coords(T, w) for w in bar]#list of triangulation points
tri=triangles(n, pts_tri)#list of sub-triangles in T
Instead of evaluating the parameterization only at the triangulation points, we also evaluate it at the middle of each side of a sub-triangle associated to the defined triangulation:
In [11]:
def divide_sides(t):
pts=[t[k] for k in range(3)]+[t[0]]
for k in range(1, 7, 2):
m=int((k-1)/2)
pts.insert(k, (t[m]+t[(m+1)%3])/2)
return pts
In [12]:
import plotly.plotly as py
from plotly.graph_objs import *
import plotly.tools as tls
py.sign_in('empet', 'my_api_key')
In [13]:
trace = Surface(
z=z,
x=x,
y=y,
colorscale='Viridis',
)
In [14]:
def line_pts(tri, surface, color='#0000A0'):
# tri is the list of triangulation sub-triangles
# surface is the function implementing a surface parameterization
lines = []
for t in tri:
pts=divide_sides(t)
coords=zip(*[surface(pts[k][0], pts[k][1]) for k in range(7)])
lines.append(Scatter3d(x=coords[0],
y=coords[1],
z=coords[2],
mode='lines',
line=Line(color=color,
width=5)))
return lines
In [15]:
axis = dict(
showbackground=True,
backgroundcolor="rgb(230, 230,230)",
gridcolor="rgb(255, 255, 255)",
zerolinecolor="rgb(255, 255, 255)",
)
data = Data([trace]+line_pts(tri, sphere))#plot both the sphere as a Surface object and the triangular wireframe
layout = Layout(
title='Triangular wireframe on sphere',
width=700,
height=700,
scene=Scene(
xaxis=XAxis(axis),
yaxis=YAxis(axis),
zaxis=ZAxis(axis),
),
showlegend=False
)
fig1 = Figure(data=data, layout=layout)
py.plot(fig1, filename='triangular-wireplot-2')
Out[15]:
Now we are plotting only the triangular wireframe:
In [16]:
data1 = Data(line_pts(tri, sphere))
layout.update(width=600,
height=600)
fig2 = Figure(data=data1, layout=layout)
py.plot(fig2, filename='triangular-wireplot-3')
Out[16]:
In a similar way we can generate any triangular wireframe on a surface given by a parameterization or the explicit equation $z=f(x,y)$.
In [1]:
from IPython.core.display import HTML
def css_styling():
styles = open("./custom.css", "r").read()
return HTML(styles)
css_styling()
Out[1]: