#!/usr/bin/env python
# coding: utf-8
# #### New to Plotly?
# Plotly's Python library is free and open source! [Get started](https://plotly.com/python/getting-started/) by downloading the client and [reading the primer](https://plotly.com/python/getting-started/).
# You can set up Plotly to work in [online](https://plotly.com/python/getting-started/#initialization-for-online-plotting) or [offline](https://plotly.com/python/getting-started/#initialization-for-offline-plotting) mode, or in [jupyter notebooks](https://plotly.com/python/getting-started/#start-plotting-online).
# We also have a quick-reference [cheatsheet](https://images.plot.ly/plotly-documentation/images/python_cheat_sheet.pdf) (new!) to help you get started!
# #### Defining and plotting triangulated surfaces
# #### with Plotly `Mesh3d`
# A triangulation of a compact surface is a finite collection of triangles that cover the surface in such a way that every point on the surface is in a triangle, and the intersection of any two triangles is either void, a common edge or a common vertex. A triangulated surface is called tri-surface.
#
# The triangulation of a surface defined as the graph of a continuous function, $z=f(x,y), (x,y)\in D\subset\mathbb{R}^2$ or in a parametric form:
# $$x=x(u,v), y=y(u,v), z=z(u,v), (u,v)\in U\subset\mathbb{R}^2,$$
# is the image through $f$,respectively through the parameterization, of the Delaunay triangulation or an user defined triangulation of the planar domain $D$, respectively $U$.
#
# The Delaunay triangulation of a planar region is defined and illustrated in a Python Plotly tutorial posted [here](https://plotly.com/python/alpha-shapes/).
#
# If the planar region $D$ ($U$) is rectangular, then one defines a meshgrid on it, and the points
# of the grid are the input points for the `scipy.spatial.Delaunay` function that defines the planar triangulation of $D$, respectively $U$.
#
# ### Triangulation of the Moebius band ###
# The Moebius band is parameterized by:
#
# $$\begin{align*}
# x(u,v)&=(1+0.5 v\cos(u/2))\cos(u)\\
# y(u,v)&=(1+0.5 v\cos(u/2))\sin(u)\quad\quad u\in[0,2\pi],\: v\in[-1,1]\\
# z(u,v)&=0.5 v\sin(u/2)
# \end{align*}
# $$
# Define a meshgrid on the rectangle $U=[0,2\pi]\times[-1,1]$:
# In[8]:
import plotly.plotly as py
import plotly.graph_objs as go
import numpy as np
import matplotlib.cm as cm
from scipy.spatial import Delaunay
u=np.linspace(0,2*np.pi, 24)
v=np.linspace(-1,1, 8)
u,v=np.meshgrid(u,v)
u=u.flatten()
v=v.flatten()
#evaluate the parameterization at the flattened u and v
tp=1+0.5*v*np.cos(u/2.)
x=tp*np.cos(u)
y=tp*np.sin(u)
z=0.5*v*np.sin(u/2.)
#define 2D points, as input data for the Delaunay triangulation of U
points2D=np.vstack([u,v]).T
tri = Delaunay(points2D)#triangulate the rectangle U
# `tri.simplices` is a `np.array` of integers, of shape (`ntri`,3), where `ntri` is the number of triangles generated by `scipy.spatial.Delaunay`.
# Each row in this array contains three indices, i, j, k, such that points2D[i,:], points2D[j,:], points2D[k,:] are vertices of a triangle in the Delaunay triangularization of the rectangle $U$.
#
# In[9]:
print tri.simplices.shape, '\n', tri.simplices[0]
# The images of the `points2D` through the surface parameterization are 3D points. The same simplices define the triangles on the surface.
# Setting a combination of keys in `Mesh3d` leads to generating and plotting of a tri-surface, in the same way as `plot_trisurf` in matplotlib or `trisurf` in Matlab does.
#
# We note that `Mesh3d` with different combination of keys can generate [alpha-shapes](https://plotly.com/python/alpha-shapes/).
# In order to plot a tri-surface, we choose a colormap, and associate to each triangle on the surface, the color in colormap, corresponding to the normalized mean value of z-coordinates of the triangle vertices.
# Define a function that maps a mean z-value to a matplotlib color, converted to a Plotly color:
# In[10]:
def map_z2color(zval, colormap, vmin, vmax):
#map the normalized value zval to a corresponding color in the colormap
if vmin>vmax:
raise ValueError('incorrect relation between vmin and vmax')
t=(zval-vmin)/float((vmax-vmin))#normalize val
R, G, B, alpha=colormap(t)
return 'rgb('+'{:d}'.format(int(R*255+0.5))+','+'{:d}'.format(int(G*255+0.5))+\
','+'{:d}'.format(int(B*255+0.5))+')'
# To plot the triangles on a surface, we set in Plotly `Mesh3d` the lists of x, y, respectively z- coordinates of the vertices, and the lists of indices, i, j, k, for x, y, z coordinates of all vertices:
# In[11]:
def tri_indices(simplices):
#simplices is a numpy array defining the simplices of the triangularization
#returns the lists of indices i, j, k
return ([triplet[c] for triplet in simplices] for c in range(3))
def plotly_trisurf(x, y, z, simplices, colormap=cm.RdBu, plot_edges=None):
#x, y, z are lists of coordinates of the triangle vertices
#simplices are the simplices that define the triangularization;
#simplices is a numpy array of shape (no_triangles, 3)
#insert here the type check for input data
points3D=np.vstack((x,y,z)).T
tri_vertices=map(lambda index: points3D[index], simplices)# vertices of the surface triangles
zmean=[np.mean(tri[:,2]) for tri in tri_vertices ]# mean values of z-coordinates of
#triangle vertices
min_zmean=np.min(zmean)
max_zmean=np.max(zmean)
facecolor=[map_z2color(zz, colormap, min_zmean, max_zmean) for zz in zmean]
I,J,K=tri_indices(simplices)
triangles=go.Mesh3d(x=x,
y=y,
z=z,
facecolor=facecolor,
i=I,
j=J,
k=K,
name=''
)
if plot_edges is None:# the triangle sides are not plotted
return [triangles]
else:
#define the lists Xe, Ye, Ze, of x, y, resp z coordinates of edge end points for each triangle
#None separates data corresponding to two consecutive triangles
lists_coord=[[[T[k%3][c] for k in range(4)]+[ None] for T in tri_vertices] for c in range(3)]
Xe, Ye, Ze=[reduce(lambda x,y: x+y, lists_coord[k]) for k in range(3)]
#define the lines to be plotted
lines=go.Scatter3d(x=Xe,
y=Ye,
z=Ze,
mode='lines',
line=dict(color= 'rgb(50,50,50)', width=1.5)
)
return [triangles, lines]
# Call this function for data associated to Moebius band:
# In[12]:
data1=plotly_trisurf(x,y,z, tri.simplices, colormap=cm.RdBu, plot_edges=True)
# Set the layout of the plot:
# In[13]:
axis = dict(
showbackground=True,
backgroundcolor="rgb(230, 230,230)",
gridcolor="rgb(255, 255, 255)",
zerolinecolor="rgb(255, 255, 255)",
)
layout = go.Layout(
title='Moebius band triangulation',
width=800,
height=800,
scene=dict(
xaxis=dict(axis),
yaxis=dict(axis),
zaxis=dict(axis),
aspectratio=dict(
x=1,
y=1,
z=0.5
),
)
)
fig1 = go.Figure(data=data1, layout=layout)
py.iplot(fig1, filename='Moebius-band-trisurf')
# ### Triangularization of the surface $z=\sin(-xy)$, defined over a disk ###
# We consider polar coordinates on the disk, $D(0, 1)$, centered at origin and of radius 1, and define
# a meshgrid on the set of points $(r, \theta)$, with $r\in[0,1]$ and $\theta\in[0,2\pi]$:
# In[14]:
n=12 # number of radii
h=1.0/(n-1)
r = np.linspace(h, 1.0, n)
theta= np.linspace(0, 2*np.pi, 36)
r,theta=np.meshgrid(r,theta)
r=r.flatten()
theta=theta.flatten()
#Convert polar coordinates to cartesian coordinates (x,y)
x=r*np.cos(theta)
y=r*np.sin(theta)
x=np.append(x, 0)# a trick to include the center of the disk in the set of points. It was avoided
# initially when we defined r=np.linspace(h, 1.0, n)
y=np.append(y,0)
z = np.sin(-x*y)
points2D=np.vstack([x,y]).T
tri=Delaunay(points2D)
# Plot the surface with a modified layout:
# In[15]:
data2=plotly_trisurf(x,y,z, tri.simplices, colormap=cm.cubehelix, plot_edges=None)
fig2 = go.Figure(data=data2, layout=layout)
fig2['layout'].update(dict(title='Triangulated surface',
scene=dict(camera=dict(eye=dict(x=1.75,
y=-0.7,
z= 0.75)
)
)))
py.iplot(fig2, filename='trisurf-cubehx')
# This example is also given as a demo for matplotlib [`plot_trisurf`](http://matplotlib.org/examples/mplot3d/trisurf3d_demo.html).
# ### Plotting tri-surfaces from data stored in ply-files ###
# A PLY (Polygon File Format or Stanford Triangle Format) format is a format for storing graphical objects
# that are represented by a triangulation of an object, resulted usually from scanning that object. A Ply file contains the coordinates of vertices, the codes for faces (triangles) and other elements, as well as the color for faces or the normal direction to faces.
#
# In the following we show how we can read a ply file via the Python package, `plyfile`. This package can be installed with `pip`.
#
# We choose a ply file from a list provided [here](http://people.sc.fsu.edu/~jburkardt/data/ply/ply.html).
# In[16]:
get_ipython().system('pip install plyfile')
from plyfile import PlyData, PlyElement
import urllib2
req = urllib2.Request('http://people.sc.fsu.edu/~jburkardt/data/ply/chopper.ply')
opener = urllib2.build_opener()
f = opener.open(req)
plydata = PlyData.read(f)
# Read the file header:
# In[17]:
for element in plydata.elements:
print element
nr_points=plydata.elements[0].count
nr_faces=plydata.elements[1].count
# Read the vertex coordinates:
# In[18]:
points=np.array([plydata['vertex'][k] for k in range(nr_points)])
points[0]
x,y,z=zip(*points)
faces=[plydata['face'][k][0] for k in range(nr_faces)]
faces[0]
# Now we can get data for a Plotly plot of the graphical object read from the ply file:
# In[19]:
data3=plotly_trisurf(x,y,z, faces, colormap=cm.RdBu, plot_edges=None)
title="Trisurf from a PLY file "+\
"Data Source: [1]"
noaxis=dict(showbackground=False,
showline=False,
zeroline=False,
showgrid=False,
showticklabels=False,
title=''
)
fig3 = go.Figure(data=data3, layout=layout)
fig3['layout'].update(dict(title=title,
width=1000,
height=1000,
scene=dict(xaxis=noaxis,
yaxis=noaxis,
zaxis=noaxis,
aspectratio=dict(x=1, y=1, z=0.4),
camera=dict(eye=dict(x=1.25, y=1.25, z= 1.25)
)
)
))
py.iplot(fig3, filename='Chopper-Ply-cls')
# This a version of the same object plotted along with triangle edges:
# In[20]:
from IPython.display import HTML
HTML('')
# #### Reference
# See https://plotly.com/python/reference/ for more information and chart attribute options!
# In[21]:
from IPython.display import display, HTML
display(HTML(''))
display(HTML(''))
get_ipython().system(' pip install git+https://github.com/plotly/publisher.git --upgrade')
import publisher
publisher.publish(
'triangulation.ipynb', 'python/surface-triangulation/', 'Surface Triangulation',
'How to make Tri-Surf plots in Python with Plotly.',
title = 'Python Surface Triangulation | plotly',
name = 'Surface Triangulation',
has_thumbnail='true', thumbnail='thumbnail/trisurf.jpg',
language='python',
display_as='3d_charts', order=11,
ipynb= '~notebook_demo/71')
# In[ ]: