Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Integration with Sphinx #5

Closed
Peque opened this issue Sep 28, 2019 · 32 comments
Closed

Integration with Sphinx #5

Peque opened this issue Sep 28, 2019 · 32 comments

Comments

@Peque
Copy link

Peque commented Sep 28, 2019

I am considering integrating this with a Sphinx extension I made which allowed you to display 3D renders out of CadQuery code in your documentation:

In order to do so, I need to embed the HTML code in the documentation. Is there any function that outputs the required HTML in this project?

I will have a look myself, but in case you read it before I do... It will be faster. 😜

@bernhard-42
Copy link
Owner

@Peque The whole project is heavily based ipywidgets (https://github.com/jupyter-widgets/ipywidgets) and pythreejs (which is a widget, https://github.com/jupyter-widgets/pythreejs). The image buttons and the navigation tree are widgets written by me. I have no idea whether this can be easily embedded somewhere else, since widgets have a mechanism to allow communication between the frontend (javascript) and the backend (python).
I have never looked into embedding it somewhere else than jupyter, however there is: https://ipywidgets.readthedocs.io/en/latest/embedding.html

@Peque
Copy link
Author

Peque commented Sep 28, 2019

Thanks, you saved me a lot of time. ❤️

@Peque
Copy link
Author

Peque commented Sep 29, 2019

@bernhard-42 I have been working on this without much success. I managed to create some HTML code with ipywidgets' embed_minimal_html(), but would not show anything when displayed on the browser.

I guess you are not using any intermediate format to pass from CadQuery/Python to threejs/JS (like glTF), right? If you were, that would be awesome, and much easier to work with. 😂

@jmwright
Copy link

I think that the CQParts team did some work with glTF, but I'm not sure if that's helpful here.

https://github.com/cqparts/cqparts

https://github.com/cqparts/cqparts/blob/c57d29e280ff3f92c3ac11337d517b7ae1d789b9/tests/t_cqparts/test_codecs.py#L313

@Peque
Copy link
Author

Peque commented Sep 29, 2019

@jmwright Thanks for the pointer. 👍 😊

To be honest, I did not consider CQParts because it is not compatible with CadQuery-OCC.

@fragmuffin Would it be possible to (easily) use CQParts' glTF exporter with the current CadQuery-OCC?

@jmwright
Copy link

I think there's been some work done on OCC compatibility.

cqparts/cqparts#144

Probably worth tagging @zignig here too.

@cua-cua
Copy link

cua-cua commented Nov 15, 2019

@bernhard-42, based on your previous comment , I managed to get the cadquery object (without image buttons and the navigation tree) working with embed_minimal_html(). Basically the idea was to use the pythreejs infrastructure to start with the debugging of html transformation.
From your example b) Example: CadQuery using Sidecar) I continued with:

from jupyter_cadquery.cad_display import CadqueryView

view = CadqueryView()
for shape in a1.collect_shapes():
    view.add_shape(shape["name"], shape["shape"], shape["color"])`
`renderer = view.render()

With this, I got a pythreejs.core.Renderer object that I displayed on jupyter notebook with:

 from IPython.display import display
display(renderer)

image

With a renderer from pythreejs, I already had the input for embed_minimal_html method with:

from ipywidgets import embed
embed.embed_minimal_html('export.html', views=renderer, title='Renderer')

The thing is that when I opened the html generated file ('export.html') with the navigator, I could not see anything and the console error was related to a " Class TreeModel" not found.

image

I removed by hand the reference to the Class TreeModel, and that worked. export_modified

My questions are:

  • Could be possible to generate an html without a reference to class TreeModel, so use a pythreejs object without TreeModel reference?
  • Could be possible to generate a object 'Renderer' that includes also buttons and navigation tree so it matches with embed_minimal_html? I tried to modify jupyter-cadquery to match exactly the structure proposed on widget-cookiecutter but I got stuck.

@bernhard-42
Copy link
Owner

@cua-cua Sounds like a good idea and I will look into it.
I am travelling for the next 6 days, so earliest would be next weekend to check how this can be achieved. I hope that point 1 (leaving out TreeModel) would be an easy job, but no idea about your second point.
In case you have time and find a solution for either of the topics before I am back at my computer, I am also happy to accept pull requests ;-)

@cua-cua
Copy link

cua-cua commented Nov 18, 2019

@bernhard-42 , I managed to remove the reference to the class TreeModel. Somehow, it appears if I include the IPython display code on the jupyter notebook:

from jupyter_cadquery.cad_display import CadqueryView
 from IPython.display import display
display(renderer)

Removing that, the HTML output is OK:

a1.collect_shapes()
    view = CadqueryView()
    for shape in a1.collect_shapes():
        view.add_shape(shape["name"], shape["shape"], shape["color"])
    renderer = view.render()
