import numpy as np
import plotly.graph_objs as go
from plotly.subplots import make_subplots
[docs]def white_layout():
bg_color = 'rgba(0,0,0,0)'
grid_color = 'rgba(220,220,220,100)'
layout = dict(showlegend=False,
autosize=True,
height=1000,
width=1000,
margin=dict(r=20, l=20, b=20, t=20),
plot_bgcolor='rgba(255,255,255,0)',
paper_bgcolor='rgba(255,255,255,0)',
scene1=dict(xaxis=dict(dict(backgroundcolor=bg_color,
gridcolor=grid_color)),
yaxis=dict(dict(backgroundcolor=bg_color,
gridcolor=grid_color)),
zaxis=dict(dict(backgroundcolor=bg_color,
gridcolor=grid_color)),
aspectmode='cube'),
scene2=dict(xaxis=dict(dict(backgroundcolor=bg_color,
gridcolor=grid_color)),
yaxis=dict(dict(backgroundcolor=bg_color,
gridcolor=grid_color)),
zaxis=dict(dict(backgroundcolor=bg_color,
gridcolor=grid_color)),
aspectmode='cube'),
scene3=dict(xaxis=dict(dict(backgroundcolor=bg_color,
gridcolor=grid_color)),
yaxis=dict(dict(backgroundcolor=bg_color,
gridcolor=grid_color)),
zaxis=dict(dict(backgroundcolor=bg_color,
gridcolor=grid_color)),
aspectmode='cube'))
return layout
[docs]def dualplot(traces_left, traces_right, spatial_size=768, layout=None,
titles=['Left', 'Right']):
if layout is None:
layout = white_layout()
fig = make_subplots(rows=1, cols=2,
specs=[[{'type': 'scatter3d'}, {'type': 'scatter3d'}]],
subplot_titles=(titles[0], titles[1]),
horizontal_spacing=0.05, vertical_spacing=0.04)
fig.add_traces(traces_left, rows=[1]*len(traces_left), cols=[1]*len(traces_left))
fig.add_traces(traces_right, rows=[1]*len(traces_right), cols=[2]*len(traces_right))
fig.layout = layout
fig.update_layout(showlegend=True,
legend=dict(xanchor="left"),
autosize=True,
height=500,
width=1000)
fig.update_layout(
scene1 = dict(
xaxis = dict(range=[0,spatial_size],),
yaxis = dict(range=[0,spatial_size],),
zaxis = dict(range=[0,spatial_size],),),
scene2 = dict(
xaxis = dict(range=[0,spatial_size],),
yaxis = dict(range=[0,spatial_size],),
zaxis = dict(range=[0,spatial_size],),),
margin=dict(r=20, l=10, b=10, t=10))
return fig
[docs]def trace_particles(particles, color='id', size=1,
scatter_points=False,
scatter_ppn=False,
highlight_primaries=False,
colorscale='rainbow'):
'''
Get Scatter3d traces for a list of <Particle> instances.
Each <Particle> will be drawn with the color specified
by its unique particle ID.
Inputs:
- particles: List[Particle]
Returns:
- traces: List[go.Scatter3d]
'''
traces, colors = [], []
for p in particles:
colors.append(getattr(p, color))
colors = np.array(colors)
cmin, cmax = int(colors.min()), int(colors.max())
opacity = 1
for p in particles:
c = int(getattr(p, color)) * np.ones(p.points.shape[0])
if highlight_primaries:
if p.is_primary:
opacity = 1
else:
opacity = 0.01
plot = go.Scatter3d(x=p.points[:,0],
y=p.points[:,1],
z=p.points[:,2],
mode='markers',
marker=dict(
size=size,
color=c,
colorscale=colorscale,
cmin=cmin, cmax=cmax,
# reversescale=True,
opacity=opacity),
hovertext=int(getattr(p, color)),
name='Particle {}'.format(p.id)
)
traces.append(plot)
if scatter_points:
if hasattr(p, 'startpoint'):
plot = go.Scatter3d(x=np.array([p.startpoint[0]]),
y=np.array([p.startpoint[1]]),
z=np.array([p.startpoint[2]]),
mode='markers',
marker=dict(
size=3,
color='red',
# colorscale=colorscale,
opacity=1),
# hovertext=p.ppn_candidates[:, 4],
name='Startpoint {}'.format(p.id))
traces.append(plot)
if hasattr(p, 'endpoint'):
plot = go.Scatter3d(x=np.array([p.endpoint[0]]),
y=np.array([p.endpoint[1]]),
z=np.array([p.endpoint[2]]),
mode='markers',
marker=dict(
size=3,
color='red',
# line=dict(width=2, color='red'),
# cmin=cmin, cmax=cmax,
# colorscale=colorscale,
opacity=1),
# hovertext=p.ppn_candidates[:, 4],
name='Endpoint {}'.format(p.id))
traces.append(plot)
elif scatter_ppn:
plot = go.Scatter3d(x=p.ppn_candidates[:, 0],
y=p.ppn_candidates[:, 1],
z=p.ppn_candidates[:, 2],
mode='markers',
marker=dict(
size=3,
color='red',
# colorscale=colorscale,
opacity=1),
# hovertext=p.ppn_candidates[:, 4],
name='PPN {}'.format(p.id))
traces.append(plot)
return traces
[docs]def trace_interactions(interactions, color='id', colorscale="rainbow"):
'''
Get Scatter3d traces for a list of <Interaction> instances.
Each <Interaction> will be drawn with the color specified
by its unique interaction ID.
Inputs:
- particles: List[Particle]
Returns:
- traces: List[go.Scatter3d]
'''
traces, colors = [], []
for inter in interactions:
colors.append(getattr(inter, color))
colors = np.array(colors)
cmin, cmax = int(colors.min()), int(colors.max())
for idx, inter in enumerate(interactions):
particles = inter.particles
voxels = []
# Merge all particles' voxels into one tensor
for p in particles:
voxels.append(p.points)
voxels = np.vstack(voxels)
plot = go.Scatter3d(x=voxels[:,0],
y=voxels[:,1],
z=voxels[:,2],
mode='markers',
marker=dict(
size=2,
color=int(getattr(inter, color)) * np.ones(voxels.shape[0]),
colorscale=colorscale,
cmin=cmin, cmax=cmax,
reversescale=True,
opacity=1),
hovertext=int(getattr(inter, color)),
name='Interaction {}'.format(getattr(inter, color))
)
traces.append(plot)
return traces
[docs]def plotly_layout3d(ranges=None, titles=None, **kwargs):
"""
Produces go.Layout object for a certain format.
INPUTS
- ranges can be used to specify the plot region in (x,y,z) directions.
The default (None) will determine the range to include all points.
Alternatively can be an array of shape (3,2) specifying (x,y,z) axis (min,max) range for a display,
or simply a list of points with shape (N,3+) where [:,0],[:,1],[:,2] correspond to (x,y,z) values and
the plotting region is decided by measuring the min,max range in each coordinates. This last option
is useful if one wants to define the region based on a set of points that is not same as what's plotted.
- titles can be specified as a length 3 array of strings for (x,y,z) axis title respectively
OUTPUTS
- The return is go.Layout object that can be given to go.Figure for visualization (together with traces)
"""
xrange,yrange,zrange=None,None,None
if ranges is None:
ranges=[None,None,None]
elif np.shape(ranges) == (3,2):
xrange,yrange,zrange=ranges
else:
xrange = (np.min(ranges[:,0]),np.max(ranges[:,0]))
yrange = (np.min(ranges[:,1]),np.max(ranges[:,1]))
zrange = (np.min(ranges[:,2]),np.max(ranges[:,2]))
layout = go.Layout(
showlegend=True,
width=768,
height=768,
#xaxis=titles[0], yaxis=titles[1], zaxis=titles[2],
margin=dict(l=0,r=0,b=0,t=0),
scene = dict(
xaxis = dict(nticks=10, range = xrange, showticklabels=True,
title='x' if titles is None else titles[0],
backgroundcolor=None,
gridcolor="rgb(255, 255, 255)",
showbackground=True,
),
yaxis = dict(nticks=10, range = yrange, showticklabels=True,
title='y' if titles is None else titles[1],
backgroundcolor=None,
gridcolor="rgb(255, 255, 255)",
showbackground=True
),
zaxis = dict(nticks=10, range = zrange, showticklabels=True,
title='z' if titles is None else titles[2],
backgroundcolor=None,
gridcolor="rgb(255, 255, 255)",
showbackground=True,
),
aspectmode='cube',
camera = dict(
up=dict(x=0, y=0, z=1),
center=dict(x=0, y=0, z=0),
eye=dict(x=1.2, y=1.2, z=0.075)
),
),
**kwargs
)
return layout