JSC
  • Index
  • Links
Jupyter-JSC
  1. 04-Tutorials
  2. In [1]:
    from pythreejs import *
    import numpy as np
    from IPython.display import display
    from ipywidgets import HTML, Text, Output, VBox
    from traitlets import link, dlink
    

    Simple sphere and text¶

    In [2]:
    ball = Mesh(geometry=SphereGeometry(radius=1, widthSegments=32, heightSegments=24), 
                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)
    
    In [3]:
    ball.scale = (0.5,) * 3
    
    In [4]:
    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)
    

    Clickable Surface¶

    In [5]:
    # 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='hotpink'))
    
    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='red'),
                     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'])
    g({'new': hover_point.position})
    hover_picker.observe(g, names=['point'])
    
    display(VBox([h, renderer, out]))
    
    ---------------------------------------------------------------------------
    ModuleNotFoundError                       Traceback (most recent call last)
    <ipython-input-5-ea9b46f5dc29> in <module>
         19 
         20 surf = Mesh(geometry=surf_g,
    ---> 21             material=MeshLambertMaterial(map=height_texture(z[::-1], 'YlGnBu_r')))
         22 
         23 surfgrid = SurfaceGrid(geometry=surf_g, material=LineBasicMaterial(color='black'),
    
    /usr/local/software/jureca/Stages/Devel-2019a/software/Jupyter/2019a-rc15-gcccoremkl-8.3.0-2019.3.199-Python-3.6.8/lib/python3.6/site-packages/pythreejs/pythreejs.py in height_texture(z, colormap)
        235     """Create a texture corresponding to the heights in z and the given colormap."""
        236     from matplotlib import cm
    --> 237     from skimage import img_as_ubyte
        238 
        239     colormap = cm.get_cmap(colormap)
    
    ModuleNotFoundError: No module named 'skimage'
    In [ ]:
    surf_g.z = list((-z[::-1]).flat)
    surf.material.map = height_texture(-z[::-1])
    

    Design our own texture¶

    In [ ]:
    import numpy as np
    from scipy import ndimage
    import matplotlib
    import matplotlib.pyplot as plt
    from skimage import img_as_ubyte 
    
    jet = matplotlib.cm.get_cmap('jet')
    
    np.random.seed(int(1)) # start random number generator
    n = int(5) # starting points
    size = int(32) # size of image
    im = np.zeros((size,size)) # create zero image
    points = size*np.random.random((2, n**2)) # locations of seed values
    im[(points[0]).astype(np.int), (points[1]).astype(np.int)] = size # seed high values
    im = ndimage.gaussian_filter(im, sigma=size/(float(4)*n)) # smooth high values into surrounding areas
    im *= 1/np.max(im)# rescale to be in the range [0,1]
    rgba_im = img_as_ubyte(jet(im)) # convert the values to rgba image using the jet colormap
    
    t = DataTexture(data=rgba_im, format='RGBAFormat', width=size, height=size)
    
    geometry = SphereGeometry(radius=1, widthSegments=16, heightSegments=10)#TorusKnotGeometry(radius=2, radialSegments=200)
    material = MeshLambertMaterial(map=t)
    
    myobject = Mesh(geometry=geometry, material=material)
    c = PerspectiveCamera(position=[0, 3, 3], fov=40,
                          children=[DirectionalLight(color='#ffffff', position=[3, 5, 1], intensity=0.5)])
    scene = Scene(children=[myobject, c, AmbientLight(color='#777777')])
    
    renderer = Renderer(camera=c, scene = scene, controls=[OrbitControls(controlling=c)], width=400, height=400)
    display(renderer)
    

    Lines¶

    In [ ]:
    # On windows, linewidth of the material has no effect
    size = 4
    linesgeom = Geometry(vertices=[[0, 0, 0],
                                        [size, 0, 0],
                                        [0, 0, 0],
                                        [0, size, 0],
                                        [0, 0, 0],
                                        [0, 0, size]],
                              colors = ['red', 'red', 'green', 'green', 'white', 'orange'])
    lines = Line(geometry=linesgeom, 
                 material=LineBasicMaterial(linewidth=5, vertexColors='VertexColors'), 
                 type='LinePieces',
                )
    scene = Scene(children=[
        lines,
        DirectionalLight(color='#ccaabb', position=[0,10,0]),
        AmbientLight(color='#cccccc'),
        ])
    c = PerspectiveCamera(position=[10, 10, 10])
    renderer = Renderer(camera=c, background='black', background_opacity=1, scene=scene, controls=[OrbitControls(controlling=c)],
                        width=400, height=400)
    display(renderer)
    

    Parametric Functions¶

    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 a THREE.Vector3(x,y,z) that should be modified in place.

    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.

    In [ ]:
    f = """
    function f(origu, origv, out) {
        // scale u and v to the ranges I want: [0, 2*pi]
        var u = 2*Math.PI*origu;
        var v = 2*Math.PI*origv;
        
        var x = Math.sin(u);
        var y = Math.cos(v);
        var z = Math.cos(u+v);
        
        out.set(x,y,z)
    }
    """
    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)
    

    Indexed Geometries¶

    The PlainGeometry lets you specify vertices and faces for a surface.

    In [ ]:
    from pythreejs import *
    from IPython.display import display
    
    vertices = [
        [0, 0, 0],
        [0, 0, 1],
        [0, 1, 0],
        [0, 1, 1],
        [1, 0, 0],
        [1, 0, 1],
        [1, 1, 0],
        [1, 1, 1]
    ]
    
    faces = [
        [0, 1, 3],
        [0, 3, 2],
        [0, 2, 4],
        [2, 6, 4],
        [0, 4, 1],
        [1, 4, 5],
        [2, 3, 6],
        [3, 7, 6],
        [1, 5, 3],
        [3, 5, 7],
        [4, 6, 5],
        [5, 6, 7]
    ]
    
    vertexcolors = ['#000000', '#0000ff', '#00ff00', '#ff0000',
                    '#00ffff', '#ff00ff', '#ffff00', '#ffffff']
    
    # Map the vertex colors into the 'color' slot of the faces
    faces = [f + [None, [vertexcolors[i] for i in f], None] for f in faces]
    
    # Create the geometry:
    cubeGeometry = Geometry(vertices=vertices,
        faces=faces,
        colors=vertexcolors)
    # Calculate normals per face, for nice crisp edges:
    cubeGeometry.exec_three_obj_method('computeFaceNormals')
    
    # Create a mesh. Note that the material need to be told to use the vertex colors.
    myobjectCube = Mesh(
        geometry=cubeGeometry,
        material=MeshLambertMaterial(vertexColors='VertexColors'),
        position=[-0.5, -0.5, -0.5],   # Center the cube
    )
    
    # Set up a scene and render it:
    cCube = PerspectiveCamera(position=[3, 3, 3], fov=20,
                          children=[DirectionalLight(color='#ffffff', position=[-3, 5, 1], intensity=0.5)])
    sceneCube = Scene(children=[myobjectCube, cCube, AmbientLight(color='#dddddd')])
    
    rendererCube = Renderer(camera=cCube, background='black', background_opacity=1,
                            scene=sceneCube, controls=[OrbitControls(controlling=cCube)])
    
    display(rendererCube)
    

    Buffer Geometries¶

    The PlainBufferGeometry object uses several tricks to speed up both the transfer of data and the rendering of the data.

    In [ ]:
    from pythreejs import *
    import numpy as np
    from IPython.display import display
    
    vertices = np.asarray([
        [0, 0, 0],
        [0, 0, 1],
        [0, 1, 0],
        [0, 1, 1],
        [1, 0, 0],
        [1, 0, 1],
        [1, 1, 0],
        [1, 1, 1]
    ], dtype='float32')
    
    faces = np.asarray([
        [0, 1, 3],
        [0, 3, 2],
        [0, 2, 4],
        [2, 6, 4],
        [0, 4, 1],
        [1, 4, 5],
        [2, 3, 6],
        [3, 7, 6],
        [1, 5, 3],
        [3, 5, 7],
        [4, 6, 5],
        [5, 6, 7]
    ], dtype='uint16').ravel()  # We need to flatten index array
    
    
    vertexcolors = np.asarray([(0,0,0), (0,0,1), (0,1,0), (1,0,0),
                               (0,1,1), (1,0,1), (1,1,0), (1,1,1)], dtype='float32')
    
    cubeGeometry = BufferGeometry(attributes=dict(
        position=BufferAttribute(vertices, normalized=False),
        index=BufferAttribute(faces, normalized=False),
        color=BufferAttribute(vertexcolors),
    ))
    
    myobjectCube = Mesh(
        geometry=cubeGeometry,
        material=MeshLambertMaterial(vertexColors='VertexColors'),
        position=[-0.5, -0.5, -0.5]   # Center the cube
    )
    cCube = PerspectiveCamera(
        position=[3, 3, 3], fov=20,
        children=[DirectionalLight(color='#ffffff', position=[-3, 5, 1], intensity=0.5)])
    sceneCube = Scene(children=[myobjectCube, cCube, AmbientLight(color='#dddddd')])
    
    rendererCube = Renderer(camera=cCube, background='black', background_opacity=1,
                            scene = sceneCube, controls=[OrbitControls(controlling=cCube)])
    
    display(rendererCube)
    

    Note that there are no face normals logic for buffer geometries, as the attributes are vertex attributes. If you want to add sharp edges for a BufferGeometry, you then have to duplicate the vertices (i.e., don't use an index attribute), and calculate the normals yourself.

© Forschungszentrum Jülich Imprint Privacy Policy Support Terms of Service