embed.embed_minimal_html('export.html', views=renderer, title='Renderer')

So, at least one point solved ^^.

Have a nice trip!

@cua-cua
Copy link

cua-cua commented Nov 18, 2019

@bernhard-42, I was wrong on the last comment. display(renderer) is not the code that generates the TreeModel reference on the HTML output, but show(a1, axes=True, grid=True, ortho=True, axes0=True) from the example. Removing it, the HTML output works (with pythreejs object)

@bernhard-42
Copy link
Owner

@cua-cua
I have fixed the TreeModel issue.

A straight forward approach now is to use

w = show(a1)

adapt the cad view as wanted (axis, viewpoint, transparency, ...) and then call

from ipywidgets.embed import embed_minimal_html
embed_minimal_html('export.html', views=[w.cq_view.renderer], title='Renderer')

Using w.cq_view.renderer this will save the exact state of the visible pythreejs view.

Of course, you can also call w = show(a1, *params) where params is the dict of show parameters you'd like to be used and then call the embed_minimal_html with views=w.cq_view.renderer

Notes:

  1. If you use sidecarthen you need to close it first:

    from jupyter_cadquery import cad_display
    cad_display.SIDECAR.close()
    
  2. For some reason (that I don't understand), every export is in perspective mode. Somehow the CombinedCamera of pythreejs does not save the state as orthographic when the view is orthographic.

  3. Buttons and treeview can be exported, however the interaction logic of the UI is implemented in Python. So the treeview and the buttons won't have any effect in an exported HTML page.

@cua-cua
Copy link

cua-cua commented Nov 25, 2019

@bernhard-42 Thank you very much! It works flawlessly ^^

@Peque
Copy link
Author

Peque commented Dec 18, 2019

@bernhard-42 Trying out a simple assembly-to-HTML code:

from cadquery import Workplane
from ipywidgets import embed
from jupyter_cadquery.cad_view import CadqueryView
from jupyter_cadquery.cadquery import Assembly
from jupyter_cadquery.cadquery import Part


# Create a simple assembly
box1 = Workplane('XY').box(10, 10, 10).translate((0, 0, 5))
a1 = Assembly([Part(box1)], "example 1")

# Generate HTML
a1.collect_shapes()
view = CadqueryView()
for shape in a1.collect_shapes():
    view.add_shape(shape["name"], shape["shape"], shape["color"])
renderer = view.render()
embed.embed_minimal_html('export.html', views=renderer, title='Renderer')

I end up with the camera initially looking at (0, 0, 0) instead of camera_target. Once I interact with it, the view is correctly positioned to look at camera_target (at the center of the part):

output

Do you know how could I fix that?

@bernhard-42
Copy link
Owner

@Peque Sorry, no idea. I don't use embedded widgets. Maybe the embeddingjavascript code does not callupdate()` at the beginning but when you interact it will - just a wild guess (it will be hidden somewhere in https://unpkg.com/@jupyter-widgets/html-manager@^0.18.0/dist/embed-amd.js)

@Peque
Copy link
Author

Peque commented Dec 23, 2019

@bernhard-42 Thanks. 😊

Opened an issue in ipywidgets, in case they can shed some light. 🤞 😇

@Peque
Copy link
Author

Peque commented Dec 29, 2019

I also asked a question in Stack Overflow, with a bounty, but it seems it is not getting much attention. 😅

@jasongrout
Copy link

@bernhard-42
Copy link
Owner

@Peque
It did take some time, but then I remembered. I had a similar problem with jupyter_cadquery:

# Workaround: Zoom forth and back to update frame. Sometimes necessary :(
self.camera.zoom = camera_zoom + 0.01
self._update()
self.camera.zoom = camera_zoom
self._update()

I am not sure, but I think it got introduced when I switched to CombinedCamera.

Not that I have a solution now, but a stronger indication that a call to update might help:

def _update(self):
self.controller.exec_three_obj_method('update')
pass

Do you have any chance of calling a javascript function?

@Peque
Copy link
Author

Peque commented Jan 2, 2020

@bernhard-42 Thanks for the update.

Do you have any chance of calling a javascript function?

🤷‍♀️

Do not know much about JS. But will ask someone in case they know the answer to that question. 😂

I also had a quick look at the code @jasongrout pointed to, but did not understand what was going on or where to look. 😅

@bernhard-42
Copy link
Owner

bernhard-42 commented Jan 3, 2020

@Peque I replaced the zoom / update cascade by self.controller.reset() and published 0.9.3.
For me the export now works as expected.
Could you please give it a try?
Thanks

@Peque
Copy link
Author

Peque commented Jan 3, 2020

@bernhard-42 I am getting the same results with 0.9.3.

Not only locally, but also with Binder. The code I am used is the code shared before. I shared instructions on to reproduce it with Binder in my StackOverflow question.

Can you share the code you are using so that I can try it locally and see if it works for me too?

@bernhard-42
Copy link
Owner

If I remove the translate(0, 0, 5) from your code
embed
it works for me.

@Peque
Copy link
Author

Peque commented Jan 3, 2020

@bernhard-42 Yeah, but that's cheating! 😜

If you remove the Z translation, then the center of the cube (where the controller is correctly pointing to) is (0, 0, 0). The problem is that the camera is initially pointing to (0, 0, 0), but should be pointing to the center of the cube.

Basically, the Z translation is part of the minimal reproducible example. It could be reproduced with other shapes, as long as their center is not at (0, 0, 0).

The perspective is also lost (i.e.: the exported cube does not use an orthogonal view).

@bernhard-42
Copy link
Owner

ok, fair enough, let's keep translate for further analysis ;-)

On the other note, I looked at the orthogonal issue and copied the example from https://github.com/jupyter-widgets/pythreejs/blob/master/examples/Combined%20Camera.ipynb and slightly changed it:

from pythreejs import *
from IPython.display import display

mesh1 = Mesh(SphereBufferGeometry(10, 8, 8), MeshLambertMaterial(color='red', opacity=0.5, transparent=True), position=[-20, 0, 0])
mesh2 = Mesh(BoxBufferGeometry(20, 16, 16), MeshLambertMaterial(color='green', opacity=0.5, transparent=True), position=[20, 0, 0])

view_width = 600
view_height = 400
camera = CombinedCamera(position=[0, 0, 60], width=view_width, height=view_height)

key_light = PointLight(position=[-100, 100, 100])
ambient_light = AmbientLight(intensity=0.4)
scene = Scene(children=[mesh1, mesh2, key_light, ambient_light, camera])
renderer = Renderer(scene=scene, camera=camera, controls=[OrbitControls(controlling=camera)],
                    width=view_width, height=view_height)
camera.mode = 'orthographic'
embed.embed_minimal_html('export4.html', views=renderer, title='Renderer')
display(renderer)

It produces an orthographic view in Jupyter but a perspective view in the export.html. Look's like CombinedCamera doesn't work for embedding.

Unfortunately, I don't get OrthographicCamera to work (it doesn't allow to rotate and I have issues with the parameters) ...

