import numpy as np
import param
from plotly import colors
from plotly.figure_factory._trisurf import trisurf as trisurface
from ...core.options import SkipRendering
from .chart import CurvePlot, ScatterPlot
from .element import ColorbarPlot, ElementPlot
from .selection import PlotlyOverlaySelectionDisplay
[docs]class Chart3DPlot(ElementPlot):
aspect = param.Parameter(default='cube')
camera_angle = param.NumericTuple(default=(0.2, 0.5, 0.1, 0.2))
camera_position = param.NumericTuple(default=(0.1, 0, -0.1))
camera_zoom = param.Integer(default=3)
projection = param.String(default='3d')
width = param.Integer(default=500)
height = param.Integer(default=500)
zticks = param.Parameter(default=None, doc="""
Ticks along z-axis specified as an integer, explicit list of
tick locations, list of tuples containing the locations.""")
def get_data(self, element, ranges, style, **kwargs):
return [dict(x=element.dimension_values(0),
y=element.dimension_values(1),
z=element.dimension_values(2))]
[docs]class SurfacePlot(Chart3DPlot, ColorbarPlot):
style_opts = ['visible', 'alpha', 'lighting', 'lightposition', 'cmap']
selection_display = PlotlyOverlaySelectionDisplay(supports_region=False)
@classmethod
def trace_kwargs(cls, is_geo=False, **kwargs):
return {'type': 'surface'}
def graph_options(self, element, ranges, style, **kwargs):
opts = super().graph_options(element, ranges, style, **kwargs)
copts = self.get_color_opts(element.vdims[0], element, ranges, style)
return dict(opts, **copts)
def get_data(self, element, ranges, style, **kwargs):
return [dict(x=element.dimension_values(0, False),
y=element.dimension_values(1, False),
z=element.dimension_values(2, flat=False))]
[docs]class Scatter3DPlot(Chart3DPlot, ScatterPlot):
style_opts = [
'visible', 'marker', 'color', 'cmap', 'alpha', 'opacity', 'size', 'sizemin'
]
_supports_geo = False
selection_display = PlotlyOverlaySelectionDisplay(supports_region=False)
@classmethod
def trace_kwargs(cls, is_geo=False, **kwargs):
return {'type': 'scatter3d', 'mode': 'markers'}
[docs]class Path3DPlot(Chart3DPlot, CurvePlot):
_per_trace = True
_nonvectorized_styles = []
@classmethod
def trace_kwargs(cls, is_geo=False, **kwargs):
return {'type': 'scatter3d', 'mode': 'lines'}
def graph_options(self, element, ranges, style, **kwargs):
opts = super().graph_options(element, ranges, style, **kwargs)
opts['line'].pop('showscale', None)
return opts
def get_data(self, element, ranges, style, **kwargs):
return [dict(x=el.dimension_values(0), y=el.dimension_values(1),
z=el.dimension_values(2))
for el in element.split()]
[docs]class TriSurfacePlot(Chart3DPlot, ColorbarPlot):
style_opts = ['cmap', 'edges_color', 'facecolor']
selection_display = PlotlyOverlaySelectionDisplay(supports_region=False)
def get_data(self, element, ranges, style, **kwargs):
try:
from scipy.spatial import Delaunay
except ImportError:
raise SkipRendering("SciPy not available, cannot plot TriSurface") from None
x, y, z = (element.dimension_values(i) for i in range(3))
points2D = np.vstack([x, y]).T
tri = Delaunay(points2D)
simplices = tri.simplices
return [dict(x=x, y=y, z=z, simplices=simplices)]
def graph_options(self, element, ranges, style, **kwargs):
opts = super().graph_options(
element, ranges, style, **kwargs
)
copts = self.get_color_opts(element.dimensions()[2], element, ranges, style)
opts['colormap'] = [tuple(v/255. for v in colors.hex_to_rgb(c))
for _, c in copts['colorscale']]
opts['scale'] = [l for l, _ in copts['colorscale']]
opts['show_colorbar'] = self.colorbar
opts['edges_color'] = style.get('edges_color', 'black')
opts['plot_edges'] = 'edges_color' in style
opts['colorbar'] = copts.get('colorbar', None)
return {k: v for k, v in opts.items() if 'legend' not in k and k != 'name'}
[docs] def init_graph(self, datum, options, index=0, **kwargs):
# Pop colorbar options since these aren't accepted by the trisurf
# figure factory.
colorbar = options.pop('colorbar', None)
trisurface_traces = trisurface(**dict(datum, **options))
# Find colorbar to set colorbar options. Colorbar is associated with
# a `scatter3d` scatter trace.
if colorbar:
marker_traces = [trace for trace in trisurface_traces
if trace.type == 'scatter3d' and
trace.mode == 'markers']
if marker_traces:
marker_traces[0].marker.colorbar = colorbar
return {'traces': [t.to_plotly_json() for t in trisurface_traces]}