Merge remote-tracking branch 'origin/v0.8.0' into datamodel-v1.0

This commit is contained in:
Thomas Krijnen
2026-04-18 20:15:28 +02:00
1655 changed files with 105534 additions and 46934 deletions
+7 -1
View File
@@ -452,6 +452,12 @@ namespace ifcopenshell {
static constexpr bool defaultvalue = false;
};
struct MakeVolume : public SettingBase<MakeVolume, bool> {
static constexpr const char* const name = "make-volume";
static constexpr const char* const description = "Try to isolate and fix a valid volume from non-manifold elements prior to opening subtraction";
static constexpr bool defaultvalue = false;
};
struct DeferProcessingFirstElement : public SettingBase<DeferProcessingFirstElement, bool, true> {
static constexpr const char* const name = "defer-processing-first-element";
static constexpr const char* const description = "Don't process first element in Iterator::initialize call()";
@@ -647,7 +653,7 @@ namespace ifcopenshell {
};
class Settings : public SettingsContainer<
std::tuple<MesherLinearDeflection, MesherAngularDeflection, ReorientShells, LengthUnit, PlaneUnit, Precision, OutputDimensionality, LayersetFirst, DisableBooleanResult, NoWireIntersectionCheck, NoWireIntersectionTolerance, PrecisionFactor, DebugBooleanOperations, BooleanAttempt2d, SurfaceColour, WeldVertices, UseWorldCoords, UnifyShapes, UseMaterialNames, ConvertBackUnits, ContextIds, ContextTypes, ContextIdentifiers, IteratorOutput, DisableOpeningSubtractions, ApplyDefaultMaterials, DontEmitNormals, GenerateUvs, ApplyLayerSets, UseElementHierarchy, ValidateQuantities, EdgeArrows, BuildingLocalPlacement, SiteLocalPlacement, ForceSpaceTransparency, CircleSegments, CgalSmoothAngleDegrees, KeepBoundingBoxes, ComputeCurvature, FunctionStepType, FunctionStepParam, NoParallelMapping, PermissiveShapeReuse, ModelOffset, ModelRotation, TriangulationType, CgalEmitOriginalEdges, OcctNoCleanTriangulation, CacheShapes, DeferProcessingFirstElement, MaxOffset, MaxOffsetDeviation, ApplyOffset>
std::tuple<MesherLinearDeflection, MesherAngularDeflection, ReorientShells, LengthUnit, PlaneUnit, Precision, OutputDimensionality, LayersetFirst, DisableBooleanResult, NoWireIntersectionCheck, NoWireIntersectionTolerance, PrecisionFactor, DebugBooleanOperations, BooleanAttempt2d, SurfaceColour, WeldVertices, UseWorldCoords, UnifyShapes, UseMaterialNames, ConvertBackUnits, ContextIds, ContextTypes, ContextIdentifiers, IteratorOutput, DisableOpeningSubtractions, ApplyDefaultMaterials, DontEmitNormals, GenerateUvs, ApplyLayerSets, UseElementHierarchy, ValidateQuantities, EdgeArrows, BuildingLocalPlacement, SiteLocalPlacement, ForceSpaceTransparency, CircleSegments, CgalSmoothAngleDegrees, KeepBoundingBoxes, ComputeCurvature, FunctionStepType, FunctionStepParam, NoParallelMapping, PermissiveShapeReuse, ModelOffset, ModelRotation, TriangulationType, CgalEmitOriginalEdges, OcctNoCleanTriangulation, CacheShapes, DeferProcessingFirstElement, MaxOffset, MaxOffsetDeviation, ApplyOffset, MakeVolume>
>
{};
}
+2 -5
View File
@@ -12,11 +12,8 @@ ifcopenshell::geometry::Converter::Converter(std::unique_ptr<ifcopenshell::geome
settings_ = mapping_->settings();
}
ifcopenshell::geometry::Converter::~Converter()
{
if (mapping_ != nullptr) {
delete mapping_;
}
ifcopenshell::geometry::Converter::~Converter() {
delete mapping_;
}
namespace {
+1 -1
View File
@@ -91,7 +91,7 @@ bool IfcGeom::Representation::BRep::calculate_volume(double& volume) const {
volume = 0.;
return false;
}
volume = s->area()->to_double();
volume = s->volume()->to_double();
return true;
}
+4
View File
@@ -1835,6 +1835,9 @@ bool CgalKernel::convert_impl(const taxonomy::boolean_result::ptr br, Conversion
CGAL::Polygon_with_holes_2<Kernel_> pwh(p, ++it, loops.end());
CGAL::Gps_segment_traits_2<Kernel_> traits;
if (!CGAL::are_holes_and_boundary_pairwise_disjoint(pwh, traits)) {
#ifdef IFOPSH_SIMPLE_KERNEL
throw std::runtime_error("Holes are not disjoint - use a different geometry kernel");
#else
// this is very slow.
// the check is also slow...
@@ -1851,6 +1854,7 @@ bool CgalKernel::convert_impl(const taxonomy::boolean_result::ptr br, Conversion
result.difference(*it);
}
result.polygons_with_holes(std::back_inserter(pwhs));
#endif
} else {
pwhs.push_back(pwh);
}
+29 -12
View File
@@ -1773,6 +1773,10 @@ namespace IfcGeom {
auto& vs = elem->geometry().verts();
auto& fs = elem->geometry().faces();
if (vs.empty() || fs.empty()) {
return;
}
gp_Trsf tr;
tr.SetValues(
m(0, 0), m(0, 1), m(0, 2), m(0, 3),
@@ -1840,23 +1844,36 @@ namespace IfcGeom {
candidates.push_back({ std::abs(Z.Dot(ref)), ref });
}
if (candidates.empty()) {
{
gp_XYZ ref(0, 0, 1);
candidates.push_back({ std::abs(Z.Dot(ref)), ref });
gp_Ax3 ax3;
gp_Trsf trsf2;
for (size_t attempt = 0; attempt < 2; ++attempt) {
if (candidates.empty() || attempt == 1) {
{
gp_XYZ ref(0, 0, 1);
candidates.push_back({std::abs(Z.Dot(ref)), ref});
}
{
gp_XYZ ref(1, 0, 0);
candidates.push_back({std::abs(Z.Dot(ref)), ref});
}
}
auto X = std::min_element(candidates.begin(), candidates.end(), [](auto& p1, auto& p2) { return p1.first < p2.first; })->second;
{
gp_XYZ ref(1, 0, 0);
candidates.push_back({ std::abs(Z.Dot(ref)), ref });
try {
ax3 = gp_Ax3(gp::Origin(), Z, X);
trsf2.SetTransformation(gp::XOY(), ax3);
} catch (Standard_ConstructionError&) {
// Try again, likely we have all identical normals in candidates so
// we cannot find a suitable candidate and need the two default axes
continue;
}
}
}
auto X = std::min_element(candidates.begin(), candidates.end(), [](auto& p1, auto& p2) { return p1.first < p2.first; })->second;
gp_Trsf trsf2;
gp_Ax3 ax3(gp::Origin(), Z, X);
trsf2.SetTransformation(gp::XOY(), ax3);
Bnd_Box tmp;
for (auto& p : vs_transformed) {
@@ -28,6 +28,7 @@
#include "boolean_utils.h"
#include "base_utils.h"
#include <BOPAlgo_MakerVolume.hxx>
#include <BRepPrimAPI_MakeRevol.hxx>
namespace {
@@ -136,7 +137,20 @@ bool IfcGeom::OpenCascadeKernel::convert_openings(const express::Base& entity, c
bool is_manifold = util::is_manifold(entity_part);
if (!is_manifold) {
logger::warning("Non-manifold first operand");
if (settings_.get<settings::MakeVolume>().get()) {
BOPAlgo_MakerVolume mv;
mv.AddArgument(entity_part);
mv.Perform();
if (mv.HasErrors()) {
logger::warning("Non-manifold first operand, --make-volume failed");
} else {
entity_part = mv.Shape();
is_manifold = util::is_manifold(entity_part);
}
}
if (!is_manifold) {
logger::warning("Non-manifold first operand, use --make-volume to try and make manifold");
}
}
TopoDS_Shape entity_part_result;
@@ -1363,4 +1377,4 @@ bool IfcGeom::OpenCascadeKernel::convert_impl(const taxonomy::revolve::ptr r, If
// if (IfcGeom::util::apply_layerset(r, const std::vector<ifcopenshell::geometry::taxonomy::style>&, ConversionResults& r2, double tol)) {
// std::swap(r, r2)
// }
// }
// }
@@ -56,6 +56,21 @@
#include "../../../ifcgeom/taxonomy.h"
#include "../../../ifcgeom/ConversionSettings.h"
namespace {
template <typename Fn>
bool handle_occt_exception(Fn&& fn) {
try {
return std::forward<Fn>(fn)();
} catch (const Standard_Failure& e) {
if (e.GetMessageString() && strlen(e.GetMessageString())) {
throw std::runtime_error(e.GetMessageString());
} else {
throw std::runtime_error("Unknown error creating geometry");
}
}
}
}
namespace IfcGeom {
class IFC_GEOMLIBRARY_API OpenCascadeKernel : public ifcopenshell::geometry::kernels::AbstractKernel {
@@ -84,6 +84,7 @@ namespace {
}
bool OpenCascadeKernel::convert_impl(const taxonomy::boolean_result::ptr br, ConversionResults& results) {
return handle_occt_exception([&]() -> bool {
bool valid_result = false;
bool first = true;
const double tol = settings_.get<settings::Precision>().get();
@@ -196,4 +197,5 @@ bool OpenCascadeKernel::convert_impl(const taxonomy::boolean_result::ptr br, Con
));
return true;
});
}
@@ -1134,6 +1134,8 @@ bool IfcGeom::util::boolean_operation(const boolean_settings& settings, const To
#endif
builder->SetFuzzyValue(fuzz);
builder->SetArguments(s1s);
// We use our own multi-threading in ifcopenshell on a per-product basis
builder->SetRunParallel(false);
copy_operand(b, b_tmp);
std::swap(b, b_tmp);
builder->SetTools(b);
@@ -72,6 +72,8 @@ bool OpenCascadeKernel::convert(const taxonomy::extrusion::ptr extrusion, TopoDS
}
bool OpenCascadeKernel::convert_impl(const taxonomy::extrusion::ptr extrusion, IfcGeom::ConversionResults& results) {
return handle_occt_exception([&]() -> bool {
TopoDS_Shape shape;
if (!convert(extrusion, shape)) {
return false;
@@ -84,4 +86,6 @@ bool OpenCascadeKernel::convert_impl(const taxonomy::extrusion::ptr extrusion, I
extrusion->surface_style
));
return true;
});
}
+4
View File
@@ -599,6 +599,8 @@ bool OpenCascadeKernel::convert(const taxonomy::face::ptr face, TopoDS_Shape& re
}
bool OpenCascadeKernel::convert_impl(const taxonomy::face::ptr face, IfcGeom::ConversionResults& results) {
return handle_occt_exception([&]() -> bool {
TopoDS_Shape shape;
if (!convert(face, shape)) {
return false;
@@ -609,4 +611,6 @@ bool OpenCascadeKernel::convert_impl(const taxonomy::face::ptr face, IfcGeom::Co
face->surface_style
));
return true;
});
}
@@ -107,6 +107,8 @@ bool OpenCascadeKernel::convert(const taxonomy::shell::ptr l, TopoDS_Shape& shap
}
bool OpenCascadeKernel::convert_impl(const taxonomy::shell::ptr shell, IfcGeom::ConversionResults& results) {
return handle_occt_exception([&]() -> bool {
TopoDS_Shape shape;
if (!convert(shell, shape)) {
return false;
@@ -118,4 +120,6 @@ bool OpenCascadeKernel::convert_impl(const taxonomy::shell::ptr shell, IfcGeom::
shell->surface_style
));
return true;
});
}
@@ -102,6 +102,8 @@ bool OpenCascadeKernel::convert(const taxonomy::solid::ptr solid, TopoDS_Shape&
}
bool OpenCascadeKernel::convert_impl(const taxonomy::solid::ptr solid, IfcGeom::ConversionResults& results) {
return handle_occt_exception([&]() -> bool {
TopoDS_Shape shape;
if (!convert(solid, shape)) {
return false;
@@ -113,4 +115,6 @@ bool OpenCascadeKernel::convert_impl(const taxonomy::solid::ptr solid, IfcGeom::
solid->surface_style
));
return true;
});
}
@@ -308,6 +308,8 @@ bool OpenCascadeKernel::convert(const taxonomy::sweep_along_curve::ptr scs, Topo
}
bool OpenCascadeKernel::convert_impl(const taxonomy::sweep_along_curve::ptr scs, IfcGeom::ConversionResults& results) {
return handle_occt_exception([&]() -> bool {
TopoDS_Shape shape;
// For tiny radii occt will fail building the sweep, in which case we enlarge the inputs to occt, and add a scale matrix to the output
bool enlarged = false;
@@ -352,4 +354,6 @@ bool OpenCascadeKernel::convert_impl(const taxonomy::sweep_along_curve::ptr scs,
scs->surface_style
));
return true;
});
}
+2 -2
View File
@@ -89,7 +89,7 @@ taxonomy::ptr mapping::map_impl(const IfcSchema::IfcObjectPlacement& inst) {
if (fallback) {
auto mapped_fallback = taxonomy::cast<taxonomy::matrix4>(map(fallback));
if (mapped_fallback != result) {
if (!result->ccomponents().isApprox(mapped_fallback->ccomponents())) {
logger::warning("Computed placement differs from fallback", inst);
}
}
@@ -229,4 +229,4 @@ if (gridp = inst.as<IfcSchema::IfcGridPlacement>()) {
trsf.PreMultiply(grid_position);
}
*/
*/
+5 -10
View File
@@ -24,16 +24,11 @@ using namespace ifcopenshell::geometry;
#ifdef SCHEMA_HAS_IfcToroidalSurface
taxonomy::ptr mapping::map_impl(const IfcSchema::IfcToroidalSurface& inst) {
return nullptr;
/*
gp_Trsf trsf;
IfcGeom::Kernel::convert(inst.Position(), trsf);
// IfcElementarySurface.Position has unit scale factor
face = BRepBuilderAPI_MakeFace(new Geom_ToroidalSurface(gp::XOY(), inst.MajorRadius() * length_unit_, inst.MinorRadius() * length_unit_), getValue(GV_PRECISION)).Face().Moved(trsf);
return true;
*/
auto torus = taxonomy::make<taxonomy::torus>();
torus->radius1 = inst.MajorRadius() * length_unit_;
torus->radius2 = inst.MinorRadius() * length_unit_;
torus->matrix = taxonomy::cast<taxonomy::matrix4>(map(inst.Position()));
return torus;
}
#endif