tpaviot / pythonocc-core

Python package for 3D geometry CAD/BIM/CAM
GNU Lesser General Public License v3.0
1.33k stars 374 forks source link

Handle_Geom_*_DownCast crash (PyOCC 7.8.1 & 7.7.2) #1366

Open LaurentCmbft opened 1 month ago

LaurentCmbft commented 1 month ago

Hello,

Here is a piece of code where I try to categorize a face using Handle_Geom_Plane_DownCast, Handle_Geom_CylindricalSurface_DownCast, ... I provide below the stp file contents (just a sphere) to test the code but same behavior is experienced on any stp file.

It works well with :

However SystemError is raised with :

Here is the error :

C:\Users\user\Projet\Miniforge311_5\python.exe C:\Users\user\Projet\Appli\Engine\error_main.py 
C:\Users\user\Projet\Appli\Engine\error_main.py :33: DeprecationWarning: Call to deprecated function BRep_Tool_Surface since pythonocc-core 7.7.1. This function will be removed in a future release, please rather use the static method BRep_Tool.Surface
  hs = BRep_Tool_Surface(face)
RuntimeError: Failed to downcast to Geom_Plane.

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "C:\Users\user\Projet\Appli\Engine\error_main.py ", line 50, in <module>
    res = get_face_types(my_topo)
                    ^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\user\Projet\Appli\Engine\error_main.py ", line 29, in get_face_types
    face_types[current_face] = get_face_type(current_face)
                               ^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\user\Projet\Appli\Engine\error_main.py ", line 34, in get_face_type
    if Handle_Geom_Plane_DownCast(hs) is not None:
       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\user\Projet\Miniforge311_5\Lib\site-packages\OCC\Core\Geom.py", line 465, in Handle_Geom_Plane_DownCast
    return _Geom.Handle_Geom_Plane_DownCast(t)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
SystemError: <built-in function Handle_Geom_Plane_DownCast> returned a result with an exception set

Here is the code (error_main.py):

from OCC.Core.IFSelect import IFSelect_RetDone
from OCC.Core.STEPControl import STEPControl_Reader

from OCC.Core.BRep import BRep_Tool_Surface
from OCC.Core.Geom import (Handle_Geom_SphericalSurface_DownCast, Handle_Geom_ToroidalSurface_DownCast, Handle_Geom_Plane_DownCast,
                           Handle_Geom_CylindricalSurface_DownCast)
from OCC.Core.TopoDS import TopoDS_Face, TopoDS_Shape

from OCC.Extend.TopologyUtils import TopologyExplorer

def get_shape_from_path(filepath: str) -> TopoDS_Shape:
    step_reader = STEPControl_Reader()
    status = step_reader.ReadFile(filepath)

    if status == IFSelect_RetDone:  # check status
        step_reader.TransferRoot(1)
        a_res_shape = step_reader.Shape(1)
        try:
            return next(TopologyExplorer(a_res_shape).solids())
        except StopIteration:
            raise ValueError('Could not import file {}'.format(filepath))
    else:
        raise ValueError(filepath)

def get_face_types(topo: TopologyExplorer) -> dict:
    face_types = {}
    for current_face in topo.faces():
        face_types[current_face] = get_face_type(current_face)
    return face_types

def get_face_type(face: TopoDS_Face) -> str:
    hs = BRep_Tool_Surface(face)
    if Handle_Geom_Plane_DownCast(hs) is not None:
        return 'plane'
    elif Handle_Geom_CylindricalSurface_DownCast(hs) is not None:
        return 'cylinder'
    elif  Handle_Geom_SphericalSurface_DownCast(hs) is not None:
        return 'sphere'
    elif Handle_Geom_ToroidalSurface_DownCast(hs) is not None:
        return 'tore'
    else:
        return 'unkown'

if __name__ == "__main__":
    shape = get_shape_from_path(r"C:\Users\user\Desktop\dump\Sphere.stp")

    my_topo = TopologyExplorer(shape, ignore_orientation=False)
    res = get_face_types(my_topo)

Here is the sphere.stp:

ISO-10303-21;
HEADER;
FILE_DESCRIPTION(('FreeCAD Model'),'2;1');
FILE_NAME('Open CASCADE Shape Model','2024-08-29T11:38:50',('Author'),(
    ''),'Open CASCADE STEP processor 7.6','FreeCAD','Unknown');
