Commit 2a76fced authored by mehtank's avatar mehtank
Browse files

Move to plotly instead of matplotlib

parent 6055b43f
......@@ -3,10 +3,7 @@ import math
import numpy
import logging
from stl import mesh
from mpl_toolkits import mplot3d
from matplotlib import pyplot
from matplotlib.colors import LightSource
from matplotlib import cm
import plotly.graph_objects as go
from rocolib.api.composables.graph.Drawing import *
from rocolib.api.composables.graph.DrawingEdge import *
......@@ -14,63 +11,76 @@ from rocolib.api.composables.graph.DrawingEdge import *
log = logging.getLogger(__name__)
def display3D(fh, ph=None, show=False):
### copied from https://chart-studio.plotly.com/~empet/15276/converting-a-stl-mesh-to-plotly-gomes/#/
def stl2mesh3d(stl_mesh):
# this function extracts the unique vertices and the lists I, J, K to define a Plotly mesh3d
p, q, r = stl_mesh.vectors.shape #(p, 3, 3)
# the array stl_mesh.vectors.reshape(p*q, r) can contain multiple copies of the same vertex;
# extract unique vertices from all mesh triangles
vertices, ixr = numpy.unique(stl_mesh.vectors.reshape(p*q, r), return_inverse=True, axis=0)
I = numpy.take(ixr, [3*k for k in range(p)])
J = numpy.take(ixr, [3*k+1 for k in range(p)])
K = numpy.take(ixr, [3*k+2 for k in range(p)])
return vertices, I, J, K
def plotlyFigure(stlmesh, color):
vertices, I, J, K = stl2mesh3d(stlmesh)
x, y, z = vertices.T
colorscale= [[0, color], [1, color]]
mesh3D = go.Mesh3d(
x=x,
y=y,
z=z,
i=I,
j=J,
k=K,
flatshading=True,
colorscale=colorscale,
intensity=z,
name='model',
showscale=False)
layout = go.Layout(
paper_bgcolor='rgba(0,0,0,0)',
plot_bgcolor='rgba(0,0,0,0)',
width=1024,
height=1024,
scene_xaxis_visible=False,
scene_yaxis_visible=False,
scene_zaxis_visible=False)
fig = go.Figure(data=[mesh3D], layout=layout)
fig.update_layout(margin=dict(r=0, l=0, b=0, t=0),
scene_aspectmode='data',
width=1024)
fig.data[0].update(lighting=dict(ambient= 0.18,
diffuse= 1,
fresnel= .1,
specular= 0,
roughness= .1,
facenormalsepsilon=0))
fig.data[0].update(lightposition=dict(x=3000,
y=3000,
z=10000));
return fig
def display3D(fh, ph=None, show=False, color="#ccccff"):
### Modified from https://pypi.org/project/numpy-stl/ docs
### and https://stackoverflow.com/questions/56864378/how-to-light-and-shade-a-poly3dcollection
# Create a new plot
figure = pyplot.figure()
axes = mplot3d.Axes3D(figure)
# Load the STL mesh
stlmesh = mesh.Mesh.from_file(None, fh=fh)
# Back to units of mm
stlmesh.vectors *= 1000
stlmesh.update_normals()
polymesh = mplot3d.art3d.Poly3DCollection(stlmesh.vectors)
if not len(stlmesh.points):
log.error("No points in STL mesh, skipping display3D entirely")
return
# Apply light source
try:
ls = LightSource(azdeg=225, altdeg=45)
# Darkest shadowed surface, in rgba
dk = numpy.array([0.3, 0.1, 0.2, 1])
# Brightest lit surface, in rgba
lt = numpy.array([0.7, 0.8, 1.0, 1])
# Interpolate between the two, based on face normal
shade = lambda s: (lt-dk) * s + dk
sns = ls.shade_normals(stlmesh.get_unit_normals(), fraction=1.0)
rgba = numpy.array([shade(s) for s in sns])
polymesh.set_facecolor(rgba)
except Exception as e:
log.error("Couldn't shade normals, skipping")
log.error(repr(e))
axes.add_collection3d(polymesh)
# Adjust limits of axes to fill the mesh, but keep 1:1:1 aspect ratio
try:
pts = stlmesh.points.reshape(-1,3)
ptp = max(numpy.ptp(pts, 0))/2.5 ### Should be 2; this is a bit bigger because margins
ctrs = [(min(pts[:,i]) + max(pts[:,i]))/2 for i in range(3)]
lims = [[ctrs[i] - ptp, ctrs[i] + ptp] for i in range(3)]
axes.auto_scale_xyz(*lims)
except Exception as e:
log.error("Couldn't rescale axes, skipping")
log.error(repr(e))
fig = plotlyFigure(stlmesh, color)
if ph is not None:
axes.axis('off')
pyplot.savefig(ph, format='png', dpi=300, transparent=True, bbox_inches='tight', pad_inches=0)
fig.write_image(ph)
if show:
# Show the plot to the screen
axes.axis('on')
pyplot.show()
#def displayTkinter(dwg, height = 500, width = 700, showFlats = True):
fig.update_layout(scene_xaxis_visible=True,
scene_yaxis_visible=True,
scene_zaxis_visible=True)
fig.show()
class DisplayApp:
def __init__(self, dwg, height = 500, width = 700, showFlats = True):
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment