Black Lives Matter. Please consider donating to Black Girls Code today.
Learn how to use COVID-19 data in open source Dash apps. Register for the Sept 23rd webinar with IQT!

Advanced Example for 3D Wireframe

The example on Plot.ly about the 3D Wireframes has helped me a lot. But there is one thing that bothered me: A wireframe is represented by a multitude of line objects. This is slightly unintuitive and makes the legend harder to read. Here’s a nice work-around: Use one linestrip and interrupt it by Not-a-Number values. I just wanted to share it, in case anybody else had the same issue:

from numpy import NaN, radians
import plotly

import numpy as np
import plotly.graph_objs as go


def wireframe3d( uRange, vRange, f, **kwargs ):
  def data_gen():

    for u in uRange:
      yield (NaN, NaN, NaN) # <- NaN functions as a 'line break'
      for v in vRange:
        yield f(u,v)

    for v in vRange:
      yield (NaN, NaN, NaN)
      for u in uRange:
        yield f(u,v)

  data = data_gen()
  next(data)
  data = np.vstack(data)

  def text_gen():

    for u in uRange:
      yield '???'
      for v in vRange:
        yield 'u: %.3f v: %.3f' % (u,v)

    for v in vRange:
      yield '???'
      for u in uRange:
        yield 'u: %.3f v: %.3f' % (u,v)

  text = text_gen()
  next(text)
  text = tuple(text)

  return go.Scatter3d(
    x = data[:,0], y = data[:,1], z = data[:,2],
    text = text, mode = 'lines', **kwargs
  )

surface = wireframe3d(
  np.linspace(-2,+2,11),
  np.linspace(-1,+1,11),
  lambda u,v: (u,v,u*v),
  line = dict(color='#FF0000', width=2),
  name = 'Surface'
)

sin = lambda x: np.sin(radians(x))
cos = lambda x: np.cos(radians(x))

sphere = wireframe3d(
  np.linspace(  0, 360, 19),
  np.linspace(-90, +90, 19),
  lambda u,v: ( cos(u)*cos(v), sin(u)*cos(v), sin(v) ),
  line = dict(color='#0000FF', width=2),
  name = 'Sphere'
)

layout = go.Layout(
  title='Wireframe Plot',
  scene = dict(
    aspectratio = dict(x=1, y=1, z=1),
    aspectmode = 'data'
  )
)

fig = go.Figure(data=[surface,sphere],layout=layout)
plotly.offline.plot(fig)