FILE_SCHEMA(('AUTOMOTIVE_DESIGN { 1 0 10303 214 1 1 1 1 }'));
ENDSEC;
DATA;
#1 = APPLICATION_PROTOCOL_DEFINITION('international standard',
  'automotive_design',2000,#2);
#2 = APPLICATION_CONTEXT(
  'core data for automotive mechanical design processes');
#3 = SHAPE_DEFINITION_REPRESENTATION(#4,#10);
#4 = PRODUCT_DEFINITION_SHAPE('','',#5);
#5 = PRODUCT_DEFINITION('design','',#6,#9);
#6 = PRODUCT_DEFINITION_FORMATION('','',#7);
#7 = PRODUCT('Sphere','Sphere','',(#8));
#8 = PRODUCT_CONTEXT('',#2,'mechanical');
#9 = PRODUCT_DEFINITION_CONTEXT('part definition',#2,'design');
#10 = ADVANCED_BREP_SHAPE_REPRESENTATION('',(#11,#15),#27);
#11 = AXIS2_PLACEMENT_3D('',#12,#13,#14);
#12 = CARTESIAN_POINT('',(0.,0.,0.));
#13 = DIRECTION('',(0.,0.,1.));
#14 = DIRECTION('',(1.,0.,-0.));
#15 = MANIFOLD_SOLID_BREP('',#16);
#16 = CLOSED_SHELL('',(#17));
#17 = ADVANCED_FACE('',(#18),#22,.T.);
#18 = FACE_BOUND('',#19,.T.);
#19 = VERTEX_LOOP('',#20);
#20 = VERTEX_POINT('',#21);
#21 = CARTESIAN_POINT('',(3.061616997868E-16,-7.498798913309E-32,-5.));
#22 = SPHERICAL_SURFACE('',#23,5.);
#23 = AXIS2_PLACEMENT_3D('',#24,#25,#26);
#24 = CARTESIAN_POINT('',(0.,0.,0.));
#25 = DIRECTION('',(0.,0.,1.));
#26 = DIRECTION('',(1.,0.,-0.));
#27 = ( GEOMETRIC_REPRESENTATION_CONTEXT(3) 
GLOBAL_UNCERTAINTY_ASSIGNED_CONTEXT((#31)) GLOBAL_UNIT_ASSIGNED_CONTEXT(
(#28,#29,#30)) REPRESENTATION_CONTEXT('Context #1',
  '3D Context with UNIT and UNCERTAINTY') );
#28 = ( LENGTH_UNIT() NAMED_UNIT(*) SI_UNIT(.MILLI.,.METRE.) );
#29 = ( NAMED_UNIT(*) PLANE_ANGLE_UNIT() SI_UNIT($,.RADIAN.) );
#30 = ( NAMED_UNIT(*) SI_UNIT($,.STERADIAN.) SOLID_ANGLE_UNIT() );
#31 = UNCERTAINTY_MEASURE_WITH_UNIT(LENGTH_MEASURE(1.E-07),#28,
  'distance_accuracy_value','confusion accuracy');
#32 = PRODUCT_RELATED_PRODUCT_CATEGORY('part',$,(#7));
#33 = MECHANICAL_DESIGN_GEOMETRIC_PRESENTATION_REPRESENTATION('',(#34),
  #27);
#34 = STYLED_ITEM('color',(#35),#15);
#35 = PRESENTATION_STYLE_ASSIGNMENT((#36,#42));
#36 = SURFACE_STYLE_USAGE(.BOTH.,#37);
#37 = SURFACE_SIDE_STYLE('',(#38));
#38 = SURFACE_STYLE_FILL_AREA(#39);
#39 = FILL_AREA_STYLE('',(#40));
#40 = FILL_AREA_STYLE_COLOUR('',#41);
#41 = COLOUR_RGB('',0.800000010877,0.800000010877,0.800000010877);
#42 = CURVE_STYLE('',#43,POSITIVE_LENGTH_MEASURE(0.1),#44);
#43 = DRAUGHTING_PRE_DEFINED_CURVE_FONT('continuous');
#44 = COLOUR_RGB('',9.803921802644E-02,9.803921802644E-02,
  9.803921802644E-02);
ENDSEC;
END-ISO-10303-21;

Thank you very much for your work on PyOCC and your help ! Regards Laurent

tpaviot commented 1 week ago

You can use a BRepAdaptor_Surface instance to check the surface type, see for instance https://github.com/tpaviot/pythonocc-demos/blob/master/examples/core_geometry_recognize_feature.py#L41