CadQuery / cadquery

A python parametric CAD scripting framework based on OCCT
https://cadquery.readthedocs.io
Other
2.97k stars 280 forks source link

Shape.tessellate seems to be slow and crashes when poly is None #705

Open bernhard-42 opened 3 years ago

bernhard-42 commented 3 years ago

1 Performance issue

I got complaints that juypter-cadquery is very slow, so I looked into it. Despite some less optimal implementations on my side, it turns out that the python code for Shape.tessellate seems to be very slow.

If I compare the latest version of pythonocc-core 7.4.1 with CadQuery 2.1 based on OCP 7.4.1 for one of the pythonocc-demos examples (https://github.com/tpaviot/pythonocc-demos/blob/master/assets/models/RC_Buggy_2_front_suspension.stp), I find the following:

This is 1.671 sec for pythonocc-code vs. 35.272 sec for CadQuery (which means CadQuery is 20x slower, already without delivering the normals)

Question: Could the tessellate algorithm (including the currently missing normals) be added as C++ code to CadQuery? Maybe similar to https://github.com/tpaviot/pythonocc-core/tree/6564f034dcebf882d4f51c897bd0a8adfe912817/src/Tesselator

2 Bug

Additionally I had to patch Shape.tessellate from

            loc = TopLoc_Location()
            poly = BRep_Tool.Triangulation_s(f.wrapped, loc)
            Trsf = loc.Transformation()
            loc = TopLoc_Location()
            poly = BRep_Tool.Triangulation_s(f.wrapped, loc)
            if poly is None:
                continue
            Trsf = loc.Transformation()

It looks like BRep_Tool.Triangulation_s sometimes comes back with None

adam-urbanczyk commented 3 years ago

Regarding (1) how about using the GLTF or VTK export functionality from OCCT7.5? The idea behind OCP was to not add (too many) custom extensions.

Regarding (2) can you share a minimal reproducible example of how to trigger the issue?

bernhard-42 commented 3 years ago

Re 2: I habe no idea how to reproduce it. Happened to me twice, and it happens with CadQuery 2.1 on the RC car example mentioned above.

Re 1: While I understand your approach with OCP, I would think that tessellation is an important time critical feature and it should be implemented the fastest way possible. Maybe this is the reason why Thomas Paviot went the extra mile to implement his tessellation class in C++ and with an interface with minimal overhead to export into numpy. But maybe I still do not understand enough of CAD and CadQuery...

I will look into VTK, maybe it solves my problem, thanks for the suggestion

bernhard-42 commented 3 years ago

Re 2: I have no idea how to reproduce it. Happened to me twice, and it happens with CadQuery 2.1 on the RC car example mentioned above.

I found the old example again (from https://eddieliberato.github.io/blog/2018-06-26-cadquery-chess-set/), it is much smaller, maybe that helps

import cadquery as cq

# Chess board squares size
cbss = 25
right_side_up_pawns = True    # False is probably better for 3d printing

# king base diameter and height
kb = cbss*0.75
kh = kb*2.5

def knight():
    mouth = cq.Workplane("top").polygon(4, kb*0.8).extrude(kb*2).translate((kb*0.75,-kb,kb*1.3))
    kn = cq.Workplane("front").circle(kb/2).workplane(offset=kh*0.7).center(-kb*0.1,0).polygon(2, kb*1.5).loft(combine=True)
    kn = kn.cut(mouth)
    return kn

k = knight()
k.val().tessellate(0.1)

image

bernhard-42 commented 3 years ago

I was able to build an optimized tesselator in Python based on OCP (see the last comments on the issue referenced above). However it still loses a lot of time by copying data back from C++ to Python world value by value. So a C++ version as in pythonocc would still be highly appreciated (it collects the results in C++ in a way that it can be copied as a complete block from C++ to Python in a few ms). I tried to build one in a separate repo, but failed on the complexity of pybind11 and OCP ...

VTK looks like yet another complex wold, which I gave up spending time just to understand whether I could get vertices, triangles and normals of a mesh at all ...

adam-urbanczyk commented 3 years ago

Here is how to get something out of VTK and OCP (NB: I use pyvista on top of vtk):


import OCP
import vtk
import pyvista as pv

shape  = cq.Workplane().box(1,1,1).faces('>Z').hole(0.7).edges().fillet(0.03).val()

vs = OCP.IVtkOCC.IVtkOCC_Shape( shape.wrapped )
sd = OCP.IVtkVTK.IVtkVTK_ShapeData()
sm = OCP.IVtkOCC.IVtkOCC_ShapeMesher()
sm.Build(vs,sd)

res = pv.PolyData(sd.getVtkPolyData())

res.points
res.faces
res.face_normals

It also supports vtk.js / itkwidgets (see below). I'd like to add this to CQ as a simple visualization in notebooks. There used to be X3D, but it is removed (PythonOCC specific).

image

bernhard-42 commented 3 years ago

Thanks @adam-urbanczyk

I gave it a try and I get the pyvista examples "airplane" and "ant" visualized in Jupyter, so my installation seems to work correctly.

However, I cannot plot any CadQuery object. In your code example above the relationship between res and mesh is not clear to me - could you please paste the full example?

The PolyData exported from OCC VTK has additional 2 arrays which I don't understand. And I didn't find sufficient info about how to deal with it in pyvista. Maybe this is the reason for the failing plots?

Furthermore, if I would like to use the code you provided for my tessellation problem, I run into another problem: For a simple example cq.Workplane().box(1,2,3) I get 68 cells and 68 normals but only 12 faces and it is unclear which normal belongs to which face.

Of course, I will do more research on both topics, but any hint/help would be really appreciated ...

adam-urbanczyk commented 3 years ago

This begins to be VTK specific, so let's move to #281

bernhard-42 commented 3 years ago

Regarding the current tessellator bug, Thomas has the same logic in pythonocc's Tessellator, see https://github.com/tpaviot/pythonocc-core/blob/a80a5c76fe466f0f428a7318cf6db8d71f4b9244/src/Tesselator/ShapeTesselator.cpp#L109-L115

Not sure if you want to fix it or move over to a VTK based tessellator

Given the results in the VTK thread I would be happy to close this issue