K3D-tools / K3D-jupyter

K3D lets you create 3D plots backed by WebGL with high-level API (surfaces, isosurfaces, voxels, mesh, cloud points, vtk objects, volume renderer, colormaps, etc). The primary aim of K3D-jupyter is to be easy for use as stand alone package like matplotlib, but also to allow interoperation with existing libraries as VTK.
MIT License
916 stars 123 forks source link

K3D on Google Colab #416

Closed effepivi closed 1 year ago

effepivi commented 1 year ago

Description

I am trying to use K3D on Google Colab. The viewer appears, I can interact with it (the grid moves) but the list of objects remains empty. I have tested using my own code, and also k3D examples. The issue always happens. See below for a screenshot and the corresponding code.

Screenshot from 2023-03-14 15-15-38

What I Did

I recycled one of K3D's examples:

!pip install k3d --quiet

from google.colab import output
output.enable_custom_widget_manager()

import sys
import platform
import k3d
import random
import numpy as np
import matplotlib

print("Platform:", platform.system())
print("K3D:", k3d.__version__)
print("Python:", sys.version)

T = 1500
N = 10
P = 100
mod = 50

positions = [None] * N
colors = [None] * N
trajectories = [None] * N
colormaps = [k3d.colormaps.matplotlib_color_maps.hot]

for n in range(N):
    positions[n] = {}
    trajectories[n] = {}
    bang = False

    pos = np.zeros((P, 3), dtype=np.float32)
    velocity = np.zeros((P, 3), dtype=np.float32)
    velocity[:, 2] = 180.0 + np.random.random(P) * 0.1    

    pos[:, 0] = np.sin(n/N * 2.0 * np.pi) * 10.0
    pos[:, 1] = np.cos(n/N * 2.0 * np.pi) * 10.0

    cm = random.choice(colormaps)
    cm_space = np.linspace(np.min(cm[0::4]), np.max(cm[0::4]), 2048)

    c = (np.interp(cm_space, cm[::4], cm[3::4]) * 255).astype(np.uint32) + \
        (np.interp(cm_space, cm[::4], cm[2::4]) * 255).astype(np.uint32) * 256 + \
        (np.interp(cm_space, cm[::4], cm[1::4]) * 255).astype(np.uint32) * 256 ** 2

    colors[n] = c[np.random.randint(0, c.shape[0], P)]

    for t in range(T):
        if t > n * 30:        
            if np.mean(velocity[:, 2] < 30.0) and not bang:
                bang = True
                velocity[:, 0] += np.random.normal(size=P) * 55
                velocity[:, 1] += np.random.normal(size=P) * 55
                velocity[:, 2] += np.random.normal(size=P) * 35

            velocity[:, 2] -= 0.05
            velocity *= 0.995                
            pos += velocity * 0.001

        if t % mod == 0:
            positions[n][str(t/100.0)] = pos.copy()

for n in range(N):
    db = np.array([positions[n][k] for k in positions[n].keys()])

    for t in range(T//mod):
        anim = np.empty((P, T//mod + 1, 3), dtype=np.float32)
        anim.fill(np.nan)
        for p in range(P):
            anim[p, 0:t+1, :] = db[0:t+1, p]
            anim[p, t + 1, :] = anim[p, t, :]

        trajectories[n][str(t * mod/100.0)] = anim

plot = k3d.plot(grid=[-100,-100,-100,100,100,100], grid_auto_fit=True, camera_auto_fit=True)

for n in range(N):
    plot += k3d.points(positions[n], colors=colors[n], shader='3d', opacity=0.85, point_size=0.5)
    plot += k3d.line(trajectories[n], shader='simple')

text_positions= {
    '0.0': np.array([0,0,100]),
    '4.3': np.array([0,0,100]),
    '4.8': np.array([0,0,20])
}

text_size= {
    '0.0': 1,
    '4.0': 1,
    '4.8': 80
}

plot += k3d.texture_text('Happy New Year!', text_positions, size=text_size, color=0xaaaaaa)
plot.display()

Web console log / python logs

/usr/local/lib/python3.9/dist-packages/traittypes/traittypes.py:97: UserWarning: Given trait value dtype "int64" does not match required type "float32". A coerced copy has been created.

In case of web console log please start from point like:

K3D: (UNMASKED_VENDOR_WEBGL) Google Inc. (NVIDIA)
...

up to the last line. If you don't want to share details about your GPU please anonymise it

Screenshot from 2023-03-14 15-15-38

Mr-McGL commented 1 year ago

Hi,

A similar issue is reported in this thread [X].

So far, you can display static plots using inline snapshots [X].

%%capture __pip_output__
!pip install k3d
!pip install pyvista
import numpy as np
import k3d
from pyvista import examples

from IPython.display import HTML

You don't need to enable custom widgets

# from google.colab import output
# output.enable_custom_widget_manager()
# https://k3d-jupyter.org/gallery/showcase/terrain.html
dem = examples.download_crater_topo()
data = dem.get_array(0).reshape(dem.dimensions[::-1])[0, :, :].astype(np.float32)

plot = k3d.plot()

obj = k3d.surface(data,
            attribute=data,
            flat_shading=False,
            color_map = k3d.colormaps.matplotlib_color_maps.viridis,
            xmin=dem.bounds[0],
            xmax=dem.bounds[1],
            ymin=dem.bounds[2],
            ymax=dem.bounds[3])

plot += obj
#plot.display()
plot.snapshot_type = 'inline'
HTML(plot.get_snapshot())

--or--

plot.snapshot_type = 'inline'
with open('output.html', 'w') as f:
f.write(plot.get_snapshot())

HTML(filename='output.html')
effepivi commented 1 year ago

Thanks, it works!

artur-trzesiok commented 1 year ago

Good news @effepivi , @Mr-McGL !

Experimental COLAB support is done: https://colab.research.google.com/drive/1aZd9vvPJBZzlX5bmaAIp8eE9f_0c3IaS?usp=sharing

effepivi commented 1 year ago

@artur-trzesiok

Thanks for letting me know. I'll try it!

Mr-McGL commented 1 year ago

Thanks. It works!!!