Pythreejs is a jupyter interactive widget bringing fast WebGL 3d visualization to the Jupyter notebook.
Pythreejs is not a 3d plotting library, it only exposes the threejs scene objects to the Jupyter kernel.
Installation:
conda install -c conda-forge pythreejs
One of the many example notebooks at https://github.com/jupyter-widgets/pythreejs/tree/master/examples
from pythreejs import *
import numpy as np
from IPython.display import display
from ipywidgets import HTML, Text, Output, VBox
from traitlets import link, dlink
ball = Mesh(geometry=SphereGeometry(radius=1),
material=MeshLambertMaterial(color='red'),
position=[2, 1, 0])
c = PerspectiveCamera(position=[0, 5, 5], up=[0, 1, 0],
children=[DirectionalLight(color='white', position=[3, 5, 1], intensity=0.5)])
scene = Scene(children=[ball, c, AmbientLight(color='#777777')])
renderer = Renderer(camera=c,
scene=scene,
controls=[OrbitControls(controlling=c)])
display(renderer)
Properties of the ball can be updated, with changes reflected immediately in the visualization.
ball.scale = (0.5,) * 3
import time, math
ball.material.color = '#4400dd'
for i in range(1, 150, 2):
ball.scale = (i / 100.,) * 3
ball.position = [math.cos(i / 10.), math.sin(i / 50.), i / 100.]
time.sleep(.05)
# Generate surface data:
view_width = 600
view_height = 400
nx, ny = (20, 20)
xmax=1
x = np.linspace(-xmax, xmax, nx)
y = np.linspace(-xmax, xmax, ny)
xx, yy = np.meshgrid(x, y)
z = xx ** 2 - yy ** 2
#z[6,1] = float('nan')
# Generate scene objects from data:
surf_g = SurfaceGeometry(z=list(z[::-1].flat),
width=2 * xmax,
height=2 * xmax,
width_segments=nx - 1,
height_segments=ny - 1)
surf = Mesh(geometry=surf_g,
material=MeshLambertMaterial(map=height_texture(z[::-1], 'YlGnBu_r')))
surfgrid = SurfaceGrid(geometry=surf_g, material=LineBasicMaterial(color='black'),
position=[0, 0, 1e-2]) # Avoid overlap by lifting grid slightly
# Set up picking bojects:
hover_point = Mesh(geometry=SphereGeometry(radius=0.05),
material=MeshLambertMaterial(color='green'))
click_picker = Picker(controlling=surf, event='dblclick')
hover_picker = Picker(controlling=surf, event='mousemove')
# Set up scene:
key_light = DirectionalLight(color='white', position=[3, 5, 1], intensity=0.4)
c = PerspectiveCamera(position=[0, 3, 3], up=[0, 0, 1], aspect=view_width / view_height,
children=[key_light])
scene = Scene(children=[surf, c, surfgrid, hover_point, AmbientLight(intensity=0.8)])
renderer = Renderer(camera=c, scene=scene,
width=view_width, height=view_height,
controls=[OrbitControls(controlling=c), click_picker, hover_picker])
# Set up picking responses:
# Add a new marker when double-clicking:
out = Output()
def f(change):
value = change['new']
with out:
print('Clicked on %s' % (value,))
point = Mesh(geometry=SphereGeometry(radius=0.05),
material=MeshLambertMaterial(color='hotpink'),
position=value)
scene.add(point)
click_picker.observe(f, names=['point'])
# Have marker follow picker point:
link((hover_point, 'position'), (hover_picker, 'point'))
# Show picker point coordinates as a label:
h = HTML()
def g(change):
h.value = 'Green point at (%.3f, %.3f, %.3f)' % tuple(change['new'])
h.value += ' Double-click to add marker'
g({'new': hover_point.position})
hover_picker.observe(g, names=['point'])
display(VBox([h, renderer, out]))
# when we change the z values of the geometry, we need to also change the height map
surf_g.z = list((-z[::-1]).flat)
surf.material.map = height_texture(-z[::-1])
To use the ParametricGeometry class, you need to specify a javascript function as a string. The function should take two parameters that vary between 0 and 1, and return a new THREE.Vector3(x,y,z)
.
If you want to build the surface in Python, you'll need to explicitly construct the vertices and faces and build a basic geometry from the vertices and faces.
f = """
function f(origu, origv, vec) {
// scale u and v to the ranges I want: [0, 2*pi]
var u = 2*Math.PI*origu;
var v = 2*Math.PI*origv;
vec.x = Math.sin(u);
vec.y = Math.cos(v);
vec.z = Math.cos(u+v);
}
"""
surf_g = ParametricGeometry(func=f, slices=16, stacks=16);
surf = Mesh(geometry=surf_g, material=MeshLambertMaterial(color='green', side='FrontSide'))
surf2 = Mesh(geometry=surf_g, material=MeshLambertMaterial(color='yellow', side='BackSide'))
c = PerspectiveCamera(position=[5, 5, 3], up=[0, 0, 1],
children=[DirectionalLight(color='white',
position=[3, 5, 1],
intensity=0.6)])
scene = Scene(children=[surf, surf2, c, AmbientLight(intensity=0.5)])
renderer = Renderer(camera=c, scene=scene, controls=[OrbitControls(controlling=c)], width=400, height=400)
display(renderer)