jupyter-widgets / pythreejs

A Jupyter - Three.js bridge
https://pythreejs.readthedocs.io
Other
942 stars 188 forks source link

"object not an instance of THREE.Object3D" when using BufferGeometry #195

Closed jpanetta closed 5 years ago

jpanetta commented 6 years ago

The code below successfully displays a triangle. But if I comment out the "time.sleep" call, three.js prints THREE.Object3D.add: object not an instance of THREE.Object3D to the JavaScript console and renders an empty scene. Apparently some time is needed to initialize the Mesh object on the JavaScript side before it can be added to scene/group.

Am I doing something wrong, or is this a bug? Is there a mechanism to notify my Python code when it is safe to add the Mesh object to the scene?

from pythreejs import *
import time

scene = Scene(children=[AmbientLight(intensity=0.5)])

vertices = BufferAttribute(array=np.array(
        [[-1.0, -1.0,  0.0],
         [ 1.0, -1.0,  0.0],
         [ 1.0,  1.0,  0.0]], dtype=np.float32), normalized=False)
index = BufferAttribute(array=np.array(
        [[0, 1, 2]], dtype=np.uint16).ravel(), normalized=False)
geom = BufferGeometry(attributes={
        'position': vertices,
        'index': index,
    })

surf=Mesh(geometry=geom, material=MeshLambertMaterial(color='red'))
time.sleep(0.5)
scene.add(surf)

c = PerspectiveCamera(position=[0, 0, 1], up=[0, 0, 1])
Renderer(camera=c, scene=scene, controls=[OrbitControls(controlling=c)])
vidartf commented 6 years ago

Thanks for the report! I'm not currently able to reproduce this. Would you mind sharing the following general information:

This is just some information so that I can narrow it a bit before asking more specific questions. Normally, the widget/comm protocol and JS code should ensure that the object is fully created before a reference to the widget is passed to three.js. If you are able, can you go to the line producing the error and set a breakpoint to see what value the object is? I.e. the object that "is not an instance of THREE.Object3D".

jpanetta commented 6 years ago

I set a breakpoint, and apparently the object is undefined. The failing command is this.children.push(a) within three.js's add function, which called from assignChildren in Object3D.js, which is in turn called from syncToThreeObj in Three.js.

vidartf commented 6 years ago

Thanks for the information. Since it happens only in notebook, and not in lab, could you try to update jupyter notebook and see if that changes anything?

jpanetta commented 6 years ago

Thanks for the suggestion--apparently updating Jupyter Notebook to 5.5.0 fixes the problem. It's unfortunate/disconcerting that the latest version in Ubuntu's packages (5.2.2) doesn't work. Do you think this is a bug that was recently fixed in Jupyter, or could it be just a coincidence that my example works now and this problem may reemerge in the future?

maartenbreddels commented 6 years ago

Did you install ipyvolume?

jpanetta commented 6 years ago

Yes, I had been playing around with ipyvolume, so it was installed/enabled.

vidartf commented 6 years ago

I think you might have encountered a proper race-condition for pythreejs. I might have been making too strong assumptions about the order in which certain code paths were taken. I need to look around a little more to make sure.

Technical break down for posterity (all for JS side):

The problem is that the syncing code (convertThreeTypeModelToThree()) is synchronous, and therefore doesn't check / use the initPromise of B. I think a lot of code depends on this currently (i.e. syncing being synchronous), so any refactoring will need to be very careful about promises being chained correctly.

maartenbreddels commented 6 years ago

Maybe the wrong threejs gets loaded, could you try uninstalling ipyvolume?

vidartf commented 6 years ago

Technical: A possible solution could be to use a custom deserializer for other pythreejs widgets on the JS side, which would only resolve the widget model once the initPromise resolves. This would need to be vetted against current assumptions.

jpanetta commented 6 years ago

It doesn't seem to have anything to do with ipyvolume. I can reproduce the bug with the following steps on a fresh Ubuntu 18.04 virtual machine:

sudo apt install jupyter-notebook
pip3 install pythreejs
sudo apt remove python3 ipywidgets # otherwise we get an error with the following command...
sudo jupyter nbextension install --py --symlink --sys-prefix pythreejs
sudo jupyter nbextension enable --py --sys-prefix pythreejs
jupyter notebook
vidartf commented 6 years ago

I added some code in #196 to play around with. I'm most concerned about possible dead-lock scenarios because of circular dependencies.

jpanetta commented 6 years ago

Thanks for looking into this! Unfortunately, when I try that pull request in my VM, I get several javascript errors just trying to create an object:

This entire block of errors is actually repeated twice even though I'm only running Object3D() once.

vidartf commented 6 years ago

Sorry, I'm a little busy atm, so I didn't have time to test the code properly (hence the WIP tag). I pushed some more fixes, but I still wouldn't call the code ready yet.

jpanetta commented 6 years ago

Sure, I understand. I wish I could be of more help, but unfortunately I'm inexperienced at javascript. It seems the latest code you pushed causes an infinite recursive call to unpack_models on widget.js:46.