diff --git a/src/ifcgeom/kernels/opencascade/wire_utils.cpp b/src/ifcgeom/kernels/opencascade/wire_utils.cpp index 4ee78a0006..53386d47bc 100644 --- a/src/ifcgeom/kernels/opencascade/wire_utils.cpp +++ b/src/ifcgeom/kernels/opencascade/wire_utils.cpp @@ -937,7 +937,18 @@ bool ifcopenshell::geom::util::convert_wire_to_faces(const TopoDS_Wire& w, TopoD } TopoDS_Face face = mf.Face(); - const double m = face_area(face); + double m; + try { + m = face_area(face); + } catch (const Standard_Failure& e) { + // Area here is only a heuristic to drop slivers from self-intersection splitting; + // don't let a rare OCCT failure computing it (e.g. on a degenerate surface) abort + // conversion of the whole element. Fall back to 0 rather than losing the face. + ifcopenshell::logger::root().warning("GEO", 232, + std::string("Failed to compute face area, assuming 0: ") + + (e.GetMessageString() ? e.GetMessageString() : e.DynamicType()->Name())); + m = 0.; + } face_list.push_back({ m, face }); if (m > max_area) { diff --git a/src/serializers/svg_serializer.cpp b/src/serializers/svg_serializer.cpp index 4027df83aa..e60689a729 100644 --- a/src/serializers/svg_serializer.cpp +++ b/src/serializers/svg_serializer.cpp @@ -25,6 +25,7 @@ #include #include +#include #include #include #include @@ -717,6 +718,41 @@ namespace { } void svg_serializer::write(const ifcopenshell::geom::native_element* brep_obj) { + // Geometry conversion for a single element can fail deep inside OCCT (e.g. a degenerate + // surface) without that being representative of the file as a whole. Catch such failures + // here, log which element was responsible, and skip just that element so the rest of the + // drawing still gets produced. + // @todo TEMPORARY: also mirror to stderr, since nothing in Bonsai currently wires up + // logger()'s output for this code path (no set_output()/get_log() call), so a warning() + // here is otherwise invisible. Remove the std::cerr lines once that's addressed upstream. + try { + write_(brep_obj); + } catch (const Standard_Failure& e) { + std::string msg = "SVG serializer OCC exception while writing element #" + + boost::lexical_cast(brep_obj->id()) + " (" + brep_obj->guid() + "): " + + (e.GetMessageString() ? e.GetMessageString() : e.DynamicType()->Name()); + logger().warning("SER", 32, msg); + std::cerr << "[SVG-SKIP] " << msg + << (elevation_ref_guid_ && *elevation_ref_guid_ == brep_obj->guid() ? " [THIS IS THE elevation-ref-guid CAMERA ELEMENT]" : "") + << std::endl; + } catch (const std::exception& e) { + std::string msg = "SVG serializer exception while writing element #" + + boost::lexical_cast(brep_obj->id()) + " (" + brep_obj->guid() + "): " + e.what(); + logger().warning("SER", 33, msg); + std::cerr << "[SVG-SKIP] " << msg + << (elevation_ref_guid_ && *elevation_ref_guid_ == brep_obj->guid() ? " [THIS IS THE elevation-ref-guid CAMERA ELEMENT]" : "") + << std::endl; + } catch (...) { + std::string msg = "SVG serializer encountered an unrecognized exception while writing element #" + + boost::lexical_cast(brep_obj->id()) + " (" + brep_obj->guid() + ")"; + logger().warning("SER", 34, msg); + std::cerr << "[SVG-SKIP] " << msg + << (elevation_ref_guid_ && *elevation_ref_guid_ == brep_obj->guid() ? " [THIS IS THE elevation-ref-guid CAMERA ELEMENT]" : "") + << std::endl; + } +} + +void svg_serializer::write_(const ifcopenshell::geom::native_element* brep_obj) { std::optional object_type; if (!brep_obj->product().get("ObjectType").isNull()) { diff --git a/src/serializers/svg_serializer.h b/src/serializers/svg_serializer.h index fdd12de1a6..8fe2fb568c 100644 --- a/src/serializers/svg_serializer.h +++ b/src/serializers/svg_serializer.h @@ -677,6 +677,9 @@ public: bool ready(); void write(const ifcopenshell::geom::triangulation_element* /*o*/) {} void write(const ifcopenshell::geom::native_element* o); +private: + void write_(const ifcopenshell::geom::native_element* o); +public: void write(path_object& p, const TopoDS_Shape& wire, std::optional> dash_array=std::nullopt, std::optional css_class=std::nullopt); void write(const geometry_data& data); path_object& start_path(const gp_Pln& p, const express::base& storey, const std::string& id);