@Peque
Copy link
Author

Peque commented Jan 3, 2020

@bernhard-42 That is great! Being able to get jupyter-cadquery out of the equation. 😊

Would you mind copying that last message on https://github.com/jupyter-widgets/ipywidgets/issues/2649 ? I think it makes more sense there.

@bernhard-42
Copy link
Owner

@Peque I adapted the pure pythreejs sample to be similar to your problem. After exporting, the view does not jump ...
Looks like this is something hidden in jupyter_cadquery :-(

from pythreejs import *
from IPython.display import display

mesh = Mesh(BoxBufferGeometry(10, 10, 10), 
            MeshLambertMaterial(color='green', opacity=0.5, transparent=True), 
            position=[0, 5, 0])

view_width = 600
view_height = 400
camera = CombinedCamera(position=[0, 0, 60], width=view_width, height=view_height)

key_light = PointLight(position=[-100, 100, 100])
ambient_light = AmbientLight(intensity=0.4)
scene = Scene(children=[mesh, key_light, ambient_light, camera])
renderer = Renderer(scene=scene, camera=camera, controls=[OrbitControls(controlling=camera)],
                    width=view_width, height=view_height)
camera.mode = 'orthographic'
camera.zoom = 4
embed.embed_minimal_html('export9.html', views=renderer, title='Renderer')
display(renderer)

@bernhard-42
Copy link
Owner

Thanks @Peque for reproducing the jumping issue without jupyter-cadquery!

@bernhard-42
Copy link
Owner

@Peque I updated your issue in the ipywidgets repository with my findings when I rewrote the jupyter-cadquery viewing code.
I hope the issue is now clearer ...

@bernhard-42
Copy link
Owner

Unfortunately, still not fixed in pythreejs :-(

@bernhard-42
Copy link
Owner

@Peque
This issue is quite old, isn't it? However, I recently published orbitcontrol-patch(see jupyter-widgets/pythreejs#308 (comment)) and integrated it into Jupyter-CadQuery - available in the latest release candidate of v2.2.0.

Exports now work as expected.

You might not be interested any more after such a long time, however, I wanted to let you know that this did not get forgotten :-)

@Peque
Copy link
Author

Peque commented May 19, 2021

@bernhard-42 Thanks for the update! Due to time constraints at this moment I will not be able to work on this, but it is good to know. 😊

@bernhard-42
Copy link
Owner

In the meantime with Jupyter CadQuery 3.0.0 the whole CAD view can be exported to a web page:

image

Hence, closing the issue

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

5 participants