bschwind / opencascade-rs

Rust bindings to the OpenCascade CAD Kernel
GNU Lesser General Public License v2.1
112 stars 21 forks source link

Sometimes the code crashes on operations like chamfer, fillet etc, instead of handling errors gracefully. #172

Open aerosayan opened 5 months ago

aerosayan commented 5 months ago

Hello,

I'm trying to create small example codes to understand the library well.

When I try to use a 0.05 mm chamfer to create this shape, the code crashes with StdFail_NotDone error.


pub fn test_0007() {
    let box1 = Shape::box_with_dimensions(1.0, 1.0, 1.0);
    let sphere = Shape::sphere(0.2).at((0.0, 0.5, 0.5).into()).build();

    let shape = box1.subtract(&sphere).chamfer(0.05);
    shape.write_step("shape.step").unwrap();
}

The error I got was:

terminate called after throwing an instance of 'StdFail_NotDone'
Aborted (core dumped)

Errors can happen obviously, there's nothing wrong with that, but there must be some way we can prevent the code from crashing.

I suspect, that most likely the ffi functions being called for chamfer crashes the code, so rust can't handle the error gracefully.

When we remove the chamfer operation, it works correctly.

This chamfer operation should be possible, and should not cause an error.

In FreeCAD, I tried the chamfer operation with 0.05 mm on all edges, and it worked correctly, as shown in the picture below.

Since this operation seems to work in FreeCAD, but not in the opencascade code, maybe something is wrong?

It could be possible that I made a mistake in using the opencascade library, but even then, would it be possible for the error to be handled gracefully with an idiomatic Result<T, Error> method?

Thanks :)

3

aerosayan commented 5 months ago

Looking more into this topic shows that the bug could be in OCCT.

  1. A frustrated user who blames OCCT for not fixing this bug for over a decade
  2. Freecad forum post on how chamfers and fillet crashes the app

For my work (CFD/FEM mesh generation), I don't need chamfer or fillet, so I can work without them.

Although, it might be good to submit patches to OCCT when we find such bugs.

Shown in the code below, one developer from the freecad forum submitted a patch to emit an exception if the fillet or chamfer operation fails.

Although, since freecad can recover from such errors, it shows that OCCT is trying to improve, and maybe cxx-rust bridge can't handle the exceptions properly, that's why the rust code crashes.

I don't know much about cxx.rs, so I will have to study more about how it handles exceptions.

--- src/ChFi3d/ChFi3d_Builder_C1.cxx    2020-11-03 08:49:55.000000000 -0600
+++ /c/Users/chennes/Desktop/ChFi3d_Builder_C1.cxx  2021-01-27 09:21:50.537703200 -0600
@@ -1459,20 +1459,30 @@
 // Ecur1=TopoDS::Edge(TopoDS_Shape (MapE1(i)));
         for ( j=1; j<= MapE2.Extent()&&!trouve; j++)
             {
          aLocalShape = TopoDS_Shape (MapE2(j));
          Ecur2=TopoDS::Edge(aLocalShape);
 //       Ecur2=TopoDS::Edge(TopoDS_Shape (MapE2(j)));
             if (Ecur2.IsSame(Ecur1))
                {Edge=Ecur1;trouve=Standard_True;}
             }
       }
+  // BEGIN FREECAD CUSTOM PATCH
+  // This patch is to address FreeCAD Bug #4543
+  // https://tracker.freecadweb.org/view.php?id=4543
+  // If this function failed to find a matching edge between two faces, throw an exception: later
+  // code assumes that Edge has been populated, but in this case it is in an invalid state.
+  // - Chris Hennes, 1/26/2021
+  if (trouve != Standard_True) {
+      throw Standard_Failure("Failed to find matching edge between two faces.");
+  }
+  // END FREECAD CUSTOM PATCH
 }
bschwind commented 5 months ago

Hi, yes we should be using Result for fallible calls. This is tracked in #24 and it can be achieved with cxx.rs and the use of exceptions.

aerosayan commented 5 months ago

Cool!

I don't know cxx.rs right now, but if anyone could show a single example of how to handle exceptions for OCCT errors, perhaps for a commonly used function like chamfer or fillet, then one by one we could implement the error handling for the other failable functions, and send PRs.

PS: Also I'm trying to understand how to implement interactive selection of sub-shapes (faces, wires, vertex) using mouse, and once I'm done learning wgpu rendering, I would try to send some PRs for it. It is little bit complicated, so no promises. haha. :sweat_smile:

DanielJoyce commented 2 months ago

https://github.com/dtolnay/cxx/issues/53

Trapping exceptions as result.

Checking opencascade, it does seem that if an operation is not done, simply calling some apis will raise exceptions

bschwind commented 2 months ago

PS: Also I'm trying to understand how to implement interactive selection of sub-shapes (faces, wires, vertex) using mouse, and once I'm done learning wgpu rendering, I would try to send some PRs for it

This is usually accomplished by "unprojecting" the mouse coordinates from screen-space, back through the camera matrix transforms. Here is an article on the subject, but there are plenty of references about it all over.

Checking opencascade, it does seem that if an operation is not done, simply calling some apis will raise exceptions

Yes, many operations have an "isDone" function that you use to check if the operation was successful. These can be pretty easily added to the various fallible operations, I've just been too lazy to implement it yet.

DanielJoyce commented 2 months ago

Can't we use the wrapper generation code to generate them?

bschwind commented 2 months ago

@DanielJoyce which generation code are you referring to? cxx.rs?