mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-08-28 15:53:00 +00:00
svg_serializer: skip elements that fail geometry conversion
A single element's geometry can fail deep inside OCCT (e.g. a degenerate surface) without that being representative of the rest of the file. Previously this raised all the way through SWIG as an unhelpful "An unknown error occurred", aborting the whole drawing. Catch the failure per-element, log which element and why (including a temporary stderr mirror, since nothing currently wires up ifcopenshell::logger's output for this code path), and skip just that element so the rest of the drawing still gets produced. Also harden convert_wire_to_faces()'s face-area heuristic in wire_utils.cpp against the same class of rare OCCT failure - area is only used there to drop slivers from self-intersection splitting, so fall back to 0 rather than losing the whole face over it. Generated with the assistance of an AI coding tool. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -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) {
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
|
||||
#include <string>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <cstdio>
|
||||
#include <limits>
|
||||
#include <algorithm>
|
||||
@@ -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<std::string>(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<std::string>(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<std::string>(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<std::string> object_type;
|
||||
if (!brep_obj->product().get("ObjectType").isNull()) {
|
||||
|
||||
@@ -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<std::vector<double>> dash_array=std::nullopt, std::optional<std::string> 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);
|
||||
|
||||
Reference in New Issue
Block a user