From ddeaf5a375584c170925b1ef09f3ea37df8906aa Mon Sep 17 00:00:00 2001 From: Thomas Krijnen Date: Fri, 16 Aug 2019 17:40:13 +0200 Subject: [PATCH] More work --- cmake/CMakeLists.txt | 65 +- src/ifcgeom/abstract_mapping.cpp | 35 + src/ifcgeom/abstract_mapping.h | 43 +- .../kernel_agnostic/AbstractKernel.cpp | 626 ++------ src/ifcgeom/kernel_agnostic/AbstractKernel.h | 74 +- .../IfcGeomIteratorImplementation.cpp | 31 - .../IfcGeomIteratorImplementation.h | 950 ------------ src/ifcgeom/kernel_agnostic/blog.txt | 71 + src/ifcgeom/kernels/opencascade/IfcGeom.h | 375 ----- .../{IfcGeomCurves.cpp => IfcGeomCurves.cpp_} | 0 .../{IfcGeomFaces.cpp => IfcGeomFaces.cpp_} | 0 ...eomFunctions.cpp => IfcGeomFunctions.cpp_} | 0 ...IfcGeomHelpers.cpp => IfcGeomHelpers.cpp_} | 0 ...lisation.cpp => IfcGeomSerialisation.cpp_} | 0 .../kernels/opencascade/IfcGeomShapes.cpp | 1160 +-------------- .../kernels/opencascade/IfcGeomShapes.cpp_ | 1289 +++++++++++++++++ .../{IfcGeomWires.cpp => IfcGeomWires.cpp_} | 0 .../{IfcRegister.cpp => IfcRegister.cpp_} | 0 .../kernels/opencascade/OpenCascadeKernel.h | 251 ++++ src/ifcgeom/schema/bind_convert_impl.i | 14 +- src/ifcgeom/schema/mapping.cpp | 646 ++++++++- src/ifcgeom/schema/mapping.h | 14 + src/ifcgeom/schema/mapping.i | 6 +- .../schema_agnostic/ConversionResult.h | 29 +- src/ifcgeom/schema_agnostic/Converter.cpp | 413 ++++++ .../schema_agnostic/{Kernel.h => Converter.h} | 60 +- src/ifcgeom/schema_agnostic/IfcGeomElement.h | 80 +- src/ifcgeom/schema_agnostic/IfcGeomFilter.h | 12 +- src/ifcgeom/schema_agnostic/IfcGeomIterator.h | 130 -- .../IfcGeomIteratorImplementation.cpp | 1 + .../IfcGeomIteratorImplementation.h | 617 ++++++++ .../schema_agnostic/IfcGeomMaterial.cpp | 35 - src/ifcgeom/schema_agnostic/IfcGeomMaterial.h | 51 - .../schema_agnostic/IfcGeomRenderStyles.h | 68 +- .../schema_agnostic/IfcGeomRepresentation.h | 81 +- .../IteratorImplementation.cpp | 47 - .../schema_agnostic/IteratorImplementation.h | 81 -- src/ifcgeom/schema_agnostic/Kernel.cpp | 268 ---- src/ifcgeom/schema_agnostic/SurfaceStyle.cpp | 93 +- .../opencascade/OpenCascadeConversionResult.h | 109 +- .../IfcGeomIteratorSettings.h => settings.h} | 28 +- src/ifcgeom/taxonomy.h | 125 +- src/ifcparse/IfcBaseClass.h | 15 + src/ifcparse/macros.h | 2 +- src/serializers/ColladaSerializer.cpp | 65 +- src/serializers/ColladaSerializer.h | 58 +- src/serializers/GeometrySerializer.h | 20 +- src/serializers/OpenCascadeBasedSerializer.h | 1 - .../schema_dependent/XmlSerializer.cpp | 112 +- .../schema_dependent/XmlSerializer.h | 3 + win/build-deps.cmd | 11 + win/run-cmake.bat | 2 + 52 files changed, 4083 insertions(+), 4184 deletions(-) create mode 100644 src/ifcgeom/abstract_mapping.cpp delete mode 100644 src/ifcgeom/kernel_agnostic/IfcGeomIteratorImplementation.cpp delete mode 100644 src/ifcgeom/kernel_agnostic/IfcGeomIteratorImplementation.h create mode 100644 src/ifcgeom/kernel_agnostic/blog.txt delete mode 100644 src/ifcgeom/kernels/opencascade/IfcGeom.h rename src/ifcgeom/kernels/opencascade/{IfcGeomCurves.cpp => IfcGeomCurves.cpp_} (100%) rename src/ifcgeom/kernels/opencascade/{IfcGeomFaces.cpp => IfcGeomFaces.cpp_} (100%) rename src/ifcgeom/kernels/opencascade/{IfcGeomFunctions.cpp => IfcGeomFunctions.cpp_} (100%) rename src/ifcgeom/kernels/opencascade/{IfcGeomHelpers.cpp => IfcGeomHelpers.cpp_} (100%) rename src/ifcgeom/kernels/opencascade/{IfcGeomSerialisation.cpp => IfcGeomSerialisation.cpp_} (100%) create mode 100644 src/ifcgeom/kernels/opencascade/IfcGeomShapes.cpp_ rename src/ifcgeom/kernels/opencascade/{IfcGeomWires.cpp => IfcGeomWires.cpp_} (100%) rename src/ifcgeom/kernels/opencascade/{IfcRegister.cpp => IfcRegister.cpp_} (100%) create mode 100644 src/ifcgeom/kernels/opencascade/OpenCascadeKernel.h create mode 100644 src/ifcgeom/schema_agnostic/Converter.cpp rename src/ifcgeom/schema_agnostic/{Kernel.h => Converter.h} (58%) delete mode 100644 src/ifcgeom/schema_agnostic/IfcGeomIterator.h create mode 100644 src/ifcgeom/schema_agnostic/IfcGeomIteratorImplementation.cpp create mode 100644 src/ifcgeom/schema_agnostic/IfcGeomIteratorImplementation.h delete mode 100644 src/ifcgeom/schema_agnostic/IfcGeomMaterial.cpp delete mode 100644 src/ifcgeom/schema_agnostic/IfcGeomMaterial.h delete mode 100644 src/ifcgeom/schema_agnostic/IteratorImplementation.cpp delete mode 100644 src/ifcgeom/schema_agnostic/IteratorImplementation.h delete mode 100644 src/ifcgeom/schema_agnostic/Kernel.cpp rename src/ifcgeom/{schema_agnostic/IfcGeomIteratorSettings.h => settings.h} (94%) diff --git a/cmake/CMakeLists.txt b/cmake/CMakeLists.txt index a65f8054fe..a77db1cddf 100644 --- a/cmake/CMakeLists.txt +++ b/cmake/CMakeLists.txt @@ -122,6 +122,7 @@ UNIFY_ENVVARS_AND_CACHE(MPFR_INCLUDE_DIR) UNIFY_ENVVARS_AND_CACHE(MPFR_LIBRARY_DIR) UNIFY_ENVVARS_AND_CACHE(VOXEL_INCLUDE_DIR) UNIFY_ENVVARS_AND_CACHE(VOXEL_LIBRARY_DIR) +UNIFY_ENVVARS_AND_CACHE(EIGEN_DIR) if (GLTF_SUPPORT AND BUILD_CONVERT) UNIFY_ENVVARS_AND_CACHE(JSON_INCLUDE_DIR) @@ -527,7 +528,7 @@ endif() INCLUDE_DIRECTORIES(${INCLUDE_DIRECTORIES} ${OCC_INCLUDE_DIR} ${OPENCOLLADA_INCLUDE_DIRS} ${Boost_INCLUDE_DIRS} ${LIBXML2_INCLUDE_DIR} ${JSON_INCLUDE_DIR} - ${CGAL_INCLUDE_DIR} ${GMP_INCLUDE_DIR} ${MPFR_INCLUDE_DIR} ${VOXEL_INCLUDE_DIR} + ${CGAL_INCLUDE_DIR} ${GMP_INCLUDE_DIR} ${MPFR_INCLUDE_DIR} ${VOXEL_INCLUDE_DIR} ${EIGEN_DIR} ) function(files_for_ifc_version IFC_VERSION RESULT_NAME) @@ -607,15 +608,15 @@ endif() set(IFCOPENSHELL_LIBRARIES IfcParse) if (BUILD_IFCGEOM) foreach(s ${SCHEMA_VERSIONS}) - set(IFCGEOM_SCHEMA_LIBRARIES ${IFCGEOM_SCHEMA_LIBRARIES} IfcGeom_ifc${s}) + set(IFCGEOM_SCHEMA_LIBRARIES ${IFCGEOM_SCHEMA_LIBRARIES} geometry_mapping_ifc${s}) endforeach() - set(IFCOPENSHELL_LIBRARIES ${IFCOPENSHELL_LIBRARIES} IfcGeom ${IFCGEOM_SCHEMA_LIBRARIES} IfcGeom ${IFCGEOM_SCHEMA_LIBRARIES}) + set(IFCOPENSHELL_LIBRARIES ${IFCOPENSHELL_LIBRARIES} IfcGeom geometry_mapping ${IFCGEOM_SCHEMA_LIBRARIES}) endif() if (BUILD_CONVERT) foreach(s ${SCHEMA_VERSIONS}) - set(SERIALIZER_SCHEMA_LIBRARIES ${SERIALIZER_SCHEMA_LIBRARIES} Serializers_ifc${s}) + set(SERIALIZER_SCHEMA_LIBRARIES ${SERIALIZER_SCHEMA_LIBRARIES} serializers_ifc${s}) endforeach() - set(IFCOPENSHELL_LIBRARIES ${IFCOPENSHELL_LIBRARIES} Serializers ${SERIALIZER_SCHEMA_LIBRARIES}) + set(IFCOPENSHELL_LIBRARIES ${IFCOPENSHELL_LIBRARIES} serializers ${SERIALIZER_SCHEMA_LIBRARIES}) endif() # IfcParse @@ -637,12 +638,21 @@ file(GLOB IFCGEOM_H_FILES ../src/ifcgeom/kernels/${kernel}/*.h) file(GLOB IFCGEOM_CPP_FILES ../src/ifcgeom/kernels/${kernel}/*.cpp) set(IFCGEOM_FILES ${IFCGEOM_CPP_FILES} ${IFCGEOM_H_FILES}) -add_library(IfcGeom_${kernel} STATIC ${IFCGEOM_FILES}) -set_target_properties(IfcGeom_${kernel} PROPERTIES COMPILE_FLAGS "-DIFC_GEOM_EXPORTS") -target_link_libraries(IfcGeom_${kernel} IfcParse ${${KERNEL_UPPER}_LIBRARIES}) -list(APPEND IfcGeom_libraries IfcGeom_${kernel}) +add_library(geometry_kernel_${kernel} STATIC ${IFCGEOM_FILES}) +set_target_properties(geometry_kernel_${kernel} PROPERTIES COMPILE_FLAGS "-DIFC_GEOM_EXPORTS") +target_link_libraries(geometry_kernel_${kernel} ${${KERNEL_UPPER}_LIBRARIES}) +list(APPEND kernel_libraries geometry_kernel_${kernel}) + endforeach() +file(GLOB SCHEMA_AGNOSTIC_H_FILES ../src/ifcgeom/kernel_agnostic/*.h) +file(GLOB SCHEMA_AGNOSTIC_CPP_FILES ../src/ifcgeom/kernel_agnostic/*.cpp) +set(SCHEMA_AGNOSTIC_FILES ${SCHEMA_AGNOSTIC_H_FILES} ${SCHEMA_AGNOSTIC_CPP_FILES}) + +add_library(geometry_kernels ${SCHEMA_AGNOSTIC_FILES}) +set_target_properties(geometry_kernels PROPERTIES COMPILE_FLAGS -DIFC_GEOM_EXPORTS) +target_link_libraries(geometry_kernels ${kernel_libraries}) + foreach(schema ${SCHEMA_VERSIONS}) file(GLOB IFCGEOM_I_FILES ../src/ifcgeom/schema/*.i) @@ -650,10 +660,10 @@ file(GLOB IFCGEOM_H_FILES ../src/ifcgeom/schema/*.h) file(GLOB IFCGEOM_CPP_FILES ../src/ifcgeom/schema/*.cpp) set(IFCGEOM_FILES ${IFCGEOM_CPP_FILES} ${IFCGEOM_H_FILES} ${IFCGEOM_I_FILES}) -add_library(IfcGeom_ifc${schema} STATIC ${IFCGEOM_FILES}) -set_target_properties(IfcGeom_ifc${schema} PROPERTIES COMPILE_FLAGS "-DIFC_GEOM_EXPORTS -DIfcSchema=Ifc${schema}") -target_link_libraries(IfcGeom_ifc${schema} IfcParse) -list(APPEND IfcGeom_libraries IfcGeom_ifc${schema}) +add_library(geometry_mapping_ifc${schema} STATIC ${IFCGEOM_FILES}) +set_target_properties(geometry_mapping_ifc${schema} PROPERTIES COMPILE_FLAGS "-DIFC_GEOM_EXPORTS -DIfcSchema=Ifc${schema}") +target_link_libraries(geometry_mapping_ifc${schema} IfcParse) +list(APPEND mapping_libraries geometry_mapping_ifc${schema}) endforeach() @@ -661,20 +671,27 @@ file(GLOB SCHEMA_AGNOSTIC_H_FILES ../src/ifcgeom/*.h) file(GLOB SCHEMA_AGNOSTIC_CPP_FILES ../src/ifcgeom/*.cpp) set(SCHEMA_AGNOSTIC_FILES ${SCHEMA_AGNOSTIC_H_FILES} ${SCHEMA_AGNOSTIC_CPP_FILES}) -add_library(IfcGeom ${SCHEMA_AGNOSTIC_FILES}) -set_target_properties(IfcGeom PROPERTIES COMPILE_FLAGS -DIFC_GEOM_EXPORTS) +add_library(geometry_mappings ${SCHEMA_AGNOSTIC_FILES}) +set_target_properties(geometry_mappings PROPERTIES COMPILE_FLAGS -DIFC_GEOM_EXPORTS) +target_link_libraries(geometry_mappings ${mapping_libraries}) if (UNIX) find_package(Threads) endif() -TARGET_LINK_LIBRARIES(IfcGeom ${IfcGeom_libraries} ${CMAKE_THREAD_LIBS_INIT}) +file(GLOB SCHEMA_AGNOSTIC_H_FILES ../src/ifcgeom/schema_agnostic/*.h) +file(GLOB SCHEMA_AGNOSTIC_CPP_FILES ../src/ifcgeom/schema_agnostic/*.cpp) +set(SCHEMA_AGNOSTIC_FILES ${SCHEMA_AGNOSTIC_H_FILES} ${SCHEMA_AGNOSTIC_CPP_FILES}) + +add_library(IfcGeom ${SCHEMA_AGNOSTIC_FILES}) +set_target_properties(IfcGeom PROPERTIES COMPILE_FLAGS -DIFC_GEOM_EXPORTS) +target_link_libraries(IfcGeom geometry_mappings geometry_kernels ${CMAKE_THREAD_LIBS_INIT}) endif(BUILD_IFCGEOM) if (BUILD_CONVERT) -# Serializers +# serializers file(GLOB SERIALIZERS_H_FILES ../src/serializers/*.h) file(GLOB SERIALIZERS_CPP_FILES ../src/serializers/*.cpp) set(SERIALIZERS_FILES ${SERIALIZERS_H_FILES} ${SERIALIZERS_CPP_FILES}) @@ -683,15 +700,15 @@ file(GLOB SERIALIZERS_S_CPP_FILES ../src/serializers/schema_dependent/*.cpp) set(SERIALIZERS_S_FILES ${SERIALIZERS_S_H_FILES} ${SERIALIZERS_S_CPP_FILES}) foreach(s ${SCHEMA_VERSIONS}) - add_library(Serializers_ifc${s} STATIC ${SERIALIZERS_S_FILES}) - set_target_properties(Serializers_ifc${s} PROPERTIES COMPILE_FLAGS "-DIFC_GEOM_EXPORTS -DIfcSchema=Ifc${s} ${CONVERT_PRECISION}") - TARGET_LINK_LIBRARIES(Serializers_ifc${s} IfcGeom ${OPENCASCADE_LIBRARIES}) + add_library(serializers_ifc${s} STATIC ${SERIALIZERS_S_FILES}) + set_target_properties(serializers_ifc${s} PROPERTIES COMPILE_FLAGS "-DIFC_GEOM_EXPORTS -DIfcSchema=Ifc${s} ${CONVERT_PRECISION}") + TARGET_LINK_LIBRARIES(serializers_ifc${s} IfcGeom ${OPENCASCADE_LIBRARIES}) endforeach() -add_library(Serializers ${SERIALIZERS_FILES}) -set_target_properties(Serializers PROPERTIES COMPILE_FLAGS "-DIFC_GEOM_EXPORTS ${CONVERT_PRECISION}") +add_library(serializers ${SERIALIZERS_FILES}) +set_target_properties(serializers PROPERTIES COMPILE_FLAGS "-DIFC_GEOM_EXPORTS ${CONVERT_PRECISION}") -TARGET_LINK_LIBRARIES(Serializers ${SERIALIZER_SCHEMA_LIBRARIES}) +TARGET_LINK_LIBRARIES(serializers ${SERIALIZER_SCHEMA_LIBRARIES}) # IfcConvert file(GLOB IFCCONVERT_CPP_FILES ../src/ifcconvert/*.cpp) @@ -778,7 +795,7 @@ INSTALL(TARGETS IfcGeom ${IfcGeom_libraries} endif() if(BUILD_CONVERT) -INSTALL(TARGETS Serializers ${SERIALIZER_SCHEMA_LIBRARIES} +INSTALL(TARGETS serializers ${SERIALIZER_SCHEMA_LIBRARIES} ARCHIVE DESTINATION ${LIBDIR} LIBRARY DESTINATION ${LIBDIR} RUNTIME DESTINATION ${BINDIR} diff --git a/src/ifcgeom/abstract_mapping.cpp b/src/ifcgeom/abstract_mapping.cpp new file mode 100644 index 0000000000..387e2c53a3 --- /dev/null +++ b/src/ifcgeom/abstract_mapping.cpp @@ -0,0 +1,35 @@ +#include "abstract_mapping.h" + +#include "../ifcparse/IfcFile.h" + +ifcopenshell::geometry::impl::MappingFactoryImplementation& ifcopenshell::geometry::impl::mapping_implementations() { + static MappingFactoryImplementation impl; + return impl; +} + +extern void init_MappingImplementation_Ifc2x3(ifcopenshell::geometry::impl::MappingFactoryImplementation*); +extern void init_MappingImplementation_Ifc4(ifcopenshell::geometry::impl::MappingFactoryImplementation*); +extern void init_MappingImplementation_Ifc4x1(ifcopenshell::geometry::impl::MappingFactoryImplementation*); +extern void init_MappingImplementation_Ifc4x2(ifcopenshell::geometry::impl::MappingFactoryImplementation*); + +ifcopenshell::geometry::impl::MappingFactoryImplementation::MappingFactoryImplementation() { + init_MappingImplementation_Ifc2x3(this); + init_MappingImplementation_Ifc4(this); + init_MappingImplementation_Ifc4x1(this); + init_MappingImplementation_Ifc4x2(this); +} + +void ifcopenshell::geometry::impl::MappingFactoryImplementation::bind(const std::string& schema_name, ifcopenshell::geometry::impl::mapping_fn fn) { + const std::string schema_name_lower = boost::to_lower_copy(schema_name); + this->insert(std::make_pair(schema_name_lower, fn)); +} + +ifcopenshell::geometry::abstract_mapping* ifcopenshell::geometry::impl::MappingFactoryImplementation::construct(IfcParse::IfcFile* file) { + const std::string schema_name_lower = boost::to_lower_copy(file->schema()->name()); + std::map::const_iterator it; + it = this->find(schema_name_lower); + if (it == end()) { + throw IfcParse::IfcException("No geometry mapping registered for " + schema_name_lower); + } + return it->second(file); +} diff --git a/src/ifcgeom/abstract_mapping.h b/src/ifcgeom/abstract_mapping.h index 1a89e3fa03..7dbb4d413e 100644 --- a/src/ifcgeom/abstract_mapping.h +++ b/src/ifcgeom/abstract_mapping.h @@ -1,15 +1,56 @@ +#ifndef ABSTRACT_MAPPING_H +#define ABSTRACT_MAPPING_H + #include "../ifcparse/IfcBaseClass.h" +#include "../ifcparse/IfcEntityList.h" #include "../ifcgeom/taxonomy.h" +#include "../ifcgeom/settings.h" + +#include + +#include +#include namespace ifcopenshell { namespace geometry { + + class Element; + class NativeElement; + + struct geometry_conversion_task { + int index; + IfcUtil::IfcBaseEntity* representation; + IfcEntityList::ptr products; + std::vector breps; + std::vector elements; + }; + + typedef boost::function filter_t; class abstract_mapping { public: virtual ifcopenshell::geometry::taxonomy::item* map(const IfcUtil::IfcBaseClass*) = 0; + virtual void get_representations(std::vector& tasks, std::vector& filters, settings& s) = 0; + virtual IfcUtil::IfcBaseEntity* get_decomposing_entity(IfcUtil::IfcBaseEntity* product, bool include_openings = true) = 0; + virtual std::map get_layers(IfcUtil::IfcBaseEntity*); }; + + namespace impl { + typedef boost::function1 mapping_fn; + + class MappingFactoryImplementation : public std::map { + public: + MappingFactoryImplementation(); + void bind(const std::string& schema_name, mapping_fn); + abstract_mapping* construct(IfcParse::IfcFile*); + }; + + MappingFactoryImplementation& mapping_implementations(); + } } -} \ No newline at end of file +} + +#endif diff --git a/src/ifcgeom/kernel_agnostic/AbstractKernel.cpp b/src/ifcgeom/kernel_agnostic/AbstractKernel.cpp index 4d920610b2..18f5d0c53d 100644 --- a/src/ifcgeom/kernel_agnostic/AbstractKernel.cpp +++ b/src/ifcgeom/kernel_agnostic/AbstractKernel.cpp @@ -2,551 +2,101 @@ #include "../../ifcgeom/schema_agnostic/IfcGeomElement.h" -#define AbstractKernel MAKE_TYPE_NAME(AbstractKernel) - -void IfcGeom::AbstractKernel::set_conversion_placement_rel_to(const IfcParse::declaration* type) { - placement_rel_to = type; -} - -void IfcGeom::AbstractKernel::setValue(GeomValue var, double value) { - switch (var) { - case GV_DEFLECTION_TOLERANCE: - deflection_tolerance = value; - break; - case GV_POINT_EQUALITY_TOLERANCE: - point_equality_tolerance = value; - break; - case GV_LENGTH_UNIT: - ifc_length_unit = value; - break; - case GV_PLANEANGLE_UNIT: - ifc_planeangle_unit = value; - break; - case GV_PRECISION: - modelling_precision = value; - break; - case GV_DIMENSIONALITY: - dimensionality = value; - break; - default: - assert(!"never reach here"); - } -} - -double IfcGeom::AbstractKernel::getValue(GeomValue var) const { - switch (var) { - case GV_DEFLECTION_TOLERANCE: - return deflection_tolerance; - case GV_MINIMAL_FACE_AREA: - // Considering a right-angled triangle, this about the smallest - // area you can obtain without the vertices being confused. - return modelling_precision * modelling_precision / 2.; - case GV_POINT_EQUALITY_TOLERANCE: - return point_equality_tolerance; - case GV_LENGTH_UNIT: - return ifc_length_unit; - break; - case GV_PLANEANGLE_UNIT: - return ifc_planeangle_unit; - break; - case GV_PRECISION: - return modelling_precision; - break; - case GV_DIMENSIONALITY: - return dimensionality; - break; - } - assert(!"never reach here"); - return 0; -} - -const IfcSchema::IfcMaterial* IfcGeom::AbstractKernel::get_single_material_association(const IfcSchema::IfcProduct* product) { - IfcSchema::IfcMaterial* single_material = 0; - IfcSchema::IfcRelAssociatesMaterial::list::ptr associated_materials = product->HasAssociations()->as(); - if (associated_materials->size() == 1) { - IfcSchema::IfcMaterialSelect* associated_material = (*associated_materials->begin())->RelatingMaterial(); - single_material = associated_material->as(); - - // NB: Single-layer layersets are also considered, regardless of --enable-layerset-slicing, this - // in accordance with other viewers. - if (!single_material && associated_material->as()) { - IfcSchema::IfcMaterialLayerSet* layerset = associated_material->as()->ForLayerSet(); - if (layerset->MaterialLayers()->size() == 1) { - IfcSchema::IfcMaterialLayer* layer = (*layerset->MaterialLayers()->begin()); - if (layer->hasMaterial()) { - single_material = layer->Material(); - } - } - } - } - return single_material; -} - -IfcSchema::IfcRepresentation* IfcGeom::AbstractKernel::representation_mapped_to(const IfcSchema::IfcRepresentation* representation) { - IfcSchema::IfcRepresentation* representation_mapped_to = 0; - IfcSchema::IfcRepresentationItem::list::ptr items = representation->Items(); - if (items->size() == 1) { - IfcSchema::IfcRepresentationItem* item = *items->begin(); - if (item->declaration().is(IfcSchema::IfcMappedItem::Class())) { - if (item->StyledByItem()->size() == 0) { - IfcSchema::IfcMappedItem* mapped_item = item->as(); - if (is_identity_transform(mapped_item->MappingTarget())) { - IfcSchema::IfcRepresentationMap* map = mapped_item->MappingSource(); - if (is_identity_transform(map->MappingOrigin())) { - representation_mapped_to = map->MappedRepresentation(); - } - } - } - } - } - return representation_mapped_to; -} - -IfcSchema::IfcProduct::list::ptr IfcGeom::AbstractKernel::products_represented_by(const IfcSchema::IfcRepresentation* representation) { - IfcSchema::IfcProduct::list::ptr products(new IfcSchema::IfcProduct::list); - - IfcSchema::IfcProductRepresentation::list::ptr prodreps = representation->OfProductRepresentation(); - - for (IfcSchema::IfcProductRepresentation::list::it it = prodreps->begin(); it != prodreps->end(); ++it) { - // http://buildingsmart-tech.org/ifc/IFC2x3/TC1/html/ifcrepresentationresource/lexical/ifcproductrepresentation.htm - // IFC2x Edition 3 NOTE Users should not instantiate the entity IfcProductRepresentation from IFC2x Edition 3 onwards. - // It will be changed into an ABSTRACT supertype in future releases of IFC. - - // IfcProductRepresentation also lacks the INVERSE relation to IfcProduct - // Let's find the IfcProducts that reference the IfcProductRepresentation anyway - products->push((*it)->data().getInverse((&IfcSchema::IfcProduct::Class()), -1)->as()); - } - - IfcSchema::IfcRepresentationMap::list::ptr maps = representation->RepresentationMap(); - if (maps->size() == 1) { - IfcSchema::IfcRepresentationMap* map = *maps->begin(); - if (is_identity_transform(map->MappingOrigin())) { - IfcSchema::IfcMappedItem::list::ptr items = map->MapUsage(); - for (IfcSchema::IfcMappedItem::list::it it = items->begin(); it != items->end(); ++it) { - IfcSchema::IfcMappedItem* item = *it; - if (item->StyledByItem()->size() != 0) continue; - - if (!is_identity_transform(item->MappingTarget())) { - continue; - } - - IfcSchema::IfcRepresentation::list::ptr reps = item->data().getInverse((&IfcSchema::IfcRepresentation::Class()), -1)->as(); - for (IfcSchema::IfcRepresentation::list::it jt = reps->begin(); jt != reps->end(); ++jt) { - IfcSchema::IfcRepresentation* rep = *jt; - if (rep->Items()->size() != 1) continue; - IfcSchema::IfcProductRepresentation::list::ptr prodreps_mapped = rep->OfProductRepresentation(); - for (IfcSchema::IfcProductRepresentation::list::it kt = prodreps_mapped->begin(); kt != prodreps_mapped->end(); ++kt) { - IfcSchema::IfcProduct::list::ptr ps = (*kt)->data().getInverse((&IfcSchema::IfcProduct::Class()), -1)->as(); - products->push(ps); - } - } - } - } - } - - return products; -} - namespace { - const IfcSchema::IfcRepresentationItem* find_item_carrying_style(const IfcSchema::IfcRepresentationItem* item) { - if (item->StyledByItem()->size()) { - return item; - } - - while (item->declaration().is(IfcSchema::IfcBooleanClippingResult::Class())) { - // All instantiations of IfcBooleanOperand (type of FirstOperand) are subtypes of - // IfcGeometricRepresentationItem - item = (IfcSchema::IfcGeometricRepresentationItem*) ((IfcSchema::IfcBooleanClippingResult*) item)->FirstOperand(); - if (item->StyledByItem()->size()) { - return item; + /* A compile-time for loop over the taxonomy kinds */ + template + struct dispatch_conversion { + static bool dispatch(ifcopenshell::geometry::kernels::AbstractKernel* kernel, const ifcopenshell::geometry::taxonomy::item* item, ifcopenshell::geometry::ConversionResults& results) { + if (N == item->kind()) { + auto concrete_item = static_cast*>(item); + return kernel->convert_impl(concrete_item, results); + } else { + return dispatch_conversion::dispatch(kernel, item, results); } } + }; - // TODO: Ideally this would be done for other entities (such as IfcCsgSolid) as well. - // But neither are these very prevalent, nor does the current IfcOpenShell style - // mechanism enable to conveniently style subshapes, which would be necessary for - // distinctly styled union operands. - - return item; - } - - - template - std::pair _get_surface_style(const IfcSchema::IfcStyledItem* si) { -#ifdef SCHEMA_HAS_IfcStyleAssignmentSelect - IfcEntityList::ptr style_assignments = si->Styles(); - for (IfcEntityList::it kt = style_assignments->begin(); kt != style_assignments->end(); ++kt) { - if (!(*kt)->declaration().is(IfcSchema::IfcPresentationStyleAssignment::Class())) { - continue; - } - IfcSchema::IfcPresentationStyleAssignment* style_assignment = (IfcSchema::IfcPresentationStyleAssignment*) *kt; -#else - IfcSchema::IfcPresentationStyleAssignment::list::ptr style_assignments = si->Styles(); - for (IfcSchema::IfcPresentationStyleAssignment::list::it kt = style_assignments->begin(); kt != style_assignments->end(); ++kt) { - IfcSchema::IfcPresentationStyleAssignment* style_assignment = *kt; -#endif - IfcEntityList::ptr styles = style_assignment->Styles(); - for (IfcEntityList::it lt = styles->begin(); lt != styles->end(); ++lt) { - IfcUtil::IfcBaseClass* style = *lt; - if (style->declaration().is(IfcSchema::IfcSurfaceStyle::Class())) { - IfcSchema::IfcSurfaceStyle* surface_style = (IfcSchema::IfcSurfaceStyle*) style; - if (surface_style->Side() != IfcSchema::IfcSurfaceSide::IfcSurfaceSide_NEGATIVE) { - IfcEntityList::ptr styles_elements = surface_style->Styles(); - for (IfcEntityList::it mt = styles_elements->begin(); mt != styles_elements->end(); ++mt) { - if ((*mt)->declaration().is(T::Class())) { - return std::make_pair(surface_style, (T*)*mt); - } - } - } - } - } - } - - return std::make_pair(0, 0); - } - - template - std::pair get_surface_style(const IfcSchema::IfcRepresentationItem* representation_item) { - // For certain representation items, most notably boolean operands, - // a style definition might reside on one of its operands. - representation_item = find_item_carrying_style(representation_item); - - if (representation_item->as()) { - return _get_surface_style(representation_item->as()); - } - IfcSchema::IfcStyledItem::list::ptr styled_items = representation_item->StyledByItem(); - if (styled_items->size()) { - // StyledByItem is a SET [0:1] OF IfcStyledItem, so we return after the first IfcStyledItem: - return _get_surface_style(*styled_items->begin()); - } - return std::make_pair(0, 0); - } - - bool process_colour(IfcSchema::IfcColourRgb* colour, double* rgb) { - if (colour != 0) { - rgb[0] = colour->Red(); - rgb[1] = colour->Green(); - rgb[2] = colour->Blue(); - } - return colour != 0; - } - - bool process_colour(IfcSchema::IfcNormalisedRatioMeasure* factor, double* rgb) { - if (factor != 0) { - const double f = *factor; - rgb[0] = rgb[1] = rgb[2] = f; - } - return factor != 0; - } - - bool process_colour(IfcSchema::IfcColourOrFactor* colour_or_factor, double* rgb) { - if (colour_or_factor == 0) { - return false; - } else if (colour_or_factor->declaration().is(IfcSchema::IfcColourRgb::Class())) { - return process_colour(static_cast(colour_or_factor), rgb); - } else if (colour_or_factor->declaration().is(IfcSchema::IfcNormalisedRatioMeasure::Class())) { - return process_colour(static_cast(colour_or_factor), rgb); - } else { + template <> + struct dispatch_conversion { + static bool dispatch(ifcopenshell::geometry::kernels::AbstractKernel*, const ifcopenshell::geometry::taxonomy::item*, ifcopenshell::geometry::ConversionResults&) { return false; } - } + }; } -const IfcGeom::SurfaceStyle* IfcGeom::AbstractKernel::get_style(const IfcSchema::IfcRepresentationItem* item) { - return internalize_surface_style(get_surface_style(item)); +bool ifcopenshell::geometry::kernels::AbstractKernel::convert(const taxonomy::item* item, ifcopenshell::geometry::ConversionResults& results) { + return dispatch_conversion<0>::dispatch(this, item, results); } -const IfcGeom::SurfaceStyle* IfcGeom::AbstractKernel::get_style(const IfcSchema::IfcMaterial* material) { - IfcSchema::IfcMaterialDefinitionRepresentation::list::ptr defs = material->HasRepresentation(); - for (IfcSchema::IfcMaterialDefinitionRepresentation::list::it jt = defs->begin(); jt != defs->end(); ++jt) { - IfcSchema::IfcRepresentation::list::ptr reps = (*jt)->Representations(); - IfcSchema::IfcStyledItem::list::ptr styles(new IfcSchema::IfcStyledItem::list); - for (IfcSchema::IfcRepresentation::list::it it = reps->begin(); it != reps->end(); ++it) { - styles->push((**it).Items()->as()); - } - for (IfcSchema::IfcStyledItem::list::it it = styles->begin(); it != styles->end(); ++it) { - const std::pair ss = get_surface_style(*it); - if (ss.second) { - return internalize_surface_style(ss); - } - } - } - IfcGeom::SurfaceStyle material_style = IfcGeom::SurfaceStyle(material->data().id(), material->Name()); - return &(style_cache[material->data().id()] = material_style); -} - -const IfcGeom::SurfaceStyle* IfcGeom::AbstractKernel::internalize_surface_style(const std::pair& shading_styles) { - if (shading_styles.second == 0) { - return 0; - } - int surface_style_id = shading_styles.first->data().id(); - std::map::const_iterator it = style_cache.find(surface_style_id); - if (it != style_cache.end()) { - return &(it->second); - } - SurfaceStyle surface_style; - - IfcSchema::IfcSurfaceStyle* style = shading_styles.first->as(); - IfcSchema::IfcSurfaceStyleShading* shading = shading_styles.second->as(); - - if (style->hasName()) { - surface_style = SurfaceStyle(surface_style_id, style->Name()); - } else { - surface_style = SurfaceStyle(surface_style_id); - } - double rgb[3]; - if (process_colour(shading->SurfaceColour(), rgb)) { - surface_style.Diffuse().reset(SurfaceStyle::ColorComponent(rgb[0], rgb[1], rgb[2])); - } - if (shading_styles.second->declaration().is(IfcSchema::IfcSurfaceStyleRendering::Class())) { - IfcSchema::IfcSurfaceStyleRendering* rendering_style = static_cast(shading_styles.second); - if (rendering_style->hasDiffuseColour() && process_colour(rendering_style->DiffuseColour(), rgb)) { - SurfaceStyle::ColorComponent diffuse = surface_style.Diffuse().get_value_or(SurfaceStyle::ColorComponent(1, 1, 1)); - surface_style.Diffuse().reset(SurfaceStyle::ColorComponent(diffuse.R() * rgb[0], diffuse.G() * rgb[1], diffuse.B() * rgb[2])); - } - if (rendering_style->hasDiffuseTransmissionColour()) { - // Not supported - } - if (rendering_style->hasReflectionColour()) { - // Not supported - } - if (rendering_style->hasSpecularColour() && process_colour(rendering_style->SpecularColour(), rgb)) { - surface_style.Specular().reset(SurfaceStyle::ColorComponent(rgb[0], rgb[1], rgb[2])); - } - if (rendering_style->hasSpecularHighlight()) { - IfcSchema::IfcSpecularHighlightSelect* highlight = rendering_style->SpecularHighlight(); - if (highlight->declaration().is(IfcSchema::IfcSpecularRoughness::Class())) { - double roughness = *((IfcSchema::IfcSpecularRoughness*)highlight); - if (roughness >= 1e-9) { - surface_style.Specularity().reset(1.0 / roughness); - } - } else if (highlight->declaration().is(IfcSchema::IfcSpecularExponent::Class())) { - surface_style.Specularity().reset(*((IfcSchema::IfcSpecularExponent*)highlight)); - } - } - if (rendering_style->hasTransmissionColour()) { - // Not supported - } - if (rendering_style->hasTransparency()) { - const double d = rendering_style->Transparency(); - surface_style.Transparency().reset(d); - } - } - return &(style_cache[surface_style_id] = surface_style); -} - - -template -IfcGeom::NativeElement* IfcGeom::AbstractKernel::create_brep_for_representation_and_product( - const IteratorSettings& settings, IfcSchema::IfcRepresentation* representation, IfcSchema::IfcProduct* product) { - std::stringstream representation_id_builder; - - representation_id_builder << representation->data().id(); - - IfcGeom::Representation::BRep* shape; - IfcGeom::ConversionResults shapes; - - if (!convert_shapes(representation, shapes)) { - return 0; - } - - if (settings.get(IteratorSettings::APPLY_LAYERSETS)) { - if (apply_layerset(product, shapes)) { - - IfcSchema::IfcRelAssociates::list::ptr associations = product->HasAssociations(); - for (IfcSchema::IfcRelAssociates::list::it it = associations->begin(); it != associations->end(); ++it) { - IfcSchema::IfcRelAssociatesMaterial* associates_material = (**it).as(); - if (associates_material) { - unsigned layerset_id = associates_material->RelatingMaterial()->data().id(); - representation_id_builder << "-layerset-" << layerset_id; - break; - } - } - - } - } - - bool material_style_applied = false; - - const IfcSchema::IfcMaterial* single_material = get_single_material_association(product); - if (single_material) { - const IfcGeom::SurfaceStyle* s = get_style(single_material); - for (IfcGeom::ConversionResults::iterator it = shapes.begin(); it != shapes.end(); ++it) { - if (!it->hasStyle() && s) { - it->setStyle(s); - material_style_applied = true; - } - } - } else { - bool some_items_without_style = false; - for (IfcGeom::ConversionResults::iterator it = shapes.begin(); it != shapes.end(); ++it) { - if (!it->hasStyle()) { - some_items_without_style = true; - break; - } - } - if (some_items_without_style) { - Logger::Warning("No material and surface styles for:", product); - } - } - - if (material_style_applied) { - representation_id_builder << "-material-" << single_material->data().id(); - } - - int parent_id = -1; - try { - IfcUtil::IfcBaseEntity* parent_object = get_decomposing_entity(product); - if (parent_object && parent_object->as()) { - parent_id = parent_object->data().id(); - } - } catch (const std::exception& e) { - Logger::Error(e); - } - - const std::string name = product->hasName() ? product->Name() : ""; - const std::string guid = product->GlobalId(); - - ConversionResultPlacement* trsf = nullptr; - try { - convert_placement(product->ObjectPlacement(), trsf); - } catch (const std::exception& e) { - Logger::Error(e); - } catch (...) { - Logger::Error("Failed to construct placement"); - } - - // Does the IfcElement have any IfcOpenings? - // Note that openings for IfcOpeningElements are not processed - IfcSchema::IfcRelVoidsElement::list::ptr openings = find_openings(product)->as(); - - const std::string product_type = product->declaration().name(); - ElementSettings element_settings(settings, getValue(GV_LENGTH_UNIT), product_type); - - if (!settings.get(IfcGeom::IteratorSettings::DISABLE_OPENING_SUBTRACTIONS) && openings && openings->size()) { - representation_id_builder << "-openings"; - for (IfcSchema::IfcRelVoidsElement::list::it it = openings->begin(); it != openings->end(); ++it) { - representation_id_builder << "-" << (*it)->data().id(); - } - - IfcGeom::ConversionResults opened_shapes; - bool caught_error = false; - try { - convert_openings(product, openings, shapes, trsf, opened_shapes); - } catch (const std::exception& e) { - Logger::Message(Logger::LOG_ERROR, std::string("Error processing openings for: ") + e.what() + ":", product); - caught_error = true; - } catch (...) { - Logger::Message(Logger::LOG_ERROR, "Error processing openings for:", product); - } - - if (caught_error && opened_shapes.size() < shapes.size()) { - opened_shapes = shapes; - } - - if (settings.get(IteratorSettings::USE_WORLD_COORDS)) { - for (IfcGeom::ConversionResults::iterator it = opened_shapes.begin(); it != opened_shapes.end(); ++it) { - it->prepend(trsf); - } - trsf = nullptr; - representation_id_builder << "-world-coords"; - } - shape = new IfcGeom::Representation::BRep(element_settings, representation_id_builder.str(), opened_shapes); - } else if (settings.get(IteratorSettings::USE_WORLD_COORDS)) { - for (IfcGeom::ConversionResults::iterator it = shapes.begin(); it != shapes.end(); ++it) { - it->prepend(trsf); - } - trsf = nullptr; - representation_id_builder << "-world-coords"; - shape = new IfcGeom::Representation::BRep(element_settings, representation_id_builder.str(), shapes); - } else { - shape = new IfcGeom::Representation::BRep(element_settings, representation_id_builder.str(), shapes); - } - - std::string context_string = ""; - if (representation->hasRepresentationIdentifier()) { - context_string = representation->RepresentationIdentifier(); - } else if (representation->ContextOfItems()->hasContextType()) { - context_string = representation->ContextOfItems()->ContextType(); - } - - auto elem = new NativeElement( - product->data().id(), - parent_id, - name, - product_type, - guid, - context_string, - trsf, - boost::shared_ptr(shape), - product - ); - - if (settings.get(IteratorSettings::VALIDATE_QUANTITIES)) { - validate_quantities(product, elem->geometry()); - } - - return elem; -} - -template -IfcGeom::NativeElement* IfcGeom::AbstractKernel::create_brep_for_processed_representation( - const IteratorSettings& /*settings*/, IfcSchema::IfcRepresentation* representation, IfcSchema::IfcProduct* product, - IfcGeom::NativeElement* brep) { - int parent_id = -1; - try { - IfcUtil::IfcBaseEntity* parent_object = get_decomposing_entity(product); - if (parent_object && parent_object->as()) { - parent_id = parent_object->data().id(); - } - } catch (const std::exception& e) { - Logger::Error(e); - } - - const std::string name = product->hasName() ? product->Name() : ""; - const std::string guid = product->GlobalId(); - - ConversionResultPlacement* trsf = nullptr; - try { - convert_placement(product->ObjectPlacement(), trsf); - } catch (const std::exception& e) { - Logger::Error(e); - } catch (...) { - Logger::Error("Failed to construct placement"); - } - - std::string context_string = ""; - if (representation->hasRepresentationIdentifier()) { - context_string = representation->RepresentationIdentifier(); - } else if (representation->ContextOfItems()->hasContextType()) { - context_string = representation->ContextOfItems()->ContextType(); - } - - const std::string product_type = product->declaration().name(); - - return new NativeElement( - product->data().id(), - parent_id, - name, - product_type, - guid, - context_string, - trsf, - brep->geometry_pointer(), - product - ); -} - -template IFC_GEOM_API IfcGeom::NativeElement* IfcGeom::AbstractKernel::create_brep_for_representation_and_product( - const IteratorSettings& settings, IfcSchema::IfcRepresentation* representation, IfcSchema::IfcProduct* product); -template IFC_GEOM_API IfcGeom::NativeElement* IfcGeom::AbstractKernel::create_brep_for_representation_and_product( - const IteratorSettings& settings, IfcSchema::IfcRepresentation* representation, IfcSchema::IfcProduct* product); -template IFC_GEOM_API IfcGeom::NativeElement* IfcGeom::AbstractKernel::create_brep_for_representation_and_product( - const IteratorSettings& settings, IfcSchema::IfcRepresentation* representation, IfcSchema::IfcProduct* product); - -template IFC_GEOM_API IfcGeom::NativeElement* IfcGeom::AbstractKernel::create_brep_for_processed_representation( - const IteratorSettings& settings, IfcSchema::IfcRepresentation* representation, IfcSchema::IfcProduct* product, IfcGeom::NativeElement* brep); -template IFC_GEOM_API IfcGeom::NativeElement* IfcGeom::AbstractKernel::create_brep_for_processed_representation( - const IteratorSettings& settings, IfcSchema::IfcRepresentation* representation, IfcSchema::IfcProduct* product, IfcGeom::NativeElement* brep); -template IFC_GEOM_API IfcGeom::NativeElement* IfcGeom::AbstractKernel::create_brep_for_processed_representation( - const IteratorSettings& settings, IfcSchema::IfcRepresentation* representation, IfcSchema::IfcProduct* product, IfcGeom::NativeElement* brep); \ No newline at end of file +//void ifcopenshell::geometry::kernels::AbstractKernel::set_conversion_placement_rel_to(const IfcParse::declaration* type) { +// placement_rel_to = type; +//} +// +//void ifcopenshell::geometry::kernels::AbstractKernel::setValue(GeomValue var, double value) { +// switch (var) { +// case GV_DEFLECTION_TOLERANCE: +// deflection_tolerance = value; +// break; +// case GV_POINT_EQUALITY_TOLERANCE: +// point_equality_tolerance = value; +// break; +// case GV_LENGTH_UNIT: +// ifc_length_unit = value; +// break; +// case GV_PLANEANGLE_UNIT: +// ifc_planeangle_unit = value; +// break; +// case GV_PRECISION: +// modelling_precision = value; +// break; +// case GV_DIMENSIONALITY: +// dimensionality = value; +// break; +// default: +// assert(!"never reach here"); +// } +//} +// +//double ifcopenshell::geometry::kernels::AbstractKernel::getValue(GeomValue var) const { +// switch (var) { +// case GV_DEFLECTION_TOLERANCE: +// return deflection_tolerance; +// case GV_MINIMAL_FACE_AREA: +// // Considering a right-angled triangle, this about the smallest +// // area you can obtain without the vertices being confused. +// return modelling_precision * modelling_precision / 2.; +// case GV_POINT_EQUALITY_TOLERANCE: +// return point_equality_tolerance; +// case GV_LENGTH_UNIT: +// return ifc_length_unit; +// break; +// case GV_PLANEANGLE_UNIT: +// return ifc_planeangle_unit; +// break; +// case GV_PRECISION: +// return modelling_precision; +// break; +// case GV_DIMENSIONALITY: +// return dimensionality; +// break; +// } +// assert(!"never reach here"); +// return 0; +//} +// +// +// +// +//template IFC_GEOM_API ifcopenshell::geometry::kernels::NativeElement* ifcopenshell::geometry::kernels::AbstractKernel::create_brep_for_representation_and_product( +// const IteratorSettings& settings, IfcSchema::IfcRepresentation* representation, IfcSchema::IfcProduct* product); +//template IFC_GEOM_API ifcopenshell::geometry::kernels::NativeElement* ifcopenshell::geometry::kernels::AbstractKernel::create_brep_for_representation_and_product( +// const IteratorSettings& settings, IfcSchema::IfcRepresentation* representation, IfcSchema::IfcProduct* product); +//template IFC_GEOM_API ifcopenshell::geometry::kernels::NativeElement* ifcopenshell::geometry::kernels::AbstractKernel::create_brep_for_representation_and_product( +// const IteratorSettings& settings, IfcSchema::IfcRepresentation* representation, IfcSchema::IfcProduct* product); +// +//template IFC_GEOM_API ifcopenshell::geometry::kernels::NativeElement* ifcopenshell::geometry::kernels::AbstractKernel::create_brep_for_processed_representation( +// const IteratorSettings& settings, IfcSchema::IfcRepresentation* representation, IfcSchema::IfcProduct* product, ifcopenshell::geometry::kernels::NativeElement* brep); +//template IFC_GEOM_API ifcopenshell::geometry::kernels::NativeElement* ifcopenshell::geometry::kernels::AbstractKernel::create_brep_for_processed_representation( +// const IteratorSettings& settings, IfcSchema::IfcRepresentation* representation, IfcSchema::IfcProduct* product, ifcopenshell::geometry::kernels::NativeElement* brep); +//template IFC_GEOM_API ifcopenshell::geometry::kernels::NativeElement* ifcopenshell::geometry::kernels::AbstractKernel::create_brep_for_processed_representation( +// const IteratorSettings& settings, IfcSchema::IfcRepresentation* representation, IfcSchema::IfcProduct* product, ifcopenshell::geometry::kernels::NativeElement* brep); \ No newline at end of file diff --git a/src/ifcgeom/kernel_agnostic/AbstractKernel.h b/src/ifcgeom/kernel_agnostic/AbstractKernel.h index dcfdd207d1..dcff03cebc 100644 --- a/src/ifcgeom/kernel_agnostic/AbstractKernel.h +++ b/src/ifcgeom/kernel_agnostic/AbstractKernel.h @@ -3,19 +3,12 @@ #include "../../ifcparse/macros.h" #include "../../ifcgeom/schema_agnostic/ifc_geom_api.h" -#include "../../ifcgeom/schema_agnostic/Kernel.h" #include "../../ifcgeom/schema_agnostic/IfcGeomRepresentation.h" +#include "../../ifcgeom/taxonomy.h" -#define INCLUDE_SCHEMA(x) STRINGIFY(../../ifcparse/x.h) -#include INCLUDE_SCHEMA(IfcSchema) -#undef INCLUDE_SCHEMA -#define INCLUDE_SCHEMA(x) STRINGIFY(../../ifcparse/x-definitions.h) -#include INCLUDE_SCHEMA(IfcSchema) -#undef INCLUDE_SCHEMA +namespace ifcopenshell { namespace geometry { namespace kernels { -namespace IfcGeom { - - class IFC_GEOM_API MAKE_TYPE_NAME(AbstractKernel) : public IfcGeom::Kernel { + class IFC_GEOM_API AbstractKernel { protected: // For stopping PlacementRelTo recursion in convert(const IfcSchema::IfcObjectPlacement* l, gp_Trsf& trsf) const IfcParse::declaration* placement_rel_to; @@ -29,11 +22,11 @@ namespace IfcGeom { double modelling_precision; double dimensionality; - std::map style_cache; + std::string geometry_library; public: - MAKE_TYPE_NAME(AbstractKernel)(const std::string& geometry_library) - : IfcGeom::Kernel(geometry_library, nullptr) + AbstractKernel(const std::string& geometry_library) + : geometry_library(geometry_library) , deflection_tolerance(0.001) , wire_creation_tolerance(0.0001) , point_equality_tolerance(0.00001) @@ -42,36 +35,39 @@ namespace IfcGeom { , ifc_planeangle_unit(-1.0) , modelling_precision(0.00001) , dimensionality(1.) - , placement_rel_to(0) - {} + , placement_rel_to(0) {} - void set_conversion_placement_rel_to(const IfcParse::declaration* type); - virtual void setValue(GeomValue var, double value); - virtual double getValue(GeomValue var) const; + bool convert(const taxonomy::item*, ifcopenshell::geometry::ConversionResults&); - const IfcSchema::IfcMaterial* get_single_material_association(const IfcSchema::IfcProduct*); - IfcSchema::IfcRepresentation* representation_mapped_to(const IfcSchema::IfcRepresentation* representation); - IfcSchema::IfcProduct::list::ptr products_represented_by(const IfcSchema::IfcRepresentation*); - const SurfaceStyle* get_style(const IfcSchema::IfcRepresentationItem*); - const SurfaceStyle* get_style(const IfcSchema::IfcMaterial*); - - virtual bool is_identity_transform(const IfcUtil::IfcBaseClass*) = 0; - virtual bool convert_shapes(const IfcUtil::IfcBaseClass*, IfcGeom::ConversionResults&) = 0; - virtual bool apply_layerset(const IfcSchema::IfcProduct* product, IfcGeom::ConversionResults& shapes) = 0; - virtual bool validate_quantities(const IfcSchema::IfcProduct* product, const IfcGeom::Representation::BRep& brep) = 0; - virtual bool convert_openings(const IfcSchema::IfcProduct* product, const IfcSchema::IfcRelVoidsElement::list::ptr& openings, const IfcGeom::ConversionResults& shapes, const ConversionResultPlacement* trsf, IfcGeom::ConversionResults& opened_shapes) = 0; - - const SurfaceStyle* internalize_surface_style(const std::pair& shading_style); - - template - IfcGeom::NativeElement* create_brep_for_representation_and_product( - const IteratorSettings&, IfcSchema::IfcRepresentation*, IfcSchema::IfcProduct*); - - template - IfcGeom::NativeElement* create_brep_for_processed_representation( - const IteratorSettings&, IfcSchema::IfcRepresentation*, IfcSchema::IfcProduct*, IfcGeom::NativeElement*); + virtual bool convert_impl(const taxonomy::matrix4*, ifcopenshell::geometry::ConversionResults&) { throw std::runtime_error("Not implemented"); } + virtual bool convert_impl(const taxonomy::point3*, ifcopenshell::geometry::ConversionResults&) { throw std::runtime_error("Not implemented"); } + virtual bool convert_impl(const taxonomy::direction3*, ifcopenshell::geometry::ConversionResults&) { throw std::runtime_error("Not implemented"); } + virtual bool convert_impl(const taxonomy::line*, ifcopenshell::geometry::ConversionResults&) { throw std::runtime_error("Not implemented"); } + virtual bool convert_impl(const taxonomy::circle*, ifcopenshell::geometry::ConversionResults&) { throw std::runtime_error("Not implemented"); } + virtual bool convert_impl(const taxonomy::ellipse*, ifcopenshell::geometry::ConversionResults&) { throw std::runtime_error("Not implemented"); } + virtual bool convert_impl(const taxonomy::bspline*, ifcopenshell::geometry::ConversionResults&) { throw std::runtime_error("Not implemented"); } + virtual bool convert_impl(const taxonomy::edge*, ifcopenshell::geometry::ConversionResults&) { throw std::runtime_error("Not implemented"); } + virtual bool convert_impl(const taxonomy::loop*, ifcopenshell::geometry::ConversionResults&) { throw std::runtime_error("Not implemented"); } + virtual bool convert_impl(const taxonomy::face*, ifcopenshell::geometry::ConversionResults&) { throw std::runtime_error("Not implemented"); } + virtual bool convert_impl(const taxonomy::extrusion*, ifcopenshell::geometry::ConversionResults&) { throw std::runtime_error("Not implemented"); } + virtual bool convert_impl(const taxonomy::node*, ifcopenshell::geometry::ConversionResults&) { throw std::runtime_error("Not implemented"); } }; + namespace impl { + typedef boost::function1 < AbstractKernel*, const std::string&> kernel_fn; + + class KernelFactoryImplementation : public std::map { + public: + KernelFactoryImplementation(); + void bind(const std::string& geometry_library, kernel_fn); + AbstractKernel* construct(const std::string& geometry_library, IfcParse::IfcFile*); + }; + + KernelFactoryImplementation& kernel_implementations(); + } + +} +} } #endif \ No newline at end of file diff --git a/src/ifcgeom/kernel_agnostic/IfcGeomIteratorImplementation.cpp b/src/ifcgeom/kernel_agnostic/IfcGeomIteratorImplementation.cpp deleted file mode 100644 index d4a0a5e3b8..0000000000 --- a/src/ifcgeom/kernel_agnostic/IfcGeomIteratorImplementation.cpp +++ /dev/null @@ -1,31 +0,0 @@ -#include "IfcGeomIteratorImplementation.h" - -namespace IfcGeom { - template class MAKE_TYPE_NAME(IteratorImplementation_); - template class MAKE_TYPE_NAME(IteratorImplementation_); - template class MAKE_TYPE_NAME(IteratorImplementation_); -} - -#define MAKE_INIT_FN__(a, b) init_ ## a ## b -#define MAKE_INIT_FN_(a, b) MAKE_INIT_FN__(a, b) -#define MAKE_INIT_FN(t) MAKE_INIT_FN_(t, IfcSchema) - -namespace { - template - struct MAKE_TYPE_NAME(factory_t) { - IfcGeom::IteratorImplementation* operator()(const std::string& geometry_engine, const IfcGeom::IteratorSettings& settings, IfcParse::IfcFile* file, const std::vector& filters, int num_threads) const { - return new IfcGeom::MAKE_TYPE_NAME(IteratorImplementation_)(geometry_engine, settings, file, filters, num_threads); - } - }; -} - -template -void MAKE_INIT_FN(IteratorImplementation_)(IteratorFactoryImplementation* mapping) { - static const std::string schema_name = STRINGIFY(IfcSchema); - MAKE_TYPE_NAME(factory_t) factory; - mapping->bind(schema_name, factory); -} - -template void MAKE_INIT_FN(IteratorImplementation_)(IteratorFactoryImplementation*); -template void MAKE_INIT_FN(IteratorImplementation_)(IteratorFactoryImplementation*); -template void MAKE_INIT_FN(IteratorImplementation_)(IteratorFactoryImplementation*); diff --git a/src/ifcgeom/kernel_agnostic/IfcGeomIteratorImplementation.h b/src/ifcgeom/kernel_agnostic/IfcGeomIteratorImplementation.h deleted file mode 100644 index e145f1cd7d..0000000000 --- a/src/ifcgeom/kernel_agnostic/IfcGeomIteratorImplementation.h +++ /dev/null @@ -1,950 +0,0 @@ -/******************************************************************************** - * * - * This file is part of IfcOpenShell. * - * * - * IfcOpenShell is free software: you can redistribute it and/or modify * - * it under the terms of the Lesser GNU General Public License as published by * - * the Free Software Foundation, either version 3.0 of the License, or * - * (at your option) any later version. * - * * - * IfcOpenShell is distributed in the hope that it will be useful, * - * but WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * - * Lesser GNU General Public License for more details. * - * * - * You should have received a copy of the Lesser GNU General Public License * - * along with this program. If not, see . * - * * - ********************************************************************************/ - -/******************************************************************************** - * * - * Geometrical data in an IFC file consists of shapes (IfcShapeRepresentation) * - * and instances (SUBTYPE OF IfcBuildingElement e.g. IfcWindow). * - * * - * IfcGeom::Representation::Triangulation is a class that represents a * - * triangulated IfcShapeRepresentation. * - * Triangulation.verts is a 1 dimensional vector of float defining the * - * cartesian coordinates of the vertices of the triangulated shape in the * - * format of [x1,y1,z1,..,xn,yn,zn] * - * Triangulation.faces is a 1 dimensional vector of int containing the * - * indices of the triangles referencing positions in Triangulation.verts * - * Triangulation.edges is a 1 dimensional vector of int in {0,1} that dictates* - * the visibility of the edges that span the faces in Triangulation.faces * - * * - * IfcGeom::Element represents the actual IfcBuildingElements. * - * IfcGeomObject.name is the GUID of the element * - * IfcGeomObject.type is the datatype of the element e.g. IfcWindow * - * IfcGeomObject.mesh is a pointer to an IfcMesh * - * IfcGeomObject.transformation.matrix is a 4x3 matrix that defines the * - * orientation and translation of the mesh in relation to the world origin * - * * - * IfcGeom::Iterator::initialize() * - * finds the most suitable representation contexts. Returns true iff * - * at least a single representation will process successfully * - * * - * IfcGeom::Iterator::get() * - * returns a pointer to the current IfcGeom::Element * - * * - * IfcGeom::Iterator::next() * - * returns true iff a following entity is available for a successive call to * - * IfcGeom::Iterator::get() * - * * - * IfcGeom::Iterator::progress() * - * returns an int in [0..100] that indicates the overall progress * - * * - ********************************************************************************/ - -#ifndef IFCGEOMITERATOR_H -#define IFCGEOMITERATOR_H - -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#include - -#include -#include -#include -#include -#include -#include - -#include "../../ifcparse/macros.h" -#include "../../ifcparse/IfcFile.h" - -#include "../../ifcgeom/schema_agnostic/IfcGeomElement.h" -#include "../../ifcgeom/schema_agnostic/IfcGeomMaterial.h" -#include "../../ifcgeom/schema_agnostic/IfcGeomIteratorSettings.h" -#include "../../ifcgeom/schema_agnostic/ConversionResult.h" - -#include "../../ifcgeom/schema_agnostic/IfcGeomFilter.h" -#include "../../ifcgeom/schema_agnostic/IteratorImplementation.h" - -#include "../../ifcgeom/kernel_agnostic/AbstractKernel.h" - -#define INCLUDE_SCHEMA(x) STRINGIFY(../../ifcparse/x.h) -#include INCLUDE_SCHEMA(IfcSchema) -#undef INCLUDE_SCHEMA - -#include - -// The infamous min & max Win32 #defines can leak here from OCE depending on the build configuration -#ifdef min -#undef min -#endif -#ifdef max -#undef max -#endif - -namespace { - template - struct geometry_conversion_task { - int index; - IfcSchema::IfcRepresentation *representation; - IfcSchema::IfcProduct::list::ptr products; - std::vector*> breps; - std::vector*> elements; - }; - - template - IfcGeom::Element* process_based_on_settings( - const IfcGeom::IteratorSettings& settings, - IfcGeom::NativeElement* elem, - IfcGeom::TriangulationElement* previous=nullptr) - { - if (settings.get(IfcGeom::IteratorSettings::USE_BREP_DATA)) { - try { - return new IfcGeom::SerializedElement(*elem); - } catch (...) { - Logger::Message(Logger::LOG_ERROR, "Getting a serialized element from model failed."); - return nullptr; - } - } else if (!settings.get(IfcGeom::IteratorSettings::DISABLE_TRIANGULATION)) { - try { - if (!previous) { - return new IfcGeom::TriangulationElement(*elem); - } else { - return new IfcGeom::TriangulationElement(*elem, previous->geometry_pointer()); - } - } catch (...) { - Logger::Message(Logger::LOG_ERROR, "Getting a triangulation element from model failed."); - return nullptr; - } - } else { - return elem; - } - } - - template - void create_element( - IfcGeom::MAKE_TYPE_NAME(AbstractKernel)* kernel, - const IfcGeom::IteratorSettings& settings, - geometry_conversion_task* rep) - { - IfcSchema::IfcRepresentation *representation = rep->representation; - IfcSchema::IfcProduct *product = *rep->products->begin(); - auto brep = kernel->create_brep_for_representation_and_product(settings, representation, product); - if (!brep) { - return; - } - - auto elem = process_based_on_settings(settings, brep); - if (!elem) { - return; - } - - rep->breps = { brep }; - rep->elements = { elem }; - - for (auto it = rep->products->begin() + 1; it != rep->products->end(); ++it) { - auto brep2 = kernel->create_brep_for_processed_representation(settings, representation, *it, brep); - if (brep2) { - auto elem2 = process_based_on_settings(settings, brep, dynamic_cast*>(elem)); - if (elem2) { - rep->breps.push_back(brep2); - rep->elements.push_back(elem2); - } - } - } - } -} - -namespace IfcGeom { - - template - class MAKE_TYPE_NAME(IteratorImplementation_) : public IteratorImplementation { - private: - - int num_threads_; - std::atomic progress_; - std::vector> tasks_; - std::vector*> all_processed_elements_; - std::vector*> all_processed_native_elements_; - typename std::vector*>::const_iterator task_result_iterator_; - typename std::vector*>::const_iterator native_task_result_iterator_; - - std::string geometry_library_; - - MAKE_TYPE_NAME(IteratorImplementation_)(const MAKE_TYPE_NAME(IteratorImplementation_)&); // N/I - MAKE_TYPE_NAME(IteratorImplementation_)& operator=(const MAKE_TYPE_NAME(IteratorImplementation_)&); // N/I - - MAKE_TYPE_NAME(AbstractKernel)* kernel; - IteratorSettings settings; - - IfcParse::IfcFile* ifc_file; - - // A container and iterator for IfcRepresentations - IfcSchema::IfcRepresentation::list::ptr representations; - IfcSchema::IfcRepresentation::list::it representation_iterator; - - // The object is fetched beforehand to be sure that get() returns a valid element - TriangulationElement* current_triangulation; - NativeElement* current_shape_model; - SerializedElement* current_serialization; - - // A container and iterator for IfcBuildingElements for the current IfcRepresentation referenced by *representation_iterator - IfcSchema::IfcProduct::list::ptr ifcproducts; - IfcSchema::IfcProduct::list::it ifcproduct_iterator; - - - IfcSchema::IfcRepresentation::list::ptr ok_mapped_representations; - - int done; - int total; - - std::string unit_name; - double unit_magnitude; - - gp_XYZ bounds_min_; - gp_XYZ bounds_max_; - - std::vector filters_; - - struct filter_match - { - filter_match(IfcSchema::IfcProduct *prod) : product(prod) {} - bool operator()(const filter_t& filter) const { return filter(product); } - - IfcSchema::IfcProduct* product; - }; - - /// @todo public/private sections all over the place: move all public to the beginning of the class - public: - typedef P Precision; - typedef PP PlacementPrecision; - - bool initialize() { - - std::set allowed_context_types; - allowed_context_types.insert("model"); - allowed_context_types.insert("plan"); - allowed_context_types.insert("notdefined"); - - std::set context_types; - if (!settings.get(IteratorSettings::EXCLUDE_SOLIDS_AND_SURFACES)) { - // Really this should only be 'Model', as per - // the standard 'Design' is deprecated. So, - // just for backwards compatibility: - context_types.insert("model"); - context_types.insert("design"); - // Some earlier (?) versions DDS-CAD output their own ContextTypes - context_types.insert("model view"); - context_types.insert("detail view"); - } - if (settings.get(IteratorSettings::INCLUDE_CURVES)) { - context_types.insert("plan"); - } - - representations = IfcSchema::IfcRepresentation::list::ptr(new IfcSchema::IfcRepresentation::list); - ok_mapped_representations = IfcSchema::IfcRepresentation::list::ptr(new IfcSchema::IfcRepresentation::list); - - IfcSchema::IfcGeometricRepresentationContext::list::it it; - IfcSchema::IfcGeometricRepresentationSubContext::list::it jt; - IfcSchema::IfcGeometricRepresentationContext::list::ptr contexts = - ifc_file->instances_by_type(); - - IfcSchema::IfcGeometricRepresentationContext::list::ptr filtered_contexts (new IfcSchema::IfcGeometricRepresentationContext::list); - - for (it = contexts->begin(); it != contexts->end(); ++it) { - IfcSchema::IfcGeometricRepresentationContext* context = *it; - if (context->declaration().is(IfcSchema::IfcGeometricRepresentationSubContext::Class())) { - // Continue, as the list of subcontexts will be considered - // by the parent's context inverse attributes. - continue; - } - try { - if (context->hasContextType()) { - std::string context_type = context->ContextType(); - boost::to_lower(context_type); - - if (allowed_context_types.find(context_type) == allowed_context_types.end()) { - Logger::Warning(std::string("ContextType '") + context->ContextType() + "' not allowed:", context); - } - if (context_types.find(context_type) != context_types.end()) { - filtered_contexts->push(context); - } - } - } catch (const std::exception& e) { - Logger::Error(e); - } - } - - // In case no contexts are identified based on their ContextType, all contexts are - // considered. Note that sub contexts are excluded as they are considered later on. - if (filtered_contexts->size() == 0) { - for (it = contexts->begin(); it != contexts->end(); ++it) { - IfcSchema::IfcGeometricRepresentationContext* context = *it; - if (!context->declaration().is(IfcSchema::IfcGeometricRepresentationSubContext::Class())) { - filtered_contexts->push(context); - } - } - } - - for (it = filtered_contexts->begin(); it != filtered_contexts->end(); ++it) { - IfcSchema::IfcGeometricRepresentationContext* context = *it; - - representations->push(context->RepresentationsInContext()); - - IfcSchema::IfcGeometricRepresentationSubContext::list::ptr sub_contexts = context->HasSubContexts(); - for (jt = sub_contexts->begin(); jt != sub_contexts->end(); ++jt) { - representations->push((*jt)->RepresentationsInContext()); - } - // There is no need for full recursion as the following is governed by the schema: - // WR31: The parent context shall not be another geometric representation sub context. - } - - if (representations->size() == 0) { - Logger::Warning("No representations encountered in relevant contexts, using all"); - representations = ifc_file->instances_by_type(); - } - - if (representations->size() == 0) { - Logger::Warning("No representations encountered, aborting"); - return false; - } - - representation_iterator = representations->begin(); - ifcproducts.reset(); - - done = 0; - total = representations->size(); - - if (num_threads_ != 1) { - collect(); - process_concurrently(); - } else { - if (!create()) { - return false; - } - } - - return true; - } - - void collect() { - int i = 0; - IfcSchema::IfcProduct::list* previous = nullptr; - while (auto rp = get_next_task()) { - // Note that get_next_task() mutates the state of the iterator - // we use that capture all products that can be processed as - // part of this representation and then keep iterating until - // the underlying list of products changes. - if (ifcproducts.get() != previous) { - previous = ifcproducts.get(); - geometry_conversion_task t; - t.index = i++; - t.representation = *representation_iterator; - t.products = ifcproducts; - tasks_.emplace_back(t); - } - - _nextShape(); - } - } - - void process_concurrently() { - size_t conc_threads = num_threads_; - if (conc_threads > tasks_.size()) { - conc_threads = tasks_.size(); - } - - std::vector kernel_pool; - kernel_pool.reserve(conc_threads); - for (unsigned i = 0; i < conc_threads; ++i) { - kernel_pool.push_back((MAKE_TYPE_NAME(AbstractKernel)*) impl::kernel_implementations().construct(ifc_file->schema()->name(), geometry_library_, ifc_file)); - } - - std::vector> threadpool; - - int old_progress = -1; - int processed = 0; - - Logger::ProgressBar(0); - - for (auto& rep : tasks_) { - MAKE_TYPE_NAME(AbstractKernel)* K = nullptr; - if (threadpool.size() < kernel_pool.size()) { - K = kernel_pool[threadpool.size()]; - } - - while (threadpool.size() == conc_threads) { - for (int i = 0; i < (int)threadpool.size(); i++) { - std::future &fu = threadpool[i]; - std::future_status status; - status = fu.wait_for(std::chrono::seconds(0)); - if (status == std::future_status::ready) { - fu.get(); - - processed += 1; - progress_ = processed * 50 / tasks_.size(); - if (progress_ != old_progress) { - Logger::ProgressBar(progress_); - old_progress = progress_; - } - - std::swap(threadpool[i], threadpool.back()); - threadpool.pop_back(); - std::swap(kernel_pool[i], kernel_pool.back()); - K = kernel_pool.back(); - break; - } // if - } // for - } // while - - std::future fu = std::async(std::launch::async, create_element, K, std::ref(settings), &rep); - threadpool.emplace_back(std::move(fu)); - } - - for (std::future &fu : threadpool) { - fu.get(); - - processed += 1; - progress_ = processed * 50 / tasks_.size(); - if (progress_ != old_progress) { - Logger::ProgressBar(progress_); - old_progress = progress_; - } - } - - for (auto& rep : tasks_) { - all_processed_elements_.insert(all_processed_elements_.end(), rep.elements.begin(), rep.elements.end()); - all_processed_native_elements_.insert(all_processed_native_elements_.end(), rep.breps.begin(), rep.breps.end()); - } - - task_result_iterator_ = all_processed_elements_.begin(); - native_task_result_iterator_ = all_processed_native_elements_.begin(); - - Logger::Status("\rDone creating geometry (" + boost::lexical_cast(all_processed_elements_.size()) + - " objects) "); - } - - /// Computes model's bounding box (bounds_min and bounds_max). - /// @note Can take several minutes for large files. - void compute_bounds() - { - for (int i = 1; i < 4; ++i) { - bounds_min_.SetCoord(i, std::numeric_limits::infinity()); - bounds_max_.SetCoord(i, -std::numeric_limits::infinity()); - } - - IfcSchema::IfcProduct::list::ptr products = ifc_file->instances_by_type(); - for (IfcSchema::IfcProduct::list::it iter = products->begin(); iter != products->end(); ++iter) { - IfcSchema::IfcProduct* product = *iter; - if (product->hasObjectPlacement()) { - // Use a fresh trsf every time in order to prevent the result to be concatenated - ConversionResultPlacement* trsf; - bool success = false; - - try { - success = kernel->convert_placement(product->ObjectPlacement(), trsf); - } catch (const std::exception& e) { - Logger::Error(e); - } catch (...) { - Logger::Error("Failed to construct placement"); - } - - if (!success) { - continue; - } - - double X, Y, Z; - trsf->TranslationPart(X, Y, Z); - bounds_min_.SetX(std::min(bounds_min_.X(), X)); - bounds_min_.SetY(std::min(bounds_min_.Y(), Y)); - bounds_min_.SetZ(std::min(bounds_min_.Z(), Z)); - bounds_max_.SetX(std::max(bounds_max_.X(), X)); - bounds_max_.SetY(std::max(bounds_max_.Y(), Y)); - bounds_max_.SetZ(std::max(bounds_max_.Z(), Z)); - } - } - } - - int progress() const { - if (num_threads_ == 1) { - return 100 * done / total; - } else { - return progress_; - } - } - - const std::string& getUnitName() const { return unit_name; } - - /// @note Double always as per IFC specification. - double getUnitMagnitude() const { return unit_magnitude; } - - std::string getLog() const { return Logger::GetLog(); } - - IfcParse::IfcFile* file() const { return ifc_file; } - - const std::vector& filters() const { return filters_; } - std::vector& filters() { return filters_; } - - const gp_XYZ& bounds_min() const { return bounds_min_; } - const gp_XYZ& bounds_max() const { return bounds_max_; } - - private: - // Move to the next IfcRepresentation - void _nextShape() { - ifcproducts.reset(); - ++ representation_iterator; - ++ done; - } - - bool geometry_reuse_ok_for_current_representation_; - - bool reuse_ok_(const IfcSchema::IfcProduct::list::ptr& products) { - // With world coords enabled, object transformations are directly applied to - // the BRep. There is no way to re-use the geometry for multiple products. - if (settings.get(IteratorSettings::USE_WORLD_COORDS)) { - return false; - } - - std::set associated_single_materials; - - for (IfcSchema::IfcProduct::list::it it = products->begin(); it != products->end(); ++it) { - IfcSchema::IfcProduct* product = *it; - - if (!settings.get(IteratorSettings::DISABLE_OPENING_SUBTRACTIONS) && kernel->find_openings(product)->size()) { - return false; - } - - if (settings.get(IteratorSettings::APPLY_LAYERSETS)) { - IfcSchema::IfcRelAssociates::list::ptr associations = product->HasAssociations(); - for (IfcSchema::IfcRelAssociates::list::it jt = associations->begin(); jt != associations->end(); ++jt) { - IfcSchema::IfcRelAssociatesMaterial* assoc = (*jt)->as(); - if (assoc) { - if (assoc->RelatingMaterial()->declaration().is(IfcSchema::IfcMaterialLayerSetUsage::Class())) { - // TODO: Check whether single layer? - return false; - } - } - } - } - - // Note that this can be a nullptr (!), but the fact that set size should be one still holds - associated_single_materials.insert(kernel->get_single_material_association(product)); - if (associated_single_materials.size() > 1) return false; - } - - return associated_single_materials.size() == 1; - } - - boost::optional> get_next_task() { - for (;;) { - IfcSchema::IfcRepresentation* representation; - - if (representation_iterator == representations->end()) { - representations.reset(); - return boost::none; // reached the end of our list of representations - } - representation = *representation_iterator; - - if (!ifcproducts) { - // Init. the list of filtered IfcProducts for this representation - ifcproducts = IfcSchema::IfcProduct::list::ptr(new IfcSchema::IfcProduct::list); - IfcSchema::IfcProduct::list::ptr unfiltered_products = kernel->products_represented_by(representation); - // Include only the desired products for processing. - for (IfcSchema::IfcProduct::list::it jt = unfiltered_products->begin(); jt != unfiltered_products->end(); ++jt) { - IfcSchema::IfcProduct* prod = *jt; - if (boost::all(filters_, filter_match(prod))) { - ifcproducts->push(prod); - } - } - - if (ifcproducts->size() == 0) { - _nextShape(); - continue; - } - - geometry_reuse_ok_for_current_representation_ = reuse_ok_(ifcproducts); - - IfcSchema::IfcRepresentationMap::list::ptr maps = representation->RepresentationMap(); - - if (!geometry_reuse_ok_for_current_representation_ && maps->size() == 1) { - // unfiltered_products contains products represented by this representation by means of mapped items. - // For example because of openings applied to products, reuse might not be acceptable and then the - // products will be processed by means of their immediate representation and not the mapped representation. - - // IfcRepresentationMaps are also used for IfcTypeProducts, so an additional check is performed whether the map - // is indeed used by IfcMappedItems. - IfcSchema::IfcRepresentationMap* map = *maps->begin(); - if (map->MapUsage()->size() > 0) { - _nextShape(); - continue; - } - } - - // Check if this represenation has (or will be) processed as part its mapped representation - bool representation_processed_as_mapped_item = false; - IfcSchema::IfcRepresentation* representation_mapped_to = kernel->representation_mapped_to(representation); - if (representation_mapped_to) { - representation_processed_as_mapped_item = geometry_reuse_ok_for_current_representation_ && ( - ok_mapped_representations->contains(representation_mapped_to) || reuse_ok_(kernel->products_represented_by(representation_mapped_to))); - } - - if (representation_processed_as_mapped_item) { - ok_mapped_representations->push(representation_mapped_to); - _nextShape(); - continue; - } - - ifcproduct_iterator = ifcproducts->begin(); - } - - // Have we reached the end of our list of IfcProducts? - if (ifcproduct_iterator == ifcproducts->end()) { - _nextShape(); - continue; - } - - IfcSchema::IfcProduct* product = *ifcproduct_iterator; - - - return std::make_pair(representation, product); - } - } - - NativeElement* create_shape_model_for_next_entity() { - for (;;) { - auto rp = get_next_task(); - if (!rp) { - return nullptr; - } - auto representation = rp->first; - auto product = rp->second; - - Logger::SetProduct(product); - - NativeElement* element; - if (ifcproduct_iterator == ifcproducts->begin() || !geometry_reuse_ok_for_current_representation_) { - element = kernel->create_brep_for_representation_and_product(settings, representation, product); - } else { - element = kernel->create_brep_for_processed_representation(settings, representation, product, current_shape_model); - } - - Logger::SetProduct(boost::none); - - if (!element) { - _nextShape(); - continue; - } - - return element; - } - } - - void free_shapes() { - // Free all possible representations of the current geometrical entity - delete current_triangulation; - current_triangulation = 0; - delete current_serialization; - current_serialization = 0; - delete current_shape_model; - current_shape_model = 0; - } - - public: - /// Returns what would be the product for the next shape representation - /// @todo Double-check and test the impl. - //IfcSchema::IfcProduct* peek_next() const - //{ - // if (ifcproducts && ifcproduct_iterator + 1 != ifcproducts->end()){ - // return *(ifcproduct_iterator + 1); - // } else { - // return 0; - // } - //} - - /// @todo Would this be as simple as the following code? - //void skip_next() { if (ifcproducts) { ++ifcproduct_iterator; } } - - /// Moves to the next shape representation, create its geometry, and returns the associated product. - /// Use get() to retrieve the created geometry. - IfcUtil::IfcBaseClass* next() { - if (num_threads_ != 1) { - task_result_iterator_++; - native_task_result_iterator_++; - if (task_result_iterator_ == all_processed_elements_.end()) { - return nullptr; - } else { - return (*task_result_iterator_)->product(); - } - } else { - // Increment the iterator over the list of products using the current - // shape representation - if (ifcproducts) { - ++ifcproduct_iterator; - } - - return create(); - } - } - - /// Gets the representation of the current geometrical entity. - Element* get() - { - // TODO: Test settings and throw - Element* ret = 0; - - if (num_threads_ != 1) { - ret = *task_result_iterator_; - } else { - if (current_triangulation) { - ret = current_triangulation; - } else if (current_serialization) { - ret = current_serialization; - } else if (current_shape_model) { - ret = current_shape_model; - } - } - - // If we want to organize the element considering their hierarchy - if (settings.get(IteratorSettings::SEARCH_FLOOR)) - { - // We are going to build a vector with the element parents. - // First, create the parent vector - std::vector*> parents; - - // if the element has a parent - if (ret->parent_id() != -1) - { - const IfcGeom::Element* parent_object = NULL; - bool hasParent = true; - - // get the parent - try { - parent_object = get_object(ret->parent_id()); - } catch (const std::exception& e) { - Logger::Error(e); - hasParent = false; - } - - // Add the previously found parent to the vector - if (hasParent) parents.insert(parents.begin(), parent_object); - - // We need to find all the parents - while (parent_object != NULL && hasParent && parent_object->parent_id() != -1) - { - // Find the next parent - try { - parent_object = get_object(parent_object->parent_id()); - } catch (const std::exception& e) { - Logger::Error(e); - hasParent = false; - } - - // Add the previously found parent to the vector - if (hasParent) parents.insert(parents.begin(), parent_object); - - hasParent = hasParent && parent_object->parent_id() != -1; - } - - // when done push the parent list in the Element object - ret->SetParents(parents); - } - } - - return ret; - } - - /// Gets the native (Open Cascade) representation of the current geometrical entity. - NativeElement* get_native() - { - // TODO: Test settings and throw - if (num_threads_ != 1) { - return *native_task_result_iterator_; - } else { - return current_shape_model; - } - } - - const Element* get_object(int id) { - ConversionResultPlacement* trsf; - int parent_id = -1; - std::string instance_type, product_name, product_guid; - IfcSchema::IfcProduct* ifc_product = 0; - - try { - IfcUtil::IfcBaseClass* ifc_entity = ifc_file->instance_by_id(id); - instance_type = ifc_entity->declaration().name(); - - if (ifc_entity->declaration().is(IfcSchema::IfcRoot::Class())) { - IfcSchema::IfcRoot* ifc_root = ifc_entity->as(); - product_guid = ifc_root->GlobalId(); - product_name = ifc_root->hasName() ? ifc_root->Name() : ""; - } - - if (ifc_entity->declaration().is(IfcSchema::IfcProduct::Class())) { - ifc_product = ifc_entity->as(); - parent_id = -1; - try { - IfcSchema::IfcObjectDefinition* parent_object = kernel->get_decomposing_entity(ifc_product)->template as(); - if (parent_object) { - parent_id = parent_object->data().id(); - } - } catch (const std::exception& e) { - Logger::Error(e); - } catch (...) { - Logger::Error("Failed to find decomposing entity"); - } - - try { - kernel->convert_placement(ifc_product->ObjectPlacement(), trsf); - } catch (const std::exception& e) { - Logger::Error(e); - } catch (...) { - Logger::Error("Failed to construct placement"); - } - } - } catch (const std::exception& e) { - Logger::Error(e); - } catch (const Standard_Failure& e) { - if (e.GetMessageString() && strlen(e.GetMessageString())) { - Logger::Error(e.GetMessageString()); - } else { - Logger::Error("Unknown error returning product"); - } - } catch (...) { - Logger::Error("Unknown error returning product"); - } - - ElementSettings element_settings(settings, unit_magnitude, instance_type); - - Element* ifc_object = new Element(element_settings, id, parent_id, product_name, instance_type, product_guid, "", trsf, ifc_product); - return ifc_object; - } - - IfcUtil::IfcBaseClass* create() { - IfcGeom::NativeElement* next_shape_model = 0; - IfcGeom::SerializedElement* next_serialization = 0; - IfcGeom::TriangulationElement* next_triangulation = 0; - - try { - next_shape_model = create_shape_model_for_next_entity(); - } catch (const std::exception& e) { - Logger::Error(e); - } catch (const Standard_Failure& e) { - if (e.GetMessageString() && strlen(e.GetMessageString())) { - Logger::Error(e.GetMessageString()); - } else { - Logger::Error("Unknown error creating geometry"); - } - } catch (...) { - Logger::Error("Unknown error creating geometry"); - } - - if (next_shape_model) { - if (settings.get(IteratorSettings::USE_BREP_DATA)) { - try { - next_serialization = new SerializedElement(*next_shape_model); - } catch (...) { - Logger::Message(Logger::LOG_ERROR, "Getting a serialized element from model failed."); - } - } else if (!settings.get(IteratorSettings::DISABLE_TRIANGULATION)) { - try { - if (ifcproduct_iterator == ifcproducts->begin() || !geometry_reuse_ok_for_current_representation_) { - next_triangulation = new TriangulationElement(*next_shape_model); - } else { - next_triangulation = new TriangulationElement(*next_shape_model, current_triangulation->geometry_pointer()); - } - } catch (...) { - Logger::Message(Logger::LOG_ERROR, "Getting a triangulation element from model failed."); - } - } - } - - free_shapes(); - - current_shape_model = next_shape_model; - current_serialization = next_serialization; - current_triangulation = next_triangulation; - - return next_shape_model ? next_shape_model->product() : 0; - } - private: - void _initialize() { - current_triangulation = 0; - current_shape_model = 0; - current_serialization = 0; - - unit_name = "METER"; - unit_magnitude = 1.f; - - kernel->setValue(IfcGeom::Kernel::GV_MAX_FACES_TO_ORIENT, settings.get(IteratorSettings::SEW_SHELLS) ? std::numeric_limits::infinity() : -1); - kernel->setValue(IfcGeom::Kernel::GV_DIMENSIONALITY, (settings.get(IteratorSettings::INCLUDE_CURVES) - ? (settings.get(IteratorSettings::EXCLUDE_SOLIDS_AND_SURFACES) ? -1. : 0.) : +1.)); - if (settings.get(IteratorSettings::BUILDING_LOCAL_PLACEMENT)) { - if (settings.get(IteratorSettings::SITE_LOCAL_PLACEMENT)) { - Logger::Message(Logger::LOG_WARNING, "building-local-placement takes precedence over site-local-placement"); - } - kernel->set_conversion_placement_rel_to(&IfcSchema::IfcBuilding::Class()); - } else if (settings.get(IteratorSettings::SITE_LOCAL_PLACEMENT)) { - kernel->set_conversion_placement_rel_to(&IfcSchema::IfcSite::Class()); - } - } - - bool owns_ifc_file; - public: - MAKE_TYPE_NAME(IteratorImplementation_)(const std::string& geometry_library, const IteratorSettings& settings, IfcParse::IfcFile* file, const std::vector& filters, int num_threads) - : settings(settings) - , ifc_file(file) - , filters_(filters) - , owns_ifc_file(false) - , num_threads_(num_threads) - , geometry_library_(geometry_library) - { - kernel = (MAKE_TYPE_NAME(AbstractKernel)*) impl::kernel_implementations().construct(file->schema()->name(), geometry_library, file); - // kernel = new Kernel(geometry_library, file); - _initialize(); - } - - ~MAKE_TYPE_NAME(IteratorImplementation_)() { - if (owns_ifc_file) { - delete ifc_file; - } - - if (settings.get(IfcGeom::IteratorSettings::DISABLE_TRIANGULATION)) { - for (auto& p : all_processed_native_elements_) { - delete p; - } - } - - for (auto& p : all_processed_elements_) { - delete p; - } - - free_shapes(); - } - }; -} - -#endif diff --git a/src/ifcgeom/kernel_agnostic/blog.txt b/src/ifcgeom/kernel_agnostic/blog.txt new file mode 100644 index 0000000000..e188c8d407 --- /dev/null +++ b/src/ifcgeom/kernel_agnostic/blog.txt @@ -0,0 +1,71 @@ +v0.6.0 + +People not following the development of IfcOpenShell actively and happily using the master branch of the github repository might be surprised to know there is a lot of activity happening in the v0.6.0 and v0.7.0 branches. This post discusses the changes in the v0.6.0 branch. The following post will elaborate on some of the design decisions we are making in the v0.7.0 branch. + +Schemas + +The most significant improvement in the v0.6.0 branch is that multiple schemas (IFC2X3, IFC4, IFC4X1 and IFC4X2) are supported from within the same executable, module or plug-in. Previously, selecting the schema had been a compile-time option. + +In IfcOpenShell and most other EXPRESS-based toolkits, the IFC schema is compiled into (a) the early-bound definitions: a class hierarchy with member functions and (b) a set of methods to operate on the schema definitions at runtime (late-bound access). C++ only allows very limited introspection (but the development of C++ is very active, see for example P1240 https://github.com/cplusplus/papers/issues/545) so to complement the lack of introspection a set of methods exists to query for example all attribute names or the sub- and supertypes of an entity. In the master branch these methods are static, in the v0.6.0 branch these are the member functions of a schema class, that is a more complete reference mirrorring the EXPRESS schema definition at runtime. See IfcBaseEntity::declararation() or IfcParse::schema::declaration_by_name("IfcWall")->as_entity()->all_attribute_names(). + +Writing schema agnostic code + +The code generated from the four schemas are completely orthogonal class hiercharies. For the C++ compiler there is no relationship between a Ifc2x3::IfcWall and a Ifc4::IfcWall. But IfcOpenShell offers three ways to write code that adapts to the schema of the file known at runtime. + +(a) preprocessor + +This is the approach taken in the IfcGeom modules in v0.6.0. Essentially the same code base is compiled multiple times where the schema is available as a preprocessor constant. This means you can enable specific code paths with for example #ifdef directives. In this way the added entities in Ifc4 (IfcBSplineSurface, yay!) can be selectively compiled for example. + +https://github.com/IfcOpenShell/IfcOpenShell/blob/v0.6.0/src/ifcgeom/IfcGeomFaces.cpp#L1127 + +Smaller code blocks can be written as macros as well. + +https://github.com/IfcOpenShell/IfcOpenShell/blob/v0.6.0/src/ifcgeom_schema_agnostic/Kernel.cpp#L74 + +Benefits: fairly readible code, full autocompletion typically in an IDE when using the static library approach +Downsides: Some infrastructure required to compile the different libraries and select the correct implementation at runtime + +(b) late-bound access + +There are two modes of accessing schemas. In the early-bound approach function signatures and return types are known at compilation time. In the late-bound approach attribute names are referenced by strings and types are + +Ifc2x3::IfcWall* wall; +// Early-bound access; +std::string global_id = wall->GlobalId(); +// Late-bound access. +std::string global_id = *wall->get("GlobalId"); +// ERROR: By dereferencing the return type, it is casted into a string, which will cause an exception *at runtime* when the types do not match. +int global_id = *wall->get("GlobalId"); + +Benefits: +fairly readible code +no complicated setup of different libraries +Downsides: +no code completion +errors are only spotted at runtime, not compile-time +late-bound manipulation of inverse attributes is not well supported currently in IfcOpenShell +less means for the compiler to create highly optimized code + +(c) templates + +C++ has very extensive support for compile time generic arguments: templates. + +template +void print_globalid(Schema::IfcWall* wall) { + std::cout << wall->GlobalId(); +} + +Benefits: +no complicated setup of different libraries +no autocompletion typically, but errors caught at compile-time +Downsides: +fairly unreadible code due to additional template and typename keywords. +error messages are harder to make sense up (due to two phase lookup rules for example) + +All three approaches are used in the IfcOpenShell code-base. + +Other improvements: + +Multi-threading in collaboration with TNO, MAUC and Airsquire + +Direct binary glTF output (previously supported through Collada and Collada2Gltf) in collaboration with Schuco US. diff --git a/src/ifcgeom/kernels/opencascade/IfcGeom.h b/src/ifcgeom/kernels/opencascade/IfcGeom.h deleted file mode 100644 index d0575a5de2..0000000000 --- a/src/ifcgeom/kernels/opencascade/IfcGeom.h +++ /dev/null @@ -1,375 +0,0 @@ -/******************************************************************************** - * * - * This file is part of IfcOpenShell. * - * * - * IfcOpenShell is free software: you can redistribute it and/or modify * - * it under the terms of the Lesser GNU General Public License as published by * - * the Free Software Foundation, either version 3.0 of the License, or * - * (at your option) any later version. * - * * - * IfcOpenShell is distributed in the hope that it will be useful, * - * but WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * - * Lesser GNU General Public License for more details. * - * * - * You should have received a copy of the Lesser GNU General Public License * - * along with this program. If not, see . * - * * - ********************************************************************************/ - -#ifndef IFCGEOM_H -#define IFCGEOM_H - -#include - -static const double ALMOST_ZERO = 1.e-9; - -template -inline static bool ALMOST_THE_SAME(const T& a, const T& b, double tolerance=ALMOST_ZERO) { - return fabs(a-b) < tolerance; -} - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "../../../ifcparse/macros.h" -#include "../../../ifcparse/IfcParse.h" -#include "../../../ifcparse/IfcBaseClass.h" - -#include "../../../ifcgeom/kernel_agnostic/AbstractKernel.h" - -#include "../../../ifcgeom/schema_agnostic/IfcGeomElement.h" -#include "../../../ifcgeom/schema_agnostic/IfcGeomRepresentation.h" -#include "../../../ifcgeom/schema_agnostic/ConversionResult.h" -#include "../../../ifcgeom/kernels/opencascade/IfcGeomShapeType.h" - -#include "../../../ifcgeom/schema_agnostic/Kernel.h" -#include "../../../ifcgeom/schema_agnostic/opencascade/OpenCascadeConversionResult.h" - -#include "../../../ifcgeom/schema_agnostic/ifc_geom_api.h" - -// Define this in case you want to conserve memory usage at all cost. This has been -// benchmarked extensively: https://github.com/IfcOpenShell/IfcOpenShell/pull/47 -// #define NO_CACHE - -#ifdef NO_CACHE - -#define IN_CACHE(T,E,t,e) -#define CACHE(T,E,e) - -#else - -#define IN_CACHE(T,E,t,e) std::map::const_iterator it = cache.T.find(E->data().id());\ -if ( it != cache.T.end() ) { e = it->second; return true; } -#define CACHE(T,E,e) cache.T[E->data().id()] = e; - -#endif - -#define INCLUDE_SCHEMA(x) STRINGIFY(../../../ifcparse/x.h) -#include INCLUDE_SCHEMA(IfcSchema) -#undef INCLUDE_SCHEMA -#define INCLUDE_SCHEMA(x) STRINGIFY(../../../ifcparse/x-definitions.h) -#include INCLUDE_SCHEMA(IfcSchema) -#undef INCLUDE_SCHEMA - -namespace IfcGeom { - class IFC_GEOM_API geometry_exception : public std::exception { - protected: - std::string message; - public: - geometry_exception(const std::string& m) - : message(m) {} - virtual ~geometry_exception() throw () {} - virtual const char* what() const throw() { - return message.c_str(); - } - }; - - class IFC_GEOM_API too_many_faces_exception : public geometry_exception { - public: - too_many_faces_exception() - : geometry_exception("Too many faces for operation") {} - }; - -class IFC_GEOM_API POSTFIX_SCHEMA(Cache) { -public: -#include "IfcRegisterCreateCache.h" - std::map Shape; -}; - -class IFC_GEOM_API POSTFIX_SCHEMA(Kernel) : public IfcGeom::POSTFIX_SCHEMA(AbstractKernel) { -private: - - /* - faceset_helper traverses the forward instance references of IfcConnectedFaceSet and then provides a mapping - M of (IfcCartesianPoint, IfcCartesianPoint) -> TopoDS_Edge, where M(a, b) is a partner of M(b, a), ie share - the same underlying edge but with orientation reversed. This then later speeds op the process of creating a - manifold Shell / Solid from this set of faces. Only IfcPolyLoop instances are used. Points within the tolerance - threshiold are merged, so consider points a, b, c, distance(a, b) < eps then M(a, b) = Null, M(a, b) = M(a, c). - */ - class faceset_helper { - private: - POSTFIX_SCHEMA(Kernel)* kernel_; - std::set duplicates_; - std::map vertex_mapping_; - std::map, TopoDS_Edge> edges_; - double eps_; - bool non_manifold_; - - template - void loop_(IfcSchema::IfcCartesianPoint::list::ptr& ps, const Fn& callback) { - if (ps->size() < 3) { - return; - } - - auto a = *(ps->end() - 1); - auto A = a->data().id(); - for (auto& b : *ps) { - auto B = b->data().id(); - auto C = vertex_mapping_[A], D = vertex_mapping_[B]; - bool fwd = C < D; - if (!fwd) { - std::swap(C, D); - } - if (C != D) { - callback(C, D, fwd); - A = B; - } - } - } - public: - faceset_helper(POSTFIX_SCHEMA(Kernel)* kernel, const IfcSchema::IfcConnectedFaceSet* l); - - ~faceset_helper(); - - bool non_manifold() const { return non_manifold_; } - bool& non_manifold() { return non_manifold_; } - - bool edge(const IfcSchema::IfcCartesianPoint* a, const IfcSchema::IfcCartesianPoint* b, TopoDS_Edge& e) { - int A = vertex_mapping_[a->data().id()]; - int B = vertex_mapping_[b->data().id()]; - if (A == B) { - return false; - } - - return edge(A, B, e); - } - - bool edge(int A, int B, TopoDS_Edge& e) { - auto it = edges_.find({A, B}); - if (it == edges_.end()) { - return false; - } - e = it->second; - return true; - } - - bool wire(const IfcSchema::IfcPolyLoop* loop, TopoDS_Wire& wire) { - if (duplicates_.find(loop) != duplicates_.end()) { - return false; - } - BRep_Builder builder; - builder.MakeWire(wire); - int count = 0; - auto ps = loop->Polygon(); - loop_(ps, [this, &builder, &wire, &count](int A, int B, bool fwd) { - TopoDS_Edge e; - if (edge(A, B, e)) { - if (!fwd) { - e.Reverse(); - } - builder.Add(wire, e); - count += 1; - } - }); - if (count >= 3) { - wire.Closed(true); - - TopTools_ListOfShape results; - if (kernel_->wire_intersections(wire, results)) { - Logger::Warning("Self-intersections with " + boost::lexical_cast(results.Extent()) + " cycles detected", loop); - kernel_->select_largest(results, wire); - non_manifold_ = true; - } - - return true; - } else { - return false; - } - } - - double epsilon() const { - return eps_; - } - }; - -#ifndef NO_CACHE - POSTFIX_SCHEMA(Cache) cache; -#endif - - faceset_helper* faceset_helper_; - -public: - POSTFIX_SCHEMA(Kernel)() - : IfcGeom::POSTFIX_SCHEMA(AbstractKernel)("opencascade") - , faceset_helper_(nullptr) - {} - - POSTFIX_SCHEMA(Kernel)(const POSTFIX_SCHEMA(Kernel)& other) - : IfcGeom::POSTFIX_SCHEMA(AbstractKernel)("opencascade") - { - *this = other; - } - - POSTFIX_SCHEMA(Kernel)& operator=(const POSTFIX_SCHEMA(Kernel)& other) { - setValue(GV_DEFLECTION_TOLERANCE, other.getValue(GV_DEFLECTION_TOLERANCE)); - setValue(GV_MAX_FACES_TO_ORIENT, other.getValue(GV_MAX_FACES_TO_ORIENT)); - setValue(GV_LENGTH_UNIT, other.getValue(GV_LENGTH_UNIT)); - setValue(GV_PLANEANGLE_UNIT, other.getValue(GV_PLANEANGLE_UNIT)); - setValue(GV_PRECISION, other.getValue(GV_PRECISION)); - setValue(GV_DIMENSIONALITY, other.getValue(GV_DIMENSIONALITY)); - setValue(GV_DEFLECTION_TOLERANCE, other.getValue(GV_DEFLECTION_TOLERANCE)); - return *this; - } - - bool convert_wire_to_face(const TopoDS_Wire& wire, TopoDS_Face& face); - bool convert_curve_to_wire(const Handle(Geom_Curve)& curve, TopoDS_Wire& wire); - bool convert_shapes(const IfcUtil::IfcBaseClass* L, ConversionResults& result); - IfcGeom::ShapeType shape_type(const IfcUtil::IfcBaseClass* L); - bool convert_shape(const IfcUtil::IfcBaseClass* L, TopoDS_Shape& result); - bool flatten_shape_list(const IfcGeom::ConversionResults& shapes, TopoDS_Shape& result, bool fuse); - bool convert_wire(const IfcUtil::IfcBaseClass* L, TopoDS_Wire& result); - bool convert_curve(const IfcUtil::IfcBaseClass* L, Handle(Geom_Curve)& result); - bool convert_face(const IfcUtil::IfcBaseClass* L, TopoDS_Shape& result); - bool convert_openings(const IfcSchema::IfcProduct* entity, const IfcSchema::IfcRelVoidsElement::list::ptr& openings, const ConversionResults& entity_shapes, const ConversionResultPlacement* entity_trsf, ConversionResults& cut_shapes); - void assert_closed_wire(TopoDS_Wire& wire); - - bool convert_layerset(const IfcSchema::IfcProduct*, std::vector&, std::vector&, std::vector&); - bool apply_layerset(const ConversionResults&, const std::vector&, const std::vector&, ConversionResults&); - bool apply_folded_layerset(const ConversionResults&, const std::vector< std::vector >&, const std::vector&, ConversionResults&); - bool fold_layers(const IfcSchema::IfcWall*, const ConversionResults&, const std::vector&, const std::vector&, std::vector< std::vector >&); - - bool split_solid_by_surface(const TopoDS_Shape&, const Handle_Geom_Surface&, TopoDS_Shape&, TopoDS_Shape&); - bool split_solid_by_shell(const TopoDS_Shape&, const TopoDS_Shape& s, TopoDS_Shape&, TopoDS_Shape&); - -#if OCC_VERSION_HEX < 0x60900 - bool boolean_operation(const TopoDS_Shape&, const TopTools_ListOfShape&, BOPAlgo_Operation, TopoDS_Shape&); - bool boolean_operation(const TopoDS_Shape&, const TopoDS_Shape&, BOPAlgo_Operation, TopoDS_Shape&); -#else - bool boolean_operation(const TopoDS_Shape&, const TopTools_ListOfShape&, BOPAlgo_Operation, TopoDS_Shape&, double fuzziness = -1.); - bool boolean_operation(const TopoDS_Shape&, const TopoDS_Shape&, BOPAlgo_Operation, TopoDS_Shape&, double fuzziness = -1.); -#endif - - bool fit_halfspace(const TopoDS_Shape& a, const TopoDS_Shape& b, TopoDS_Shape& box, double& height); - - const Handle_Geom_Curve intersect(const Handle_Geom_Surface&, const Handle_Geom_Surface&); - const Handle_Geom_Curve intersect(const Handle_Geom_Surface&, const TopoDS_Face&); - const Handle_Geom_Curve intersect(const TopoDS_Face&, const Handle_Geom_Surface&); - bool intersect(const Handle_Geom_Curve&, const Handle_Geom_Surface&, gp_Pnt&); - bool intersect(const Handle_Geom_Curve&, const TopoDS_Face&, gp_Pnt&); - bool intersect(const Handle_Geom_Curve&, const TopoDS_Shape&, std::vector&); - bool intersect(const Handle_Geom_Surface&, const TopoDS_Shape&, std::vector< std::pair >&); - bool closest(const gp_Pnt&, const std::vector&, gp_Pnt&); - bool project(const Handle_Geom_Curve&, const gp_Pnt&, gp_Pnt& p, double& u, double& d); - bool project(const Handle_Geom_Surface&, const TopoDS_Shape&, double& u1, double& v1, double& u2, double& v2, double widen=0.1); - - bool find_wall_end_points(const IfcSchema::IfcWall*, gp_Pnt& start, gp_Pnt& end); - - bool create_solid_from_compound(const TopoDS_Shape& compound, TopoDS_Shape& solid); - bool create_solid_from_faces(const TopTools_ListOfShape& face_list, TopoDS_Shape& solid); - bool is_compound(const TopoDS_Shape& shape); - bool is_convex(const TopoDS_Wire& wire); - TopoDS_Shape halfspace_from_plane(const gp_Pln& pln,const gp_Pnt& cent); - gp_Pln plane_from_face(const TopoDS_Face& face); - gp_Pnt point_above_plane(const gp_Pln& pln, bool agree=true); - const TopoDS_Shape& ensure_fit_for_subtraction(const TopoDS_Shape& shape, TopoDS_Shape& solid); - bool profile_helper(int numVerts, double* verts, int numFillets, int* filletIndices, double* filletRadii, gp_Trsf2d trsf, TopoDS_Shape& face); - void apply_tolerance(TopoDS_Shape& s, double t); - bool fill_nonmanifold_wires_with_planar_faces(TopoDS_Shape& shape); - void remove_duplicate_points_from_loop(TColgp_SequenceOfPnt& polygon, bool closed, double tol=-1.); - void remove_collinear_points_from_loop(TColgp_SequenceOfPnt& polygon, bool closed, double tol=-1.); - bool wire_to_sequence_of_point(const TopoDS_Wire&, TColgp_SequenceOfPnt&); - void sequence_of_point_to_wire(const TColgp_SequenceOfPnt&, TopoDS_Wire&, bool closed); - bool approximate_plane_through_wire(const TopoDS_Wire&, gp_Pln&, double eps=-1.); - bool flatten_wire(TopoDS_Wire&); - /// Triangulate the set of wires. The firstmost wire is assumed to be the outer wire. - bool triangulate_wire(const std::vector&, TopTools_ListOfShape&); - bool wire_intersections(const TopoDS_Wire & wire, TopTools_ListOfShape & wires); - void select_largest(const TopTools_ListOfShape& shapes, TopoDS_Shape& largest); - - static double shape_volume(const TopoDS_Shape& s); - static double face_area(const TopoDS_Face& f); - - static TopoDS_Shape apply_transformation(const TopoDS_Shape&, const OpenCascadePlacement*); - static TopoDS_Shape apply_transformation(const TopoDS_Shape&, const gp_Trsf&); - static TopoDS_Shape apply_transformation(const TopoDS_Shape&, const gp_GTrsf&); - - virtual bool is_identity_transform(const IfcUtil::IfcBaseClass*); - virtual bool apply_layerset(const IfcSchema::IfcProduct* product, IfcGeom::ConversionResults& shapes); - virtual bool validate_quantities(const IfcSchema::IfcProduct* product, const IfcGeom::Representation::BRep& brep); - - IfcSchema::IfcRepresentation* find_representation(const IfcSchema::IfcProduct*, const std::string&); - - std::pair initializeUnits(IfcSchema::IfcUnitAssignment*); - - void purge_cache() { - // Rather hack-ish, but a stopgap solution to keep memory under control - // for large files. SurfaceStyles need to be kept at all costs, as they - // are read later on when serializing Collada files. -#ifndef NO_CACHE - cache = POSTFIX_SCHEMA(Cache)(); -#endif - } - -#include "IfcRegisterGeomHeader.h" - - virtual IfcGeom::NativeElement* convert( - const IteratorSettings& settings, IfcUtil::IfcBaseClass* representation, - IfcUtil::IfcBaseClass* product) - { - return create_brep_for_representation_and_product(settings, (IfcSchema::IfcRepresentation*) representation, (IfcSchema::IfcProduct*) product); - } - - virtual ConversionResults convert(IfcUtil::IfcBaseClass* item) { - ConversionResults items; - bool success = convert_shapes(item, items); - if (!success) { - throw IfcParse::IfcException("Failed to process representation item"); - } - return items; - } - - virtual bool convert_placement(IfcUtil::IfcBaseClass* item, ConversionResultPlacement*& trsf) { - if (item->as()) { - gp_Trsf occt_trsf; - if (convert(item->as(), occt_trsf)) { - trsf = new OpenCascadePlacement(occt_trsf); - return true; - } - } - return false; - } - -}; - -IfcUtil::IfcBaseClass* POSTFIX_SCHEMA(tesselate_)(const TopoDS_Shape& shape, double deflection); -IfcUtil::IfcBaseClass* POSTFIX_SCHEMA(serialise_)(const TopoDS_Shape& shape, bool advanced); - -} -#endif diff --git a/src/ifcgeom/kernels/opencascade/IfcGeomCurves.cpp b/src/ifcgeom/kernels/opencascade/IfcGeomCurves.cpp_ similarity index 100% rename from src/ifcgeom/kernels/opencascade/IfcGeomCurves.cpp rename to src/ifcgeom/kernels/opencascade/IfcGeomCurves.cpp_ diff --git a/src/ifcgeom/kernels/opencascade/IfcGeomFaces.cpp b/src/ifcgeom/kernels/opencascade/IfcGeomFaces.cpp_ similarity index 100% rename from src/ifcgeom/kernels/opencascade/IfcGeomFaces.cpp rename to src/ifcgeom/kernels/opencascade/IfcGeomFaces.cpp_ diff --git a/src/ifcgeom/kernels/opencascade/IfcGeomFunctions.cpp b/src/ifcgeom/kernels/opencascade/IfcGeomFunctions.cpp_ similarity index 100% rename from src/ifcgeom/kernels/opencascade/IfcGeomFunctions.cpp rename to src/ifcgeom/kernels/opencascade/IfcGeomFunctions.cpp_ diff --git a/src/ifcgeom/kernels/opencascade/IfcGeomHelpers.cpp b/src/ifcgeom/kernels/opencascade/IfcGeomHelpers.cpp_ similarity index 100% rename from src/ifcgeom/kernels/opencascade/IfcGeomHelpers.cpp rename to src/ifcgeom/kernels/opencascade/IfcGeomHelpers.cpp_ diff --git a/src/ifcgeom/kernels/opencascade/IfcGeomSerialisation.cpp b/src/ifcgeom/kernels/opencascade/IfcGeomSerialisation.cpp_ similarity index 100% rename from src/ifcgeom/kernels/opencascade/IfcGeomSerialisation.cpp rename to src/ifcgeom/kernels/opencascade/IfcGeomSerialisation.cpp_ diff --git a/src/ifcgeom/kernels/opencascade/IfcGeomShapes.cpp b/src/ifcgeom/kernels/opencascade/IfcGeomShapes.cpp index 5001639a6d..df2abbefe9 100644 --- a/src/ifcgeom/kernels/opencascade/IfcGeomShapes.cpp +++ b/src/ifcgeom/kernels/opencascade/IfcGeomShapes.cpp @@ -101,33 +101,37 @@ #include -#include "../../../ifcgeom/kernels/opencascade/IfcGeom.h" +#include "OpenCascadeKernel.h" #include -#define Kernel POSTFIX_SCHEMA(Kernel) +#include "../../../ifcparse/IfcLogger.h" -bool IfcGeom::Kernel::convert(const IfcSchema::IfcExtrudedAreaSolid* l, TopoDS_Shape& shape) { - const double height = l->Depth() * getValue(GV_LENGTH_UNIT); - if (height < getValue(GV_PRECISION)) { - Logger::Message(Logger::LOG_ERROR, "Non-positive extrusion height encountered for:", l); +using namespace ifcopenshell::geometry; +using namespace ifcopenshell::geometry::kernels; + +bool OpenCascadeKernel::convert(const taxonomy::extrusion& extrusion, TopoDS_Shape& shape) { + const double& height = extrusion.depth; + + if (height < precision_) { + Logger::Error("Non-positive extrusion height encountered for:", extrusion.instance); return false; } TopoDS_Shape face; - if ( !convert_face(l->SweptArea(),face) ) return false; - - gp_Trsf trsf; - bool has_position = true; -#ifdef SCHEMA_IfcSweptAreaSolid_Position_IS_OPTIONAL - has_position = l->hasPosition(); -#endif - if (has_position) { - IfcGeom::Kernel::convert(l->Position(), trsf); + if (!convert(extrusion.basis, face)) { + return false; } + gp_Trsf trsf; + if (!convert(extrusion.matrix, trsf)) { + Logger::Error("Unable to move extrusion"); + } + gp_Dir dir; - convert(l->ExtrudedDirection(),dir); + if (!convert(extrusion.direction, dir)) { + return false; + } shape.Nullify(); @@ -157,7 +161,7 @@ bool IfcGeom::Kernel::convert(const IfcSchema::IfcExtrudedAreaSolid* l, TopoDS_S shape = BRepPrimAPI_MakePrism(face, height*dir); } - if (has_position && !shape.IsNull()) { + if (!shape.IsNull()) { // IfcSweptAreaSolid.Position (trsf) is an IfcAxis2Placement3D // and therefore has a unit scale factor shape.Move(trsf); @@ -165,1125 +169,3 @@ bool IfcGeom::Kernel::convert(const IfcSchema::IfcExtrudedAreaSolid* l, TopoDS_S return !shape.IsNull(); } - -#ifdef SCHEMA_HAS_IfcExtrudedAreaSolidTapered -bool IfcGeom::Kernel::convert(const IfcSchema::IfcExtrudedAreaSolidTapered* l, TopoDS_Shape& shape) { - const double height = l->Depth() * getValue(GV_LENGTH_UNIT); - if (height < getValue(GV_PRECISION)) { - Logger::Message(Logger::LOG_ERROR, "Non-positive extrusion height encountered for:", l); - return false; - } - - TopoDS_Shape face1, face2; - if (!convert_face(l->SweptArea(), face1)) return false; - if (!convert_face(l->EndSweptArea(), face2)) return false; - - gp_Trsf trsf; - bool has_position = true; -#ifdef SCHEMA_IfcSweptAreaSolid_Position_IS_OPTIONAL - has_position = l->hasPosition(); -#endif - if (has_position) { - IfcGeom::Kernel::convert(l->Position(), trsf); - } - - gp_Dir dir; - convert(l->ExtrudedDirection(), dir); - - gp_Trsf end_profile; - end_profile.SetTranslation(height * dir); - - TopoDS_Edge spine_edge = BRepBuilderAPI_MakeEdge(gp_Pnt(), gp_Pnt((height * dir).XYZ())).Edge(); - TopoDS_Wire wire = BRepBuilderAPI_MakeWire(spine_edge).Wire(); - - shape.Nullify(); - - TopExp_Explorer exp1(face1, TopAbs_WIRE); - TopExp_Explorer exp2(face2, TopAbs_WIRE); - - TopoDS_Vertex v1, v2; - TopExp::Vertices(wire, v1, v2); - - TopoDS_Shape shell; - TopoDS_Compound compound; - BRep_Builder compound_builder; - - for (; exp1.More() && exp2.More(); exp1.Next(), exp2.Next()) { - const TopoDS_Wire& w1 = TopoDS::Wire(exp1.Current()); - const TopoDS_Wire& w2 = TopoDS::Wire(exp2.Current()); - - BRepOffsetAPI_MakePipeShell builder(wire); - builder.Add(w1, v1); - builder.Add(w2.Moved(end_profile), v2); - - TopoDS_Shape result = builder.Shape(); - - BRepOffsetAPI_Sewing sewer; - sewer.SetTolerance(getValue(GV_PRECISION)); - sewer.SetMaxTolerance(getValue(GV_PRECISION)); - sewer.SetMinTolerance(getValue(GV_PRECISION)); - - sewer.Add(result); - sewer.Add(BRepBuilderAPI_MakeFace(w1).Face()); - sewer.Add(BRepBuilderAPI_MakeFace(w2).Face().Moved(end_profile)); - - sewer.Perform(); - - result = sewer.SewedShape(); - - if (shell.IsNull()) { - shell = result; - } else if (l->SweptArea()->declaration().is(IfcSchema::IfcCircleHollowProfileDef::Class()) || - l->SweptArea()->declaration().is(IfcSchema::IfcRectangleHollowProfileDef::Class())) - { - /// @todo a bit of of a hack, should be sufficient - shell = BRepAlgoAPI_Cut(shell, result).Shape(); - break; - } else { - if (compound.IsNull()) { - compound_builder.MakeCompound(compound); - compound_builder.Add(compound, shell); - } - compound_builder.Add(compound, result); - } - } - - if (!compound.IsNull()) { - shell = compound; - } - - shape = shell; - - if (exp1.More() != exp2.More()) { - Logger::Message(Logger::LOG_ERROR, "Inconsistent profiles encountered for:", l); - } - - if (has_position && !shape.IsNull()) { - // IfcSweptAreaSolid.Position (trsf) is an IfcAxis2Placement3D - // and therefore has a unit scale factor - shape.Move(trsf); - } - - return !shape.IsNull(); -} -#endif - -bool IfcGeom::Kernel::convert(const IfcSchema::IfcSurfaceOfLinearExtrusion* l, TopoDS_Shape& shape) { - TopoDS_Wire wire; - if ( !convert_wire(l->SweptCurve(), wire) ) { - TopoDS_Face face; - if ( !convert_face(l->SweptCurve(),face) ) return false; - TopExp_Explorer exp(face, TopAbs_WIRE); - wire = TopoDS::Wire(exp.Current()); - } - const double height = l->Depth() * getValue(GV_LENGTH_UNIT); - - gp_Trsf trsf; - bool has_position = true; -#ifdef SCHEMA_IfcSweptSurface_Position_IS_OPTIONAL - has_position = l->hasPosition(); -#endif - if (has_position) { - IfcGeom::Kernel::convert(l->Position(), trsf); - } - - gp_Dir dir; - convert(l->ExtrudedDirection(),dir); - - shape = BRepPrimAPI_MakePrism(wire, height*dir); - - if (has_position) { - // IfcSweptSurface.Position (trsf) is an IfcAxis2Placement3D - // and therefore has a unit scale factor - shape.Move(trsf); - } - - return !shape.IsNull(); -} - -bool IfcGeom::Kernel::convert(const IfcSchema::IfcSurfaceOfRevolution* l, TopoDS_Shape& shape) { - TopoDS_Wire wire; - if ( !convert_wire(l->SweptCurve(), wire) ) { - TopoDS_Face face; - if ( !convert_face(l->SweptCurve(),face) ) return false; - TopExp_Explorer exp(face, TopAbs_WIRE); - wire = TopoDS::Wire(exp.Current()); - } - - gp_Ax1 ax1; - IfcGeom::Kernel::convert(l->AxisPosition(), ax1); - - gp_Trsf trsf; - bool has_position = true; -#ifdef SCHEMA_IfcSweptSurface_Position_IS_OPTIONAL - has_position = l->hasPosition(); -#endif - if (has_position) { - IfcGeom::Kernel::convert(l->Position(), trsf); - } - - shape = BRepPrimAPI_MakeRevol(wire, ax1); - - if (has_position) { - // IfcSweptSurface.Position (trsf) is an IfcAxis2Placement3D - // and therefore has a unit scale factor - shape.Move(trsf); - } - - return !shape.IsNull(); -} - -bool IfcGeom::Kernel::convert(const IfcSchema::IfcRevolvedAreaSolid* l, TopoDS_Shape& shape) { - const double ang = l->Angle() * getValue(GV_PLANEANGLE_UNIT); - - TopoDS_Face face; - if ( ! convert_face(l->SweptArea(),face) ) return false; - - gp_Ax1 ax1; - IfcGeom::Kernel::convert(l->Axis(), ax1); - - gp_Trsf trsf; - bool has_position = true; -#ifdef SCHEMA_IfcSweptAreaSolid_Position_IS_OPTIONAL - has_position = l->hasPosition(); -#endif - if (has_position) { - IfcGeom::Kernel::convert(l->Position(), trsf); - } - - if (ang >= M_PI * 2. - ALMOST_ZERO) { - shape = BRepPrimAPI_MakeRevol(face, ax1); - } else { - shape = BRepPrimAPI_MakeRevol(face, ax1, ang); - } - - if (has_position) { - // IfcSweptAreaSolid.Position (trsf) is an IfcAxis2Placement3D - // and therefore has a unit scale factor - shape.Move(trsf); - } - - return !shape.IsNull(); -} - -bool IfcGeom::Kernel::convert(const IfcSchema::IfcManifoldSolidBrep* l, ConversionResults& shape) { - TopoDS_Shape s; - const SurfaceStyle* collective_style = get_style(l); - if (convert_shape(l->Outer(),s) ) { - const SurfaceStyle* indiv_style = get_style(l->Outer()); - - IfcSchema::IfcClosedShell::list::ptr voids(new IfcSchema::IfcClosedShell::list); - if (l->declaration().is(IfcSchema::IfcFacetedBrepWithVoids::Class())) { - voids = l->as()->Voids(); - } -#ifdef SCHEMA_HAS_IfcAdvancedBrepWithVoids - if (l->declaration().is(IfcSchema::IfcAdvancedBrepWithVoids::Class())) { - voids = l->as()->Voids(); - } -#endif - - for (IfcSchema::IfcClosedShell::list::it it = voids->begin(); it != voids->end(); ++it) { - TopoDS_Shape s2; - /// @todo No extensive shapefixing since shells should be disjoint. - /// @todo Awaiting generalized boolean ops module with appropriate checking - if (convert_shape(l->Outer(), s2)) { - s = BRepAlgoAPI_Cut(s, s2).Shape(); - } - } - - shape.push_back(ConversionResult(l->data().id(), new OpenCascadeShape(s), indiv_style ? indiv_style : collective_style)); - return true; - } - return false; -} - -bool IfcGeom::Kernel::convert(const IfcSchema::IfcFaceBasedSurfaceModel* l, ConversionResults& shapes) { - bool part_success = false; - IfcSchema::IfcConnectedFaceSet::list::ptr facesets = l->FbsmFaces(); - const SurfaceStyle* collective_style = get_style(l); - for( IfcSchema::IfcConnectedFaceSet::list::it it = facesets->begin(); it != facesets->end(); ++ it ) { - TopoDS_Shape s; - const SurfaceStyle* shell_style = get_style(*it); - if (convert_shape(*it,s)) { - shapes.push_back(ConversionResult(l->data().id(), new OpenCascadeShape(s), shell_style ? shell_style : collective_style)); - part_success |= true; - } - } - return part_success; -} - -bool IfcGeom::Kernel::convert(const IfcSchema::IfcHalfSpaceSolid* l, TopoDS_Shape& shape) { - IfcSchema::IfcSurface* surface = l->BaseSurface(); - if ( ! surface->declaration().is(IfcSchema::IfcPlane::Class()) ) { - Logger::Message(Logger::LOG_ERROR, "Unsupported BaseSurface:", surface); - return false; - } - gp_Pln pln; - IfcGeom::Kernel::convert((IfcSchema::IfcPlane*)surface,pln); - const gp_Pnt pnt = pln.Location().Translated( l->AgreementFlag() ? -pln.Axis().Direction() : pln.Axis().Direction()); - shape = BRepPrimAPI_MakeHalfSpace(BRepBuilderAPI_MakeFace(pln),pnt).Solid(); - return true; -} - -bool IfcGeom::Kernel::convert(const IfcSchema::IfcPolygonalBoundedHalfSpace* l, TopoDS_Shape& shape) { - TopoDS_Shape halfspace; - if ( ! IfcGeom::Kernel::convert((IfcSchema::IfcHalfSpaceSolid*)l,halfspace) ) return false; - - TopoDS_Wire wire; - if ( ! convert_wire(l->PolygonalBoundary(),wire) || ! wire.Closed() ) return false; - - gp_Trsf trsf; - if ( ! convert(l->Position(),trsf) ) return false; - - TColgp_SequenceOfPnt points; - if (wire_to_sequence_of_point(wire, points)) { - // Boolean subtractions not very robust for narrow operands, - // increase minimal point spacing to eliminate such shapes. - const double t = getValue(GV_PRECISION) * 10.; - remove_duplicate_points_from_loop(points, wire.Closed() != 0, t); // Note: wire always closed, as per if statement above - remove_collinear_points_from_loop(points, wire.Closed() != 0, t); - if (points.Length() < 3) { - Logger::Message(Logger::LOG_ERROR, "Not enough points retained from:", l->PolygonalBoundary()); - return false; - } - sequence_of_point_to_wire(points, wire, wire.Closed() != 0); - } - - TopoDS_Shape prism = BRepPrimAPI_MakePrism(BRepBuilderAPI_MakeFace(wire),gp_Vec(0,0,200)); - gp_Trsf down; down.SetTranslation(gp_Vec(0,0,-100.0)); - - // `trsf` and `down` both have a unit scale factor - prism.Move(trsf*down); - - shape = BRepAlgoAPI_Common(halfspace,prism); - return true; -} - -bool IfcGeom::Kernel::convert(const IfcSchema::IfcShellBasedSurfaceModel* l, ConversionResults& shapes) { - IfcEntityList::ptr shells = l->SbsmBoundary(); - const SurfaceStyle* collective_style = get_style(l); - for( IfcEntityList::it it = shells->begin(); it != shells->end(); ++ it ) { - TopoDS_Shape s; - const SurfaceStyle* shell_style = 0; - if ((*it)->declaration().is(IfcSchema::IfcRepresentationItem::Class())) { - shell_style = get_style((IfcSchema::IfcRepresentationItem*)*it); - } - if (convert_shape(*it,s)) { - shapes.push_back(ConversionResult(l->data().id(), new OpenCascadeShape(s), shell_style ? shell_style : collective_style)); - } - } - return true; -} - -bool IfcGeom::Kernel::convert(const IfcSchema::IfcBooleanResult* l, TopoDS_Shape& shape) { - - TopoDS_Shape s1, s2; - ConversionResults items1; - TopoDS_Wire boundary_wire; - IfcSchema::IfcBooleanOperand* operand1 = l->FirstOperand(); - IfcSchema::IfcBooleanOperand* operand2 = l->SecondOperand(); - bool has_halfspace_operand = false; - - BOPAlgo_Operation occ_op; - - const IfcSchema::IfcBooleanOperator::Value op = l->Operator(); - if (op == IfcSchema::IfcBooleanOperator::IfcBooleanOperator_DIFFERENCE) { - occ_op = BOPAlgo_CUT; - } else if (op == IfcSchema::IfcBooleanOperator::IfcBooleanOperator_INTERSECTION) { - occ_op = BOPAlgo_COMMON; - } else if (op == IfcSchema::IfcBooleanOperator::IfcBooleanOperator_UNION) { - occ_op = BOPAlgo_FUSE; - } else { - return false; - } - - std::vector second_operands; - second_operands.push_back(operand2); - - if (occ_op == BOPAlgo_CUT) { - bool process_as_list = true; - while (true) { - auto res1 = operand1->as(); - if (res1) { - if (res1->Operator() == op) { - operand1 = res1->FirstOperand(); - second_operands.push_back(res1->SecondOperand()); - } else { - process_as_list = false; - break; - } - } else { - break; - } - } - - if (!process_as_list) { - operand1 = l->FirstOperand(); - second_operands = { operand2 }; - } - } - - if ( shape_type(operand1) == ST_SHAPELIST ) { - if (!(convert_shapes(operand1, items1) && flatten_shape_list(items1, s1, true))) { - return false; - } - } else if ( shape_type(operand1) == ST_SHAPE ) { - if ( ! convert_shape(operand1, s1) ) { - return false; - } - { TopoDS_Solid temp_solid; - s1 = ensure_fit_for_subtraction(s1, temp_solid); } - } else { - Logger::Message(Logger::LOG_ERROR, "Invalid representation item for boolean operation", operand1); - return false; - } - - const double first_operand_volume = shape_volume(s1); - if (first_operand_volume <= ALMOST_ZERO) { - Logger::Message(Logger::LOG_WARNING, "Empty solid for:", l->FirstOperand()); - } - - TopTools_ListOfShape second_operand_shapes; - - for (auto& op2 : second_operands) { - TopoDS_Shape s2; - - bool shape2_processed = false; - - bool is_halfspace = op2->declaration().is(IfcSchema::IfcHalfSpaceSolid::Class()); - bool is_unbounded_halfspace = is_halfspace && !op2->declaration().is(IfcSchema::IfcPolygonalBoundedHalfSpace::Class()); - has_halfspace_operand |= is_halfspace; - - { - if (shape_type(op2) == ST_SHAPELIST) { - ConversionResults items2; - shape2_processed = convert_shapes(op2, items2) && flatten_shape_list(items2, s2, true); - } else if (shape_type(op2) == ST_SHAPE) { - shape2_processed = convert_shape(op2, s2); - if (shape2_processed) { - TopoDS_Solid temp_solid; - s2 = ensure_fit_for_subtraction(s2, temp_solid); - } - } else { - Logger::Message(Logger::LOG_ERROR, "Invalid representation item for boolean operation", op2); - } - } - - if (is_unbounded_halfspace) { - TopoDS_Shape temp; - double d; - if (fit_halfspace(s1, s2, temp, d)) { - if (d < getValue(GV_PRECISION)) { - Logger::Message(Logger::LOG_WARNING, "Halfspace subtraction yields unchanged volume:", l); - continue; - } else { - s2 = temp; - } - } - } - - if (!shape2_processed) { - Logger::Message(Logger::LOG_ERROR, "Failed to convert SecondOperand:", op2); - continue; - } - - if (op2->declaration().is(IfcSchema::IfcHalfSpaceSolid::Class())) { - const double second_operand_volume = shape_volume(s2); - if (second_operand_volume <= ALMOST_ZERO) { - Logger::Message(Logger::LOG_WARNING, "Empty solid for:", op2); - } - } - - second_operand_shapes.Append(s2); - } - - /* - // TK: A little debugging trick to output both operands for visual inspection - - BRep_Builder builder; - TopoDS_Compound compound; - builder.MakeCompound(compound); - builder.Add(compound, s1); - for (const auto& s2 : second_operand_shapes) { - builder.Add(compound, s2); - } - shape = compound; - return true; - */ - -#if OCC_VERSION_HEX < 0x60900 - // @todo: this currently does not compile anymore, do we still need this? - bool valid_result = boolean_operation(s1, s2, occ_op, shape); -#else - bool valid_result = boolean_operation(s1, second_operand_shapes, occ_op, shape); -#endif - - if (op == IfcSchema::IfcBooleanOperator::IfcBooleanOperator_DIFFERENCE) { - // In case of a subtraction, a check on volume is performed. - if (valid_result) { - const double volume_after_subtraction = shape_volume(shape); - if ( ALMOST_THE_SAME(first_operand_volume,volume_after_subtraction) ) - Logger::Message(Logger::LOG_WARNING,"Subtraction yields unchanged volume:",l); - } else { - Logger::Message(Logger::LOG_ERROR,"Failed to process subtraction:",l); - shape = s1; - } - // NB: After issuing error the first operand is returned! - return true; - } else { - return valid_result; - } - return false; -} - -bool IfcGeom::Kernel::convert(const IfcSchema::IfcConnectedFaceSet* l, TopoDS_Shape& shape) { - std::unique_ptr helper_scope; - helper_scope.reset(new faceset_helper(this, l)); - - IfcSchema::IfcFace::list::ptr faces = l->CfsFaces(); - - double min_face_area = faceset_helper_ - ? (faceset_helper_->epsilon() * faceset_helper_->epsilon() / 20.) - : getValue(GV_MINIMAL_FACE_AREA); - - TopTools_ListOfShape face_list; - for (IfcSchema::IfcFace::list::it it = faces->begin(); it != faces->end(); ++it) { - bool success = false; - TopoDS_Face face; - - try { - success = convert_face(*it, face); - } catch (const std::exception& e) { - Logger::Error(e); - } catch (const Standard_Failure& e) { - if (e.GetMessageString() && strlen(e.GetMessageString())) { - Logger::Error(e.GetMessageString()); - } else { - Logger::Error("Unknown error creating face"); - } - } catch (...) { - Logger::Error("Unknown error creating face"); - } - - if (!success) { - Logger::Message(Logger::LOG_WARNING, "Failed to convert face:", (*it)); - continue; - } - - if (face.ShapeType() == TopAbs_COMPOUND) { - TopoDS_Iterator face_it(face, false); - for (; face_it.More(); face_it.Next()) { - if (face_it.Value().ShapeType() == TopAbs_FACE) { - // This should really be the case. This is not asserted. - const TopoDS_Face& triangle = TopoDS::Face(face_it.Value()); - if (face_area(triangle) > min_face_area) { - face_list.Append(triangle); - } else { - Logger::Message(Logger::LOG_WARNING, "Degenerate face:", (*it)); - } - } - } - } else { - if (face_area(face) > min_face_area) { - face_list.Append(face); - } else { - Logger::Message(Logger::LOG_WARNING, "Degenerate face:", (*it)); - } - } - } - - if (face_list.Extent() == 0) { - return false; - } - - if (face_list.Extent() > getValue(GV_MAX_FACES_TO_ORIENT) || !create_solid_from_faces(face_list, shape)) { - TopoDS_Compound compound; - BRep_Builder builder; - builder.MakeCompound(compound); - - TopTools_ListIteratorOfListOfShape face_iterator; - for (face_iterator.Initialize(face_list); face_iterator.More(); face_iterator.Next()) { - builder.Add(compound, face_iterator.Value()); - } - shape = compound; - } - - return true; -} - -bool IfcGeom::Kernel::convert(const IfcSchema::IfcMappedItem* l, ConversionResults& shapes) { - gp_GTrsf gtrsf; - IfcSchema::IfcCartesianTransformationOperator* transform = l->MappingTarget(); - if ( transform->declaration().is(IfcSchema::IfcCartesianTransformationOperator3DnonUniform::Class()) ) { - IfcGeom::Kernel::convert((IfcSchema::IfcCartesianTransformationOperator3DnonUniform*)transform,gtrsf); - } else if ( transform->declaration().is(IfcSchema::IfcCartesianTransformationOperator2DnonUniform::Class()) ) { - Logger::Message(Logger::LOG_ERROR, "Unsupported MappingTarget:", transform); - return false; - } else if ( transform->declaration().is(IfcSchema::IfcCartesianTransformationOperator3D::Class()) ) { - gp_Trsf trsf; - IfcGeom::Kernel::convert((IfcSchema::IfcCartesianTransformationOperator3D*)transform,trsf); - gtrsf = trsf; - } else if ( transform->declaration().is(IfcSchema::IfcCartesianTransformationOperator2D::Class()) ) { - gp_Trsf2d trsf_2d; - IfcGeom::Kernel::convert((IfcSchema::IfcCartesianTransformationOperator2D*)transform,trsf_2d); - gtrsf = (gp_Trsf) trsf_2d; - } - IfcSchema::IfcRepresentationMap* map = l->MappingSource(); - IfcSchema::IfcAxis2Placement* placement = map->MappingOrigin(); - gp_Trsf trsf; - if (placement->declaration().is(IfcSchema::IfcAxis2Placement3D::Class())) { - IfcGeom::Kernel::convert((IfcSchema::IfcAxis2Placement3D*)placement,trsf); - } else { - gp_Trsf2d trsf_2d; - IfcGeom::Kernel::convert((IfcSchema::IfcAxis2Placement2D*)placement,trsf_2d); - trsf = trsf_2d; - } - gtrsf.Multiply(trsf); - - const IfcGeom::SurfaceStyle* mapped_item_style = get_style(l); - - const size_t previous_size = shapes.size(); - bool b = convert_shapes(map->MappedRepresentation(), shapes); - - for (size_t i = previous_size; i < shapes.size(); ++ i ) { - OpenCascadePlacement p(gtrsf); - shapes[i].prepend(&p); - - // Apply styles assigned to the mapped item only if on - // a more granular level no styles have been applied - if (!shapes[i].hasStyle()) { - shapes[i].setStyle(mapped_item_style); - } - } - - return b; -} - -bool IfcGeom::Kernel::convert(const IfcSchema::IfcRepresentation* l, ConversionResults& shapes) { - IfcSchema::IfcRepresentationItem::list::ptr items = l->Items(); - bool part_succes = false; - if ( items->size() ) { - for ( IfcSchema::IfcRepresentationItem::list::it it = items->begin(); it != items->end(); ++ it ) { - IfcSchema::IfcRepresentationItem* representation_item = *it; - if ( shape_type(representation_item) == ST_SHAPELIST ) { - part_succes |= convert_shapes(*it, shapes); - } else { - TopoDS_Shape s; - if (convert_shape(representation_item,s)) { - shapes.push_back(ConversionResult(representation_item->data().id(), new OpenCascadeShape(s), get_style(representation_item))); - part_succes |= true; - } - } - } - } - return part_succes; -} - -bool IfcGeom::Kernel::convert(const IfcSchema::IfcGeometricSet* l, ConversionResults& shapes) { - IfcEntityList::ptr elements = l->Elements(); - if ( !elements->size() ) return false; - bool part_succes = false; - const IfcGeom::SurfaceStyle* parent_style = get_style(l); - for ( IfcEntityList::it it = elements->begin(); it != elements->end(); ++ it ) { - IfcSchema::IfcGeometricSetSelect* element = *it; - TopoDS_Shape s; - if (convert_shape(element, s)) { - part_succes = true; - const IfcGeom::SurfaceStyle* style = 0; - if (element->declaration().is(IfcSchema::IfcPoint::Class())) { - style = get_style((IfcSchema::IfcPoint*) element); - } else if (element->declaration().is(IfcSchema::IfcCurve::Class())) { - style = get_style((IfcSchema::IfcCurve*) element); - } else if (element->declaration().is(IfcSchema::IfcSurface::Class())) { - style = get_style((IfcSchema::IfcSurface*) element); - } - shapes.push_back(ConversionResult(l->data().id(), new OpenCascadeShape(s), style ? style : parent_style)); - } - } - return part_succes; -} - -bool IfcGeom::Kernel::convert(const IfcSchema::IfcBlock* l, TopoDS_Shape& shape) { - const double dx = l->XLength() * getValue(GV_LENGTH_UNIT); - const double dy = l->YLength() * getValue(GV_LENGTH_UNIT); - const double dz = l->ZLength() * getValue(GV_LENGTH_UNIT); - - BRepPrimAPI_MakeBox builder(dx, dy, dz); - gp_Trsf trsf; - IfcGeom::Kernel::convert(l->Position(),trsf); - - // IfcCsgPrimitive3D.Position has unit scale factor - shape = builder.Solid().Moved(trsf); - - return true; -} - -bool IfcGeom::Kernel::convert(const IfcSchema::IfcRectangularPyramid* l, TopoDS_Shape& shape) { - const double dx = l->XLength() * getValue(GV_LENGTH_UNIT); - const double dy = l->YLength() * getValue(GV_LENGTH_UNIT); - const double dz = l->Height() * getValue(GV_LENGTH_UNIT); - - BRepPrimAPI_MakeWedge builder(dx, dz, dy, dx / 2., dy / 2., dx / 2., dy / 2.); - - gp_Trsf trsf1, trsf2; - trsf2.SetValues( - 1, 0, 0, 0, - 0, 0, 1, 0, - 0, 1, 0, 0 -#if OCC_VERSION_HEX < 0x60800 - , Precision::Angular(), Precision::Confusion() -#endif - ); - - IfcGeom::Kernel::convert(l->Position(), trsf1); - shape = BRepBuilderAPI_Transform(builder.Solid(), trsf1 * trsf2); - return true; -} - -bool IfcGeom::Kernel::convert(const IfcSchema::IfcRightCircularCylinder* l, TopoDS_Shape& shape) { - const double r = l->Radius() * getValue(GV_LENGTH_UNIT); - const double h = l->Height() * getValue(GV_LENGTH_UNIT); - - BRepPrimAPI_MakeCylinder builder(r, h); - gp_Trsf trsf; - IfcGeom::Kernel::convert(l->Position(),trsf); - - // IfcCsgPrimitive3D.Position has unit scale factor - shape = builder.Solid().Moved(trsf); - - return true; -} - -bool IfcGeom::Kernel::convert(const IfcSchema::IfcRightCircularCone* l, TopoDS_Shape& shape) { - const double r = l->BottomRadius() * getValue(GV_LENGTH_UNIT); - const double h = l->Height() * getValue(GV_LENGTH_UNIT); - - BRepPrimAPI_MakeCone builder(r, 0., h); - gp_Trsf trsf; - IfcGeom::Kernel::convert(l->Position(),trsf); - - // IfcCsgPrimitive3D.Position has unit scale factor - shape = builder.Solid().Moved(trsf); - - return true; -} - -bool IfcGeom::Kernel::convert(const IfcSchema::IfcSphere* l, TopoDS_Shape& shape) { - const double r = l->Radius() * getValue(GV_LENGTH_UNIT); - - BRepPrimAPI_MakeSphere builder(r); - gp_Trsf trsf; - IfcGeom::Kernel::convert(l->Position(),trsf); - - // IfcCsgPrimitive3D.Position has unit scale factor - shape = builder.Solid().Moved(trsf); - - return true; -} - -bool IfcGeom::Kernel::convert(const IfcSchema::IfcCsgSolid* l, TopoDS_Shape& shape) { - return convert_shape(l->TreeRootExpression(), shape); -} - -bool IfcGeom::Kernel::convert(const IfcSchema::IfcCurveBoundedPlane* l, TopoDS_Shape& face) { - gp_Pln pln; - if (!IfcGeom::Kernel::convert(l->BasisSurface(), pln)) { - return false; - } - - gp_Trsf trsf; - trsf.SetTransformation(pln.Position(), gp::XOY()); - - TopoDS_Wire outer; - if (!convert_wire(l->OuterBoundary(), outer)) { - return false; - } - - BRepBuilderAPI_MakeFace mf(outer); - - if (!mf.IsDone() || mf.Shape().IsNull()) { - Logger::Error("Invalid outer boundary:", l->OuterBoundary()); - return false; - } - - IfcSchema::IfcCurve::list::ptr boundaries = l->InnerBoundaries(); - - for (IfcSchema::IfcCurve::list::it it = boundaries->begin(); it != boundaries->end(); ++it) { - TopoDS_Wire inner; - if (convert_wire(*it, inner)) { - mf.Add(inner); - } - } - - ShapeFix_Shape sfs(mf.Face()); - sfs.Perform(); - - // `trsf` consitutes the placement of the plane and therefore has unit scale factor - face = TopoDS::Face(sfs.Shape()).Moved(trsf); - - return true; -} - -bool IfcGeom::Kernel::convert(const IfcSchema::IfcRectangularTrimmedSurface* l, TopoDS_Shape& face) { - if (!l->BasisSurface()->declaration().is(IfcSchema::IfcPlane::Class())) { - Logger::Message(Logger::LOG_ERROR, "Unsupported BasisSurface:", l->BasisSurface()); - return false; - } - gp_Pln pln; - IfcGeom::Kernel::convert((IfcSchema::IfcPlane*) l->BasisSurface(), pln); - - BRepBuilderAPI_MakeFace mf(pln, l->U1(), l->U2(), l->V1(), l->V2()); - - face = mf.Face(); - - return true; -} - -bool IfcGeom::Kernel::convert(const IfcSchema::IfcSurfaceCurveSweptAreaSolid* l, TopoDS_Shape& shape) { - gp_Trsf directrix; - TopoDS_Shape face; - TopoDS_Wire wire, section; - - if (!l->ReferenceSurface()->declaration().is(IfcSchema::IfcPlane::Class())) { - Logger::Message(Logger::LOG_WARNING, "Reference surface not supported", l->ReferenceSurface()); - return false; - } - - gp_Trsf trsf; - bool has_position = true; -#ifdef SCHEMA_IfcSweptAreaSolid_Position_IS_OPTIONAL - has_position = l->hasPosition(); -#endif - if (has_position) { - IfcGeom::Kernel::convert(l->Position(), trsf); - } - - if (!convert_face(l->SweptArea(), face) || - !convert_wire(l->Directrix(), wire) ) { - return false; - } - - gp_Pln pln; - gp_Pnt directrix_origin; - gp_Vec directrix_tangent; - bool directrix_on_plane = true; - IfcGeom::Kernel::convert((IfcSchema::IfcPlane*) l->ReferenceSurface(), pln); - - // As per Informal propositions 2: The Directrix shall lie on the ReferenceSurface. - // This is not always the case with the test files in the repository. I am not sure - // how to deal with this and whether my interpretation of the propositions is - // correct. However, if it has been asserted that the vertices of the directrix do - // not conform to the ReferenceSurface, the ReferenceSurface is ignored. - { - for (TopExp_Explorer exp(wire, TopAbs_VERTEX); exp.More(); exp.Next()) { - if (pln.Distance(BRep_Tool::Pnt(TopoDS::Vertex(exp.Current()))) > ALMOST_ZERO) { - directrix_on_plane = false; - Logger::Message(Logger::LOG_WARNING, "The Directrix does not lie on the ReferenceSurface", l); - break; - } - } - } - - { - TopExp_Explorer exp(wire, TopAbs_EDGE); - TopoDS_Edge edge = TopoDS::Edge(exp.Current()); - double u0, u1; - Handle(Geom_Curve) crv = BRep_Tool::Curve(edge, u0, u1); - crv->D1(u0, directrix_origin, directrix_tangent); - } - - if (pln.Axis().Direction().IsNormal(directrix_tangent, Precision::Approximation()) && directrix_on_plane) { - directrix.SetTransformation(gp_Ax3(directrix_origin, directrix_tangent, pln.Axis().Direction()), gp::XOY()); - } else { - directrix.SetTransformation(gp_Ax3(directrix_origin, directrix_tangent), gp::XOY()); - } - face = BRepBuilderAPI_Transform(face, directrix); - - // NB: Note that StartParam and EndParam param are ignored and the assumption is - // made that the parametric range over which to be swept matches the IfcCurve in - // its entirety. - BRepOffsetAPI_MakePipeShell builder(wire); - - { TopExp_Explorer exp(face, TopAbs_WIRE); - section = TopoDS::Wire(exp.Current()); } - - builder.Add(section); - builder.SetTransitionMode(BRepBuilderAPI_RightCorner); - if (directrix_on_plane) { - builder.SetMode(pln.Axis().Direction()); - } - builder.Build(); - builder.MakeSolid(); - shape = builder.Shape(); - - if (has_position) { - // IfcSweptAreaSolid.Position (trsf) is an IfcAxis2Placement3D - // and therefore has a unit scale factor - shape.Move(trsf); - } - - return true; -} - -namespace { - bool wire_is_c1_continuous(const TopoDS_Wire& w, double tol) { - // NB Note that c0 continuity is NOT checked! - - TopTools_IndexedDataMapOfShapeListOfShape map; - TopExp::MapShapesAndAncestors(w, TopAbs_VERTEX, TopAbs_EDGE, map); - for (int i = 1; i <= map.Extent(); ++i) { - const auto& li = map.FindFromIndex(i); - if (li.Extent() == 2) { - const TopoDS_Vertex& v = TopoDS::Vertex(map.FindKey(i)); - - const TopoDS_Edge& e0 = TopoDS::Edge(li.First()); - const TopoDS_Edge& e1 = TopoDS::Edge(li.Last()); - - double u0 = BRep_Tool::Parameter(v, e0); - double u1 = BRep_Tool::Parameter(v, e1); - - double _, __; - Handle(Geom_Curve) c0 = BRep_Tool::Curve(e0, _, __); - Handle(Geom_Curve) c1 = BRep_Tool::Curve(e1, _, __); - - gp_Pnt p; - gp_Vec v0, v1; - c0->D1(u0, p, v0); - c1->D1(u1, p, v1); - - if (1. - std::abs(v0.Normalized().Dot(v1.Normalized())) > tol) { - return false; - } - } - } - return true; - } -} - -bool IfcGeom::Kernel::convert(const IfcSchema::IfcSweptDiskSolid* l, TopoDS_Shape& shape) { - TopoDS_Wire wire, section1, section2; - - bool hasInnerRadius = l->hasInnerRadius(); - - if (!convert_wire(l->Directrix(), wire)) { - return false; - } - - gp_Ax2 directrix; - { - gp_Pnt directrix_origin; - gp_Vec directrix_tangent; - - TopoDS_Edge edge; - - // Find first edge - TopoDS_Vertex v0, v1; - TopExp::Vertices(wire, v0, v1); - TopTools_IndexedDataMapOfShapeListOfShape map; - TopExp::MapShapesAndAncestors(wire, TopAbs_VERTEX, TopAbs_EDGE, map); - if (map.Contains(v0) && map.FindFromKey(v0).Extent() == 1) { - edge = TopoDS::Edge(map.FindFromKey(v0).First()); - } else { - Logger::Error("Unable to locate first edge of:", l->Directrix()); - return false; - } - - double u0, u1; - Handle(Geom_Curve) crv = BRep_Tool::Curve(edge, u0, u1); - crv->D1(u0, directrix_origin, directrix_tangent); - directrix = gp_Ax2(directrix_origin, directrix_tangent); - } - - const double r1 = l->Radius() * getValue(GV_LENGTH_UNIT); - Handle(Geom_Circle) circle = new Geom_Circle(directrix, r1); - section1 = BRepBuilderAPI_MakeWire(BRepBuilderAPI_MakeEdge(circle)); - - if (hasInnerRadius) { - const double r2 = l->InnerRadius() * getValue(GV_LENGTH_UNIT); - if (r2 < getValue(GV_PRECISION)) { - // Subtraction of pipes with small radii is unstable. - hasInnerRadius = false; - } else { - Handle(Geom_Circle) circle2 = new Geom_Circle(directrix, r2); - section2 = BRepBuilderAPI_MakeWire(BRepBuilderAPI_MakeEdge(circle2)); - } - } - - // This is not used anymore, BRepBuilderAPI_RightCorner is always used now. - // const bool is_continuous = wire_is_c1_continuous(wire, 1.e-3); - - // NB: Note that StartParam and EndParam param are ignored and the assumption is - // made that the parametric range over which to be swept matches the IfcCurve in - // its entirety. - { BRepOffsetAPI_MakePipeShell builder(wire); - builder.Add(section1); - builder.SetTransitionMode(BRepBuilderAPI_RightCorner); - builder.Build(); - builder.MakeSolid(); - shape = builder.Shape(); } - - if (hasInnerRadius) { - BRepOffsetAPI_MakePipeShell builder(wire); - builder.Add(section2); - builder.SetTransitionMode(BRepBuilderAPI_RightCorner); - builder.Build(); - builder.MakeSolid(); - TopoDS_Shape inner = builder.Shape(); - - BRepAlgoAPI_Cut brep_cut(shape, inner); - bool is_valid = false; - if (brep_cut.IsDone()) { - TopoDS_Shape result = brep_cut; - - ShapeFix_Shape fix(result); - fix.Perform(); - result = fix.Shape(); - - is_valid = BRepCheck_Analyzer(result).IsValid() != 0; - if (is_valid) { - shape = result; - } - } - - if (!is_valid) { - Logger::Message(Logger::LOG_WARNING, "Failed to subtract inner radius void for:", l); - } - } - - return true; -} - -#ifdef SCHEMA_HAS_IfcCylindricalSurface - -bool IfcGeom::Kernel::convert(const IfcSchema::IfcCylindricalSurface* l, TopoDS_Shape& face) { - gp_Trsf trsf; - IfcGeom::Kernel::convert(l->Position(),trsf); - - // IfcElementarySurface.Position has unit scale factor -#if OCC_VERSION_HEX < 0x60502 - face = BRepBuilderAPI_MakeFace(new Geom_CylindricalSurface(gp::XOY(), l->Radius() * getValue(GV_LENGTH_UNIT))).Face().Moved(trsf); -#else - face = BRepBuilderAPI_MakeFace(new Geom_CylindricalSurface(gp::XOY(), l->Radius() * getValue(GV_LENGTH_UNIT)), getValue(GV_PRECISION)).Face().Moved(trsf); -#endif - return true; -} - -#endif - -#ifdef SCHEMA_HAS_IfcAdvancedBrep - -bool IfcGeom::Kernel::convert(const IfcSchema::IfcAdvancedBrep* l, TopoDS_Shape& shape) { - return convert(l->Outer(), shape); -} - -#endif - -#ifdef SCHEMA_HAS_IfcTriangulatedFaceSet - -bool IfcGeom::Kernel::convert(const IfcSchema::IfcTriangulatedFaceSet* l, TopoDS_Shape& shape) { - IfcSchema::IfcCartesianPointList3D* point_list = l->Coordinates(); - const std::vector< std::vector > coordinates = point_list->CoordList(); - std::vector points; - points.reserve(coordinates.size()); - for (std::vector< std::vector >::const_iterator it = coordinates.begin(); it != coordinates.end(); ++it) { - const std::vector& coords = *it; - if (coords.size() != 3) { - Logger::Message(Logger::LOG_ERROR, "Invalid dimensions encountered on Coordinates", l); - return false; - } - points.push_back(gp_Pnt(coords[0] * getValue(GV_LENGTH_UNIT), - coords[1] * getValue(GV_LENGTH_UNIT), - coords[2] * getValue(GV_LENGTH_UNIT))); - } - - std::vector< std::vector > indices = l->CoordIndex(); - - std::vector faces; - faces.reserve(indices.size()); - - for(std::vector< std::vector >::const_iterator it = indices.begin(); it != indices.end(); ++ it) { - const std::vector& tri = *it; - if (tri.size() != 3) { - Logger::Message(Logger::LOG_ERROR, "Invalid dimensions encountered on CoordIndex", l); - return false; - } - - const int min_index = *std::min_element(tri.begin(), tri.end()); - const int max_index = *std::max_element(tri.begin(), tri.end()); - - if (min_index < 1 || max_index > (int) points.size()) { - Logger::Message(Logger::LOG_ERROR, "Contents of CoordIndex out of bounds", l); - return false; - } - - const gp_Pnt& a = points[tri[0] - 1]; // account for zero- vs - const gp_Pnt& b = points[tri[1] - 1]; // one-based indices in - const gp_Pnt& c = points[tri[2] - 1]; // c++ and express - - TopoDS_Wire wire = BRepBuilderAPI_MakePolygon(a, b, c, true).Wire(); - TopoDS_Face face = BRepBuilderAPI_MakeFace(wire).Face(); - - TopoDS_Iterator face_it(face, false); - const TopoDS_Wire& w = TopoDS::Wire(face_it.Value()); - const bool reversed = w.Orientation() == TopAbs_REVERSED; - if (reversed) { - face.Reverse(); - } - - if (face_area(face) > getValue(GV_MINIMAL_FACE_AREA)) { - faces.push_back(face); - } - } - - if (faces.empty()) return false; - - bool valid_shell = false; - - // @todo Do this more efficiently by creating proper half-edge pairs. - BRepOffsetAPI_Sewing sewing_builder; - sewing_builder.SetTolerance(getValue(GV_POINT_EQUALITY_TOLERANCE)); - sewing_builder.SetMaxTolerance(getValue(GV_POINT_EQUALITY_TOLERANCE)); - sewing_builder.SetMinTolerance(getValue(GV_POINT_EQUALITY_TOLERANCE)); - - for (std::vector::const_iterator it = faces.begin(); it != faces.end(); ++it) { - sewing_builder.Add(*it); - } - - try { - sewing_builder.Perform(); - shape = sewing_builder.SewedShape(); - valid_shell = BRepCheck_Analyzer(shape).IsValid(); - } catch(...) {} - - if (valid_shell) { - try { - ShapeFix_Solid solid; - solid.LimitTolerance(getValue(GV_POINT_EQUALITY_TOLERANCE)); - TopoDS_Solid solid_shape = solid.SolidFromShell(TopoDS::Shell(shape)); - if (!solid_shape.IsNull()) { - try { - BRepClass3d_SolidClassifier classifier(solid_shape); - shape = solid_shape; - } catch (...) {} - } - } catch(...) {} - } else { - Logger::Message(Logger::LOG_WARNING, "Failed to sew faceset:", l); - } - - if (!valid_shell) { - TopoDS_Compound compound; - BRep_Builder builder; - builder.MakeCompound(compound); - - for (std::vector::const_iterator it = faces.begin(); it != faces.end(); ++it) { - builder.Add(compound, *it); - } - - shape = compound; - } - - return true; -} - -#endif diff --git a/src/ifcgeom/kernels/opencascade/IfcGeomShapes.cpp_ b/src/ifcgeom/kernels/opencascade/IfcGeomShapes.cpp_ new file mode 100644 index 0000000000..5001639a6d --- /dev/null +++ b/src/ifcgeom/kernels/opencascade/IfcGeomShapes.cpp_ @@ -0,0 +1,1289 @@ +/******************************************************************************** + * * + * This file is part of IfcOpenShell. * + * * + * IfcOpenShell is free software: you can redistribute it and/or modify * + * it under the terms of the Lesser GNU General Public License as published by * + * the Free Software Foundation, either version 3.0 of the License, or * + * (at your option) any later version. * + * * + * IfcOpenShell is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * Lesser GNU General Public License for more details. * + * * + * You should have received a copy of the Lesser GNU General Public License * + * along with this program. If not, see . * + * * + ********************************************************************************/ + +/******************************************************************************** + * * + * Implementations of the various conversion functions defined in IfcRegister.h * + * * + ********************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include + +#include + +#include +#include + +#include + +#include + +#include "../../../ifcgeom/kernels/opencascade/IfcGeom.h" + +#include + +#define Kernel POSTFIX_SCHEMA(Kernel) + +bool IfcGeom::Kernel::convert(const IfcSchema::IfcExtrudedAreaSolid* l, TopoDS_Shape& shape) { + const double height = l->Depth() * getValue(GV_LENGTH_UNIT); + if (height < getValue(GV_PRECISION)) { + Logger::Message(Logger::LOG_ERROR, "Non-positive extrusion height encountered for:", l); + return false; + } + + TopoDS_Shape face; + if ( !convert_face(l->SweptArea(),face) ) return false; + + gp_Trsf trsf; + bool has_position = true; +#ifdef SCHEMA_IfcSweptAreaSolid_Position_IS_OPTIONAL + has_position = l->hasPosition(); +#endif + if (has_position) { + IfcGeom::Kernel::convert(l->Position(), trsf); + } + + gp_Dir dir; + convert(l->ExtrudedDirection(),dir); + + shape.Nullify(); + + if (face.ShapeType() == TopAbs_COMPOUND) { + + // For compounds (most likely the result of a IfcCompositeProfileDef) + // create a compound solid shape. + + TopExp_Explorer exp(face, TopAbs_FACE); + + TopoDS_CompSolid compound; + BRep_Builder builder; + builder.MakeCompSolid(compound); + + int num_faces_extruded = 0; + for (; exp.More(); exp.Next(), ++num_faces_extruded) { + builder.Add(compound, BRepPrimAPI_MakePrism(exp.Current(), height*dir)); + } + + if (num_faces_extruded) { + shape = compound; + } + + } + + if (shape.IsNull()) { + shape = BRepPrimAPI_MakePrism(face, height*dir); + } + + if (has_position && !shape.IsNull()) { + // IfcSweptAreaSolid.Position (trsf) is an IfcAxis2Placement3D + // and therefore has a unit scale factor + shape.Move(trsf); + } + + return !shape.IsNull(); +} + +#ifdef SCHEMA_HAS_IfcExtrudedAreaSolidTapered +bool IfcGeom::Kernel::convert(const IfcSchema::IfcExtrudedAreaSolidTapered* l, TopoDS_Shape& shape) { + const double height = l->Depth() * getValue(GV_LENGTH_UNIT); + if (height < getValue(GV_PRECISION)) { + Logger::Message(Logger::LOG_ERROR, "Non-positive extrusion height encountered for:", l); + return false; + } + + TopoDS_Shape face1, face2; + if (!convert_face(l->SweptArea(), face1)) return false; + if (!convert_face(l->EndSweptArea(), face2)) return false; + + gp_Trsf trsf; + bool has_position = true; +#ifdef SCHEMA_IfcSweptAreaSolid_Position_IS_OPTIONAL + has_position = l->hasPosition(); +#endif + if (has_position) { + IfcGeom::Kernel::convert(l->Position(), trsf); + } + + gp_Dir dir; + convert(l->ExtrudedDirection(), dir); + + gp_Trsf end_profile; + end_profile.SetTranslation(height * dir); + + TopoDS_Edge spine_edge = BRepBuilderAPI_MakeEdge(gp_Pnt(), gp_Pnt((height * dir).XYZ())).Edge(); + TopoDS_Wire wire = BRepBuilderAPI_MakeWire(spine_edge).Wire(); + + shape.Nullify(); + + TopExp_Explorer exp1(face1, TopAbs_WIRE); + TopExp_Explorer exp2(face2, TopAbs_WIRE); + + TopoDS_Vertex v1, v2; + TopExp::Vertices(wire, v1, v2); + + TopoDS_Shape shell; + TopoDS_Compound compound; + BRep_Builder compound_builder; + + for (; exp1.More() && exp2.More(); exp1.Next(), exp2.Next()) { + const TopoDS_Wire& w1 = TopoDS::Wire(exp1.Current()); + const TopoDS_Wire& w2 = TopoDS::Wire(exp2.Current()); + + BRepOffsetAPI_MakePipeShell builder(wire); + builder.Add(w1, v1); + builder.Add(w2.Moved(end_profile), v2); + + TopoDS_Shape result = builder.Shape(); + + BRepOffsetAPI_Sewing sewer; + sewer.SetTolerance(getValue(GV_PRECISION)); + sewer.SetMaxTolerance(getValue(GV_PRECISION)); + sewer.SetMinTolerance(getValue(GV_PRECISION)); + + sewer.Add(result); + sewer.Add(BRepBuilderAPI_MakeFace(w1).Face()); + sewer.Add(BRepBuilderAPI_MakeFace(w2).Face().Moved(end_profile)); + + sewer.Perform(); + + result = sewer.SewedShape(); + + if (shell.IsNull()) { + shell = result; + } else if (l->SweptArea()->declaration().is(IfcSchema::IfcCircleHollowProfileDef::Class()) || + l->SweptArea()->declaration().is(IfcSchema::IfcRectangleHollowProfileDef::Class())) + { + /// @todo a bit of of a hack, should be sufficient + shell = BRepAlgoAPI_Cut(shell, result).Shape(); + break; + } else { + if (compound.IsNull()) { + compound_builder.MakeCompound(compound); + compound_builder.Add(compound, shell); + } + compound_builder.Add(compound, result); + } + } + + if (!compound.IsNull()) { + shell = compound; + } + + shape = shell; + + if (exp1.More() != exp2.More()) { + Logger::Message(Logger::LOG_ERROR, "Inconsistent profiles encountered for:", l); + } + + if (has_position && !shape.IsNull()) { + // IfcSweptAreaSolid.Position (trsf) is an IfcAxis2Placement3D + // and therefore has a unit scale factor + shape.Move(trsf); + } + + return !shape.IsNull(); +} +#endif + +bool IfcGeom::Kernel::convert(const IfcSchema::IfcSurfaceOfLinearExtrusion* l, TopoDS_Shape& shape) { + TopoDS_Wire wire; + if ( !convert_wire(l->SweptCurve(), wire) ) { + TopoDS_Face face; + if ( !convert_face(l->SweptCurve(),face) ) return false; + TopExp_Explorer exp(face, TopAbs_WIRE); + wire = TopoDS::Wire(exp.Current()); + } + const double height = l->Depth() * getValue(GV_LENGTH_UNIT); + + gp_Trsf trsf; + bool has_position = true; +#ifdef SCHEMA_IfcSweptSurface_Position_IS_OPTIONAL + has_position = l->hasPosition(); +#endif + if (has_position) { + IfcGeom::Kernel::convert(l->Position(), trsf); + } + + gp_Dir dir; + convert(l->ExtrudedDirection(),dir); + + shape = BRepPrimAPI_MakePrism(wire, height*dir); + + if (has_position) { + // IfcSweptSurface.Position (trsf) is an IfcAxis2Placement3D + // and therefore has a unit scale factor + shape.Move(trsf); + } + + return !shape.IsNull(); +} + +bool IfcGeom::Kernel::convert(const IfcSchema::IfcSurfaceOfRevolution* l, TopoDS_Shape& shape) { + TopoDS_Wire wire; + if ( !convert_wire(l->SweptCurve(), wire) ) { + TopoDS_Face face; + if ( !convert_face(l->SweptCurve(),face) ) return false; + TopExp_Explorer exp(face, TopAbs_WIRE); + wire = TopoDS::Wire(exp.Current()); + } + + gp_Ax1 ax1; + IfcGeom::Kernel::convert(l->AxisPosition(), ax1); + + gp_Trsf trsf; + bool has_position = true; +#ifdef SCHEMA_IfcSweptSurface_Position_IS_OPTIONAL + has_position = l->hasPosition(); +#endif + if (has_position) { + IfcGeom::Kernel::convert(l->Position(), trsf); + } + + shape = BRepPrimAPI_MakeRevol(wire, ax1); + + if (has_position) { + // IfcSweptSurface.Position (trsf) is an IfcAxis2Placement3D + // and therefore has a unit scale factor + shape.Move(trsf); + } + + return !shape.IsNull(); +} + +bool IfcGeom::Kernel::convert(const IfcSchema::IfcRevolvedAreaSolid* l, TopoDS_Shape& shape) { + const double ang = l->Angle() * getValue(GV_PLANEANGLE_UNIT); + + TopoDS_Face face; + if ( ! convert_face(l->SweptArea(),face) ) return false; + + gp_Ax1 ax1; + IfcGeom::Kernel::convert(l->Axis(), ax1); + + gp_Trsf trsf; + bool has_position = true; +#ifdef SCHEMA_IfcSweptAreaSolid_Position_IS_OPTIONAL + has_position = l->hasPosition(); +#endif + if (has_position) { + IfcGeom::Kernel::convert(l->Position(), trsf); + } + + if (ang >= M_PI * 2. - ALMOST_ZERO) { + shape = BRepPrimAPI_MakeRevol(face, ax1); + } else { + shape = BRepPrimAPI_MakeRevol(face, ax1, ang); + } + + if (has_position) { + // IfcSweptAreaSolid.Position (trsf) is an IfcAxis2Placement3D + // and therefore has a unit scale factor + shape.Move(trsf); + } + + return !shape.IsNull(); +} + +bool IfcGeom::Kernel::convert(const IfcSchema::IfcManifoldSolidBrep* l, ConversionResults& shape) { + TopoDS_Shape s; + const SurfaceStyle* collective_style = get_style(l); + if (convert_shape(l->Outer(),s) ) { + const SurfaceStyle* indiv_style = get_style(l->Outer()); + + IfcSchema::IfcClosedShell::list::ptr voids(new IfcSchema::IfcClosedShell::list); + if (l->declaration().is(IfcSchema::IfcFacetedBrepWithVoids::Class())) { + voids = l->as()->Voids(); + } +#ifdef SCHEMA_HAS_IfcAdvancedBrepWithVoids + if (l->declaration().is(IfcSchema::IfcAdvancedBrepWithVoids::Class())) { + voids = l->as()->Voids(); + } +#endif + + for (IfcSchema::IfcClosedShell::list::it it = voids->begin(); it != voids->end(); ++it) { + TopoDS_Shape s2; + /// @todo No extensive shapefixing since shells should be disjoint. + /// @todo Awaiting generalized boolean ops module with appropriate checking + if (convert_shape(l->Outer(), s2)) { + s = BRepAlgoAPI_Cut(s, s2).Shape(); + } + } + + shape.push_back(ConversionResult(l->data().id(), new OpenCascadeShape(s), indiv_style ? indiv_style : collective_style)); + return true; + } + return false; +} + +bool IfcGeom::Kernel::convert(const IfcSchema::IfcFaceBasedSurfaceModel* l, ConversionResults& shapes) { + bool part_success = false; + IfcSchema::IfcConnectedFaceSet::list::ptr facesets = l->FbsmFaces(); + const SurfaceStyle* collective_style = get_style(l); + for( IfcSchema::IfcConnectedFaceSet::list::it it = facesets->begin(); it != facesets->end(); ++ it ) { + TopoDS_Shape s; + const SurfaceStyle* shell_style = get_style(*it); + if (convert_shape(*it,s)) { + shapes.push_back(ConversionResult(l->data().id(), new OpenCascadeShape(s), shell_style ? shell_style : collective_style)); + part_success |= true; + } + } + return part_success; +} + +bool IfcGeom::Kernel::convert(const IfcSchema::IfcHalfSpaceSolid* l, TopoDS_Shape& shape) { + IfcSchema::IfcSurface* surface = l->BaseSurface(); + if ( ! surface->declaration().is(IfcSchema::IfcPlane::Class()) ) { + Logger::Message(Logger::LOG_ERROR, "Unsupported BaseSurface:", surface); + return false; + } + gp_Pln pln; + IfcGeom::Kernel::convert((IfcSchema::IfcPlane*)surface,pln); + const gp_Pnt pnt = pln.Location().Translated( l->AgreementFlag() ? -pln.Axis().Direction() : pln.Axis().Direction()); + shape = BRepPrimAPI_MakeHalfSpace(BRepBuilderAPI_MakeFace(pln),pnt).Solid(); + return true; +} + +bool IfcGeom::Kernel::convert(const IfcSchema::IfcPolygonalBoundedHalfSpace* l, TopoDS_Shape& shape) { + TopoDS_Shape halfspace; + if ( ! IfcGeom::Kernel::convert((IfcSchema::IfcHalfSpaceSolid*)l,halfspace) ) return false; + + TopoDS_Wire wire; + if ( ! convert_wire(l->PolygonalBoundary(),wire) || ! wire.Closed() ) return false; + + gp_Trsf trsf; + if ( ! convert(l->Position(),trsf) ) return false; + + TColgp_SequenceOfPnt points; + if (wire_to_sequence_of_point(wire, points)) { + // Boolean subtractions not very robust for narrow operands, + // increase minimal point spacing to eliminate such shapes. + const double t = getValue(GV_PRECISION) * 10.; + remove_duplicate_points_from_loop(points, wire.Closed() != 0, t); // Note: wire always closed, as per if statement above + remove_collinear_points_from_loop(points, wire.Closed() != 0, t); + if (points.Length() < 3) { + Logger::Message(Logger::LOG_ERROR, "Not enough points retained from:", l->PolygonalBoundary()); + return false; + } + sequence_of_point_to_wire(points, wire, wire.Closed() != 0); + } + + TopoDS_Shape prism = BRepPrimAPI_MakePrism(BRepBuilderAPI_MakeFace(wire),gp_Vec(0,0,200)); + gp_Trsf down; down.SetTranslation(gp_Vec(0,0,-100.0)); + + // `trsf` and `down` both have a unit scale factor + prism.Move(trsf*down); + + shape = BRepAlgoAPI_Common(halfspace,prism); + return true; +} + +bool IfcGeom::Kernel::convert(const IfcSchema::IfcShellBasedSurfaceModel* l, ConversionResults& shapes) { + IfcEntityList::ptr shells = l->SbsmBoundary(); + const SurfaceStyle* collective_style = get_style(l); + for( IfcEntityList::it it = shells->begin(); it != shells->end(); ++ it ) { + TopoDS_Shape s; + const SurfaceStyle* shell_style = 0; + if ((*it)->declaration().is(IfcSchema::IfcRepresentationItem::Class())) { + shell_style = get_style((IfcSchema::IfcRepresentationItem*)*it); + } + if (convert_shape(*it,s)) { + shapes.push_back(ConversionResult(l->data().id(), new OpenCascadeShape(s), shell_style ? shell_style : collective_style)); + } + } + return true; +} + +bool IfcGeom::Kernel::convert(const IfcSchema::IfcBooleanResult* l, TopoDS_Shape& shape) { + + TopoDS_Shape s1, s2; + ConversionResults items1; + TopoDS_Wire boundary_wire; + IfcSchema::IfcBooleanOperand* operand1 = l->FirstOperand(); + IfcSchema::IfcBooleanOperand* operand2 = l->SecondOperand(); + bool has_halfspace_operand = false; + + BOPAlgo_Operation occ_op; + + const IfcSchema::IfcBooleanOperator::Value op = l->Operator(); + if (op == IfcSchema::IfcBooleanOperator::IfcBooleanOperator_DIFFERENCE) { + occ_op = BOPAlgo_CUT; + } else if (op == IfcSchema::IfcBooleanOperator::IfcBooleanOperator_INTERSECTION) { + occ_op = BOPAlgo_COMMON; + } else if (op == IfcSchema::IfcBooleanOperator::IfcBooleanOperator_UNION) { + occ_op = BOPAlgo_FUSE; + } else { + return false; + } + + std::vector second_operands; + second_operands.push_back(operand2); + + if (occ_op == BOPAlgo_CUT) { + bool process_as_list = true; + while (true) { + auto res1 = operand1->as(); + if (res1) { + if (res1->Operator() == op) { + operand1 = res1->FirstOperand(); + second_operands.push_back(res1->SecondOperand()); + } else { + process_as_list = false; + break; + } + } else { + break; + } + } + + if (!process_as_list) { + operand1 = l->FirstOperand(); + second_operands = { operand2 }; + } + } + + if ( shape_type(operand1) == ST_SHAPELIST ) { + if (!(convert_shapes(operand1, items1) && flatten_shape_list(items1, s1, true))) { + return false; + } + } else if ( shape_type(operand1) == ST_SHAPE ) { + if ( ! convert_shape(operand1, s1) ) { + return false; + } + { TopoDS_Solid temp_solid; + s1 = ensure_fit_for_subtraction(s1, temp_solid); } + } else { + Logger::Message(Logger::LOG_ERROR, "Invalid representation item for boolean operation", operand1); + return false; + } + + const double first_operand_volume = shape_volume(s1); + if (first_operand_volume <= ALMOST_ZERO) { + Logger::Message(Logger::LOG_WARNING, "Empty solid for:", l->FirstOperand()); + } + + TopTools_ListOfShape second_operand_shapes; + + for (auto& op2 : second_operands) { + TopoDS_Shape s2; + + bool shape2_processed = false; + + bool is_halfspace = op2->declaration().is(IfcSchema::IfcHalfSpaceSolid::Class()); + bool is_unbounded_halfspace = is_halfspace && !op2->declaration().is(IfcSchema::IfcPolygonalBoundedHalfSpace::Class()); + has_halfspace_operand |= is_halfspace; + + { + if (shape_type(op2) == ST_SHAPELIST) { + ConversionResults items2; + shape2_processed = convert_shapes(op2, items2) && flatten_shape_list(items2, s2, true); + } else if (shape_type(op2) == ST_SHAPE) { + shape2_processed = convert_shape(op2, s2); + if (shape2_processed) { + TopoDS_Solid temp_solid; + s2 = ensure_fit_for_subtraction(s2, temp_solid); + } + } else { + Logger::Message(Logger::LOG_ERROR, "Invalid representation item for boolean operation", op2); + } + } + + if (is_unbounded_halfspace) { + TopoDS_Shape temp; + double d; + if (fit_halfspace(s1, s2, temp, d)) { + if (d < getValue(GV_PRECISION)) { + Logger::Message(Logger::LOG_WARNING, "Halfspace subtraction yields unchanged volume:", l); + continue; + } else { + s2 = temp; + } + } + } + + if (!shape2_processed) { + Logger::Message(Logger::LOG_ERROR, "Failed to convert SecondOperand:", op2); + continue; + } + + if (op2->declaration().is(IfcSchema::IfcHalfSpaceSolid::Class())) { + const double second_operand_volume = shape_volume(s2); + if (second_operand_volume <= ALMOST_ZERO) { + Logger::Message(Logger::LOG_WARNING, "Empty solid for:", op2); + } + } + + second_operand_shapes.Append(s2); + } + + /* + // TK: A little debugging trick to output both operands for visual inspection + + BRep_Builder builder; + TopoDS_Compound compound; + builder.MakeCompound(compound); + builder.Add(compound, s1); + for (const auto& s2 : second_operand_shapes) { + builder.Add(compound, s2); + } + shape = compound; + return true; + */ + +#if OCC_VERSION_HEX < 0x60900 + // @todo: this currently does not compile anymore, do we still need this? + bool valid_result = boolean_operation(s1, s2, occ_op, shape); +#else + bool valid_result = boolean_operation(s1, second_operand_shapes, occ_op, shape); +#endif + + if (op == IfcSchema::IfcBooleanOperator::IfcBooleanOperator_DIFFERENCE) { + // In case of a subtraction, a check on volume is performed. + if (valid_result) { + const double volume_after_subtraction = shape_volume(shape); + if ( ALMOST_THE_SAME(first_operand_volume,volume_after_subtraction) ) + Logger::Message(Logger::LOG_WARNING,"Subtraction yields unchanged volume:",l); + } else { + Logger::Message(Logger::LOG_ERROR,"Failed to process subtraction:",l); + shape = s1; + } + // NB: After issuing error the first operand is returned! + return true; + } else { + return valid_result; + } + return false; +} + +bool IfcGeom::Kernel::convert(const IfcSchema::IfcConnectedFaceSet* l, TopoDS_Shape& shape) { + std::unique_ptr helper_scope; + helper_scope.reset(new faceset_helper(this, l)); + + IfcSchema::IfcFace::list::ptr faces = l->CfsFaces(); + + double min_face_area = faceset_helper_ + ? (faceset_helper_->epsilon() * faceset_helper_->epsilon() / 20.) + : getValue(GV_MINIMAL_FACE_AREA); + + TopTools_ListOfShape face_list; + for (IfcSchema::IfcFace::list::it it = faces->begin(); it != faces->end(); ++it) { + bool success = false; + TopoDS_Face face; + + try { + success = convert_face(*it, face); + } catch (const std::exception& e) { + Logger::Error(e); + } catch (const Standard_Failure& e) { + if (e.GetMessageString() && strlen(e.GetMessageString())) { + Logger::Error(e.GetMessageString()); + } else { + Logger::Error("Unknown error creating face"); + } + } catch (...) { + Logger::Error("Unknown error creating face"); + } + + if (!success) { + Logger::Message(Logger::LOG_WARNING, "Failed to convert face:", (*it)); + continue; + } + + if (face.ShapeType() == TopAbs_COMPOUND) { + TopoDS_Iterator face_it(face, false); + for (; face_it.More(); face_it.Next()) { + if (face_it.Value().ShapeType() == TopAbs_FACE) { + // This should really be the case. This is not asserted. + const TopoDS_Face& triangle = TopoDS::Face(face_it.Value()); + if (face_area(triangle) > min_face_area) { + face_list.Append(triangle); + } else { + Logger::Message(Logger::LOG_WARNING, "Degenerate face:", (*it)); + } + } + } + } else { + if (face_area(face) > min_face_area) { + face_list.Append(face); + } else { + Logger::Message(Logger::LOG_WARNING, "Degenerate face:", (*it)); + } + } + } + + if (face_list.Extent() == 0) { + return false; + } + + if (face_list.Extent() > getValue(GV_MAX_FACES_TO_ORIENT) || !create_solid_from_faces(face_list, shape)) { + TopoDS_Compound compound; + BRep_Builder builder; + builder.MakeCompound(compound); + + TopTools_ListIteratorOfListOfShape face_iterator; + for (face_iterator.Initialize(face_list); face_iterator.More(); face_iterator.Next()) { + builder.Add(compound, face_iterator.Value()); + } + shape = compound; + } + + return true; +} + +bool IfcGeom::Kernel::convert(const IfcSchema::IfcMappedItem* l, ConversionResults& shapes) { + gp_GTrsf gtrsf; + IfcSchema::IfcCartesianTransformationOperator* transform = l->MappingTarget(); + if ( transform->declaration().is(IfcSchema::IfcCartesianTransformationOperator3DnonUniform::Class()) ) { + IfcGeom::Kernel::convert((IfcSchema::IfcCartesianTransformationOperator3DnonUniform*)transform,gtrsf); + } else if ( transform->declaration().is(IfcSchema::IfcCartesianTransformationOperator2DnonUniform::Class()) ) { + Logger::Message(Logger::LOG_ERROR, "Unsupported MappingTarget:", transform); + return false; + } else if ( transform->declaration().is(IfcSchema::IfcCartesianTransformationOperator3D::Class()) ) { + gp_Trsf trsf; + IfcGeom::Kernel::convert((IfcSchema::IfcCartesianTransformationOperator3D*)transform,trsf); + gtrsf = trsf; + } else if ( transform->declaration().is(IfcSchema::IfcCartesianTransformationOperator2D::Class()) ) { + gp_Trsf2d trsf_2d; + IfcGeom::Kernel::convert((IfcSchema::IfcCartesianTransformationOperator2D*)transform,trsf_2d); + gtrsf = (gp_Trsf) trsf_2d; + } + IfcSchema::IfcRepresentationMap* map = l->MappingSource(); + IfcSchema::IfcAxis2Placement* placement = map->MappingOrigin(); + gp_Trsf trsf; + if (placement->declaration().is(IfcSchema::IfcAxis2Placement3D::Class())) { + IfcGeom::Kernel::convert((IfcSchema::IfcAxis2Placement3D*)placement,trsf); + } else { + gp_Trsf2d trsf_2d; + IfcGeom::Kernel::convert((IfcSchema::IfcAxis2Placement2D*)placement,trsf_2d); + trsf = trsf_2d; + } + gtrsf.Multiply(trsf); + + const IfcGeom::SurfaceStyle* mapped_item_style = get_style(l); + + const size_t previous_size = shapes.size(); + bool b = convert_shapes(map->MappedRepresentation(), shapes); + + for (size_t i = previous_size; i < shapes.size(); ++ i ) { + OpenCascadePlacement p(gtrsf); + shapes[i].prepend(&p); + + // Apply styles assigned to the mapped item only if on + // a more granular level no styles have been applied + if (!shapes[i].hasStyle()) { + shapes[i].setStyle(mapped_item_style); + } + } + + return b; +} + +bool IfcGeom::Kernel::convert(const IfcSchema::IfcRepresentation* l, ConversionResults& shapes) { + IfcSchema::IfcRepresentationItem::list::ptr items = l->Items(); + bool part_succes = false; + if ( items->size() ) { + for ( IfcSchema::IfcRepresentationItem::list::it it = items->begin(); it != items->end(); ++ it ) { + IfcSchema::IfcRepresentationItem* representation_item = *it; + if ( shape_type(representation_item) == ST_SHAPELIST ) { + part_succes |= convert_shapes(*it, shapes); + } else { + TopoDS_Shape s; + if (convert_shape(representation_item,s)) { + shapes.push_back(ConversionResult(representation_item->data().id(), new OpenCascadeShape(s), get_style(representation_item))); + part_succes |= true; + } + } + } + } + return part_succes; +} + +bool IfcGeom::Kernel::convert(const IfcSchema::IfcGeometricSet* l, ConversionResults& shapes) { + IfcEntityList::ptr elements = l->Elements(); + if ( !elements->size() ) return false; + bool part_succes = false; + const IfcGeom::SurfaceStyle* parent_style = get_style(l); + for ( IfcEntityList::it it = elements->begin(); it != elements->end(); ++ it ) { + IfcSchema::IfcGeometricSetSelect* element = *it; + TopoDS_Shape s; + if (convert_shape(element, s)) { + part_succes = true; + const IfcGeom::SurfaceStyle* style = 0; + if (element->declaration().is(IfcSchema::IfcPoint::Class())) { + style = get_style((IfcSchema::IfcPoint*) element); + } else if (element->declaration().is(IfcSchema::IfcCurve::Class())) { + style = get_style((IfcSchema::IfcCurve*) element); + } else if (element->declaration().is(IfcSchema::IfcSurface::Class())) { + style = get_style((IfcSchema::IfcSurface*) element); + } + shapes.push_back(ConversionResult(l->data().id(), new OpenCascadeShape(s), style ? style : parent_style)); + } + } + return part_succes; +} + +bool IfcGeom::Kernel::convert(const IfcSchema::IfcBlock* l, TopoDS_Shape& shape) { + const double dx = l->XLength() * getValue(GV_LENGTH_UNIT); + const double dy = l->YLength() * getValue(GV_LENGTH_UNIT); + const double dz = l->ZLength() * getValue(GV_LENGTH_UNIT); + + BRepPrimAPI_MakeBox builder(dx, dy, dz); + gp_Trsf trsf; + IfcGeom::Kernel::convert(l->Position(),trsf); + + // IfcCsgPrimitive3D.Position has unit scale factor + shape = builder.Solid().Moved(trsf); + + return true; +} + +bool IfcGeom::Kernel::convert(const IfcSchema::IfcRectangularPyramid* l, TopoDS_Shape& shape) { + const double dx = l->XLength() * getValue(GV_LENGTH_UNIT); + const double dy = l->YLength() * getValue(GV_LENGTH_UNIT); + const double dz = l->Height() * getValue(GV_LENGTH_UNIT); + + BRepPrimAPI_MakeWedge builder(dx, dz, dy, dx / 2., dy / 2., dx / 2., dy / 2.); + + gp_Trsf trsf1, trsf2; + trsf2.SetValues( + 1, 0, 0, 0, + 0, 0, 1, 0, + 0, 1, 0, 0 +#if OCC_VERSION_HEX < 0x60800 + , Precision::Angular(), Precision::Confusion() +#endif + ); + + IfcGeom::Kernel::convert(l->Position(), trsf1); + shape = BRepBuilderAPI_Transform(builder.Solid(), trsf1 * trsf2); + return true; +} + +bool IfcGeom::Kernel::convert(const IfcSchema::IfcRightCircularCylinder* l, TopoDS_Shape& shape) { + const double r = l->Radius() * getValue(GV_LENGTH_UNIT); + const double h = l->Height() * getValue(GV_LENGTH_UNIT); + + BRepPrimAPI_MakeCylinder builder(r, h); + gp_Trsf trsf; + IfcGeom::Kernel::convert(l->Position(),trsf); + + // IfcCsgPrimitive3D.Position has unit scale factor + shape = builder.Solid().Moved(trsf); + + return true; +} + +bool IfcGeom::Kernel::convert(const IfcSchema::IfcRightCircularCone* l, TopoDS_Shape& shape) { + const double r = l->BottomRadius() * getValue(GV_LENGTH_UNIT); + const double h = l->Height() * getValue(GV_LENGTH_UNIT); + + BRepPrimAPI_MakeCone builder(r, 0., h); + gp_Trsf trsf; + IfcGeom::Kernel::convert(l->Position(),trsf); + + // IfcCsgPrimitive3D.Position has unit scale factor + shape = builder.Solid().Moved(trsf); + + return true; +} + +bool IfcGeom::Kernel::convert(const IfcSchema::IfcSphere* l, TopoDS_Shape& shape) { + const double r = l->Radius() * getValue(GV_LENGTH_UNIT); + + BRepPrimAPI_MakeSphere builder(r); + gp_Trsf trsf; + IfcGeom::Kernel::convert(l->Position(),trsf); + + // IfcCsgPrimitive3D.Position has unit scale factor + shape = builder.Solid().Moved(trsf); + + return true; +} + +bool IfcGeom::Kernel::convert(const IfcSchema::IfcCsgSolid* l, TopoDS_Shape& shape) { + return convert_shape(l->TreeRootExpression(), shape); +} + +bool IfcGeom::Kernel::convert(const IfcSchema::IfcCurveBoundedPlane* l, TopoDS_Shape& face) { + gp_Pln pln; + if (!IfcGeom::Kernel::convert(l->BasisSurface(), pln)) { + return false; + } + + gp_Trsf trsf; + trsf.SetTransformation(pln.Position(), gp::XOY()); + + TopoDS_Wire outer; + if (!convert_wire(l->OuterBoundary(), outer)) { + return false; + } + + BRepBuilderAPI_MakeFace mf(outer); + + if (!mf.IsDone() || mf.Shape().IsNull()) { + Logger::Error("Invalid outer boundary:", l->OuterBoundary()); + return false; + } + + IfcSchema::IfcCurve::list::ptr boundaries = l->InnerBoundaries(); + + for (IfcSchema::IfcCurve::list::it it = boundaries->begin(); it != boundaries->end(); ++it) { + TopoDS_Wire inner; + if (convert_wire(*it, inner)) { + mf.Add(inner); + } + } + + ShapeFix_Shape sfs(mf.Face()); + sfs.Perform(); + + // `trsf` consitutes the placement of the plane and therefore has unit scale factor + face = TopoDS::Face(sfs.Shape()).Moved(trsf); + + return true; +} + +bool IfcGeom::Kernel::convert(const IfcSchema::IfcRectangularTrimmedSurface* l, TopoDS_Shape& face) { + if (!l->BasisSurface()->declaration().is(IfcSchema::IfcPlane::Class())) { + Logger::Message(Logger::LOG_ERROR, "Unsupported BasisSurface:", l->BasisSurface()); + return false; + } + gp_Pln pln; + IfcGeom::Kernel::convert((IfcSchema::IfcPlane*) l->BasisSurface(), pln); + + BRepBuilderAPI_MakeFace mf(pln, l->U1(), l->U2(), l->V1(), l->V2()); + + face = mf.Face(); + + return true; +} + +bool IfcGeom::Kernel::convert(const IfcSchema::IfcSurfaceCurveSweptAreaSolid* l, TopoDS_Shape& shape) { + gp_Trsf directrix; + TopoDS_Shape face; + TopoDS_Wire wire, section; + + if (!l->ReferenceSurface()->declaration().is(IfcSchema::IfcPlane::Class())) { + Logger::Message(Logger::LOG_WARNING, "Reference surface not supported", l->ReferenceSurface()); + return false; + } + + gp_Trsf trsf; + bool has_position = true; +#ifdef SCHEMA_IfcSweptAreaSolid_Position_IS_OPTIONAL + has_position = l->hasPosition(); +#endif + if (has_position) { + IfcGeom::Kernel::convert(l->Position(), trsf); + } + + if (!convert_face(l->SweptArea(), face) || + !convert_wire(l->Directrix(), wire) ) { + return false; + } + + gp_Pln pln; + gp_Pnt directrix_origin; + gp_Vec directrix_tangent; + bool directrix_on_plane = true; + IfcGeom::Kernel::convert((IfcSchema::IfcPlane*) l->ReferenceSurface(), pln); + + // As per Informal propositions 2: The Directrix shall lie on the ReferenceSurface. + // This is not always the case with the test files in the repository. I am not sure + // how to deal with this and whether my interpretation of the propositions is + // correct. However, if it has been asserted that the vertices of the directrix do + // not conform to the ReferenceSurface, the ReferenceSurface is ignored. + { + for (TopExp_Explorer exp(wire, TopAbs_VERTEX); exp.More(); exp.Next()) { + if (pln.Distance(BRep_Tool::Pnt(TopoDS::Vertex(exp.Current()))) > ALMOST_ZERO) { + directrix_on_plane = false; + Logger::Message(Logger::LOG_WARNING, "The Directrix does not lie on the ReferenceSurface", l); + break; + } + } + } + + { + TopExp_Explorer exp(wire, TopAbs_EDGE); + TopoDS_Edge edge = TopoDS::Edge(exp.Current()); + double u0, u1; + Handle(Geom_Curve) crv = BRep_Tool::Curve(edge, u0, u1); + crv->D1(u0, directrix_origin, directrix_tangent); + } + + if (pln.Axis().Direction().IsNormal(directrix_tangent, Precision::Approximation()) && directrix_on_plane) { + directrix.SetTransformation(gp_Ax3(directrix_origin, directrix_tangent, pln.Axis().Direction()), gp::XOY()); + } else { + directrix.SetTransformation(gp_Ax3(directrix_origin, directrix_tangent), gp::XOY()); + } + face = BRepBuilderAPI_Transform(face, directrix); + + // NB: Note that StartParam and EndParam param are ignored and the assumption is + // made that the parametric range over which to be swept matches the IfcCurve in + // its entirety. + BRepOffsetAPI_MakePipeShell builder(wire); + + { TopExp_Explorer exp(face, TopAbs_WIRE); + section = TopoDS::Wire(exp.Current()); } + + builder.Add(section); + builder.SetTransitionMode(BRepBuilderAPI_RightCorner); + if (directrix_on_plane) { + builder.SetMode(pln.Axis().Direction()); + } + builder.Build(); + builder.MakeSolid(); + shape = builder.Shape(); + + if (has_position) { + // IfcSweptAreaSolid.Position (trsf) is an IfcAxis2Placement3D + // and therefore has a unit scale factor + shape.Move(trsf); + } + + return true; +} + +namespace { + bool wire_is_c1_continuous(const TopoDS_Wire& w, double tol) { + // NB Note that c0 continuity is NOT checked! + + TopTools_IndexedDataMapOfShapeListOfShape map; + TopExp::MapShapesAndAncestors(w, TopAbs_VERTEX, TopAbs_EDGE, map); + for (int i = 1; i <= map.Extent(); ++i) { + const auto& li = map.FindFromIndex(i); + if (li.Extent() == 2) { + const TopoDS_Vertex& v = TopoDS::Vertex(map.FindKey(i)); + + const TopoDS_Edge& e0 = TopoDS::Edge(li.First()); + const TopoDS_Edge& e1 = TopoDS::Edge(li.Last()); + + double u0 = BRep_Tool::Parameter(v, e0); + double u1 = BRep_Tool::Parameter(v, e1); + + double _, __; + Handle(Geom_Curve) c0 = BRep_Tool::Curve(e0, _, __); + Handle(Geom_Curve) c1 = BRep_Tool::Curve(e1, _, __); + + gp_Pnt p; + gp_Vec v0, v1; + c0->D1(u0, p, v0); + c1->D1(u1, p, v1); + + if (1. - std::abs(v0.Normalized().Dot(v1.Normalized())) > tol) { + return false; + } + } + } + return true; + } +} + +bool IfcGeom::Kernel::convert(const IfcSchema::IfcSweptDiskSolid* l, TopoDS_Shape& shape) { + TopoDS_Wire wire, section1, section2; + + bool hasInnerRadius = l->hasInnerRadius(); + + if (!convert_wire(l->Directrix(), wire)) { + return false; + } + + gp_Ax2 directrix; + { + gp_Pnt directrix_origin; + gp_Vec directrix_tangent; + + TopoDS_Edge edge; + + // Find first edge + TopoDS_Vertex v0, v1; + TopExp::Vertices(wire, v0, v1); + TopTools_IndexedDataMapOfShapeListOfShape map; + TopExp::MapShapesAndAncestors(wire, TopAbs_VERTEX, TopAbs_EDGE, map); + if (map.Contains(v0) && map.FindFromKey(v0).Extent() == 1) { + edge = TopoDS::Edge(map.FindFromKey(v0).First()); + } else { + Logger::Error("Unable to locate first edge of:", l->Directrix()); + return false; + } + + double u0, u1; + Handle(Geom_Curve) crv = BRep_Tool::Curve(edge, u0, u1); + crv->D1(u0, directrix_origin, directrix_tangent); + directrix = gp_Ax2(directrix_origin, directrix_tangent); + } + + const double r1 = l->Radius() * getValue(GV_LENGTH_UNIT); + Handle(Geom_Circle) circle = new Geom_Circle(directrix, r1); + section1 = BRepBuilderAPI_MakeWire(BRepBuilderAPI_MakeEdge(circle)); + + if (hasInnerRadius) { + const double r2 = l->InnerRadius() * getValue(GV_LENGTH_UNIT); + if (r2 < getValue(GV_PRECISION)) { + // Subtraction of pipes with small radii is unstable. + hasInnerRadius = false; + } else { + Handle(Geom_Circle) circle2 = new Geom_Circle(directrix, r2); + section2 = BRepBuilderAPI_MakeWire(BRepBuilderAPI_MakeEdge(circle2)); + } + } + + // This is not used anymore, BRepBuilderAPI_RightCorner is always used now. + // const bool is_continuous = wire_is_c1_continuous(wire, 1.e-3); + + // NB: Note that StartParam and EndParam param are ignored and the assumption is + // made that the parametric range over which to be swept matches the IfcCurve in + // its entirety. + { BRepOffsetAPI_MakePipeShell builder(wire); + builder.Add(section1); + builder.SetTransitionMode(BRepBuilderAPI_RightCorner); + builder.Build(); + builder.MakeSolid(); + shape = builder.Shape(); } + + if (hasInnerRadius) { + BRepOffsetAPI_MakePipeShell builder(wire); + builder.Add(section2); + builder.SetTransitionMode(BRepBuilderAPI_RightCorner); + builder.Build(); + builder.MakeSolid(); + TopoDS_Shape inner = builder.Shape(); + + BRepAlgoAPI_Cut brep_cut(shape, inner); + bool is_valid = false; + if (brep_cut.IsDone()) { + TopoDS_Shape result = brep_cut; + + ShapeFix_Shape fix(result); + fix.Perform(); + result = fix.Shape(); + + is_valid = BRepCheck_Analyzer(result).IsValid() != 0; + if (is_valid) { + shape = result; + } + } + + if (!is_valid) { + Logger::Message(Logger::LOG_WARNING, "Failed to subtract inner radius void for:", l); + } + } + + return true; +} + +#ifdef SCHEMA_HAS_IfcCylindricalSurface + +bool IfcGeom::Kernel::convert(const IfcSchema::IfcCylindricalSurface* l, TopoDS_Shape& face) { + gp_Trsf trsf; + IfcGeom::Kernel::convert(l->Position(),trsf); + + // IfcElementarySurface.Position has unit scale factor +#if OCC_VERSION_HEX < 0x60502 + face = BRepBuilderAPI_MakeFace(new Geom_CylindricalSurface(gp::XOY(), l->Radius() * getValue(GV_LENGTH_UNIT))).Face().Moved(trsf); +#else + face = BRepBuilderAPI_MakeFace(new Geom_CylindricalSurface(gp::XOY(), l->Radius() * getValue(GV_LENGTH_UNIT)), getValue(GV_PRECISION)).Face().Moved(trsf); +#endif + return true; +} + +#endif + +#ifdef SCHEMA_HAS_IfcAdvancedBrep + +bool IfcGeom::Kernel::convert(const IfcSchema::IfcAdvancedBrep* l, TopoDS_Shape& shape) { + return convert(l->Outer(), shape); +} + +#endif + +#ifdef SCHEMA_HAS_IfcTriangulatedFaceSet + +bool IfcGeom::Kernel::convert(const IfcSchema::IfcTriangulatedFaceSet* l, TopoDS_Shape& shape) { + IfcSchema::IfcCartesianPointList3D* point_list = l->Coordinates(); + const std::vector< std::vector > coordinates = point_list->CoordList(); + std::vector points; + points.reserve(coordinates.size()); + for (std::vector< std::vector >::const_iterator it = coordinates.begin(); it != coordinates.end(); ++it) { + const std::vector& coords = *it; + if (coords.size() != 3) { + Logger::Message(Logger::LOG_ERROR, "Invalid dimensions encountered on Coordinates", l); + return false; + } + points.push_back(gp_Pnt(coords[0] * getValue(GV_LENGTH_UNIT), + coords[1] * getValue(GV_LENGTH_UNIT), + coords[2] * getValue(GV_LENGTH_UNIT))); + } + + std::vector< std::vector > indices = l->CoordIndex(); + + std::vector faces; + faces.reserve(indices.size()); + + for(std::vector< std::vector >::const_iterator it = indices.begin(); it != indices.end(); ++ it) { + const std::vector& tri = *it; + if (tri.size() != 3) { + Logger::Message(Logger::LOG_ERROR, "Invalid dimensions encountered on CoordIndex", l); + return false; + } + + const int min_index = *std::min_element(tri.begin(), tri.end()); + const int max_index = *std::max_element(tri.begin(), tri.end()); + + if (min_index < 1 || max_index > (int) points.size()) { + Logger::Message(Logger::LOG_ERROR, "Contents of CoordIndex out of bounds", l); + return false; + } + + const gp_Pnt& a = points[tri[0] - 1]; // account for zero- vs + const gp_Pnt& b = points[tri[1] - 1]; // one-based indices in + const gp_Pnt& c = points[tri[2] - 1]; // c++ and express + + TopoDS_Wire wire = BRepBuilderAPI_MakePolygon(a, b, c, true).Wire(); + TopoDS_Face face = BRepBuilderAPI_MakeFace(wire).Face(); + + TopoDS_Iterator face_it(face, false); + const TopoDS_Wire& w = TopoDS::Wire(face_it.Value()); + const bool reversed = w.Orientation() == TopAbs_REVERSED; + if (reversed) { + face.Reverse(); + } + + if (face_area(face) > getValue(GV_MINIMAL_FACE_AREA)) { + faces.push_back(face); + } + } + + if (faces.empty()) return false; + + bool valid_shell = false; + + // @todo Do this more efficiently by creating proper half-edge pairs. + BRepOffsetAPI_Sewing sewing_builder; + sewing_builder.SetTolerance(getValue(GV_POINT_EQUALITY_TOLERANCE)); + sewing_builder.SetMaxTolerance(getValue(GV_POINT_EQUALITY_TOLERANCE)); + sewing_builder.SetMinTolerance(getValue(GV_POINT_EQUALITY_TOLERANCE)); + + for (std::vector::const_iterator it = faces.begin(); it != faces.end(); ++it) { + sewing_builder.Add(*it); + } + + try { + sewing_builder.Perform(); + shape = sewing_builder.SewedShape(); + valid_shell = BRepCheck_Analyzer(shape).IsValid(); + } catch(...) {} + + if (valid_shell) { + try { + ShapeFix_Solid solid; + solid.LimitTolerance(getValue(GV_POINT_EQUALITY_TOLERANCE)); + TopoDS_Solid solid_shape = solid.SolidFromShell(TopoDS::Shell(shape)); + if (!solid_shape.IsNull()) { + try { + BRepClass3d_SolidClassifier classifier(solid_shape); + shape = solid_shape; + } catch (...) {} + } + } catch(...) {} + } else { + Logger::Message(Logger::LOG_WARNING, "Failed to sew faceset:", l); + } + + if (!valid_shell) { + TopoDS_Compound compound; + BRep_Builder builder; + builder.MakeCompound(compound); + + for (std::vector::const_iterator it = faces.begin(); it != faces.end(); ++it) { + builder.Add(compound, *it); + } + + shape = compound; + } + + return true; +} + +#endif diff --git a/src/ifcgeom/kernels/opencascade/IfcGeomWires.cpp b/src/ifcgeom/kernels/opencascade/IfcGeomWires.cpp_ similarity index 100% rename from src/ifcgeom/kernels/opencascade/IfcGeomWires.cpp rename to src/ifcgeom/kernels/opencascade/IfcGeomWires.cpp_ diff --git a/src/ifcgeom/kernels/opencascade/IfcRegister.cpp b/src/ifcgeom/kernels/opencascade/IfcRegister.cpp_ similarity index 100% rename from src/ifcgeom/kernels/opencascade/IfcRegister.cpp rename to src/ifcgeom/kernels/opencascade/IfcRegister.cpp_ diff --git a/src/ifcgeom/kernels/opencascade/OpenCascadeKernel.h b/src/ifcgeom/kernels/opencascade/OpenCascadeKernel.h new file mode 100644 index 0000000000..e274fe3ab5 --- /dev/null +++ b/src/ifcgeom/kernels/opencascade/OpenCascadeKernel.h @@ -0,0 +1,251 @@ +/******************************************************************************** + * * + * This file is part of IfcOpenShell. * + * * + * IfcOpenShell is free software: you can redistribute it and/or modify * + * it under the terms of the Lesser GNU General Public License as published by * + * the Free Software Foundation, either version 3.0 of the License, or * + * (at your option) any later version. * + * * + * IfcOpenShell is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * Lesser GNU General Public License for more details. * + * * + * You should have received a copy of the Lesser GNU General Public License * + * along with this program. If not, see . * + * * + ********************************************************************************/ + +#ifndef OPENCASCADEKERNEL_H +#define OPENCASCADEKERNEL_H + +#include + +static const double ALMOST_ZERO = 1.e-9; + +template +inline static bool ALMOST_THE_SAME(const T& a, const T& b, double tolerance=ALMOST_ZERO) { + return fabs(a-b) < tolerance; +} + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../../../ifcgeom/kernel_agnostic/AbstractKernel.h" + +#include "../../../ifcgeom/schema_agnostic/IfcGeomElement.h" +#include "../../../ifcgeom/schema_agnostic/IfcGeomRepresentation.h" +#include "../../../ifcgeom/schema_agnostic/ConversionResult.h" +#include "../../../ifcgeom/kernels/opencascade/IfcGeomShapeType.h" + +#include "../../../ifcgeom/schema_agnostic/opencascade/OpenCascadeConversionResult.h" + +#include "../../../ifcgeom/schema_agnostic/ifc_geom_api.h" + +#include "../../../ifcgeom/taxonomy.h" + +// Define this in case you want to conserve memory usage at all cost. This has been +// benchmarked extensively: https://github.com/IfcOpenShell/IfcOpenShell/pull/47 +// #define NO_CACHE + +#ifdef NO_CACHE + +#define IN_CACHE(T,E,t,e) +#define CACHE(T,E,e) + +#else + +#define IN_CACHE(T,E,t,e) std::map::const_iterator it = cache.T.find(E->data().id());\ +if ( it != cache.T.end() ) { e = it->second; return true; } +#define CACHE(T,E,e) cache.T[E->data().id()] = e; + +#endif + +namespace ifcopenshell { +namespace geometry { +namespace kernels { + + class IFC_GEOM_API geometry_exception : public std::exception { + protected: + std::string message; + public: + geometry_exception(const std::string& m) + : message(m) {} + virtual ~geometry_exception() throw () {} + virtual const char* what() const throw() { + return message.c_str(); + } + }; + + class IFC_GEOM_API too_many_faces_exception : public geometry_exception { + public: + too_many_faces_exception() + : geometry_exception("Too many faces for operation") {} + }; + + /* + class IFC_GEOM_API POSTFIX_SCHEMA(Cache) { + public: +#include "IfcRegisterCreateCache.h" + std::map Shape; + }; + */ + + + class IFC_GEOM_API OpenCascadeKernel : public AbstractKernel { + private: + /* + // faceset_helper traverses the forward instance references of IfcConnectedFaceSet and then provides a mapping + // M of (IfcCartesianPoint, IfcCartesianPoint) -> TopoDS_Edge, where M(a, b) is a partner of M(b, a), ie share + // the same underlying edge but with orientation reversed. This then later speeds op the process of creating a + // manifold Shell / Solid from this set of faces. Only IfcPolyLoop instances are used. Points within the tolerance + // threshiold are merged, so consider points a, b, c, distance(a, b) < eps then M(a, b) = Null, M(a, b) = M(a, c). + class faceset_helper { + private: + OpenCascadeKernel* kernel_; + std::set duplicates_; + std::map vertex_mapping_; + std::map, TopoDS_Edge> edges_; + double eps_; + bool non_manifold_; + + template + void loop_(IfcSchema::IfcCartesianPoint::list::ptr& ps, const Fn& callback) { + if (ps->size() < 3) { + return; + } + + auto a = *(ps->end() - 1); + auto A = a->data().id(); + for (auto& b : *ps) { + auto B = b->data().id(); + auto C = vertex_mapping_[A], D = vertex_mapping_[B]; + bool fwd = C < D; + if (!fwd) { + std::swap(C, D); + } + if (C != D) { + callback(C, D, fwd); + A = B; + } + } + } + public: + faceset_helper(OpenCascadeKernel* kernel, const IfcSchema::IfcConnectedFaceSet* l); + + ~faceset_helper(); + + bool non_manifold() const { return non_manifold_; } + bool& non_manifold() { return non_manifold_; } + + bool edge(const IfcSchema::IfcCartesianPoint* a, const IfcSchema::IfcCartesianPoint* b, TopoDS_Edge& e) { + int A = vertex_mapping_[a->data().id()]; + int B = vertex_mapping_[b->data().id()]; + if (A == B) { + return false; + } + + return edge(A, B, e); + } + + bool edge(int A, int B, TopoDS_Edge& e) { + auto it = edges_.find({ A, B }); + if (it == edges_.end()) { + return false; + } + e = it->second; + return true; + } + + bool wire(const IfcSchema::IfcPolyLoop* loop, TopoDS_Wire& wire) { + if (duplicates_.find(loop) != duplicates_.end()) { + return false; + } + BRep_Builder builder; + builder.MakeWire(wire); + int count = 0; + auto ps = loop->Polygon(); + loop_(ps, [this, &builder, &wire, &count](int A, int B, bool fwd) { + TopoDS_Edge e; + if (edge(A, B, e)) { + if (!fwd) { + e.Reverse(); + } + builder.Add(wire, e); + count += 1; + } + }); + if (count >= 3) { + wire.Closed(true); + + TopTools_ListOfShape results; + if (kernel_->wire_intersections(wire, results)) { + Logger::Warning("Self-intersections with " + boost::lexical_cast(results.Extent()) + " cycles detected", loop); + kernel_->select_largest(results, wire); + non_manifold_ = true; + } + + return true; + } else { + return false; + } + } + + double epsilon() const { + return eps_; + } + }; + +#ifndef NO_CACHE + POSTFIX_SCHEMA(Cache) cache; +#endif +*/ + + class faceset_helper {}; + + faceset_helper* faceset_helper_; + double precision_; + + public: + OpenCascadeKernel() + : AbstractKernel("opencascade") + , faceset_helper_(nullptr) {} + + OpenCascadeKernel(const OpenCascadeKernel& other) + : AbstractKernel("opencascade") { + *this = other; + } + + bool convert(const geometry::taxonomy::extrusion&, TopoDS_Shape&); + bool convert(const geometry::taxonomy::face&, TopoDS_Shape&); + bool convert(const geometry::taxonomy::matrix4&, gp_Trsf&); + bool convert(const geometry::taxonomy::direction3&, gp_Dir&); + }; + + /* + IfcUtil::IfcBaseClass* POSTFIX_SCHEMA(tesselate_)(const TopoDS_Shape& shape, double deflection); + IfcUtil::IfcBaseClass* POSTFIX_SCHEMA(serialise_)(const TopoDS_Shape& shape, bool advanced); + */ + +} +} +} + +#endif diff --git a/src/ifcgeom/schema/bind_convert_impl.i b/src/ifcgeom/schema/bind_convert_impl.i index abc10ab4f0..89af251420 100644 --- a/src/ifcgeom/schema/bind_convert_impl.i +++ b/src/ifcgeom/schema/bind_convert_impl.i @@ -5,7 +5,19 @@ #define BIND(T) \ if (l->declaration().is(IfcSchema::T::Class())) { \ try { \ - return map((IfcSchema::T*)l); \ + taxonomy::item* item = map((IfcSchema::T*)l); \ + item->instance = l; \ + try { \ + if (l->as()) { \ + auto style = find_style(l->as()); \ + if (style) { \ + ((taxonomy::geom_item*)item)->surface_style = as(map(style)); \ + } \ + } \ + } catch (const std::exception& e) { \ + Logger::Message(Logger::LOG_ERROR, std::string(e.what()) + "\nFailed to convert:", l); \ + } \ + return item; \ } catch (const std::exception& e) { \ Logger::Message(Logger::LOG_ERROR, std::string(e.what()) + "\nFailed to convert:", l); \ } \ diff --git a/src/ifcgeom/schema/mapping.cpp b/src/ifcgeom/schema/mapping.cpp index 64b3dc81e9..f21e58cf34 100644 --- a/src/ifcgeom/schema/mapping.cpp +++ b/src/ifcgeom/schema/mapping.cpp @@ -20,19 +20,69 @@ #include "mapping.h" #include "../../ifcparse/IfcLogger.h" +#include "../../ifcparse/IfcFile.h" using namespace IfcUtil; using namespace ifcopenshell::geometry; -#define mapping POSTFIX_SCHEMA(mapping) - -taxonomy::item* mapping::map(const IfcBaseClass* l) { -#include "bind_convert_impl.i" - Logger::Message(Logger::LOG_ERROR, "No operation defined for:", l); - return nullptr; +namespace { + struct POSTFIX_SCHEMA(factory_t) { + abstract_mapping* operator()(IfcParse::IfcFile* file) const { + ifcopenshell::geometry::POSTFIX_SCHEMA(mapping)* m = new ifcopenshell::geometry::POSTFIX_SCHEMA(mapping)(file); + return m; + } + }; } +void MAKE_INIT_FN(MappingImplementation)(ifcopenshell::geometry::impl::MappingFactoryImplementation* mapping) { + static const std::string schema_name = STRINGIFY(IfcSchema); + POSTFIX_SCHEMA(factory_t) factory; + mapping->bind(schema_name, factory); +} + +#define mapping POSTFIX_SCHEMA(mapping) + namespace { + // Hacks around not wanting to use if constexpr + template + class loop_to_face_upgrade { + public: + loop_to_face_upgrade(taxonomy::item*) {} + + operator bool() const { + return false; + } + + operator taxonomy::face() const { + throw taxonomy::topology_error(); + } + + operator T() const { + throw taxonomy::topology_error(); + } + }; + + template <> + class loop_to_face_upgrade { + private: + boost::optional face_; + public: + loop_to_face_upgrade(taxonomy::item* item) { + taxonomy::loop* loop = dynamic_cast(item); + if (loop) { + face_ = taxonomy::face(loop->instance, loop->matrix, *loop); + } + } + + operator bool() const { + return face_.is_initialized(); + } + + operator taxonomy::face() const { + return *face_; + } + }; + // A RAII-based mechanism to cast the conversion results // from map() into the right type expected by the higher // level typology items. An exception is thrown if the @@ -52,33 +102,601 @@ namespace { as(taxonomy::item* item) : item_(item) {} operator T() const { if (!item_) { - throw taxonomy::topology_error; + throw taxonomy::topology_error(); } T* t = dynamic_cast(item_); if (t) { return *t; } else { - if constexpr (std::is_same::value) { - topology::loop* loop = dynamic_cast(item_); - if (loop) { - return topology::face(loop.id, loop.matrix, *loop); + { + loop_to_face_upgrade upgrade(item_); + if (upgrade) { + return upgrade; } } - throw taxonomy::topology_error; + throw taxonomy::topology_error(); } } ~as() { - delete item; + delete item_; } }; }; taxonomy::item* mapping::map(const IfcSchema::IfcExtrudedAreaSolid* inst) { + // @todo length unit return new taxonomy::extrusion( - inst->data().id(), + inst, as(map(inst->Position())), as(map(inst->SweptArea())), as(map(inst->ExtrudedDirection())), inst->Depth() ); } + +taxonomy::item* mapping::map(const IfcSchema::IfcAxis2Placement3D* inst) { + // @todo length unit + return new taxonomy::matrix4(); +} + +IfcSchema::IfcProduct::list::ptr mapping::products_represented_by(const IfcSchema::IfcRepresentation* representation) { + IfcSchema::IfcProduct::list::ptr products(new IfcSchema::IfcProduct::list); + + IfcSchema::IfcProductRepresentation::list::ptr prodreps = representation->OfProductRepresentation(); + + for (IfcSchema::IfcProductRepresentation::list::it it = prodreps->begin(); it != prodreps->end(); ++it) { + // http://buildingsmart-tech.org/ifc/IFC2x3/TC1/html/ifcrepresentationresource/lexical/ifcproductrepresentation.htm + // IFC2x Edition 3 NOTE Users should not instantiate the entity IfcProductRepresentation from IFC2x Edition 3 onwards. + // It will be changed into an ABSTRACT supertype in future releases of IFC. + + // IfcProductRepresentation also lacks the INVERSE relation to IfcProduct + // Let's find the IfcProducts that reference the IfcProductRepresentation anyway + products->push((*it)->data().getInverse((&IfcSchema::IfcProduct::Class()), -1)->as()); + } + + IfcSchema::IfcRepresentationMap::list::ptr maps = representation->RepresentationMap(); + if (maps->size() == 1) { + IfcSchema::IfcRepresentationMap* rmap = *maps->begin(); + taxonomy::matrix4 origin = as(map(rmap->MappingOrigin())); + if (origin.components.isIdentity()) { + IfcSchema::IfcMappedItem::list::ptr items = rmap->MapUsage(); + for (IfcSchema::IfcMappedItem::list::it it = items->begin(); it != items->end(); ++it) { + IfcSchema::IfcMappedItem* item = *it; + if (item->StyledByItem()->size() != 0) continue; + + taxonomy::matrix4 target = as(map(item->MappingTarget())); + if (target.components.isIdentity()) { + continue; + } + + IfcSchema::IfcRepresentation::list::ptr reps = item->data().getInverse((&IfcSchema::IfcRepresentation::Class()), -1)->as(); + for (IfcSchema::IfcRepresentation::list::it jt = reps->begin(); jt != reps->end(); ++jt) { + IfcSchema::IfcRepresentation* rep = *jt; + if (rep->Items()->size() != 1) continue; + IfcSchema::IfcProductRepresentation::list::ptr prodreps_mapped = rep->OfProductRepresentation(); + for (IfcSchema::IfcProductRepresentation::list::it kt = prodreps_mapped->begin(); kt != prodreps_mapped->end(); ++kt) { + IfcSchema::IfcProduct::list::ptr ps = (*kt)->data().getInverse((&IfcSchema::IfcProduct::Class()), -1)->as(); + products->push(ps); + } + } + } + } + } + + return products; +} + +namespace { + IfcSchema::IfcProduct::list::ptr filter_products(IfcSchema::IfcProduct::list::ptr unfiltered_products, std::vector& filters) { + auto ifcproducts = IfcSchema::IfcProduct::list::ptr(new IfcSchema::IfcProduct::list); + for (IfcSchema::IfcProduct::list::it jt = unfiltered_products->begin(); jt != unfiltered_products->end(); ++jt) { + IfcSchema::IfcProduct* prod = *jt; + if (boost::all(filters, [prod](const filter_t& f) { return f(prod); })) { + ifcproducts->push(prod); + } + } + return ifcproducts; + } +} + +bool mapping::reuse_ok_(settings& s, const IfcSchema::IfcProduct::list::ptr& products) { + // With world coords enabled, object transformations are directly applied to + // the BRep. There is no way to re-use the geometry for multiple products. + if (s.get(settings::USE_WORLD_COORDS)) { + return false; + } + + std::set associated_single_materials; + + for (IfcSchema::IfcProduct::list::it it = products->begin(); it != products->end(); ++it) { + IfcSchema::IfcProduct* product = *it; + + if (!s.get(settings::DISABLE_OPENING_SUBTRACTIONS) && find_openings(product)->size()) { + return false; + } + + if (s.get(settings::APPLY_LAYERSETS)) { + IfcSchema::IfcRelAssociates::list::ptr associations = product->HasAssociations(); + for (IfcSchema::IfcRelAssociates::list::it jt = associations->begin(); jt != associations->end(); ++jt) { + IfcSchema::IfcRelAssociatesMaterial* assoc = (*jt)->as(); + if (assoc) { + if (assoc->RelatingMaterial()->declaration().is(IfcSchema::IfcMaterialLayerSetUsage::Class())) { + // TODO: Check whether single layer? + return false; + } + } + } + } + + // Note that this can be a nullptr (!), but the fact that set size should be one still holds + associated_single_materials.insert(get_single_material_association(product)); + if (associated_single_materials.size() > 1) return false; + } + + return associated_single_materials.size() == 1; +} + +IfcEntityList::ptr mapping::find_openings(IfcSchema::IfcProduct* product) { + + IfcEntityList::ptr openings(new IfcEntityList); + if (product->declaration().is(IfcSchema::IfcElement::Class()) && !product->declaration().is(IfcSchema::IfcOpeningElement::Class())) { + IfcSchema::IfcElement* element = (IfcSchema::IfcElement*)product; + openings = element->HasOpenings()->generalize(); + } + + // Is the IfcElement a decomposition of an IfcElement with any IfcOpeningElements? + IfcSchema::IfcObjectDefinition* obdef = product->as(); + for (;;) { + auto decomposes = obdef->Decomposes()->generalize(); + if (decomposes->size() != 1) break; + IfcSchema::IfcObjectDefinition* rel_obdef = (*decomposes->begin())->as()->RelatingObject(); + if (rel_obdef->declaration().is(IfcSchema::IfcElement::Class()) && !rel_obdef->declaration().is(IfcSchema::IfcOpeningElement::Class())) { + IfcSchema::IfcElement* element = (IfcSchema::IfcElement*)rel_obdef; + openings->push(element->HasOpenings()->generalize()); + } + + obdef = rel_obdef; + } + + return openings; +} + + +void mapping::get_representations(std::vector& tasks, std::vector& filters, settings& s) { + IfcSchema::IfcRepresentation::list::ptr representations(new IfcSchema::IfcRepresentation::list); + + std::set allowed_context_types; + allowed_context_types.insert("model"); + allowed_context_types.insert("plan"); + allowed_context_types.insert("notdefined"); + + std::set context_types; + if (!s.get(settings::EXCLUDE_SOLIDS_AND_SURFACES)) { + // Really this should only be 'Model', as per + // the standard 'Design' is deprecated. So, + // just for backwards compatibility: + context_types.insert("model"); + context_types.insert("design"); + // Some earlier (?) versions DDS-CAD output their own ContextTypes + context_types.insert("model view"); + context_types.insert("detail view"); + } + if (s.get(settings::INCLUDE_CURVES)) { + context_types.insert("plan"); + } + + IfcSchema::IfcGeometricRepresentationContext::list::it it; + IfcSchema::IfcGeometricRepresentationSubContext::list::it jt; + IfcSchema::IfcGeometricRepresentationContext::list::ptr contexts = + file_->instances_by_type(); + + IfcSchema::IfcGeometricRepresentationContext::list::ptr filtered_contexts(new IfcSchema::IfcGeometricRepresentationContext::list); + + for (it = contexts->begin(); it != contexts->end(); ++it) { + IfcSchema::IfcGeometricRepresentationContext* context = *it; + if (context->declaration().is(IfcSchema::IfcGeometricRepresentationSubContext::Class())) { + // Continue, as the list of subcontexts will be considered + // by the parent's context inverse attributes. + continue; + } + try { + if (context->hasContextType()) { + std::string context_type = context->ContextType(); + boost::to_lower(context_type); + + if (allowed_context_types.find(context_type) == allowed_context_types.end()) { + Logger::Warning(std::string("ContextType '") + context->ContextType() + "' not allowed:", context); + } + if (context_types.find(context_type) != context_types.end()) { + filtered_contexts->push(context); + } + } + } catch (const std::exception& e) { + Logger::Error(e); + } + } + + // In case no contexts are identified based on their ContextType, all contexts are + // considered. Note that sub contexts are excluded as they are considered later on. + if (filtered_contexts->size() == 0) { + for (it = contexts->begin(); it != contexts->end(); ++it) { + IfcSchema::IfcGeometricRepresentationContext* context = *it; + if (!context->declaration().is(IfcSchema::IfcGeometricRepresentationSubContext::Class())) { + filtered_contexts->push(context); + } + } + } + + for (it = filtered_contexts->begin(); it != filtered_contexts->end(); ++it) { + IfcSchema::IfcGeometricRepresentationContext* context = *it; + + representations->push(context->RepresentationsInContext()); + + IfcSchema::IfcGeometricRepresentationSubContext::list::ptr sub_contexts = context->HasSubContexts(); + for (jt = sub_contexts->begin(); jt != sub_contexts->end(); ++jt) { + representations->push((*jt)->RepresentationsInContext()); + } + // There is no need for full recursion as the following is governed by the schema: + // WR31: The parent context shall not be another geometric representation sub context. + } + + if (representations->size() == 0) { + Logger::Warning("No representations encountered in relevant contexts, using all"); + representations = file_->instances_by_type(); + } + + IfcSchema::IfcRepresentation::list::ptr ok_mapped_representations; + + int task_index = 0; + + for (auto representation : *representations) { + + // Init. the list of filtered IfcProducts for this representation + + // Include only the desired products for processing. + IfcSchema::IfcProduct::list::ptr ifcproducts = filter_products(products_represented_by(representation), filters); + + if (ifcproducts->size() == 0) { + continue; + } + + auto geometry_reuse_ok_for_current_representation_ = reuse_ok_(s, ifcproducts); + + IfcSchema::IfcRepresentationMap::list::ptr maps = representation->RepresentationMap(); + + if (!geometry_reuse_ok_for_current_representation_ && maps->size() == 1) { + // unfiltered_products contains products represented by this representation by means of mapped items. + // For example because of openings applied to products, reuse might not be acceptable and then the + // products will be processed by means of their immediate representation and not the mapped representation. + + // IfcRepresentationMaps are also used for IfcTypeProducts, so an additional check is performed whether the map + // is indeed used by IfcMappedItems. + IfcSchema::IfcRepresentationMap* map = *maps->begin(); + if (map->MapUsage()->size() > 0) { + continue; + } + } + + // Check if this represenation has (or will be) processed as part its mapped representation + bool representation_processed_as_mapped_item = false; + IfcSchema::IfcRepresentation* rep_mapped_to = representation_mapped_to(representation); + if (rep_mapped_to) { + representation_processed_as_mapped_item = geometry_reuse_ok_for_current_representation_ && ( + ok_mapped_representations->contains(rep_mapped_to) || reuse_ok_(s, filter_products(products_represented_by(rep_mapped_to), filters))); + } + + if (representation_processed_as_mapped_item) { + ok_mapped_representations->push(rep_mapped_to); + continue; + } + + geometry_conversion_task task; + task.index = task_index++; + task.representation = representation; + task.products = ifcproducts->generalize(); + + tasks.emplace_back(task); + } +} + +const IfcSchema::IfcMaterial* mapping::get_single_material_association(const IfcSchema::IfcProduct* product) { + IfcSchema::IfcMaterial* single_material = 0; + IfcSchema::IfcRelAssociatesMaterial::list::ptr associated_materials = product->HasAssociations()->as(); + if (associated_materials->size() == 1) { + IfcSchema::IfcMaterialSelect* associated_material = (*associated_materials->begin())->RelatingMaterial(); + single_material = associated_material->as(); + + // NB: Single-layer layersets are also considered, regardless of --enable-layerset-slicing, this + // in accordance with other viewers. + if (!single_material && associated_material->as()) { + IfcSchema::IfcMaterialLayerSet* layerset = associated_material->as()->ForLayerSet(); + if (layerset->MaterialLayers()->size() == 1) { + IfcSchema::IfcMaterialLayer* layer = (*layerset->MaterialLayers()->begin()); + if (layer->hasMaterial()) { + single_material = layer->Material(); + } + } + } + } + return single_material; +} + +IfcSchema::IfcRepresentation* mapping::representation_mapped_to(const IfcSchema::IfcRepresentation* representation) { + IfcSchema::IfcRepresentation* representation_mapped_to = 0; + IfcSchema::IfcRepresentationItem::list::ptr items = representation->Items(); + if (items->size() == 1) { + IfcSchema::IfcRepresentationItem* item = *items->begin(); + if (item->declaration().is(IfcSchema::IfcMappedItem::Class())) { + if (item->StyledByItem()->size() == 0) { + IfcSchema::IfcMappedItem* mapped_item = item->as(); + taxonomy::matrix4 target = as(map(mapped_item->MappingTarget())); + if (target.components.isIdentity()) { + IfcSchema::IfcRepresentationMap* rmap = mapped_item->MappingSource(); + taxonomy::matrix4 origin = as(map(rmap->MappingOrigin())); + if (origin.components.isIdentity()) { + representation_mapped_to = rmap->MappedRepresentation(); + } + } + } + } + } + return representation_mapped_to; +} + +namespace { + const IfcSchema::IfcRepresentationItem* find_item_carrying_style(const IfcSchema::IfcRepresentationItem* item) { + if (item->StyledByItem()->size()) { + return item; + } + + while (item->declaration().is(IfcSchema::IfcBooleanClippingResult::Class())) { + // All instantiations of IfcBooleanOperand (type of FirstOperand) are subtypes of + // IfcGeometricRepresentationItem + item = (IfcSchema::IfcGeometricRepresentationItem*) ((IfcSchema::IfcBooleanClippingResult*) item)->FirstOperand(); + if (item->StyledByItem()->size()) { + return item; + } + } + + // TODO: Ideally this would be done for other entities (such as IfcCsgSolid) as well. + // But neither are these very prevalent, nor does the current IfcOpenShell style + // mechanism enable to conveniently style subshapes, which would be necessary for + // distinctly styled union operands. + + return item; + } + + template + std::pair get_surface_style(const IfcSchema::IfcStyledItem* si) { +#ifdef SCHEMA_HAS_IfcStyleAssignmentSelect + IfcEntityList::ptr style_assignments = si->Styles(); + for (IfcEntityList::it kt = style_assignments->begin(); kt != style_assignments->end(); ++kt) { + if (!(*kt)->declaration().is(IfcSchema::IfcPresentationStyleAssignment::Class())) { + continue; + } + IfcSchema::IfcPresentationStyleAssignment* style_assignment = (IfcSchema::IfcPresentationStyleAssignment*) *kt; +#else + IfcSchema::IfcPresentationStyleAssignment::list::ptr style_assignments = si->Styles(); + for (IfcSchema::IfcPresentationStyleAssignment::list::it kt = style_assignments->begin(); kt != style_assignments->end(); ++kt) { + IfcSchema::IfcPresentationStyleAssignment* style_assignment = *kt; +#endif + IfcEntityList::ptr styles = style_assignment->Styles(); + for (IfcEntityList::it lt = styles->begin(); lt != styles->end(); ++lt) { + IfcUtil::IfcBaseClass* style = *lt; + if (style->declaration().is(IfcSchema::IfcSurfaceStyle::Class())) { + IfcSchema::IfcSurfaceStyle* surface_style = (IfcSchema::IfcSurfaceStyle*) style; + if (surface_style->Side() != IfcSchema::IfcSurfaceSide::IfcSurfaceSide_NEGATIVE) { + IfcEntityList::ptr styles_elements = surface_style->Styles(); + for (IfcEntityList::it mt = styles_elements->begin(); mt != styles_elements->end(); ++mt) { + if ((*mt)->declaration().is(T::Class())) { + return std::make_pair(surface_style, (T*)*mt); + } + } + } + } + } + } + + return std::make_pair(0, 0); + } + + const IfcSchema::IfcStyledItem* find_style(const IfcSchema::IfcRepresentationItem* representation_item) { + // For certain representation items, most notably boolean operands, + // a style definition might reside on one of its operands. + representation_item = find_item_carrying_style(representation_item); + + if (representation_item->as()) { + return representation_item->as(); + } + + IfcSchema::IfcStyledItem::list::ptr styled_items = representation_item->StyledByItem(); + if (styled_items->size()) { + // StyledByItem is a SET [0:1] OF IfcStyledItem, so we return after the first IfcStyledItem: + return *styled_items->begin(); + } + + return nullptr; + } + + bool process_colour(IfcSchema::IfcColourRgb* colour, double* rgb) { + if (colour != 0) { + rgb[0] = colour->Red(); + rgb[1] = colour->Green(); + rgb[2] = colour->Blue(); + } + return colour != 0; + } + + bool process_colour(IfcSchema::IfcNormalisedRatioMeasure* factor, double* rgb) { + if (factor != 0) { + const double f = *factor; + rgb[0] = rgb[1] = rgb[2] = f; + } + return factor != 0; + } + + bool process_colour(IfcSchema::IfcColourOrFactor* colour_or_factor, double* rgb) { + if (colour_or_factor == 0) { + return false; + } else if (colour_or_factor->declaration().is(IfcSchema::IfcColourRgb::Class())) { + return process_colour(static_cast(colour_or_factor), rgb); + } else if (colour_or_factor->declaration().is(IfcSchema::IfcNormalisedRatioMeasure::Class())) { + return process_colour(static_cast(colour_or_factor), rgb); + } else { + return false; + } + } +} + +taxonomy::item* mapping::map(const IfcSchema::IfcMaterial* material) { + IfcSchema::IfcMaterialDefinitionRepresentation::list::ptr defs = material->HasRepresentation(); + for (IfcSchema::IfcMaterialDefinitionRepresentation::list::it jt = defs->begin(); jt != defs->end(); ++jt) { + IfcSchema::IfcRepresentation::list::ptr reps = (*jt)->Representations(); + IfcSchema::IfcStyledItem::list::ptr styles(new IfcSchema::IfcStyledItem::list); + for (IfcSchema::IfcRepresentation::list::it it = reps->begin(); it != reps->end(); ++it) { + styles->push((**it).Items()->as()); + } + for (IfcSchema::IfcStyledItem::list::it it = styles->begin(); it != styles->end(); ++it) { + return map(*it); + } + } + + taxonomy::style* material_style = new taxonomy::style; + return material_style; + + // @todo + // IfcGeom::SurfaceStyle material_style = IfcGeom::SurfaceStyle(material->data().id(), material->Name()); + // return &(style_cache[material->data().id()] = material_style); +} + +taxonomy::item* mapping::map(const IfcSchema::IfcStyledItem* inst) { + static taxonomy::colour white = taxonomy::colour(1., 1., 1.); + + taxonomy::style* surface_style = new taxonomy::style; + + auto style_pair = get_surface_style(inst); + + IfcSchema::IfcSurfaceStyle* style = style_pair.first; + IfcSchema::IfcSurfaceStyleShading* shading = style_pair.second; + + surface_style->instance = style; + if (style->hasName()) { + surface_style->name = style->Name(); + } + + double rgb[3]; + if (process_colour(shading->SurfaceColour(), rgb)) { + surface_style->diffuse.emplace(); + (*surface_style->diffuse).components << rgb[0], rgb[1], rgb[2]; + } + + if (auto rendering_style = shading->as()) { + if (rendering_style->hasDiffuseColour() && process_colour(rendering_style->DiffuseColour(), rgb)) { + const taxonomy::colour& old_diffuse = surface_style->diffuse.get_value_or(white); + surface_style->diffuse.reset(taxonomy::colour(old_diffuse.r() * rgb[0], old_diffuse.g() * rgb[1], old_diffuse.b() * rgb[2])); + } + if (rendering_style->hasDiffuseTransmissionColour()) { + // Not supported + } + if (rendering_style->hasReflectionColour()) { + // Not supported + } + if (rendering_style->hasSpecularColour() && process_colour(rendering_style->SpecularColour(), rgb)) { + surface_style->specular.reset(taxonomy::colour(rgb[0], rgb[1], rgb[2])); + } + if (rendering_style->hasSpecularHighlight()) { + IfcSchema::IfcSpecularHighlightSelect* highlight = rendering_style->SpecularHighlight(); + if (highlight->declaration().is(IfcSchema::IfcSpecularRoughness::Class())) { + double roughness = *((IfcSchema::IfcSpecularRoughness*)highlight); + if (roughness >= 1e-9) { + surface_style->specularity.reset(1.0 / roughness); + } + } else if (highlight->declaration().is(IfcSchema::IfcSpecularExponent::Class())) { + surface_style->specularity.reset(*((IfcSchema::IfcSpecularExponent*)highlight)); + } + } + if (rendering_style->hasTransmissionColour()) { + // Not supported + } + if (rendering_style->hasTransparency()) { + const double d = rendering_style->Transparency(); + surface_style->transparency.reset(d); + } + } + + return surface_style; +} + + +taxonomy::item* mapping::map(const IfcBaseClass* l) { +#include "bind_convert_impl.i" + Logger::Message(Logger::LOG_ERROR, "No operation defined for:", l); + return nullptr; +} + +namespace { + IfcUtil::IfcBaseEntity* get_RelatingObject(IfcSchema::IfcRelDecomposes* decompose) { +#ifdef SCHEMA_IfcRelDecomposes_HAS_RelatingObject + return decompose->RelatingObject(); +#else + IfcSchema::IfcRelAggregates* aggr = decompose->as(); + if (aggr != nullptr) { + return aggr->RelatingObject(); + } + return nullptr; +#endif + } +} + + +IfcUtil::IfcBaseEntity* get_decomposing_entity(IfcUtil::IfcBaseEntity* inst, bool include_openings) { + IfcSchema::IfcObjectDefinition* parent = 0; + auto product = inst->as(); + if (!product) { + return parent; + } + + /* In case of an opening element, parent to the RelatingBuildingElement */ + if (include_openings && product->declaration().is(IfcSchema::IfcOpeningElement::Class())) { + IfcSchema::IfcOpeningElement* opening = (IfcSchema::IfcOpeningElement*)product; + IfcSchema::IfcRelVoidsElement::list::ptr voids = opening->VoidsElements(); + if (voids->size()) { + IfcSchema::IfcRelVoidsElement* ifc_void = *voids->begin(); + parent = ifc_void->RelatingBuildingElement(); + } + } else if (product->declaration().is(IfcSchema::IfcElement::Class())) { + IfcSchema::IfcElement* element = (IfcSchema::IfcElement*)product; + IfcSchema::IfcRelFillsElement::list::ptr fills = element->FillsVoids(); + /* In case of a RelatedBuildingElement parent to the opening element */ + if (fills->size() && include_openings) { + for (IfcSchema::IfcRelFillsElement::list::it it = fills->begin(); it != fills->end(); ++it) { + IfcSchema::IfcRelFillsElement* fill = *it; + IfcSchema::IfcObjectDefinition* ifc_objectdef = fill->RelatingOpeningElement(); + if (product == ifc_objectdef) continue; + parent = ifc_objectdef; + } + } + /* Else simply parent to the containing structure */ + if (!parent) { + IfcSchema::IfcRelContainedInSpatialStructure::list::ptr parents = element->ContainedInStructure(); + if (parents->size()) { + IfcSchema::IfcRelContainedInSpatialStructure* container = *parents->begin(); + parent = container->RelatingStructure(); + } + } + } + /* Parent decompositions to the RelatingObject */ + if (!parent) { + IfcEntityList::ptr parents = product->data().getInverse((&IfcSchema::IfcRelAggregates::Class()), -1); + parents->push(product->data().getInverse((&IfcSchema::IfcRelNests::Class()), -1)); + for (IfcEntityList::it it = parents->begin(); it != parents->end(); ++it) { + IfcSchema::IfcRelDecomposes* decompose = (IfcSchema::IfcRelDecomposes*)*it; + IfcUtil::IfcBaseEntity* ifc_objectdef; + ifc_objectdef = get_RelatingObject(decompose); + if (product == ifc_objectdef) continue; + parent = ifc_objectdef->as(); + } + } + + return parent; +} diff --git a/src/ifcgeom/schema/mapping.h b/src/ifcgeom/schema/mapping.h index 99d97be600..ff63308389 100644 --- a/src/ifcgeom/schema/mapping.h +++ b/src/ifcgeom/schema/mapping.h @@ -1,5 +1,6 @@ #include "../abstract_mapping.h" #include "../../ifcparse/macros.h" +#include "../../ifcparse/IfcFile.h" #define INCLUDE_SCHEMA(x) STRINGIFY(../../ifcparse/x.h) #include INCLUDE_SCHEMA(IfcSchema) @@ -13,7 +14,20 @@ namespace ifcopenshell { namespace geometry { class POSTFIX_SCHEMA(mapping) : public abstract_mapping { + private: + IfcParse::IfcFile* file_; + public: + POSTFIX_SCHEMA(mapping)(IfcParse::IfcFile* file) : file_(file) {} virtual ifcopenshell::geometry::taxonomy::item* map(const IfcUtil::IfcBaseClass*); + virtual void get_representations(std::vector& tasks, std::vector& filters, settings& s); + + const IfcSchema::IfcMaterial* get_single_material_association(const IfcSchema::IfcProduct* product); + IfcSchema::IfcRepresentation* representation_mapped_to(const IfcSchema::IfcRepresentation* representation); + IfcSchema::IfcProduct::list::ptr products_represented_by(const IfcSchema::IfcRepresentation* representation); + bool reuse_ok_(settings& s, const IfcSchema::IfcProduct::list::ptr& products); + IfcEntityList::ptr find_openings(IfcSchema::IfcProduct* product); + IfcUtil::IfcBaseEntity* get_decomposing_entity(IfcUtil::IfcBaseEntity* product, bool include_openings); + #include "bind_convert_decl.i" }; diff --git a/src/ifcgeom/schema/mapping.i b/src/ifcgeom/schema/mapping.i index b6a863d353..1c86d47634 100644 --- a/src/ifcgeom/schema/mapping.i +++ b/src/ifcgeom/schema/mapping.i @@ -126,4 +126,8 @@ BIND(IfcCartesianTransformationOperator2D); BIND(IfcCartesianTransformationOperator3D); BIND(IfcObjectPlacement); BIND(IfcVector); -BIND(IfcPlane); \ No newline at end of file +BIND(IfcPlane); + +BIND(IfcColourRgb); +BIND(IfcMaterial); +BIND(IfcStyledItem); diff --git a/src/ifcgeom/schema_agnostic/ConversionResult.h b/src/ifcgeom/schema_agnostic/ConversionResult.h index b7b9301e2e..ec88055893 100644 --- a/src/ifcgeom/schema_agnostic/ConversionResult.h +++ b/src/ifcgeom/schema_agnostic/ConversionResult.h @@ -21,12 +21,12 @@ #define IFCSHAPELIST_H #include "../../ifcgeom/schema_agnostic/IfcGeomRenderStyles.h" -#include "../../ifcgeom/schema_agnostic/IfcGeomIteratorSettings.h" +#include "../../ifcgeom/settings.h" +#include "../../ifcgeom/taxonomy.h" -namespace IfcGeom { +namespace ifcopenshell { namespace geometry { namespace Representation { - template class IFC_GEOM_API Triangulation; } @@ -44,8 +44,8 @@ namespace IfcGeom { class IFC_GEOM_API ConversionResultShape { public: - virtual void Triangulate(const IfcGeom::IteratorSettings & settings, const IfcGeom::ConversionResultPlacement* place, IfcGeom::Representation::Triangulation* t, int surface_style_id) const = 0; - virtual void Triangulate(const IfcGeom::IteratorSettings & settings, const IfcGeom::ConversionResultPlacement* place, IfcGeom::Representation::Triangulation* t, int surface_style_id) const = 0; + virtual void Triangulate(const ifcopenshell::geometry::settings & settings, const ifcopenshell::geometry::ConversionResultPlacement* place, ifcopenshell::geometry::Representation::Triangulation* t, int surface_style_id) const = 0; + virtual void Serialize(std::string&) const = 0; virtual ConversionResultShape* clone() const = 0; virtual int surface_genus() const = 0; @@ -57,16 +57,16 @@ namespace IfcGeom { int id; ConversionResultPlacement* placement; ConversionResultShape* shape; - const SurfaceStyle* style; + ifcopenshell::geometry::taxonomy::style style; public: - ConversionResult(int id, const ConversionResultPlacement* placement, const ConversionResultShape* shape, const SurfaceStyle* style) + ConversionResult(int id, const ConversionResultPlacement* placement, const ConversionResultShape* shape, const ifcopenshell::geometry::taxonomy::style& style) : id(id), placement(placement->clone()), shape(shape->clone()), style(style) {} ConversionResult(int id, const ConversionResultPlacement* placement, const ConversionResultShape* shape) - : id(id), placement(placement->clone()), shape(shape->clone()), style(0) {} - ConversionResult(int id, const ConversionResultShape* shape, const SurfaceStyle* style) + : id(id), placement(placement->clone()), shape(shape->clone()) {} + ConversionResult(int id, const ConversionResultShape* shape, const ifcopenshell::geometry::taxonomy::style& style) : id(id), placement(0), shape(shape->clone()), style(style) {} ConversionResult(int id, const ConversionResultShape* shape) - : id(id), placement(0), shape(shape->clone()), style(0) {} + : id(id), placement(0), shape(shape->clone()) {} void append(const ConversionResultPlacement* trsf) { if (placement == 0) { placement = trsf->clone(); @@ -83,12 +83,13 @@ namespace IfcGeom { } const ConversionResultShape* Shape() const { return shape; } const ConversionResultPlacement* Placement() const { return placement; } - bool hasStyle() const { return style != 0; } - const SurfaceStyle& Style() const { return *style; } - void setStyle(const SurfaceStyle* newStyle) { style = newStyle; } + // @todo + bool hasStyle() const { return style.diffuse.is_initialized(); } + const ifcopenshell::geometry::taxonomy::style& Style() const { return style; } + void setStyle(const ifcopenshell::geometry::taxonomy::style& newStyle) { style = newStyle; } int ItemId() const { return id; } }; typedef std::vector ConversionResults; -} +}} #endif diff --git a/src/ifcgeom/schema_agnostic/Converter.cpp b/src/ifcgeom/schema_agnostic/Converter.cpp new file mode 100644 index 0000000000..cee8ad501f --- /dev/null +++ b/src/ifcgeom/schema_agnostic/Converter.cpp @@ -0,0 +1,413 @@ +#include "Converter.h" + +#include "../../ifcgeom/schema_agnostic/IfcGeomElement.h" + +ifcopenshell::geometry::Converter::Converter(const std::string& geometry_library, IfcParse::IfcFile* file) { + kernel_ = kernels::impl::kernel_implementations().construct(geometry_library, file); + mapping_ = impl::mapping_implementations().construct(file); +} + +ifcopenshell::geometry::NativeElement* ifcopenshell::geometry::Converter::create_brep_for_representation_and_product( + const ifcopenshell::geometry::settings& settings, IfcUtil::IfcBaseEntity* representation, IfcUtil::IfcBaseEntity* product) { + + std::stringstream representation_id_builder; + + const std::string product_type = product->declaration().name(); + // @todo + element_settings s(settings, 1.0 /*getValue(GV_LENGTH_UNIT) */, product_type); + + int parent_id = -1; + try { + IfcUtil::IfcBaseEntity* parent_object = mapping_->get_decomposing_entity(product); + if (parent_object) { + parent_id = parent_object->data().id(); + } + } catch (const std::exception& e) { + Logger::Error(e); + } + + ConversionResultPlacement* trsf = nullptr; + try { + convert_placement(product, trsf); + } catch (const std::exception& e) { + Logger::Error(e); + } catch (...) { + Logger::Error("Failed to construct placement"); + } + + const std::string guid = product->get_value("GlobalId"); + const std::string name = product->get_value_or("Name", ""); + + representation_id_builder << representation->data().id(); + + ifcopenshell::geometry::Representation::BRep* shape; + ifcopenshell::geometry::ConversionResults shapes; + + auto rep_item = mapping_->map(representation); + auto placement = mapping_->map(product); + kernel_->convert(rep_item, shapes); + + shape = new ifcopenshell::geometry::Representation::BRep(s, representation_id_builder.str(), shapes); + + return new NativeElement( + product->data().id(), + parent_id, + name, + product_type, + guid, + // @todo + "", + trsf, + boost::shared_ptr(shape), + product + ); + + /* + std::stringstream representation_id_builder; + + representation_id_builder << representation->data().id(); + + ifcopenshell::geometry::kernels::Representation::BRep* shape; + ifcopenshell::geometry::kernels::ConversionResults shapes; + + if (!convert_shapes(representation, shapes)) { + return 0; + } + + if (settings.get(IteratorSettings::APPLY_LAYERSETS)) { + if (apply_layerset(product, shapes)) { + + IfcSchema::IfcRelAssociates::list::ptr associations = product->HasAssociations(); + for (IfcSchema::IfcRelAssociates::list::it it = associations->begin(); it != associations->end(); ++it) { + IfcSchema::IfcRelAssociatesMaterial* associates_material = (**it).as(); + if (associates_material) { + unsigned layerset_id = associates_material->RelatingMaterial()->data().id(); + representation_id_builder << "-layerset-" << layerset_id; + break; + } + } + + } + } + + bool material_style_applied = false; + + const IfcSchema::IfcMaterial* single_material = get_single_material_association(product); + if (single_material) { + const ifcopenshell::geometry::kernels::SurfaceStyle* s = get_style(single_material); + for (ifcopenshell::geometry::kernels::ConversionResults::iterator it = shapes.begin(); it != shapes.end(); ++it) { + if (!it->hasStyle() && s) { + it->setStyle(s); + material_style_applied = true; + } + } + } else { + bool some_items_without_style = false; + for (ifcopenshell::geometry::kernels::ConversionResults::iterator it = shapes.begin(); it != shapes.end(); ++it) { + if (!it->hasStyle()) { + some_items_without_style = true; + break; + } + } + if (some_items_without_style) { + Logger::Warning("No material and surface styles for:", product); + } + } + + if (material_style_applied) { + representation_id_builder << "-material-" << single_material->data().id(); + } + + ConversionResultPlacement* trsf = nullptr; + try { + convert_placement(product->ObjectPlacement(), trsf); + } catch (const std::exception& e) { + Logger::Error(e); + } catch (...) { + Logger::Error("Failed to construct placement"); + } + + // Does the IfcElement have any IfcOpenings? + // Note that openings for IfcOpeningElements are not processed + IfcSchema::IfcRelVoidsElement::list::ptr openings = find_openings(product)->as(); + + const std::string product_type = product->declaration().name(); + ElementSettings element_settings(settings, getValue(GV_LENGTH_UNIT), product_type); + + if (!settings.get(ifcopenshell::geometry::kernels::IteratorSettings::DISABLE_OPENING_SUBTRACTIONS) && openings && openings->size()) { + representation_id_builder << "-openings"; + for (IfcSchema::IfcRelVoidsElement::list::it it = openings->begin(); it != openings->end(); ++it) { + representation_id_builder << "-" << (*it)->data().id(); + } + + ifcopenshell::geometry::kernels::ConversionResults opened_shapes; + bool caught_error = false; + try { + convert_openings(product, openings, shapes, trsf, opened_shapes); + } catch (const std::exception& e) { + Logger::Message(Logger::LOG_ERROR, std::string("Error processing openings for: ") + e.what() + ":", product); + caught_error = true; + } catch (...) { + Logger::Message(Logger::LOG_ERROR, "Error processing openings for:", product); + } + + if (caught_error && opened_shapes.size() < shapes.size()) { + opened_shapes = shapes; + } + + if (settings.get(IteratorSettings::USE_WORLD_COORDS)) { + for (ifcopenshell::geometry::kernels::ConversionResults::iterator it = opened_shapes.begin(); it != opened_shapes.end(); ++it) { + it->prepend(trsf); + } + trsf = nullptr; + representation_id_builder << "-world-coords"; + } + shape = new ifcopenshell::geometry::kernels::Representation::BRep(element_settings, representation_id_builder.str(), opened_shapes); + } else if (settings.get(IteratorSettings::USE_WORLD_COORDS)) { + for (ifcopenshell::geometry::kernels::ConversionResults::iterator it = shapes.begin(); it != shapes.end(); ++it) { + it->prepend(trsf); + } + trsf = nullptr; + representation_id_builder << "-world-coords"; + shape = new ifcopenshell::geometry::kernels::Representation::BRep(element_settings, representation_id_builder.str(), shapes); + } else { + shape = new ifcopenshell::geometry::kernels::Representation::BRep(element_settings, representation_id_builder.str(), shapes); + } + + std::string context_string = ""; + if (representation->hasRepresentationIdentifier()) { + context_string = representation->RepresentationIdentifier(); + } else if (representation->ContextOfItems()->hasContextType()) { + context_string = representation->ContextOfItems()->ContextType(); + } + + auto elem = new NativeElement( + product->data().id(), + parent_id, + name, + product_type, + guid, + context_string, + trsf, + boost::shared_ptr(shape), + product + ); + + if (settings.get(IteratorSettings::VALIDATE_QUANTITIES)) { + validate_quantities(product, elem->geometry()); + } + + return elem; + + */ +} + +/* +template +ifcopenshell::geometry::kernels::NativeElement* ifcopenshell::geometry::kernels::AbstractKernel::create_brep_for_processed_representation( + const IteratorSettings& //* settings /, IfcSchema::IfcRepresentation* representation, IfcSchema::IfcProduct* product, + ifcopenshell::geometry::kernels::NativeElement* brep) { + int parent_id = -1; + try { + IfcUtil::IfcBaseEntity* parent_object = get_decomposing_entity(product); + if (parent_object && parent_object->as()) { + parent_id = parent_object->data().id(); + } + } catch (const std::exception& e) { + Logger::Error(e); + } + + const std::string name = product->hasName() ? product->Name() : ""; + const std::string guid = product->GlobalId(); + + ConversionResultPlacement* trsf = nullptr; + try { + convert_placement(product->ObjectPlacement(), trsf); + } catch (const std::exception& e) { + Logger::Error(e); + } catch (...) { + Logger::Error("Failed to construct placement"); + } + + std::string context_string = ""; + if (representation->hasRepresentationIdentifier()) { + context_string = representation->RepresentationIdentifier(); + } else if (representation->ContextOfItems()->hasContextType()) { + context_string = representation->ContextOfItems()->ContextType(); + } + + const std::string product_type = product->declaration().name(); + + return new NativeElement( + product->data().id(), + parent_id, + name, + product_type, + guid, + context_string, + trsf, + brep->geometry_pointer(), + product + ); +} +*/ +//#include "../../ifcparse/Ifc2x3.h" +//#include "../../ifcparse/Ifc4.h" +// +//// @todo remove +//#include "../../ifcgeom/schema_agnostic/opencascade/OpenCascadeConversionResult.h" +// +//#include +//#include +//#include +//#include +// +//IfcGeom::Kernel::Kernel(const std::string& geometry_library, IfcParse::IfcFile* file) { +// if (file != 0) { +// if (file->schema() == 0) { +// throw IfcParse::IfcException("No schema associated with file"); +// } +// +// const std::string& schema_name = file->schema()->name(); +// implementation_ = impl::kernel_implementations().construct(schema_name, geometry_library, file); +// } +//} +// +//int IfcGeom::Kernel::count(const ConversionResultShape* s_, int t_, bool unique) { +// // @todo make kernel agnostic +// const TopoDS_Shape& s = ((OpenCascadeShape*) s_)->shape(); +// TopAbs_ShapeEnum t = (TopAbs_ShapeEnum) t_; +// +// if (unique) { +// TopTools_IndexedMapOfShape map; +// TopExp::MapShapes(s, t, map); +// return map.Extent(); +// } else { +// int i = 0; +// TopExp_Explorer exp(s, t); +// for (; exp.More(); exp.Next()) { +// ++i; +// } +// return i; +// } +//} +// +// +//int IfcGeom::Kernel::surface_genus(const ConversionResultShape* s_) { +// // @todo make kernel agnostic +// const TopoDS_Shape& s = ((OpenCascadeShape*) s_)->shape(); +// OpenCascadeShape Ss(s); +// +// int nv = count(&Ss, (int) TopAbs_VERTEX, true); +// int ne = count(&Ss, (int) TopAbs_EDGE, true); +// int nf = count(&Ss, (int) TopAbs_FACE, true); +// +// const int euler = nv - ne + nf; +// const int genus = (2 - euler) / 2; +// +// return genus; +//} +// +//IfcGeom::impl::KernelFactoryImplementation& IfcGeom::impl::kernel_implementations() { +// static KernelFactoryImplementation impl; +// return impl; +//} +// +//extern void init_KernelImplementation_opencascade_Ifc2x3(IfcGeom::impl::KernelFactoryImplementation*); +//extern void init_KernelImplementation_opencascade_Ifc4(IfcGeom::impl::KernelFactoryImplementation*); +//#ifdef IFOPSH_USE_CGAL +//extern void init_KernelImplementation_cgal_Ifc2x3(IfcGeom::impl::KernelFactoryImplementation*); +//extern void init_KernelImplementation_cgal_Ifc4(IfcGeom::impl::KernelFactoryImplementation*); +//#endif +// +//IfcGeom::impl::KernelFactoryImplementation::KernelFactoryImplementation() { +// init_KernelImplementation_opencascade_Ifc2x3(this); +// init_KernelImplementation_opencascade_Ifc4(this); +//#ifdef IFOPSH_USE_CGAL +// init_KernelImplementation_cgal_Ifc2x3(this); +// init_KernelImplementation_cgal_Ifc4(this); +//#endif +//} +// +//void IfcGeom::impl::KernelFactoryImplementation::bind(const std::string& schema_name, const std::string& geometry_library, IfcGeom::impl::kernel_fn fn) { +// const std::string schema_name_lower = boost::to_lower_copy(schema_name); +// this->insert(std::make_pair(std::make_pair(schema_name_lower, geometry_library), fn)); +//} +// +//IfcGeom::Kernel* IfcGeom::impl::KernelFactoryImplementation::construct(const std::string& schema_name, const std::string& geometry_library, IfcParse::IfcFile* file) { +// const std::string schema_name_lower = boost::to_lower_copy(schema_name); +// std::map, IfcGeom::impl::kernel_fn>::const_iterator it; +// it = this->find(std::make_pair(schema_name_lower, geometry_library)); +// if (it == end()) { +// throw IfcParse::IfcException("No geometry kernel registered for " + schema_name); +// } +// return it->second(file); +//} +// +// +//IfcUtil::IfcBaseEntity* IfcGeom::Kernel::get_decomposing_entity(IfcUtil::IfcBaseEntity* inst, bool include_openings) { +// if (inst->as()) { +// return get_decomposing_entity_impl(inst->as(), include_openings); +// } else if (inst->as()) { +// return get_decomposing_entity_impl(inst->as(), include_openings); +// } else if (inst->declaration().name() == "IfcProject") { +// return nullptr; +// } else { +// throw IfcParse::IfcException("Unexpected entity " + inst->declaration().name()); +// } +//} +// +//namespace { +// template +// static std::map get_layers_impl(typename Schema::IfcProduct* prod) { +// std::map layers; +// if (prod->hasRepresentation()) { +// IfcEntityList::ptr r = IfcParse::traverse(prod->Representation()); +// typename Schema::IfcRepresentation::list::ptr representations = r->template as(); +// for (typename Schema::IfcRepresentation::list::it it = representations->begin(); it != representations->end(); ++it) { +// typename Schema::IfcPresentationLayerAssignment::list::ptr a = (*it)->LayerAssignments(); +// for (typename Schema::IfcPresentationLayerAssignment::list::it jt = a->begin(); jt != a->end(); ++jt) { +// layers[(*jt)->Name()] = *jt; +// } +// } +// } +// return layers; +// } +//} +// +//std::map IfcGeom::Kernel::get_layers(IfcUtil::IfcBaseEntity* inst) { +// if (inst->as()) { +// return get_layers_impl(inst->as()); +// } else if (inst->as()) { +// return get_layers_impl(inst->as()); +// } else { +// throw IfcParse::IfcException("Unexpected entity " + inst->declaration().name()); +// } +//} +// +//bool IfcGeom::Kernel::is_manifold(const ConversionResultShape* s_) { +// // @todo make kernel agnostic +// const TopoDS_Shape& a = ((OpenCascadeShape*) s_)->shape(); +// +// if (a.ShapeType() == TopAbs_COMPOUND || a.ShapeType() == TopAbs_SOLID) { +// TopoDS_Iterator it(a); +// for (; it.More(); it.Next()) { +// OpenCascadeShape s(it.Value()); +// if (!is_manifold(&s)) { +// return false; +// } +// } +// return true; +// } else { +// TopTools_IndexedDataMapOfShapeListOfShape map; +// TopExp::MapShapesAndAncestors(a, TopAbs_EDGE, TopAbs_FACE, map); +// +// for (int i = 1; i <= map.Extent(); ++i) { +// if (map.FindFromIndex(i).Extent() != 2) { +// return false; +// } +// } +// +// return true; +// } +//} diff --git a/src/ifcgeom/schema_agnostic/Kernel.h b/src/ifcgeom/schema_agnostic/Converter.h similarity index 58% rename from src/ifcgeom/schema_agnostic/Kernel.h rename to src/ifcgeom/schema_agnostic/Converter.h index abb484189b..06cd31ad3b 100644 --- a/src/ifcgeom/schema_agnostic/Kernel.h +++ b/src/ifcgeom/schema_agnostic/Converter.h @@ -2,19 +2,21 @@ #define ITERATOR_KERNEL_H #include "../../ifcparse/IfcFile.h" -#include "../../ifcgeom/schema_agnostic/IfcGeomIteratorSettings.h" +#include "../../ifcgeom/settings.h" #include "../../ifcgeom/schema_agnostic/ConversionResult.h" +#include "../../ifcgeom/abstract_mapping.h" +#include "../../ifcgeom/kernel_agnostic/AbstractKernel.h" #include -namespace IfcGeom { +namespace ifcopenshell { namespace geometry { - template class NativeElement; - class Kernel { + class Converter { private: - Kernel* implementation_; + abstract_mapping* mapping_; + kernels::AbstractKernel* kernel_; public: // Tolerances and settings for various geometrical operations: @@ -47,10 +49,13 @@ namespace IfcGeom { GV_DIMENSIONALITY }; - Kernel(const std::string& geometry_library, IfcParse::IfcFile* file_ = 0); + Converter(const std::string& geometry_library, IfcParse::IfcFile* file); - virtual ~Kernel() {} + ~Converter() {} + abstract_mapping* mapping() const { return mapping_; } + + /* virtual void setValue(GeomValue var, double value) { implementation_->setValue(var, value); } @@ -58,43 +63,42 @@ namespace IfcGeom { virtual double getValue(GeomValue var) const { return implementation_->getValue(var); } + */ + /* virtual NativeElement* convert( const IteratorSettings& settings, IfcUtil::IfcBaseClass* representation, IfcUtil::IfcBaseClass* product) { return implementation_->convert(settings, representation, product); } + */ - virtual ConversionResults convert(IfcUtil::IfcBaseClass* item) { - return implementation_->convert(item); + ifcopenshell::geometry::ConversionResults convert(IfcUtil::IfcBaseClass* item) { + auto geom_item = mapping_->map(item); + ifcopenshell::geometry::ConversionResults results; + kernel_->convert(geom_item, results); + return results; } - virtual bool convert_placement(IfcUtil::IfcBaseClass* item, ConversionResultPlacement*& trsf) { - return implementation_->convert_placement(item, trsf); + bool convert_placement(IfcUtil::IfcBaseClass* item, ifcopenshell::geometry::ConversionResultPlacement*& trsf) { + throw std::runtime_error("not implemented"); + // return implementation_->convert_placement(item, trsf); } - static int count(const ConversionResultShape*, int, bool unique=false); - static int surface_genus(const ConversionResultShape*); + ifcopenshell::geometry::NativeElement* create_brep_for_representation_and_product(const ifcopenshell::geometry::settings& settings, IfcUtil::IfcBaseEntity* representation, IfcUtil::IfcBaseEntity* product); + ifcopenshell::geometry::NativeElement* create_brep_for_processed_representation(const ifcopenshell::geometry::settings& settings, IfcUtil::IfcBaseEntity* representation, IfcUtil::IfcBaseEntity* product, ifcopenshell::geometry::NativeElement* brep); - static bool is_manifold(const ConversionResultShape*); + /* + static int count(const ifcopenshell::geometry::ConversionResultShape*, int, bool unique=false); + static int surface_genus(const ifcopenshell::geometry::ConversionResultShape*); + + static bool is_manifold(const ifcopenshell::geometry::ConversionResultShape*); static IfcUtil::IfcBaseEntity* get_decomposing_entity(IfcUtil::IfcBaseEntity*, bool include_openings=true); static std::map get_layers(IfcUtil::IfcBaseEntity*); static IfcEntityList::ptr find_openings(IfcUtil::IfcBaseEntity* product); + */ }; - - namespace impl { - typedef boost::function1 kernel_fn; - - class KernelFactoryImplementation : public std::map, kernel_fn> { - public: - KernelFactoryImplementation(); - void bind(const std::string& schema_name, const std::string& geometry_library, kernel_fn); - Kernel* construct(const std::string& schema_name, const std::string& geometry_library, IfcParse::IfcFile*); - }; - - KernelFactoryImplementation& kernel_implementations(); - } -} +}} #endif \ No newline at end of file diff --git a/src/ifcgeom/schema_agnostic/IfcGeomElement.h b/src/ifcgeom/schema_agnostic/IfcGeomElement.h index 0cefed19de..d51329a342 100644 --- a/src/ifcgeom/schema_agnostic/IfcGeomElement.h +++ b/src/ifcgeom/schema_agnostic/IfcGeomElement.h @@ -27,18 +27,17 @@ #include "../../ifcparse/Argument.h" #include "../../ifcgeom/schema_agnostic/IfcGeomRepresentation.h" -#include "../../ifcgeom/schema_agnostic/IfcGeomIteratorSettings.h" +#include "../../ifcgeom/settings.h" #include "ifc_geom_api.h" -namespace IfcGeom { +namespace ifcopenshell { namespace geometry { - template class Matrix { private: - std::vector

_data; + std::vector _data; public: - Matrix(const ElementSettings& settings, const IfcGeom::ConversionResultPlacement* trsf) { + Matrix(const element_settings& settings, const ConversionResultPlacement* trsf) { // Convert the gp_Trsf into a 4x3 Matrix // Note that in case the CONVERT_BACK_UNITS setting is enabled // the translation component of the matrix needs to be divided @@ -49,30 +48,29 @@ namespace IfcGeom { const double trsf_value = (trsf == nullptr) ? (i == j ? 1. : 0.) : trsf->Value(j,i); - const double matrix_value = (i == 4 && settings.get(IteratorSettings::CONVERT_BACK_UNITS)) + const double matrix_value = (i == 4 && settings.get(settings::CONVERT_BACK_UNITS)) ? trsf_value / settings.unit_magnitude() : trsf_value; - _data.push_back(static_cast

(matrix_value)); + _data.push_back(static_cast(matrix_value)); } } } - const std::vector

& data() const { return _data; } + const std::vector& data() const { return _data; } }; - template class Transformation { private: - ElementSettings settings_; + element_settings settings_; ConversionResultPlacement* trsf_; - Matrix

matrix_; + Matrix matrix_; public: - Transformation(const ElementSettings& settings, const IfcGeom::ConversionResultPlacement* trsf) + Transformation(const element_settings& settings, const ConversionResultPlacement* trsf) : settings_(settings) , trsf_(trsf ? trsf->clone() : nullptr) , matrix_(settings, trsf) {} - const IfcGeom::ConversionResultPlacement* data() const { return trsf_; } - const Matrix

& matrix() const { return matrix_; } + const ConversionResultPlacement* data() const { return trsf_; } + const Matrix& matrix() const { return matrix_; } Transformation inverted() const { return Transformation(settings_, trsf_->inverted()); @@ -83,7 +81,6 @@ namespace IfcGeom { } }; - template class Element { private: int _id; @@ -93,17 +90,17 @@ namespace IfcGeom { std::string _guid; std::string _context; std::string _unique_id; - Transformation _transformation; + Transformation _transformation; IfcUtil::IfcBaseEntity* product_; - std::vector*> _parents; + std::vector _parents; public: - friend bool operator == (const Element & element1, const Element & element2) { + friend bool operator == (const Element & element1, const Element & element2) { return element1.id() == element2.id(); } // Use the id to compare, or the elevation is the elements are IfcBuildingStoreys and the elevation is set - friend bool operator < (const Element & element1, const Element & element2) { + friend bool operator < (const Element & element1, const Element & element2) { if (element1.type() == "IfcBuildingStorey" && element2.type() == "IfcBuildingStorey") { size_t attr_index = element1.product()->declaration().attribute_index("Elevation"); Argument* elev_attr1 = element1.product()->data().getArgument(attr_index); @@ -127,13 +124,13 @@ namespace IfcGeom { const std::string& guid() const { return _guid; } const std::string& context() const { return _context; } const std::string& unique_id() const { return _unique_id; } - const Transformation& transformation() const { return _transformation; } + const Transformation& transformation() const { return _transformation; } IfcUtil::IfcBaseEntity* product() const { return product_; } - const std::vector*> parents() const { return _parents; } - void SetParents(std::vector*> newparents) { _parents = newparents; } + const std::vector parents() const { return _parents; } + void SetParents(std::vector newparents) { _parents = newparents; } - Element(const ElementSettings& settings, int id, int parent_id, const std::string& name, const std::string& type, - const std::string& guid, const std::string& context, const IfcGeom::ConversionResultPlacement* trsf, IfcUtil::IfcBaseEntity* product) + Element(const element_settings& settings, int id, int parent_id, const std::string& name, const std::string& type, + const std::string& guid, const std::string& context, const ConversionResultPlacement* trsf, IfcUtil::IfcBaseEntity* product) : _id(id), _parent_id(parent_id), _name(name), _type(type), _guid(guid), _context(context), _transformation(settings, trsf) , product_(product) { @@ -162,17 +159,16 @@ namespace IfcGeom { virtual ~Element() {} }; - template - class NativeElement : public Element { + class NativeElement : public Element { private: boost::shared_ptr _geometry; public: const boost::shared_ptr& geometry_pointer() const { return _geometry; } const Representation::BRep& geometry() const { return *_geometry; } NativeElement(int id, int parent_id, const std::string& name, const std::string& type, const std::string& guid, - const std::string& context, const IfcGeom::ConversionResultPlacement* trsf, const boost::shared_ptr& geometry, + const std::string& context, const ConversionResultPlacement* trsf, const boost::shared_ptr& geometry, IfcUtil::IfcBaseEntity* product) - : Element(geometry->settings() ,id, parent_id, name, type, guid, context, trsf, product) + : Element(geometry->settings() ,id, parent_id, name, type, guid, context, trsf, product) , _geometry(geometry) {} @@ -184,19 +180,18 @@ namespace IfcGeom { NativeElement& operator=(const NativeElement& other); }; - template - class TriangulationElement : public Element { + class TriangulationElement : public Element { private: - boost::shared_ptr< Representation::Triangulation

> _geometry; + boost::shared_ptr _geometry; public: - const Representation::Triangulation

& geometry() const { return *_geometry; } - const boost::shared_ptr< Representation::Triangulation

>& geometry_pointer() const { return _geometry; } - TriangulationElement(const NativeElement& shape_model) - : Element(shape_model) - , _geometry(boost::shared_ptr >(new Representation::Triangulation

(shape_model.geometry()))) + const Representation::Triangulation& geometry() const { return *_geometry; } + const boost::shared_ptr< Representation::Triangulation >& geometry_pointer() const { return _geometry; } + TriangulationElement(const NativeElement& shape_model) + : Element(shape_model) + , _geometry(boost::shared_ptr(new Representation::Triangulation(shape_model.geometry()))) {} - TriangulationElement(const Element& element, const boost::shared_ptr >& geometry) - : Element(element) + TriangulationElement(const Element& element, const boost::shared_ptr& geometry) + : Element(element) , _geometry(geometry) {} private: @@ -204,14 +199,13 @@ namespace IfcGeom { TriangulationElement& operator=(const TriangulationElement& other); }; - template - class SerializedElement : public Element { + class SerializedElement : public Element { private: Representation::Serialization* _geometry; public: const Representation::Serialization& geometry() const { return *_geometry; } - SerializedElement(const NativeElement& shape_model) - : Element(shape_model) + SerializedElement(const NativeElement& shape_model) + : Element(shape_model) , _geometry(new Representation::Serialization(shape_model.geometry())) {} virtual ~SerializedElement() { @@ -221,6 +215,6 @@ namespace IfcGeom { SerializedElement(const SerializedElement& other); SerializedElement& operator=(const SerializedElement& other); }; -} +}} #endif diff --git a/src/ifcgeom/schema_agnostic/IfcGeomFilter.h b/src/ifcgeom/schema_agnostic/IfcGeomFilter.h index 1d457abeb1..523cc0b8f0 100644 --- a/src/ifcgeom/schema_agnostic/IfcGeomFilter.h +++ b/src/ifcgeom/schema_agnostic/IfcGeomFilter.h @@ -23,8 +23,9 @@ #ifndef IFCGEOMFILTER_H #define IFCGEOMFILTER_H -#include "../../ifcgeom/schema_agnostic/Kernel.h" +#include "../../ifcgeom/kernel_agnostic/AbstractKernel.h" #include "../../ifcparse/IfcFile.h" +#include "../../ifcgeom/abstract_mapping.h" #include #include @@ -65,7 +66,11 @@ namespace IfcGeom { bool traverse_match(IfcUtil::IfcBaseEntity* prod, const filter_t& pred) const { IfcUtil::IfcBaseEntity* parent, *current = prod; - while ((parent = IfcGeom::Kernel::get_decomposing_entity(current, traverse_openings)) != nullptr) { + // @todo examine if this can indeed be static. For now usage is only + // in IfcConvert so invocation is bound to a single file with a single + // schema. + static auto mapping = ifcopenshell::geometry::impl::mapping_implementations().construct(prod->data().file); + while ((parent = mapping->get_decomposing_entity(current, traverse_openings)) != nullptr) { if (pred(parent)) { return true; } @@ -170,7 +175,8 @@ namespace IfcGeom { : wildcard_filter(include, traverse, patterns) {} bool match(IfcUtil::IfcBaseEntity* prod) const { - layer_map_t layers = IfcGeom::Kernel::get_layers(prod); + static auto mapping = ifcopenshell::geometry::impl::mapping_implementations().construct(prod->data().file); + layer_map_t layers = mapping->get_layers(prod); return std::find_if(layers.begin(), layers.end(), wildcards_match(values)) != layers.end(); } diff --git a/src/ifcgeom/schema_agnostic/IfcGeomIterator.h b/src/ifcgeom/schema_agnostic/IfcGeomIterator.h deleted file mode 100644 index 4b4fe95f2e..0000000000 --- a/src/ifcgeom/schema_agnostic/IfcGeomIterator.h +++ /dev/null @@ -1,130 +0,0 @@ -/******************************************************************************** - * * - * This file is part of IfcOpenShell. * - * * - * IfcOpenShell is free software: you can redistribute it and/or modify * - * it under the terms of the Lesser GNU General Public License as published by * - * the Free Software Foundation, either version 3.0 of the License, or * - * (at your option) any later version. * - * * - * IfcOpenShell is distributed in the hope that it will be useful, * - * but WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * - * Lesser GNU General Public License for more details. * - * * - * You should have received a copy of the Lesser GNU General Public License * - * along with this program. If not, see . * - * * - ********************************************************************************/ - -/******************************************************************************** - * * - * Geometrical data in an IFC file consists of shapes (IfcShapeRepresentation) * - * and instances (SUBTYPE OF IfcBuildingElement e.g. IfcWindow). * - * * - * IfcGeom::Representation::Triangulation is a class that represents a * - * triangulated IfcShapeRepresentation. * - * Triangulation.verts is a 1 dimensional vector of float defining the * - * cartesian coordinates of the vertices of the triangulated shape in the * - * format of [x1,y1,z1,..,xn,yn,zn] * - * Triangulation.faces is a 1 dimensional vector of int containing the * - * indices of the triangles referencing positions in Triangulation.verts * - * Triangulation.edges is a 1 dimensional vector of int in {0,1} that dictates* - * the visibility of the edges that span the faces in Triangulation.faces * - * * - * IfcGeom::Element represents the actual IfcBuildingElements. * - * IfcGeomObject.name is the GUID of the element * - * IfcGeomObject.type is the datatype of the element e.g. IfcWindow * - * IfcGeomObject.mesh is a pointer to an IfcMesh * - * IfcGeomObject.transformation.matrix is a 4x3 matrix that defines the * - * orientation and translation of the mesh in relation to the world origin * - * * - * IfcGeom::Iterator::initialize() * - * finds the most suitable representation contexts. Returns true iff * - * at least a single representation will process successfully * - * * - * IfcGeom::Iterator::get() * - * returns a pointer to the current IfcGeom::Element * - * * - * IfcGeom::Iterator::next() * - * returns true iff a following entity is available for a successive call to * - * IfcGeom::Iterator::get() * - * * - * IfcGeom::Iterator::progress() * - * returns an int in [0..100] that indicates the overall progress * - * * - ********************************************************************************/ - -#ifndef IFCGEOMITERATOR_H -#define IFCGEOMITERATOR_H - -#include "../../ifcgeom/schema_agnostic/IteratorImplementation.h" - -// The infamous min & max Win32 #defines can leak here from OCE depending on the build configuration -#ifdef min -#undef min -#endif -#ifdef max -#undef max -#endif - -namespace IfcGeom { - - template - class Iterator { - private: - Iterator(const Iterator&); // N/I - Iterator& operator=(const Iterator&); // N/I - - IfcParse::IfcFile* file_; - IfcGeom::IteratorSettings settings_; - std::vector filters_; - - IteratorImplementation* implementation_; - - public: - Iterator(const IfcGeom::IteratorSettings& settings, IfcParse::IfcFile* file, const std::string& geometry_library="opencascade", int num_threads = 1) - : file_(file) - , settings_(settings) - { - implementation_ = iterator_implementations().construct(file_->schema()->name(), geometry_library, settings, file, filters_, num_threads); - } - - Iterator(const IfcGeom::IteratorSettings& settings, IfcParse::IfcFile* file, const std::vector& filters, const std::string& geometry_library = "opencascade", int num_threads = 1) - : file_(file) - , settings_(settings) - , filters_(filters) - { - implementation_ = iterator_implementations().construct(file_->schema()->name(), geometry_library, settings, file, filters_, num_threads); - } - - bool initialize() { - return implementation_->initialize(); - } - - int progress() const { return implementation_->progress(); } - - void compute_bounds() { implementation_->compute_bounds(); } - - const gp_XYZ& bounds_min() const { return implementation_->bounds_min(); } - const gp_XYZ& bounds_max() const { return implementation_->bounds_max(); } - - const std::string& unit_name() const { return implementation_->getUnitName(); } - - double unit_magnitude() const { return implementation_->getUnitMagnitude(); } - - IfcParse::IfcFile* file() const { return implementation_->file(); } - - IfcUtil::IfcBaseClass* next() const { return implementation_->next(); } - - Element* get() { return implementation_->get(); } - - NativeElement* get_native() { return implementation_->get_native(); } - - const Element* get_object(int id) { return implementation_->get_object(id); } - - IfcUtil::IfcBaseClass* create() { return implementation_->create(); } - }; -} - -#endif diff --git a/src/ifcgeom/schema_agnostic/IfcGeomIteratorImplementation.cpp b/src/ifcgeom/schema_agnostic/IfcGeomIteratorImplementation.cpp new file mode 100644 index 0000000000..6340b79d04 --- /dev/null +++ b/src/ifcgeom/schema_agnostic/IfcGeomIteratorImplementation.cpp @@ -0,0 +1 @@ +#include "IfcGeomIteratorImplementation.h" diff --git a/src/ifcgeom/schema_agnostic/IfcGeomIteratorImplementation.h b/src/ifcgeom/schema_agnostic/IfcGeomIteratorImplementation.h new file mode 100644 index 0000000000..85d436644b --- /dev/null +++ b/src/ifcgeom/schema_agnostic/IfcGeomIteratorImplementation.h @@ -0,0 +1,617 @@ +/******************************************************************************** + * * + * This file is part of IfcOpenShell. * + * * + * IfcOpenShell is free software: you can redistribute it and/or modify * + * it under the terms of the Lesser GNU General Public License as published by * + * the Free Software Foundation, either version 3.0 of the License, or * + * (at your option) any later version. * + * * + * IfcOpenShell is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * Lesser GNU General Public License for more details. * + * * + * You should have received a copy of the Lesser GNU General Public License * + * along with this program. If not, see . * + * * + ********************************************************************************/ + +/******************************************************************************** + * * + * Geometrical data in an IFC file consists of shapes (IfcShapeRepresentation) * + * and instances (SUBTYPE OF IfcBuildingElement e.g. IfcWindow). * + * * + * ifcopenshell::geometry::Representation::Triangulation is a class that represents a * + * triangulated IfcShapeRepresentation. * + * Triangulation.verts is a 1 dimensional vector of float defining the * + * cartesian coordinates of the vertices of the triangulated shape in the * + * format of [x1,y1,z1,..,xn,yn,zn] * + * Triangulation.faces is a 1 dimensional vector of int containing the * + * indices of the triangles referencing positions in Triangulation.verts * + * Triangulation.edges is a 1 dimensional vector of int in {0,1} that dictates* + * the visibility of the edges that span the faces in Triangulation.faces * + * * + * ifcopenshell::geometry::Element represents the actual IfcBuildingElements. * + * IfcGeomObject.name is the GUID of the element * + * IfcGeomObject.type is the datatype of the element e.g. IfcWindow * + * IfcGeomObject.mesh is a pointer to an IfcMesh * + * IfcGeomObject.transformation.matrix is a 4x3 matrix that defines the * + * orientation and translation of the mesh in relation to the world origin * + * * + * ifcopenshell::geometry::Iterator::initialize() * + * finds the most suitable representation contexts. Returns true iff * + * at least a single representation will process successfully * + * * + * ifcopenshell::geometry::Iterator::get() * + * returns a pointer to the current ifcopenshell::geometry::Element * + * * + * ifcopenshell::geometry::Iterator::next() * + * returns true iff a following entity is available for a successive call to * + * ifcopenshell::geometry::Iterator::get() * + * * + * ifcopenshell::geometry::Iterator::progress() * + * returns an int in [0..100] that indicates the overall progress * + * * + ********************************************************************************/ + +#ifndef IFCGEOMITERATOR_H +#define IFCGEOMITERATOR_H + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include + +#include "../../ifcparse/macros.h" +#include "../../ifcparse/IfcFile.h" + +#include "../../ifcgeom/schema_agnostic/IfcGeomElement.h" +#include "../../ifcgeom/settings.h" +#include "../../ifcgeom/schema_agnostic/ConversionResult.h" + +#include "../../ifcgeom/schema_agnostic/IfcGeomFilter.h" + +#include "../../ifcgeom/kernel_agnostic/AbstractKernel.h" + +#include "../../ifcgeom/schema_agnostic/Converter.h" + +#define INCLUDE_SCHEMA(x) STRINGIFY(../../ifcparse/x.h) +#include INCLUDE_SCHEMA(IfcSchema) +#undef INCLUDE_SCHEMA + +#include + +// The infamous min & max Win32 #defines can leak here from OCE depending on the build configuration +#ifdef min +#undef min +#endif +#ifdef max +#undef max +#endif + +namespace { + ifcopenshell::geometry::Element* process_based_on_settings( + const ifcopenshell::geometry::settings& settings, + ifcopenshell::geometry::NativeElement* elem, + ifcopenshell::geometry::TriangulationElement* previous=nullptr) + { + if (settings.get(ifcopenshell::geometry::settings::USE_BREP_DATA)) { + try { + return new ifcopenshell::geometry::SerializedElement(*elem); + } catch (...) { + Logger::Message(Logger::LOG_ERROR, "Getting a serialized element from model failed."); + return nullptr; + } + } else if (!settings.get(ifcopenshell::geometry::settings::DISABLE_TRIANGULATION)) { + try { + if (!previous) { + return new ifcopenshell::geometry::TriangulationElement(*elem); + } else { + return new ifcopenshell::geometry::TriangulationElement(*elem, previous->geometry_pointer()); + } + } catch (...) { + Logger::Message(Logger::LOG_ERROR, "Getting a triangulation element from model failed."); + return nullptr; + } + } else { + return elem; + } + } + + void create_element( + ifcopenshell::geometry::Converter* converter, + const ifcopenshell::geometry::settings& settings, + ifcopenshell::geometry::geometry_conversion_task* rep) + { + IfcUtil::IfcBaseEntity* representation = rep->representation; + IfcUtil::IfcBaseEntity* product = (IfcUtil::IfcBaseEntity*) *rep->products->begin(); + auto brep = converter->create_brep_for_representation_and_product(settings, representation, product); + if (!brep) { + return; + } + + auto elem = process_based_on_settings(settings, brep); + if (!elem) { + return; + } + + rep->breps = { brep }; + rep->elements = { elem }; + + for (auto it = rep->products->begin() + 1; it != rep->products->end(); ++it) { + auto brep2 = converter->create_brep_for_processed_representation(settings, representation, (IfcUtil::IfcBaseEntity*) *it, brep); + if (brep2) { + auto elem2 = process_based_on_settings(settings, brep, dynamic_cast(elem)); + if (elem2) { + rep->breps.push_back(brep2); + rep->elements.push_back(elem2); + } + } + } + } +} + +namespace ifcopenshell { namespace geometry { + + class Iterator { + private: + + int num_threads_; + std::atomic progress_; + std::vector tasks_; + std::vector::iterator task_iterator_; + + std::vector all_processed_elements_; + std::vector all_processed_native_elements_; + size_t task_result_index_; + + std::string geometry_library_; + + Iterator(const Iterator&); // N/I + Iterator& operator=(const Iterator&); // N/I + + Converter* converter_; + settings settings_; + + IfcParse::IfcFile* ifc_file; + + int done; + int total; + + std::string unit_name; + double unit_magnitude; + + gp_XYZ bounds_min_; + gp_XYZ bounds_max_; + + std::vector filters_; + + /// @todo public/private sections all over the place: move all public to the beginning of the class + public: + + bool initialize() { + converter_->mapping()->get_representations(tasks_, filters_, settings_); + + if (tasks_.size() == 0) { + Logger::Warning("No representations encountered, aborting"); + return false; + } + + task_iterator_ = tasks_.begin(); + + done = 0; + total = tasks_.size(); + + if (num_threads_ != 1) { + process_concurrently(); + } else { + if (!create()) { + return false; + } + } + + return true; + } + + void process_concurrently() { + size_t conc_threads = num_threads_; + if (conc_threads > tasks_.size()) { + conc_threads = tasks_.size(); + } + + std::vector kernel_pool; + kernel_pool.reserve(conc_threads); + for (unsigned i = 0; i < conc_threads; ++i) { + kernel_pool.push_back(new Converter(geometry_library_, ifc_file)); + } + + std::vector> threadpool; + + int old_progress = -1; + int processed = 0; + + Logger::ProgressBar(0); + + for (auto& rep : tasks_) { + Converter* K = nullptr; + if (threadpool.size() < kernel_pool.size()) { + K = kernel_pool[threadpool.size()]; + } + + while (threadpool.size() == conc_threads) { + for (int i = 0; i < (int)threadpool.size(); i++) { + std::future &fu = threadpool[i]; + std::future_status status; + status = fu.wait_for(std::chrono::seconds(0)); + if (status == std::future_status::ready) { + fu.get(); + + processed += 1; + progress_ = processed * 50 / tasks_.size(); + if (progress_ != old_progress) { + Logger::ProgressBar(progress_); + old_progress = progress_; + } + + std::swap(threadpool[i], threadpool.back()); + threadpool.pop_back(); + std::swap(kernel_pool[i], kernel_pool.back()); + K = kernel_pool.back(); + break; + } // if + } // for + } // while + + std::future fu = std::async(std::launch::async, create_element, K, std::ref(settings_), &rep); + threadpool.emplace_back(std::move(fu)); + } + + for (std::future &fu : threadpool) { + fu.get(); + + processed += 1; + progress_ = processed * 50 / tasks_.size(); + if (progress_ != old_progress) { + Logger::ProgressBar(progress_); + old_progress = progress_; + } + } + + for (auto& rep : tasks_) { + all_processed_elements_.insert(all_processed_elements_.end(), rep.elements.begin(), rep.elements.end()); + all_processed_native_elements_.insert(all_processed_native_elements_.end(), rep.breps.begin(), rep.breps.end()); + } + + task_result_index_ = 0; + + Logger::Status("\rDone creating geometry (" + boost::lexical_cast(all_processed_elements_.size()) + + " objects) "); + } + + /// Computes model's bounding box (bounds_min and bounds_max). + /// @note Can take several minutes for large files. + void compute_bounds() + { + // @todo + + /* + for (int i = 1; i < 4; ++i) { + bounds_min_.SetCoord(i, std::numeric_limits::infinity()); + bounds_max_.SetCoord(i, -std::numeric_limits::infinity()); + } + + IfcSchema::IfcProduct::list::ptr products = ifc_file->instances_by_type(); + for (IfcSchema::IfcProduct::list::it iter = products->begin(); iter != products->end(); ++iter) { + IfcSchema::IfcProduct* product = *iter; + if (product->hasObjectPlacement()) { + // Use a fresh trsf every time in order to prevent the result to be concatenated + ConversionResultPlacement* trsf; + bool success = false; + + try { + success = kernel->convert_placement(product->ObjectPlacement(), trsf); + } catch (const std::exception& e) { + Logger::Error(e); + } catch (...) { + Logger::Error("Failed to construct placement"); + } + + if (!success) { + continue; + } + + double X, Y, Z; + trsf->TranslationPart(X, Y, Z); + bounds_min_.SetX(std::min(bounds_min_.X(), X)); + bounds_min_.SetY(std::min(bounds_min_.Y(), Y)); + bounds_min_.SetZ(std::min(bounds_min_.Z(), Z)); + bounds_max_.SetX(std::max(bounds_max_.X(), X)); + bounds_max_.SetY(std::max(bounds_max_.Y(), Y)); + bounds_max_.SetZ(std::max(bounds_max_.Z(), Z)); + } + } + */ + } + + int progress() const { + if (num_threads_ == 1) { + return 100 * done / total; + } else { + return progress_; + } + } + + const std::string& getUnitName() const { return unit_name; } + + /// @note Double always as per IFC specification. + double getUnitMagnitude() const { return unit_magnitude; } + + std::string getLog() const { return Logger::GetLog(); } + + IfcParse::IfcFile* file() const { return ifc_file; } + + const std::vector& filters() const { return filters_; } + std::vector& filters() { return filters_; } + + const gp_XYZ& bounds_min() const { return bounds_min_; } + const gp_XYZ& bounds_max() const { return bounds_max_; } + + private: + // Move to the next IfcRepresentation + void _nextShape() { + ++task_iterator_; + ++done; + } + + IfcUtil::IfcBaseClass* create_shape_model_for_next_entity() { + geometry_conversion_task* task = nullptr; + while (task_iterator_ != tasks_.end()) { + task = &*task_iterator_++; + create_element(converter_, settings_, task); + if (task->elements.empty()) { + task = nullptr; + } else { + break; + } + } + if (task) { + all_processed_elements_.insert(all_processed_elements_.end(), task->elements.begin(), task->elements.end()); + all_processed_native_elements_.insert(all_processed_native_elements_.end(), task->breps.begin(), task->breps.end()); + return (*task->products)[0]; + } else { + return nullptr; + } + } + + public: + + /// Moves to the next shape representation, create its geometry, and returns the associated product. + /// Use get() to retrieve the created geometry. + IfcUtil::IfcBaseClass* next() { + if (num_threads_ != 1) { + task_result_index_++; + if (task_result_index_ == all_processed_elements_.size()) { + return nullptr; + } else { + return all_processed_elements_[task_result_index_]->product(); + } + } else { + // Increment the iterator over the list of products using the current + // shape representation + ++task_result_index_; + if (task_result_index_ == all_processed_elements_.size()) { + return create(); + } + } + } + + /// Gets the representation of the current geometrical entity. + Element* get() + { + // TODO: Test settings and throw + Element* ret = 0; + + ret = all_processed_elements_[task_result_index_]; + + // If we want to organize the element considering their hierarchy + if (settings_.get(settings::SEARCH_FLOOR)) + { + // We are going to build a vector with the element parents. + // First, create the parent vector + std::vector parents; + + // if the element has a parent + if (ret->parent_id() != -1) + { + const ifcopenshell::geometry::Element* parent_object = NULL; + bool hasParent = true; + + // get the parent + try { + parent_object = get_object(ret->parent_id()); + } catch (const std::exception& e) { + Logger::Error(e); + hasParent = false; + } + + // Add the previously found parent to the vector + if (hasParent) parents.insert(parents.begin(), parent_object); + + // We need to find all the parents + while (parent_object != NULL && hasParent && parent_object->parent_id() != -1) + { + // Find the next parent + try { + parent_object = get_object(parent_object->parent_id()); + } catch (const std::exception& e) { + Logger::Error(e); + hasParent = false; + } + + // Add the previously found parent to the vector + if (hasParent) parents.insert(parents.begin(), parent_object); + + hasParent = hasParent && parent_object->parent_id() != -1; + } + + // when done push the parent list in the Element object + ret->SetParents(parents); + } + } + + return ret; + } + + /// Gets the native (Open Cascade) representation of the current geometrical entity. + NativeElement* get_native() + { + return all_processed_native_elements_[task_result_index_]; + } + + const Element* get_object(int id) { + // @todo + return nullptr; + /* + ConversionResultPlacement* trsf; + int parent_id = -1; + std::string instance_type, product_name, product_guid; + IfcSchema::IfcProduct* ifc_product = 0; + + try { + IfcUtil::IfcBaseClass* ifc_entity = ifc_file->instance_by_id(id); + instance_type = ifc_entity->declaration().name(); + + if (ifc_entity->declaration().is(IfcSchema::IfcRoot::Class())) { + IfcSchema::IfcRoot* ifc_root = ifc_entity->as(); + product_guid = ifc_root->GlobalId(); + product_name = ifc_root->hasName() ? ifc_root->Name() : ""; + } + + if (ifc_entity->declaration().is(IfcSchema::IfcProduct::Class())) { + ifc_product = ifc_entity->as(); + parent_id = -1; + try { + IfcSchema::IfcObjectDefinition* parent_object = kernel->get_decomposing_entity(ifc_product)->template as(); + if (parent_object) { + parent_id = parent_object->data().id(); + } + } catch (const std::exception& e) { + Logger::Error(e); + } catch (...) { + Logger::Error("Failed to find decomposing entity"); + } + + try { + kernel->convert_placement(ifc_product->ObjectPlacement(), trsf); + } catch (const std::exception& e) { + Logger::Error(e); + } catch (...) { + Logger::Error("Failed to construct placement"); + } + } + } catch (const std::exception& e) { + Logger::Error(e); + } catch (const Standard_Failure& e) { + if (e.GetMessageString() && strlen(e.GetMessageString())) { + Logger::Error(e.GetMessageString()); + } else { + Logger::Error("Unknown error returning product"); + } + } catch (...) { + Logger::Error("Unknown error returning product"); + } + + ElementSettings element_settings(settings, unit_magnitude, instance_type); + + Element* ifc_object = new Element(element_settings, id, parent_id, product_name, instance_type, product_guid, "", trsf, ifc_product); + return ifc_object; + */ + } + + IfcUtil::IfcBaseClass* create() { + IfcUtil::IfcBaseClass* product = nullptr; + try { + product = create_shape_model_for_next_entity(); + } catch (const std::exception& e) { + Logger::Error(e); + } catch (const Standard_Failure& e) { + if (e.GetMessageString() && strlen(e.GetMessageString())) { + Logger::Error(e.GetMessageString()); + } else { + Logger::Error("Unknown error creating geometry"); + } + } catch (...) { + Logger::Error("Unknown error creating geometry"); + } + return product; + } + private: + void _initialize() { + unit_name = "METER"; + unit_magnitude = 1.f; + + // @todo + + /* + kernel->setValue(ifcopenshell::geometry::Kernel::GV_MAX_FACES_TO_ORIENT, settings.get(settings::SEW_SHELLS) ? std::numeric_limits::infinity() : -1); + kernel->setValue(ifcopenshell::geometry::Kernel::GV_DIMENSIONALITY, (settings.get(settings::INCLUDE_CURVES) + ? (settings.get(settings::EXCLUDE_SOLIDS_AND_SURFACES) ? -1. : 0.) : +1.)); + if (settings.get(settings::BUILDING_LOCAL_PLACEMENT)) { + if (settings.get(settings::SITE_LOCAL_PLACEMENT)) { + Logger::Message(Logger::LOG_WARNING, "building-local-placement takes precedence over site-local-placement"); + } + kernel->set_conversion_placement_rel_to(&IfcSchema::IfcBuilding::Class()); + } else if (settings.get(settings::SITE_LOCAL_PLACEMENT)) { + kernel->set_conversion_placement_rel_to(&IfcSchema::IfcSite::Class()); + } + */ + } + + bool owns_ifc_file; + public: + Iterator(const std::string& geometry_library, const settings& settings, IfcParse::IfcFile* file, const std::vector& filters, int num_threads) + : settings_(settings) + , ifc_file(file) + , filters_(filters) + , owns_ifc_file(false) + , num_threads_(num_threads) + , geometry_library_(geometry_library) + { + _initialize(); + } + + ~Iterator() { + if (owns_ifc_file) { + delete ifc_file; + } + + if (settings_.get(settings::DISABLE_TRIANGULATION)) { + for (auto& p : all_processed_native_elements_) { + delete p; + } + } + + for (auto& p : all_processed_elements_) { + delete p; + } + } + }; +}} + +#endif diff --git a/src/ifcgeom/schema_agnostic/IfcGeomMaterial.cpp b/src/ifcgeom/schema_agnostic/IfcGeomMaterial.cpp deleted file mode 100644 index 3593ed7673..0000000000 --- a/src/ifcgeom/schema_agnostic/IfcGeomMaterial.cpp +++ /dev/null @@ -1,35 +0,0 @@ -/******************************************************************************** - * * - * This file is part of IfcOpenShell. * - * * - * IfcOpenShell is free software: you can redistribute it and/or modify * - * it under the terms of the Lesser GNU General Public License as published by * - * the Free Software Foundation, either version 3.0 of the License, or * - * (at your option) any later version. * - * * - * IfcOpenShell is distributed in the hope that it will be useful, * - * but WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * - * Lesser GNU General Public License for more details. * - * * - * You should have received a copy of the Lesser GNU General Public License * - * along with this program. If not, see . * - * * - ********************************************************************************/ - -#include "IfcGeomMaterial.h" - -static double black[3] = {0.,0.,0.}; - -IfcGeom::Material::Material(const IfcGeom::SurfaceStyle* style) : style(style) {} -bool IfcGeom::Material::hasDiffuse() const { return style->Diffuse() ? true : false; } -bool IfcGeom::Material::hasSpecular() const { return style->Specular() ? true : false; } -bool IfcGeom::Material::hasTransparency() const { return style->Transparency() ? true : false; } -bool IfcGeom::Material::hasSpecularity() const { return style->Specularity() ? true : false; } -const double* IfcGeom::Material::diffuse() const { if (hasDiffuse()) return &((*style->Diffuse()).R()); else return black; } -const double* IfcGeom::Material::specular() const { if (hasSpecular()) return &((*style->Specular()).R()); else return black; } -double IfcGeom::Material::transparency() const { if (hasTransparency()) return *style->Transparency(); else return 0; } -double IfcGeom::Material::specularity() const { if (hasSpecularity()) return *style->Specularity(); else return 0; } -const std::string &IfcGeom::Material::name() const { return style->Name(); } -const std::string &IfcGeom::Material::original_name() const { return style->original_name(); } -bool IfcGeom::Material::operator==(const IfcGeom::Material& other) const { return style == other.style; } diff --git a/src/ifcgeom/schema_agnostic/IfcGeomMaterial.h b/src/ifcgeom/schema_agnostic/IfcGeomMaterial.h deleted file mode 100644 index c700aa7760..0000000000 --- a/src/ifcgeom/schema_agnostic/IfcGeomMaterial.h +++ /dev/null @@ -1,51 +0,0 @@ -/******************************************************************************** - * * - * This file is part of IfcOpenShell. * - * * - * IfcOpenShell is free software: you can redistribute it and/or modify * - * it under the terms of the Lesser GNU General Public License as published by * - * the Free Software Foundation, either version 3.0 of the License, or * - * (at your option) any later version. * - * * - * IfcOpenShell is distributed in the hope that it will be useful, * - * but WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * - * Lesser GNU General Public License for more details. * - * * - * You should have received a copy of the Lesser GNU General Public License * - * along with this program. If not, see . * - * * - ********************************************************************************/ - -#ifndef IFCGEOMMATERIAL_H -#define IFCGEOMMATERIAL_H - -#include - -#include "../../ifcgeom/schema_agnostic/IfcGeomRenderStyles.h" - -namespace IfcGeom { - - class IFC_GEOM_API Material { - private: - const IfcGeom::SurfaceStyle* style; - public: - explicit Material(const IfcGeom::SurfaceStyle* style = 0); // TODO default constructor for vector? - // Material(const Material& other); - // Material& operator=(const Material& other); - bool hasDiffuse() const; - bool hasSpecular() const; - bool hasTransparency() const; - bool hasSpecularity() const; - const double* diffuse() const; - const double* specular() const; - double transparency() const; - double specularity() const; - const std::string &name() const; - const std::string &original_name() const; - bool operator==(const Material& other) const; - }; - -} - -#endif diff --git a/src/ifcgeom/schema_agnostic/IfcGeomRenderStyles.h b/src/ifcgeom/schema_agnostic/IfcGeomRenderStyles.h index 5c98a70469..76b9b56924 100644 --- a/src/ifcgeom/schema_agnostic/IfcGeomRenderStyles.h +++ b/src/ifcgeom/schema_agnostic/IfcGeomRenderStyles.h @@ -21,6 +21,7 @@ #define IFCGEOMRENDERSTYLES_H #include "../../ifcgeom/schema_agnostic/ifc_geom_api.h" +#include "../../ifcgeom/taxonomy.h" #include #include @@ -29,72 +30,7 @@ #include namespace IfcGeom { - class IFC_GEOM_API SurfaceStyle { - public: - class ColorComponent { - private: - double data[3]; - public: - ColorComponent(double r, double g, double b) { - data[0] = r; data[1] = g; data[2] = b; - } - const double& R() const { return data[0]; } - const double& G() const { return data[1]; } - const double& B() const { return data[2]; } - double& R() { return data[0]; } - double& G() { return data[1]; } - double& B() { return data[2]; } - }; - private: - std::string name; - std::string original_name_; - boost::optional id; - boost::optional diffuse, specular; - boost::optional transparency; - boost::optional specularity; - public: - SurfaceStyle() : name("surface-style") {} - SurfaceStyle(int id) : id(id) { - std::stringstream sstr; - sstr << "surface-style-" << id; - this->name = sstr.str(); - } - SurfaceStyle(const std::string& name) : name(name), original_name_(name) {} - SurfaceStyle(int id, const std::string& name) : original_name_(name), id(id) - { - std::stringstream sstr; - std::string sanitized = name; - boost::to_lower(sanitized); - boost::replace_all(sanitized, " ", "-"); - sstr << "surface-style-" << id << "-" << sanitized; - this->name = sstr.str(); - } - - // Not used at this point. In fact, equality testing in the current - // architecture can just as easily be accomplished by comparing the - // pointer addresses of the styles, as they are always referenced - // from out of a global map of some sort. - bool operator==(const SurfaceStyle& other) { - return name == other.name; - } - - /// ID name, e.g. "surface-style-66675-metal---aluminium" - const std::string& Name() const { return name; } - - /// Original name, if available, e.g. "Metal - Aluminium" - const std::string& original_name() const { return original_name_; } - - const boost::optional& Diffuse() const { return diffuse; } - const boost::optional& Specular() const { return specular; } - const boost::optional& Transparency() const { return transparency; } - const boost::optional& Specularity() const { return specularity; } - boost::optional& Diffuse() { return diffuse; } - boost::optional& Specular() { return specular; } - boost::optional& Transparency() { return transparency; } - boost::optional& Specularity() { return specularity; } - }; - - IFC_GEOM_API const SurfaceStyle* get_default_style(const std::string& ifc_type); + IFC_GEOM_API const ifcopenshell::geometry::taxonomy::style& get_default_style(const std::string& ifc_type); IFC_GEOM_API void set_default_style_file(const std::string& json_file); } diff --git a/src/ifcgeom/schema_agnostic/IfcGeomRepresentation.h b/src/ifcgeom/schema_agnostic/IfcGeomRepresentation.h index a2886803fc..2f354acc3d 100644 --- a/src/ifcgeom/schema_agnostic/IfcGeomRepresentation.h +++ b/src/ifcgeom/schema_agnostic/IfcGeomRepresentation.h @@ -20,13 +20,12 @@ #ifndef IFCGEOMREPRESENTATION_H #define IFCGEOMREPRESENTATION_H -#include "../../ifcgeom/schema_agnostic/IfcGeomIteratorSettings.h" -#include "../../ifcgeom/schema_agnostic/IfcGeomMaterial.h" +#include "../../ifcgeom/settings.h" #include "../../ifcgeom/schema_agnostic/ConversionResult.h" #include -namespace IfcGeom { +namespace ifcopenshell { namespace geometry { namespace Representation { @@ -34,37 +33,37 @@ namespace IfcGeom { Representation(const Representation&); //N/A Representation& operator =(const Representation&); //N/A protected: - const ElementSettings settings_; + const element_settings settings_; public: - explicit Representation(const ElementSettings& settings) + explicit Representation(const element_settings& settings) : settings_(settings) {} - const ElementSettings& settings() const { return settings_; } + const element_settings& settings() const { return settings_; } virtual ~Representation() {} }; class IFC_GEOM_API BRep : public Representation { private: std::string id_; - const IfcGeom::ConversionResults shapes_; + const ifcopenshell::geometry::ConversionResults shapes_; BRep(const BRep& other); BRep& operator=(const BRep& other); public: - BRep(const ElementSettings& settings, const std::string& id, const IfcGeom::ConversionResults& shapes) + BRep(const element_settings& settings, const std::string& id, const ifcopenshell::geometry::ConversionResults& shapes) : Representation(settings) , id_(id) , shapes_(shapes) {} virtual ~BRep() {} - IfcGeom::ConversionResults::const_iterator begin() const { return shapes_.begin(); } - IfcGeom::ConversionResults::const_iterator end() const { return shapes_.end(); } - const IfcGeom::ConversionResults& shapes() const { return shapes_; } + ifcopenshell::geometry::ConversionResults::const_iterator begin() const { return shapes_.begin(); } + ifcopenshell::geometry::ConversionResults::const_iterator end() const { return shapes_.end(); } + const ifcopenshell::geometry::ConversionResults& shapes() const { return shapes_; } const std::string& id() const { return id_; } - IfcGeom::ConversionResultShape* as_compound(bool force_meters = false) const; + ifcopenshell::geometry::ConversionResultShape* as_compound(bool force_meters = false) const; bool calculate_volume(double&) const; bool calculate_surface_area(double&) const; - bool calculate_projected_surface_area(const IfcGeom::ConversionResultPlacement* ax, double& along_x, double& along_y, double& along_z) const; + bool calculate_projected_surface_area(const ifcopenshell::geometry::ConversionResultPlacement* ax, double& along_x, double& along_y, double& along_z) const; }; class IFC_GEOM_API Serialization : public Representation { @@ -84,57 +83,55 @@ namespace IfcGeom { Serialization& operator=(const Serialization&); }; - template class Triangulation : public Representation { private: // A nested pair of floats and a material index to be able to store an XYZ coordinate in a map. // TODO: Make this a std::tuple when compilers add support for that. - typedef typename std::pair > Coordinate; + typedef typename std::pair > Coordinate; typedef typename std::pair VertexKey; typedef std::map VertexKeyMap; typedef std::pair Edge; std::string id_; - std::vector

_verts; + std::vector _verts; std::vector _faces; std::vector _edges; - std::vector

_normals; - std::vector

uvs_; + std::vector _normals; + std::vector uvs_; std::vector _material_ids; - std::vector _materials; + std::vector _materials; VertexKeyMap welds; public: const std::string& id() const { return id_; } - const std::vector

& verts() const { return _verts; } + const std::vector& verts() const { return _verts; } const std::vector& faces() const { return _faces; } const std::vector& edges() const { return _edges; } - const std::vector

& normals() const { return _normals; } - const std::vector

& uvs() const { return uvs_; } + const std::vector& normals() const { return _normals; } + const std::vector& uvs() const { return uvs_; } const std::vector& material_ids() const { return _material_ids; } - const std::vector& materials() const { return _materials; } + const std::vector& materials() const { return _materials; } Triangulation(const BRep& shape_model) : Representation(shape_model.settings()) , id_(shape_model.id()) { - for ( IfcGeom::ConversionResults::const_iterator iit = shape_model.begin(); iit != shape_model.end(); ++ iit ) { + for ( ifcopenshell::geometry::ConversionResults::const_iterator iit = shape_model.begin(); iit != shape_model.end(); ++ iit ) { int surface_style_id = -1; if (iit->hasStyle()) { - Material adapter(&iit->Style()); - std::vector::const_iterator jt = std::find(_materials.begin(), _materials.end(), adapter); + std::vector::const_iterator jt = std::find(_materials.begin(), _materials.end(), iit->Style()); if (jt == _materials.end()) { surface_style_id = (int)_materials.size(); - _materials.push_back(adapter); + _materials.push_back(iit->Style()); } else { surface_style_id = (int)(jt - _materials.begin()); } } - if (settings().get(IteratorSettings::APPLY_DEFAULT_MATERIALS) && surface_style_id == -1) { - Material material(IfcGeom::get_default_style(settings().element_type())); - std::vector::const_iterator mit = std::find(_materials.begin(), _materials.end(), material); + if (settings().get(ifcopenshell::geometry::settings::APPLY_DEFAULT_MATERIALS) && surface_style_id == -1) { + const ifcopenshell::geometry::taxonomy::style& material = IfcGeom::get_default_style(settings().element_type()); + std::vector::const_iterator mit = std::find(_materials.begin(), _materials.end(), material); if (mit == _materials.end()) { surface_style_id = (int)_materials.size(); _materials.push_back(material); @@ -150,16 +147,16 @@ namespace IfcGeom { /// Generates UVs for a single mesh using box projection. /// @todo Very simple impl. Assumes that input vertices and normals match 1:1. - static std::vector

box_project_uvs(const std::vector

&vertices, const std::vector

&normals) + static std::vector box_project_uvs(const std::vector &vertices, const std::vector &normals) { - std::vector

uvs; + std::vector uvs; uvs.resize(vertices.size() / 3 * 2); for (size_t uv_idx = 0, v_idx = 0; uv_idx < uvs.size() && v_idx < vertices.size() && v_idx < normals.size(); uv_idx += 2, v_idx += 3) { - P n_x = normals[v_idx], n_y = normals[v_idx + 1], n_z = normals[v_idx + 2]; - P v_x = vertices[v_idx], v_y = vertices[v_idx + 1], v_z = vertices[v_idx + 2]; + double n_x = normals[v_idx], n_y = normals[v_idx + 1], n_z = normals[v_idx + 2]; + double v_x = vertices[v_idx], v_y = vertices[v_idx + 1], v_z = vertices[v_idx + 2]; if (std::abs(n_x) > std::abs(n_y) && std::abs(n_x) > std::abs(n_z)) { uvs[uv_idx] = v_z; @@ -181,13 +178,13 @@ namespace IfcGeom { public: // Welds vertices that belong to different faces - int addVertex(int material_index, P X, P Y, P Z) { - const bool convert = settings().get(IteratorSettings::CONVERT_BACK_UNITS); - X = static_cast

(convert ? (X / settings().unit_magnitude()) : X); - Y = static_cast

(convert ? (Y / settings().unit_magnitude()) : Y); - Z = static_cast

(convert ? (Z / settings().unit_magnitude()) : Z); + int addVertex(int material_index, double X, double Y, double Z) { + const bool convert = settings().get(ifcopenshell::geometry::settings::CONVERT_BACK_UNITS); + X = static_cast(convert ? (X / settings().unit_magnitude()) : X); + Y = static_cast(convert ? (Y / settings().unit_magnitude()) : Y); + Z = static_cast(convert ? (Z / settings().unit_magnitude()) : Z); int i = (int) _verts.size() / 3; - if (settings().get(IteratorSettings::WELD_VERTICES)) { + if (settings().get(ifcopenshell::geometry::settings::WELD_VERTICES)) { const VertexKey key = std::make_pair(material_index, std::make_pair(X, std::make_pair(Y, Z))); typename VertexKeyMap::const_iterator it = welds.find(key); if ( it != welds.end() ) return it->second; @@ -207,7 +204,7 @@ namespace IfcGeom { edges_temp.push_back(e); } - inline void addNormal(P X, P Y, P Z) { + inline void addNormal(double X, double Y, double Z) { _normals.push_back(X); _normals.push_back(Y); _normals.push_back(Z); @@ -233,6 +230,6 @@ namespace IfcGeom { }; } -} +}} #endif diff --git a/src/ifcgeom/schema_agnostic/IteratorImplementation.cpp b/src/ifcgeom/schema_agnostic/IteratorImplementation.cpp deleted file mode 100644 index 10fde43794..0000000000 --- a/src/ifcgeom/schema_agnostic/IteratorImplementation.cpp +++ /dev/null @@ -1,47 +0,0 @@ -#include "IteratorImplementation.h" - -#include - -template -IteratorFactoryImplementation& iterator_implementations() { - static IteratorFactoryImplementation impl; - return impl; -} - -template IteratorFactoryImplementation& iterator_implementations(); -template IteratorFactoryImplementation& iterator_implementations(); -template IteratorFactoryImplementation& iterator_implementations(); - -template -extern void init_IteratorImplementation_Ifc2x3(IteratorFactoryImplementation*); - -template -extern void init_IteratorImplementation_Ifc4(IteratorFactoryImplementation*); - -template -IteratorFactoryImplementation::IteratorFactoryImplementation() { - init_IteratorImplementation_Ifc2x3(this); - init_IteratorImplementation_Ifc4(this); -} - -template -void IteratorFactoryImplementation::bind(const std::string& schema_name, typename get_factory_type::type fn) { - const std::string schema_name_lower = boost::to_lower_copy(schema_name); - this->insert(std::make_pair(schema_name_lower, fn)); -} - -template -IfcGeom::IteratorImplementation* IteratorFactoryImplementation::construct(const std::string& schema_name, const std::string& geometry_library, const IfcGeom::IteratorSettings& settings, IfcParse::IfcFile* file, const std::vector& filters, int num_threads) { - const std::string schema_name_lower = boost::to_lower_copy(schema_name); - typename std::map::type>::const_iterator it; - it = this->find(schema_name_lower); - if (it == this->end()) { - throw IfcParse::IfcException("No geometry iterator registered for " + schema_name); - } - return it->second(geometry_library, settings, file, filters, num_threads); -} - - -template class IteratorFactoryImplementation; -template class IteratorFactoryImplementation; -template class IteratorFactoryImplementation; diff --git a/src/ifcgeom/schema_agnostic/IteratorImplementation.h b/src/ifcgeom/schema_agnostic/IteratorImplementation.h deleted file mode 100644 index fc9b9e1754..0000000000 --- a/src/ifcgeom/schema_agnostic/IteratorImplementation.h +++ /dev/null @@ -1,81 +0,0 @@ -#ifndef ITERATOR_IMPLEMENTATION_H -#define ITERATOR_IMPLEMENTATION_H - -#include "../../ifcparse/IfcFile.h" -#include "../../ifcgeom/schema_agnostic/IfcGeomFilter.h" -#include "../../ifcgeom/schema_agnostic/IfcGeomIteratorSettings.h" - -#include - -#include - -#include -#include - -namespace IfcGeom { - template - class IteratorImplementation; - - template - class Element; - - template - class NativeElement; -} - -typedef boost::function5*, const std::string&, const IfcGeom::IteratorSettings&, IfcParse::IfcFile*, const std::vector&, int> iterator_float_float_fn; -typedef boost::function5*, const std::string&, const IfcGeom::IteratorSettings&, IfcParse::IfcFile*, const std::vector&, int> iterator_float_double_fn; -typedef boost::function5*, const std::string&, const IfcGeom::IteratorSettings&, IfcParse::IfcFile*, const std::vector&, int> iterator_double_double_fn; - -template -struct get_factory_type {}; - -template <> -struct get_factory_type { - typedef iterator_float_float_fn type; -}; - -template <> -struct get_factory_type { - typedef iterator_float_double_fn type; -}; - -template <> -struct get_factory_type { - typedef iterator_double_double_fn type; -}; - -template -class IteratorFactoryImplementation : public std::map::type> { -public: - IteratorFactoryImplementation(); - void bind(const std::string& schema_name, typename get_factory_type::type fn); - IfcGeom::IteratorImplementation* construct(const std::string& schema_name, const std::string& geometry_library, const IfcGeom::IteratorSettings&, IfcParse::IfcFile*, const std::vector&, int); -}; - -template -IteratorFactoryImplementation& iterator_implementations(); - -namespace IfcGeom { - - template - class IteratorImplementation { - public: - virtual bool initialize() = 0; - virtual void compute_bounds() = 0; - virtual const gp_XYZ& bounds_min() const = 0; - virtual const gp_XYZ& bounds_max() const = 0; - virtual int progress() const = 0; - virtual const std::string& getUnitName() const = 0; - virtual double getUnitMagnitude() const = 0; - virtual IfcParse::IfcFile* file() const = 0; - virtual IfcUtil::IfcBaseClass* next() = 0; - virtual Element* get() = 0; - virtual NativeElement* get_native() = 0; - virtual const Element* get_object(int id) = 0; - virtual IfcUtil::IfcBaseClass* create() = 0; - }; - -} - -#endif \ No newline at end of file diff --git a/src/ifcgeom/schema_agnostic/Kernel.cpp b/src/ifcgeom/schema_agnostic/Kernel.cpp deleted file mode 100644 index 0f897f3b68..0000000000 --- a/src/ifcgeom/schema_agnostic/Kernel.cpp +++ /dev/null @@ -1,268 +0,0 @@ -#include "Kernel.h" - -#include "../../ifcparse/Ifc2x3.h" -#include "../../ifcparse/Ifc4.h" - -// @todo remove -#include "../../ifcgeom/schema_agnostic/opencascade/OpenCascadeConversionResult.h" - -#include -#include -#include -#include - -IfcGeom::Kernel::Kernel(const std::string& geometry_library, IfcParse::IfcFile* file) { - if (file != 0) { - if (file->schema() == 0) { - throw IfcParse::IfcException("No schema associated with file"); - } - - const std::string& schema_name = file->schema()->name(); - implementation_ = impl::kernel_implementations().construct(schema_name, geometry_library, file); - } -} - -int IfcGeom::Kernel::count(const ConversionResultShape* s_, int t_, bool unique) { - // @todo make kernel agnostic - const TopoDS_Shape& s = ((OpenCascadeShape*) s_)->shape(); - TopAbs_ShapeEnum t = (TopAbs_ShapeEnum) t_; - - if (unique) { - TopTools_IndexedMapOfShape map; - TopExp::MapShapes(s, t, map); - return map.Extent(); - } else { - int i = 0; - TopExp_Explorer exp(s, t); - for (; exp.More(); exp.Next()) { - ++i; - } - return i; - } -} - - -int IfcGeom::Kernel::surface_genus(const ConversionResultShape* s_) { - // @todo make kernel agnostic - const TopoDS_Shape& s = ((OpenCascadeShape*) s_)->shape(); - OpenCascadeShape Ss(s); - - int nv = count(&Ss, (int) TopAbs_VERTEX, true); - int ne = count(&Ss, (int) TopAbs_EDGE, true); - int nf = count(&Ss, (int) TopAbs_FACE, true); - - const int euler = nv - ne + nf; - const int genus = (2 - euler) / 2; - - return genus; -} - -IfcGeom::impl::KernelFactoryImplementation& IfcGeom::impl::kernel_implementations() { - static KernelFactoryImplementation impl; - return impl; -} - -extern void init_KernelImplementation_opencascade_Ifc2x3(IfcGeom::impl::KernelFactoryImplementation*); -extern void init_KernelImplementation_opencascade_Ifc4(IfcGeom::impl::KernelFactoryImplementation*); -#ifdef IFOPSH_USE_CGAL -extern void init_KernelImplementation_cgal_Ifc2x3(IfcGeom::impl::KernelFactoryImplementation*); -extern void init_KernelImplementation_cgal_Ifc4(IfcGeom::impl::KernelFactoryImplementation*); -#endif - -IfcGeom::impl::KernelFactoryImplementation::KernelFactoryImplementation() { - init_KernelImplementation_opencascade_Ifc2x3(this); - init_KernelImplementation_opencascade_Ifc4(this); -#ifdef IFOPSH_USE_CGAL - init_KernelImplementation_cgal_Ifc2x3(this); - init_KernelImplementation_cgal_Ifc4(this); -#endif -} - -void IfcGeom::impl::KernelFactoryImplementation::bind(const std::string& schema_name, const std::string& geometry_library, IfcGeom::impl::kernel_fn fn) { - const std::string schema_name_lower = boost::to_lower_copy(schema_name); - this->insert(std::make_pair(std::make_pair(schema_name_lower, geometry_library), fn)); -} - -IfcGeom::Kernel* IfcGeom::impl::KernelFactoryImplementation::construct(const std::string& schema_name, const std::string& geometry_library, IfcParse::IfcFile* file) { - const std::string schema_name_lower = boost::to_lower_copy(schema_name); - std::map, IfcGeom::impl::kernel_fn>::const_iterator it; - it = this->find(std::make_pair(schema_name_lower, geometry_library)); - if (it == end()) { - throw IfcParse::IfcException("No geometry kernel registered for " + schema_name); - } - return it->second(file); -} - -#define CREATE_GET_DECOMPOSING_ENTITY(IfcSchema) \ - \ -IfcSchema::IfcObjectDefinition* get_decomposing_entity_impl(IfcSchema::IfcProduct* product, bool include_openings) {\ - IfcSchema::IfcObjectDefinition* parent = 0; \ - \ - /* In case of an opening element, parent to the RelatingBuildingElement */ \ - if (include_openings && product->declaration().is(IfcSchema::IfcOpeningElement::Class())) { \ - IfcSchema::IfcOpeningElement* opening = (IfcSchema::IfcOpeningElement*)product; \ - IfcSchema::IfcRelVoidsElement::list::ptr voids = opening->VoidsElements(); \ - if (voids->size()) { \ - IfcSchema::IfcRelVoidsElement* ifc_void = *voids->begin(); \ - parent = ifc_void->RelatingBuildingElement(); \ - } \ - } else if (product->declaration().is(IfcSchema::IfcElement::Class())) { \ - IfcSchema::IfcElement* element = (IfcSchema::IfcElement*)product; \ - IfcSchema::IfcRelFillsElement::list::ptr fills = element->FillsVoids(); \ - /* In case of a RelatedBuildingElement parent to the opening element */ \ - if (fills->size() && include_openings) { \ - for (IfcSchema::IfcRelFillsElement::list::it it = fills->begin(); it != fills->end(); ++it) { \ - IfcSchema::IfcRelFillsElement* fill = *it; \ - IfcSchema::IfcObjectDefinition* ifc_objectdef = fill->RelatingOpeningElement(); \ - if (product == ifc_objectdef) continue; \ - parent = ifc_objectdef; \ - } \ - } \ - /* Else simply parent to the containing structure */ \ - if (!parent) { \ - IfcSchema::IfcRelContainedInSpatialStructure::list::ptr parents = element->ContainedInStructure(); \ - if (parents->size()) { \ - IfcSchema::IfcRelContainedInSpatialStructure* container = *parents->begin(); \ - parent = container->RelatingStructure(); \ - } \ - } \ - } \ - \ - /* Parent decompositions to the RelatingObject */ \ - if (!parent) { \ - IfcEntityList::ptr parents = product->data().getInverse((&IfcSchema::IfcRelAggregates::Class()), -1); \ - parents->push(product->data().getInverse((&IfcSchema::IfcRelNests::Class()), -1)); \ - for (IfcEntityList::it it = parents->begin(); it != parents->end(); ++it) { \ - IfcSchema::IfcRelDecomposes* decompose = (IfcSchema::IfcRelDecomposes*)*it; \ - IfcUtil::IfcBaseEntity* ifc_objectdef; \ - \ - ifc_objectdef = get_RelatingObject(decompose); \ - \ - if (product == ifc_objectdef) continue; \ - parent = ifc_objectdef->as(); \ - } \ - } \ - return parent; \ -} - -namespace { - IfcUtil::IfcBaseEntity* get_RelatingObject(Ifc4::IfcRelDecomposes* decompose) { - Ifc4::IfcRelAggregates* aggr = decompose->as(); - if (aggr != nullptr) { - return aggr->RelatingObject(); - } - return nullptr; - } - - IfcUtil::IfcBaseEntity* get_RelatingObject(Ifc2x3::IfcRelDecomposes* decompose) { - return decompose->RelatingObject(); - } - - CREATE_GET_DECOMPOSING_ENTITY(Ifc2x3); - CREATE_GET_DECOMPOSING_ENTITY(Ifc4); -} - -IfcUtil::IfcBaseEntity* IfcGeom::Kernel::get_decomposing_entity(IfcUtil::IfcBaseEntity* inst, bool include_openings) { - if (inst->as()) { - return get_decomposing_entity_impl(inst->as(), include_openings); - } else if (inst->as()) { - return get_decomposing_entity_impl(inst->as(), include_openings); - } else if (inst->declaration().name() == "IfcProject") { - return nullptr; - } else { - throw IfcParse::IfcException("Unexpected entity " + inst->declaration().name()); - } -} - -namespace { - template - static std::map get_layers_impl(typename Schema::IfcProduct* prod) { - std::map layers; - if (prod->hasRepresentation()) { - IfcEntityList::ptr r = IfcParse::traverse(prod->Representation()); - typename Schema::IfcRepresentation::list::ptr representations = r->template as(); - for (typename Schema::IfcRepresentation::list::it it = representations->begin(); it != representations->end(); ++it) { - typename Schema::IfcPresentationLayerAssignment::list::ptr a = (*it)->LayerAssignments(); - for (typename Schema::IfcPresentationLayerAssignment::list::it jt = a->begin(); jt != a->end(); ++jt) { - layers[(*jt)->Name()] = *jt; - } - } - } - return layers; - } -} - -std::map IfcGeom::Kernel::get_layers(IfcUtil::IfcBaseEntity* inst) { - if (inst->as()) { - return get_layers_impl(inst->as()); - } else if (inst->as()) { - return get_layers_impl(inst->as()); - } else { - throw IfcParse::IfcException("Unexpected entity " + inst->declaration().name()); - } -} - -bool IfcGeom::Kernel::is_manifold(const ConversionResultShape* s_) { - // @todo make kernel agnostic - const TopoDS_Shape& a = ((OpenCascadeShape*) s_)->shape(); - - if (a.ShapeType() == TopAbs_COMPOUND || a.ShapeType() == TopAbs_SOLID) { - TopoDS_Iterator it(a); - for (; it.More(); it.Next()) { - OpenCascadeShape s(it.Value()); - if (!is_manifold(&s)) { - return false; - } - } - return true; - } else { - TopTools_IndexedDataMapOfShapeListOfShape map; - TopExp::MapShapesAndAncestors(a, TopAbs_EDGE, TopAbs_FACE, map); - - for (int i = 1; i <= map.Extent(); ++i) { - if (map.FindFromIndex(i).Extent() != 2) { - return false; - } - } - - return true; - } -} - -namespace { - template - IfcEntityList::ptr find_openings_helper(typename Schema::IfcProduct* product) { - - typename IfcEntityList::ptr openings(new IfcEntityList); - if (product->declaration().is(Schema::IfcElement::Class()) && !product->declaration().is(Schema::IfcOpeningElement::Class())) { - typename Schema::IfcElement* element = (typename Schema::IfcElement*)product; - openings = element->HasOpenings()->generalize(); - } - - // Is the IfcElement a decomposition of an IfcElement with any IfcOpeningElements? - typename Schema::IfcObjectDefinition* obdef = product->template as(); - for (;;) { - auto decomposes = obdef->Decomposes()->generalize(); - if (decomposes->size() != 1) break; - typename Schema::IfcObjectDefinition* rel_obdef = (*decomposes->begin())->template as()->RelatingObject(); - if (rel_obdef->declaration().is(Schema::IfcElement::Class()) && !rel_obdef->declaration().is(Schema::IfcOpeningElement::Class())) { - typename Schema::IfcElement* element = (typename Schema::IfcElement*)rel_obdef; - openings->push(element->HasOpenings()->generalize()); - } - - obdef = rel_obdef; - } - - return openings; - } -} - -IfcEntityList::ptr IfcGeom::Kernel::find_openings(IfcUtil::IfcBaseEntity* inst) { - if (inst->as()) { - return find_openings_helper(inst->as()); - } else if (inst->as()) { - return find_openings_helper(inst->as()); - } else { - throw IfcParse::IfcException("Unexpected entity " + inst->declaration().name()); - } -} diff --git a/src/ifcgeom/schema_agnostic/SurfaceStyle.cpp b/src/ifcgeom/schema_agnostic/SurfaceStyle.cpp index 6368cd9514..a936e30e19 100644 --- a/src/ifcgeom/schema_agnostic/SurfaceStyle.cpp +++ b/src/ifcgeom/schema_agnostic/SurfaceStyle.cpp @@ -7,49 +7,49 @@ namespace pt = boost::property_tree; -static std::map default_materials; -static IfcGeom::SurfaceStyle default_material; +static std::map default_materials; +static ifcopenshell::geometry::taxonomy::style default_material; static bool default_materials_initialized = false; void InitDefaultMaterials() { - default_materials.insert(std::make_pair("IfcSite", IfcGeom::SurfaceStyle("IfcSite"))); - default_materials["IfcSite"].Diffuse().reset(IfcGeom::SurfaceStyle::ColorComponent(0.75, 0.8, 0.65)); + default_materials.insert(std::make_pair("IfcSite", ifcopenshell::geometry::taxonomy::style("IfcSite"))); + default_materials["IfcSite"].diffuse.reset(ifcopenshell::geometry::taxonomy::colour(0.75, 0.8, 0.65)); - default_materials.insert(std::make_pair("IfcSlab", IfcGeom::SurfaceStyle("IfcSlab"))); - default_materials["IfcSlab"].Diffuse().reset(IfcGeom::SurfaceStyle::ColorComponent(0.4, 0.4, 0.4)); + default_materials.insert(std::make_pair("IfcSlab", ifcopenshell::geometry::taxonomy::style("IfcSlab"))); + default_materials["IfcSlab"].diffuse.reset(ifcopenshell::geometry::taxonomy::colour(0.4, 0.4, 0.4)); - default_materials.insert(std::make_pair("IfcWallStandardCase", IfcGeom::SurfaceStyle("IfcWallStandardCase"))); - default_materials["IfcWallStandardCase"].Diffuse().reset(IfcGeom::SurfaceStyle::ColorComponent(0.9, 0.9, 0.9)); + default_materials.insert(std::make_pair("IfcWallStandardCase", ifcopenshell::geometry::taxonomy::style("IfcWallStandardCase"))); + default_materials["IfcWallStandardCase"].diffuse.reset(ifcopenshell::geometry::taxonomy::colour(0.9, 0.9, 0.9)); - default_materials.insert(std::make_pair("IfcWall", IfcGeom::SurfaceStyle("IfcWall"))); - default_materials["IfcWall"].Diffuse().reset(IfcGeom::SurfaceStyle::ColorComponent(0.9, 0.9, 0.9)); + default_materials.insert(std::make_pair("IfcWall", ifcopenshell::geometry::taxonomy::style("IfcWall"))); + default_materials["IfcWall"].diffuse.reset(ifcopenshell::geometry::taxonomy::colour(0.9, 0.9, 0.9)); - default_materials.insert(std::make_pair("IfcWindow", IfcGeom::SurfaceStyle("IfcWindow"))); - default_materials["IfcWindow"].Diffuse().reset(IfcGeom::SurfaceStyle::ColorComponent(0.75, 0.8, 0.75)); - default_materials["IfcWindow"].Transparency().reset(0.3); + default_materials.insert(std::make_pair("IfcWindow", ifcopenshell::geometry::taxonomy::style("IfcWindow"))); + default_materials["IfcWindow"].diffuse.reset(ifcopenshell::geometry::taxonomy::colour(0.75, 0.8, 0.75)); + default_materials["IfcWindow"].transparency.reset(0.3); - default_materials.insert(std::make_pair("IfcDoor", IfcGeom::SurfaceStyle("IfcDoor"))); - default_materials["IfcDoor"].Diffuse().reset(IfcGeom::SurfaceStyle::ColorComponent(0.55, 0.3, 0.15)); + default_materials.insert(std::make_pair("IfcDoor", ifcopenshell::geometry::taxonomy::style("IfcDoor"))); + default_materials["IfcDoor"].diffuse.reset(ifcopenshell::geometry::taxonomy::colour(0.55, 0.3, 0.15)); - default_materials.insert(std::make_pair("IfcBeam", IfcGeom::SurfaceStyle("IfcBeam"))); - default_materials["IfcBeam"].Diffuse().reset(IfcGeom::SurfaceStyle::ColorComponent(0.75, 0.7, 0.7)); + default_materials.insert(std::make_pair("IfcBeam", ifcopenshell::geometry::taxonomy::style("IfcBeam"))); + default_materials["IfcBeam"].diffuse.reset(ifcopenshell::geometry::taxonomy::colour(0.75, 0.7, 0.7)); - default_materials.insert(std::make_pair("IfcRailing", IfcGeom::SurfaceStyle("IfcRailing"))); - default_materials["IfcRailing"].Diffuse().reset(IfcGeom::SurfaceStyle::ColorComponent(0.65, 0.6, 0.6)); + default_materials.insert(std::make_pair("IfcRailing", ifcopenshell::geometry::taxonomy::style("IfcRailing"))); + default_materials["IfcRailing"].diffuse.reset(ifcopenshell::geometry::taxonomy::colour(0.65, 0.6, 0.6)); - default_materials.insert(std::make_pair("IfcMember", IfcGeom::SurfaceStyle("IfcMember"))); - default_materials["IfcMember"].Diffuse().reset(IfcGeom::SurfaceStyle::ColorComponent(0.65, 0.6, 0.6)); + default_materials.insert(std::make_pair("IfcMember", ifcopenshell::geometry::taxonomy::style("IfcMember"))); + default_materials["IfcMember"].diffuse.reset(ifcopenshell::geometry::taxonomy::colour(0.65, 0.6, 0.6)); - default_materials.insert(std::make_pair("IfcPlate", IfcGeom::SurfaceStyle("IfcPlate"))); - default_materials["IfcPlate"].Diffuse().reset(IfcGeom::SurfaceStyle::ColorComponent(0.8, 0.8, 0.8)); + default_materials.insert(std::make_pair("IfcPlate", ifcopenshell::geometry::taxonomy::style("IfcPlate"))); + default_materials["IfcPlate"].diffuse.reset(ifcopenshell::geometry::taxonomy::colour(0.8, 0.8, 0.8)); - default_material = IfcGeom::SurfaceStyle("DefaultMaterial"); - default_material.Diffuse().reset(IfcGeom::SurfaceStyle::ColorComponent(0.7, 0.7, 0.7)); + default_material = ifcopenshell::geometry::taxonomy::style("DefaultMaterial"); + default_material.diffuse.reset(ifcopenshell::geometry::taxonomy::colour(0.7, 0.7, 0.7)); default_materials_initialized = true; } -boost::optional read_colour_component(const boost::optional list) { +boost::optional read_colour_component(const boost::optional list) { if (!list) { return boost::none; } @@ -65,7 +65,7 @@ boost::optional read_colour_component(con if (i != 3) { throw std::runtime_error("rgb array less than 3 elements large (was " + std::to_string(i) + ")"); } - return IfcGeom::SurfaceStyle::ColorComponent(rgb[0], rgb[1], rgb[2]); + return ifcopenshell::geometry::taxonomy::colour(rgb[0], rgb[1], rgb[2]); } void IfcGeom::set_default_style_file(const std::string& json_file) { @@ -78,46 +78,45 @@ void IfcGeom::set_default_style_file(const std::string& json_file) { for (pt::ptree::value_type &material_pair : root) { std::string name = material_pair.first; - default_materials.insert(std::make_pair(name, IfcGeom::SurfaceStyle(name))); + default_materials.insert(std::make_pair(name, ifcopenshell::geometry::taxonomy::style(name))); pt::ptree material = material_pair.second; boost::optional diffuse = material.get_child_optional("diffuse"); - default_materials[name].Diffuse() = read_colour_component(diffuse); + default_materials[name].diffuse = read_colour_component(diffuse); boost::optional specular = material.get_child_optional("specular"); - default_materials[name].Specular() = read_colour_component(specular); + default_materials[name].specular = read_colour_component(specular); if (material.get_child_optional("specular-roughness")) { - default_materials[name].Specularity().reset(1.0 / material.get("specular-roughness")); + default_materials[name].specularity.reset(1.0 / material.get("specular-roughness")); } if (material.get_child_optional("transparency")) { - default_materials[name].Transparency() = material.get("transparency"); + default_materials[name].transparency = material.get("transparency"); } } // Is "*" present? If yes, remove it and make it the default style. - std::map::const_iterator it = default_materials.find("*"); + std::map::const_iterator it = default_materials.find("*"); if (it != default_materials.end()) { - IfcGeom::SurfaceStyle star = it->second; - default_material.Diffuse() = star.Diffuse(); - default_material.Specular() = star.Specular(); - default_material.Specularity() = star.Specularity(); - default_material.Transparency() = star.Transparency(); + ifcopenshell::geometry::taxonomy::style star = it->second; + default_material.diffuse = star.diffuse; + default_material.specular = star.specular; + default_material.specularity = star.specularity; + default_material.transparency = star.transparency; default_materials.erase(it); } } -const IfcGeom::SurfaceStyle* IfcGeom::get_default_style(const std::string& s) { +const ifcopenshell::geometry::taxonomy::style& IfcGeom::get_default_style(const std::string& s) { if (!default_materials_initialized) InitDefaultMaterials(); - std::map::const_iterator it = default_materials.find(s); + std::map::const_iterator it = default_materials.find(s); if (it == default_materials.end()) { - default_materials.insert(std::make_pair(s, IfcGeom::SurfaceStyle(s))); - default_materials[s].Diffuse() = default_material.Diffuse(); - default_materials[s].Specular() = default_material.Specular(); - default_materials[s].Specularity() = default_material.Specularity(); - default_materials[s].Transparency() = default_material.Transparency(); + default_materials.insert(std::make_pair(s, ifcopenshell::geometry::taxonomy::style(s))); + default_materials[s].diffuse = default_material.diffuse; + default_materials[s].specular = default_material.specular; + default_materials[s].specularity = default_material.specularity; + default_materials[s].transparency = default_material.transparency; it = default_materials.find(s); } - const IfcGeom::SurfaceStyle& surface_style = it->second; - return &surface_style; + return it->second; } diff --git a/src/ifcgeom/schema_agnostic/opencascade/OpenCascadeConversionResult.h b/src/ifcgeom/schema_agnostic/opencascade/OpenCascadeConversionResult.h index 75a41dd1b1..51680f645d 100644 --- a/src/ifcgeom/schema_agnostic/opencascade/OpenCascadeConversionResult.h +++ b/src/ifcgeom/schema_agnostic/opencascade/OpenCascadeConversionResult.h @@ -36,75 +36,76 @@ #include "../../../ifcgeom/schema_agnostic/ConversionResult.h" -namespace IfcGeom { +namespace ifcopenshell { + namespace geometry { - class OpenCascadePlacement : public ConversionResultPlacement { - public: - OpenCascadePlacement(const gp_GTrsf& trsf) - : trsf_(trsf) {} + class OpenCascadePlacement : public ConversionResultPlacement { + public: + OpenCascadePlacement(const gp_GTrsf& trsf) + : trsf_(trsf) {} - const gp_GTrsf& trsf() const { return trsf_; } - operator const gp_GTrsf& () { return trsf_; } + const gp_GTrsf& trsf() const { return trsf_; } + operator const gp_GTrsf& () { return trsf_; } - virtual double Value(int i, int j) const { - return trsf_.Value(i, j); - } + virtual double Value(int i, int j) const { + return trsf_.Value(i, j); + } - virtual void Multiply(const ConversionResultPlacement* other) { - trsf_.Multiply(((OpenCascadePlacement*)other)->trsf_); - } + virtual void Multiply(const ConversionResultPlacement* other) { + trsf_.Multiply(((OpenCascadePlacement*)other)->trsf_); + } - virtual void PreMultiply(const ConversionResultPlacement* other) { - trsf_.PreMultiply(((OpenCascadePlacement*)other)->trsf_); - } + virtual void PreMultiply(const ConversionResultPlacement* other) { + trsf_.PreMultiply(((OpenCascadePlacement*)other)->trsf_); + } - virtual ConversionResultPlacement* clone() const { - return new OpenCascadePlacement(trsf_); - } + virtual ConversionResultPlacement* clone() const { + return new OpenCascadePlacement(trsf_); + } - virtual ConversionResultPlacement* inverted() const { - return new OpenCascadePlacement(trsf_.Inverted()); - } + virtual ConversionResultPlacement* inverted() const { + return new OpenCascadePlacement(trsf_.Inverted()); + } - virtual ConversionResultPlacement* multiplied(const ConversionResultPlacement* other) const { - return new OpenCascadePlacement(trsf_.Multiplied(((OpenCascadePlacement*)other)->trsf_)); - } + virtual ConversionResultPlacement* multiplied(const ConversionResultPlacement* other) const { + return new OpenCascadePlacement(trsf_.Multiplied(((OpenCascadePlacement*)other)->trsf_)); + } - virtual void TranslationPart(double& X, double& Y, double& Z) const { - X = trsf_.TranslationPart().X(); - Y = trsf_.TranslationPart().Y(); - Z = trsf_.TranslationPart().Z(); - } - private: - gp_GTrsf trsf_; - }; - - class OpenCascadeShape : public ConversionResultShape { - public: - OpenCascadeShape(const TopoDS_Shape& shape) - : shape_(shape) - {} + virtual void TranslationPart(double& X, double& Y, double& Z) const { + X = trsf_.TranslationPart().X(); + Y = trsf_.TranslationPart().Y(); + Z = trsf_.TranslationPart().Z(); + } + private: + gp_GTrsf trsf_; + }; - const TopoDS_Shape& shape() const { return shape_; } - operator const TopoDS_Shape& () { return shape_; } + class OpenCascadeShape : public ConversionResultShape { + public: + OpenCascadeShape(const TopoDS_Shape& shape) + : shape_(shape) {} - virtual void Triangulate(const IfcGeom::IteratorSettings & settings, const IfcGeom::ConversionResultPlacement * place, IfcGeom::Representation::Triangulation* t, int surface_style_id) const; + const TopoDS_Shape& shape() const { return shape_; } + operator const TopoDS_Shape& () { return shape_; } - virtual void Triangulate(const IfcGeom::IteratorSettings & settings, const IfcGeom::ConversionResultPlacement * place, IfcGeom::Representation::Triangulation* t, int surface_style_id) const; + virtual void Triangulate(const settings & settings, const ConversionResultPlacement * place, Representation::Triangulation* t, int surface_style_id) const; - virtual void Serialize(std::string&) const { - throw std::runtime_error("Not implemented"); - } + virtual void Triangulate(const settings & settings, const ConversionResultPlacement * place, Representation::Triangulation* t, int surface_style_id) const; - virtual ConversionResultShape* clone() const { - return new OpenCascadeShape(shape_); - } + virtual void Serialize(std::string&) const { + throw std::runtime_error("Not implemented"); + } - virtual int surface_genus() const; - private: - TopoDS_Shape shape_; - }; - + virtual ConversionResultShape* clone() const { + return new OpenCascadeShape(shape_); + } + + virtual int surface_genus() const; + private: + TopoDS_Shape shape_; + }; + + } } #endif \ No newline at end of file diff --git a/src/ifcgeom/schema_agnostic/IfcGeomIteratorSettings.h b/src/ifcgeom/settings.h similarity index 94% rename from src/ifcgeom/schema_agnostic/IfcGeomIteratorSettings.h rename to src/ifcgeom/settings.h index 1ad0850423..63032e9187 100644 --- a/src/ifcgeom/schema_agnostic/IfcGeomIteratorSettings.h +++ b/src/ifcgeom/settings.h @@ -20,14 +20,17 @@ #ifndef IFCGEOMITERATORSETTINGS_H #define IFCGEOMITERATORSETTINGS_H -#include "ifc_geom_api.h" -#include "../../ifcparse/IfcException.h" -#include "../../ifcparse/IfcBaseClass.h" -#include "../../ifcparse/IfcLogger.h" +// #include "ifc_geom_api.h" -namespace IfcGeom -{ - class IFC_GEOM_API IteratorSettings +#define IFC_GEOM_API + +#include "../ifcparse/IfcException.h" +#include "../ifcparse/IfcBaseClass.h" +#include "../ifcparse/IfcLogger.h" + +namespace ifcopenshell { namespace geometry { + + class IFC_GEOM_API settings { public: /// Enumeration of setting identifiers. These settings define the @@ -93,7 +96,7 @@ namespace IfcGeom /// Used to store logical OR combination of setting flags. typedef unsigned SettingField; - IteratorSettings() + settings() : settings_(WELD_VERTICES) // OR options that default to true here , deflection_tolerance_(1.e-3) { @@ -136,13 +139,13 @@ namespace IfcGeom double deflection_tolerance_; }; - class IFC_GEOM_API ElementSettings : public IteratorSettings + class IFC_GEOM_API element_settings : public settings { public: - ElementSettings(const IteratorSettings& settings, + element_settings(const settings& s, double unit_magnitude, const std::string& element_type) - : IteratorSettings(settings) + : settings(s) , unit_magnitude_(unit_magnitude) , element_type_(element_type) { @@ -155,6 +158,7 @@ namespace IfcGeom double unit_magnitude_; std::string element_type_; }; -} + +}} #endif diff --git a/src/ifcgeom/taxonomy.h b/src/ifcgeom/taxonomy.h index 90ed25b764..4f32f05bb6 100644 --- a/src/ifcgeom/taxonomy.h +++ b/src/ifcgeom/taxonomy.h @@ -1,6 +1,17 @@ +#ifndef TAXONOMY_H +#define TAXONOMY_H + +#include "../ifcparse/IfcBaseClass.h" + #include +#include + +#include +#include +#include #include +#include namespace ifcopenshell { @@ -8,11 +19,16 @@ namespace geometry { namespace taxonomy { -struct item { - int instance_id; - virtual item* clone() const = 0; +enum kinds { MATRIX4, POINT3, DIRECTION3, LINE, CIRCLE, ELLIPSE, BSPLINE, EDGE, LOOP, FACE, EXTRUSION, NODE, COLOUR, STYLE }; - item(int id) : instance_id(id) {} +struct item { + const IfcUtil::IfcBaseClass* instance; + virtual item* clone() const = 0; + virtual kinds kind() const = 0; + + item(const IfcUtil::IfcBaseClass* instance = nullptr) : instance(instance) {} + + EIGEN_MAKE_ALIGNED_OPERATOR_NEW }; struct matrix4 : public item { @@ -20,53 +36,94 @@ struct matrix4 : public item { IDENTITY, AFFINE_WO_SCALE, AFFINE_W_UNIFORM_SCALE, AFFINE_W_NONUNIFORM_SCALE, OTHER }; tag_t tag; - std::array components; - matrix4() : components({1. ,0., 0., 0., 0., 1., 0., 0., 0., 0., 1. ,0., 0., 0., 0., 1.}), tag(IDENTITY) {} + + Eigen::Matrix4d components; + + matrix4() : components(Eigen::Matrix4d::Identity()), tag(IDENTITY) {} virtual item* clone() const { return new matrix4(*this); } + virtual kinds kind() const { return MATRIX4; } +}; + +struct colour : public item { + Eigen::Vector3d components; + + virtual item* clone() const { return new colour(*this); } + virtual kinds kind() const { return COLOUR; } + + colour() : components(Eigen::Vector3d::Zero()) {} + colour(double r, double g, double b) { components << r, g, b; } + + const double& r() const { return components[0]; } + const double& g() const { return components[1]; } + const double& b() const { return components[2]; } +}; + +struct style : public item { + // @todo this is not very efficient wrt alignment + boost::optional name; + boost::optional diffuse; + boost::optional specular; + boost::optional specularity, transparency; + + virtual item* clone() const { return new style(*this); } + virtual kinds kind() const { return STYLE; } + + // @todo equality implementation based on values? + bool operator==(const style& other) const { return instance == other.instance; } + + style() {} + style(const std::string& name) : name(name) {} }; struct geom_item : public item { - // geometry::style surface_style; + style surface_style; matrix4 matrix; - geom_item(int id) : item(id) {} - geom_item(int id, matrix4 m) : item(id), matrix(m) {} + geom_item(const IfcUtil::IfcBaseClass* instance = nullptr) : item(instance) {} + geom_item(const IfcUtil::IfcBaseClass* instance, matrix4 m) : item(instance), matrix(m) {} }; template struct cartesian_base : public geom_item { - std::array components; + Eigen::Vector3d components; - cartesian_base(double x, double y, double z = 0.) : components{ {x, y, z} } {} + cartesian_base() : components(Eigen::Vector3d::Zero()) {} + cartesian_base(double x, double y, double z = 0.) { components << x, y, z; } }; struct point3 : public cartesian_base<3> { virtual item* clone() const { return new point3(*this); } + virtual kinds kind() const { return POINT3; } point3(double x, double y, double z = 0.) : cartesian_base(x, y, z) {} }; struct direction3 : public cartesian_base<3> { virtual item* clone() const { return new direction3(*this); } + virtual kinds kind() const { return DIRECTION3; } direction3(double x, double y, double z = 0.) : cartesian_base(x, y, z) {} }; struct line : public geom_item { virtual item* clone() const { return new line(*this); } + virtual kinds kind() const { return LINE; } }; struct circle : public geom_item { virtual item* clone() const { return new circle(*this); } + virtual kinds kind() const { return CIRCLE; } }; struct ellipse : public geom_item { virtual item* clone() const { return new ellipse(*this); } + virtual kinds kind() const { return ELLIPSE; } }; struct bspline : public geom_item { virtual item* clone() const { return new bspline(*this); } + virtual kinds kind() const { return BSPLINE; } }; typedef boost::variant curve; @@ -76,12 +133,14 @@ struct edge : public geom_item { boost::optional basis; virtual item* clone() const { return new edge(*this); } + virtual kinds kind() const { return EDGE; } }; struct loop : public geom_item { std::vector edges; virtual item* clone() const { return new loop(*this); } + virtual kinds kind() const { return LOOP; } }; struct face : public geom_item { @@ -89,18 +148,19 @@ struct face : public geom_item { std::vector inner; virtual item* clone() const { return new face(*this); } + virtual kinds kind() const { return FACE; } - face(int id, loop o) : geom_item(id), outer(o) {} - face(int id, loop o, std::vector i) : geom_item(id), outer(o), inner(i) {} - face(int id, matrix4 m, loop o) : geom_item(id, m), outer(o) {} - face(int id, matrix4 m, loop o, std::vector i) : geom_item(id, m), outer(o), inner(i) {} + face(const IfcUtil::IfcBaseClass* instance, loop o) : geom_item(instance), outer(o) {} + face(const IfcUtil::IfcBaseClass* instance, loop o, std::vector i) : geom_item(instance), outer(o), inner(i) {} + face(const IfcUtil::IfcBaseClass* instance, matrix4 m, loop o) : geom_item(instance, m), outer(o) {} + face(const IfcUtil::IfcBaseClass* instance, matrix4 m, loop o, std::vector i) : geom_item(instance, m), outer(o), inner(i) {} }; struct sweep : public geom_item { face basis; - sweep(int id, face b) : geom_item(id), basis(b) {} - sweep(int id, matrix4 m, face b) : geom_item(id, m), basis(b) {} + sweep(const IfcUtil::IfcBaseClass* instance, face b) : geom_item(instance), basis(b) {} + sweep(const IfcUtil::IfcBaseClass* instance, matrix4 m, face b) : geom_item(instance, m), basis(b) {} }; struct extrusion : public sweep { @@ -108,11 +168,36 @@ struct extrusion : public sweep { double depth; virtual item* clone() const { return new extrusion(*this); } - extrusion(int id, matrix4 m, face basis, direction3 dir, double d) : sweep(id, m, basis), direction(dir), depth(d) {} + virtual kinds kind() const { return EXTRUSION; } + + extrusion(const IfcUtil::IfcBaseClass* instance, matrix4 m, face basis, direction3 dir, double d) : sweep(instance, m, basis), direction(dir), depth(d) {} +}; + +struct node : public geom_item { + std::map representations; + std::vector children; + + virtual item* clone() const { return new node(*this); } + virtual kinds kind() const { return NODE; } + + node(const IfcUtil::IfcBaseClass* instance, matrix4 m, const std::map& representations, const std::vector& children) : geom_item(instance, m), representations(representations), children(children) {} +}; + +namespace impl { + // enum kinds { MATRIX4, POINT3, DIRECTION3, LINE, CIRCLE, ELLIPSE, BSPLINE, EDGE, LOOP, FACE, EXTRUSION, NODE }; + typedef std::tuple KindsTuple; +} + +struct type_by_kind { + template + using type = typename std::tuple_element::type; + + static const size_t max = std::tuple_size< impl::KindsTuple>::value; }; class topology_error : public std::runtime_error { - +public: + topology_error() : std::runtime_error("Generic topology error") {} }; } @@ -122,3 +207,5 @@ class topology_error : public std::runtime_error { } } + +#endif \ No newline at end of file diff --git a/src/ifcparse/IfcBaseClass.h b/src/ifcparse/IfcBaseClass.h index 3caa6534fd..81395e972d 100644 --- a/src/ifcparse/IfcBaseClass.h +++ b/src/ifcparse/IfcBaseClass.h @@ -78,6 +78,21 @@ namespace IfcUtil { virtual const IfcParse::entity& declaration() const = 0; Argument* get(const std::string& name) const; + + template + T get_value_or(const std::string& name, const T& if_null) const { + auto arg = get(name); + if (arg->isNull()) { + return if_null; + } else { + return *arg; + } + } + + template + T get_value(const std::string& name) const { + return *get(name); + } }; // TODO: Investigate whether these should be template classes instead diff --git a/src/ifcparse/macros.h b/src/ifcparse/macros.h index 9552139bf5..ce3132b2a1 100644 --- a/src/ifcparse/macros.h +++ b/src/ifcparse/macros.h @@ -27,7 +27,7 @@ #define STRINGIFY_(x) #x #define STRINGIFY(x) STRINGIFY_(x) -#define MAKE_INIT_FN__(a, b) init_ ## a ## b +#define MAKE_INIT_FN__(a, b) init_ ## a ## _ ## b #define MAKE_INIT_FN_(a, b) MAKE_INIT_FN__(a, b) #define MAKE_INIT_FN(t) MAKE_INIT_FN_(t, IfcSchema) diff --git a/src/serializers/ColladaSerializer.cpp b/src/serializers/ColladaSerializer.cpp index 4a4ae6934b..d5434c92da 100644 --- a/src/serializers/ColladaSerializer.cpp +++ b/src/serializers/ColladaSerializer.cpp @@ -66,7 +66,7 @@ void ColladaSerializer::ColladaExporter::ColladaGeometries::write( const std::string &mesh_id, const std::string &/**<@todo 'default_material_name' unused, remove? */, const std::vector& positions, const std::vector& normals, const std::vector& faces, const std::vector& edges, - const std::vector& material_ids, const std::vector& /**<@todo 'materials' unused, remove? */, + const std::vector& material_ids, const std::vector& /**<@todo 'materials' unused, remove? */, const std::vector& uvs, const std::vector& material_references) { openMesh(mesh_id); @@ -179,7 +179,7 @@ void ColladaSerializer::ColladaExporter::ColladaGeometries::close() { void ColladaSerializer::ColladaExporter::ColladaScene::add( const std::string& node_id, const std::string& node_name, const std::string& geom_name, - const std::vector& material_ids, const IfcGeom::Transformation& transformation) + const std::vector& material_ids, const ifcopenshell::geometry::Transformation& transformation) { if (!scene_opened) { openVisualScene(scene_id); @@ -194,13 +194,13 @@ void ColladaSerializer::ColladaExporter::ColladaScene::add( // The matrix attribute of an entity is basically a 4x3 representation of its ObjectPlacement. // Note that this placement is absolute, ie it is multiplied with all parent placements. - IfcGeom::Transformation* relative_trsf = 0; - const IfcGeom::Transformation* transformation_towrite = &transformation; + ifcopenshell::geometry::Transformation* relative_trsf = 0; + const ifcopenshell::geometry::Transformation* transformation_towrite = &transformation; // If this is not the first parent, get the relative placement if (parentNodes.size() > 0) { - relative_trsf = new IfcGeom::Transformation(matrixStack.top().multiplied(transformation)); + relative_trsf = new ifcopenshell::geometry::Transformation(matrixStack.top().multiplied(transformation)); transformation_towrite = relative_trsf; } @@ -236,22 +236,22 @@ void ColladaSerializer::ColladaExporter::ColladaScene::add( node.end(); } -void ColladaSerializer::ColladaExporter::ColladaScene::addParent(const IfcGeom::Element& parent){ +void ColladaSerializer::ColladaExporter::ColladaScene::addParent(const ifcopenshell::geometry::Element& parent){ //we open the visual scene tag if it's not. if (!scene_opened) { openVisualScene(scene_id); scene_opened = true; } - const IfcGeom::Transformation& parent_trsf = parent.transformation(); + const ifcopenshell::geometry::Transformation& parent_trsf = parent.transformation(); - IfcGeom::Transformation* relative_trsf = 0; - const IfcGeom::Transformation* transformation_towrite = &parent_trsf; + ifcopenshell::geometry::Transformation* relative_trsf = 0; + const ifcopenshell::geometry::Transformation* transformation_towrite = &parent_trsf; // If this is not the first parent, get the relative placement if (parentNodes.size() > 0) { - relative_trsf = new IfcGeom::Transformation(matrixStack.top().multiplied(parent_trsf)); + relative_trsf = new ifcopenshell::geometry::Transformation(matrixStack.top().multiplied(parent_trsf)); transformation_towrite = relative_trsf; } @@ -311,24 +311,24 @@ void ColladaSerializer::ColladaExporter::ColladaScene::write() { } void ColladaSerializer::ColladaExporter::ColladaMaterials::ColladaEffects::write( - const IfcGeom::Material &material, const std::string &material_uri) + const ifcopenshell::geometry::taxonomy::style &material, const std::string &material_uri) { openEffect(material_uri + "-fx"); COLLADASW::EffectProfile effect(mSW); effect.setShaderType(COLLADASW::EffectProfile::LAMBERT); - if (material.hasDiffuse()) { - const double* diffuse = material.diffuse(); + if (material.diffuse) { + auto diffuse = material.diffuse.get().components; effect.setDiffuse(COLLADASW::ColorOrTexture(COLLADASW::Color(diffuse[0],diffuse[1],diffuse[2]))); } - if (material.hasSpecular()) { - const double* specular = material.specular(); + if (material.specular) { + auto specular = material.specular.get().components; effect.setSpecular(COLLADASW::ColorOrTexture(COLLADASW::Color(specular[0],specular[1],specular[2]))); } - if (material.hasSpecularity()) { - effect.setShininess(material.specularity()); + if (material.specularity) { + effect.setShininess(*material.specularity); } - if (material.hasTransparency()) { - const double transparency = material.transparency(); + if (material.transparency) { + const double transparency = *material.transparency; if (transparency > 0) { // The default opacity mode for Collada is A_ONE, which apparently indicates a // transparency value of 1 to be fully opaque. Hence transparency is inverted. @@ -343,13 +343,14 @@ void ColladaSerializer::ColladaExporter::ColladaMaterials::ColladaEffects::close closeLibrary(); } -void ColladaSerializer::ColladaExporter::ColladaMaterials::add(const IfcGeom::Material& material) { +void ColladaSerializer::ColladaExporter::ColladaMaterials::add(const ifcopenshell::geometry::taxonomy::style& material) { if (!contains(material)) { - std::string material_name = (serializer->settings().get(SerializerSettings::USE_MATERIAL_NAMES) - ? material.original_name() : material.name()); + // @todo original_name + std::string material_name = *(serializer->settings().get(SerializerSettings::USE_MATERIAL_NAMES) + ? material.name : material.name); if (material_name.empty()) { - material_name = "missing-material-" + material.name(); + material_name = "missing-material-" + *material.name; } collada_id(material_name); @@ -360,19 +361,19 @@ void ColladaSerializer::ColladaExporter::ColladaMaterials::add(const IfcGeom::Ma } } -std::string ColladaSerializer::ColladaExporter::ColladaMaterials::getMaterialUri(const IfcGeom::Material& material) { - std::vector::iterator it = std::find(materials.begin(), materials.end(), material); +std::string ColladaSerializer::ColladaExporter::ColladaMaterials::getMaterialUri(const ifcopenshell::geometry::taxonomy::style& material) { + std::vector::iterator it = std::find(materials.begin(), materials.end(), material); ptrdiff_t index = std::distance(materials.begin(), it); return material_uris.at(index); } -bool ColladaSerializer::ColladaExporter::ColladaMaterials::contains(const IfcGeom::Material& material) { +bool ColladaSerializer::ColladaExporter::ColladaMaterials::contains(const ifcopenshell::geometry::taxonomy::style& material) { return std::find(materials.begin(), materials.end(), material) != materials.end(); } void ColladaSerializer::ColladaExporter::ColladaMaterials::write() { effects.close(); - BOOST_FOREACH(const IfcGeom::Material& material, materials) { + BOOST_FOREACH(const ifcopenshell::geometry::taxonomy::style& material, materials) { std::string material_name = getMaterialUri(material); openMaterial(material_name); @@ -395,9 +396,9 @@ void ColladaSerializer::ColladaExporter::startDocument(const std::string& unit_n asset.add(); } -void ColladaSerializer::ColladaExporter::write(const IfcGeom::TriangulationElement* o) +void ColladaSerializer::ColladaExporter::write(const ifcopenshell::geometry::TriangulationElement* o) { - const IfcGeom::Representation::Triangulation& mesh = o->geometry(); + const ifcopenshell::geometry::Representation::Triangulation& mesh = o->geometry(); std::string name = serializer->object_id(o); collada_id(name); @@ -406,7 +407,7 @@ void ColladaSerializer::ColladaExporter::write(const IfcGeom::TriangulationEleme collada_id(representation_id); std::vector material_references; - BOOST_FOREACH(const IfcGeom::Material& material, mesh.materials()) { + BOOST_FOREACH(const ifcopenshell::geometry::taxonomy::style& material, mesh.materials()) { materials.add(material); std::string material_name = materials.getMaterialUri(material); @@ -456,7 +457,7 @@ std::string ColladaSerializer::differentiateSlabTypes(const IfcUtil::IfcBaseEnti return result; } -std::string ColladaSerializer::object_id(const IfcGeom::Element* o) /*override*/ +std::string ColladaSerializer::object_id(const ifcopenshell::geometry::Element* o) /*override*/ { if (settings_.get(SerializerSettings::USE_ELEMENT_TYPES)) { const std::string slabSuffix = (o->product() && o->product()->declaration().name() == "IfcSlab") @@ -549,7 +550,7 @@ void ColladaSerializer::writeHeader() { exporter.startDocument(unit_name, unit_magnitude); } -void ColladaSerializer::write(const IfcGeom::TriangulationElement* o) { +void ColladaSerializer::write(const ifcopenshell::geometry::TriangulationElement* o) { exporter.write(o); } diff --git a/src/serializers/ColladaSerializer.h b/src/serializers/ColladaSerializer.h index 3236dc3e69..849612e81e 100644 --- a/src/serializers/ColladaSerializer.h +++ b/src/serializers/ColladaSerializer.h @@ -41,8 +41,6 @@ #pragma GCC diagnostic pop #endif -#include "../ifcgeom/schema_agnostic/IfcGeomIterator.h" - #include "../serializers/GeometrySerializer.h" #include @@ -68,14 +66,14 @@ private: , serializer(_serializer) {} void addFloatSource(const std::string& mesh_id, const std::string& suffix, - const std::vector& floats, const char* coords = "XYZ"); + const std::vector& floats, const char* coords = "XYZ"); /// @todo pass simply DeferredObject? void write( const std::string &mesh_id, const std::string &default_material_name, - const std::vector& positions, const std::vector& normals, + const std::vector& positions, const std::vector& normals, const std::vector& faces, const std::vector& edges, - const std::vector& material_ids, const std::vector& materials, - const std::vector& uvs, const std::vector& material_references); + const std::vector& material_ids, const std::vector& materials, + const std::vector& uvs, const std::vector& material_references); void close(); ColladaSerializer *serializer; }; @@ -88,7 +86,7 @@ private: const std::string scene_id; bool scene_opened; std::stack parentNodes; - std::stack > matrixStack; + std::stack matrixStack; public: ColladaScene(const std::string& scene_id, COLLADASW::StreamWriter& stream, ColladaSerializer *_serializer) : COLLADASW::LibraryVisualScenes(&stream) @@ -97,8 +95,8 @@ private: , serializer(_serializer) {} void add(const std::string& node_id, const std::string& node_name, const std::string& geom_name, - const std::vector& material_ids, const IfcGeom::Transformation& matrix); - void addParent(const IfcGeom::Element& parent); + const std::vector& material_ids, const ifcopenshell::geometry::Transformation& matrix); + void addParent(const ifcopenshell::geometry::Element& parent); void closeParent(); COLLADASW::Node* GetDirectParent(); void write(); @@ -117,11 +115,11 @@ private: explicit ColladaEffects(COLLADASW::StreamWriter& stream) : COLLADASW::LibraryEffects(&stream) {} - void write(const IfcGeom::Material &material, const std::string &material_uri); + void write(const ifcopenshell::geometry::taxonomy::style &material, const std::string &material_uri); void close(); ColladaSerializer *serializer; }; - std::vector materials; + std::vector materials; std::vector material_uris; public: explicit ColladaMaterials(COLLADASW::StreamWriter& stream, ColladaSerializer *_serializer) @@ -129,9 +127,9 @@ private: , serializer(_serializer) , effects(stream) {} - void add(const IfcGeom::Material& material); - std::string getMaterialUri(const IfcGeom::Material& material); - bool contains(const IfcGeom::Material& material); + void add(const ifcopenshell::geometry::taxonomy::style& material); + std::string getMaterialUri(const ifcopenshell::geometry::taxonomy::style& material); + bool contains(const ifcopenshell::geometry::taxonomy::style& material); void write(); ColladaSerializer *serializer; ColladaEffects effects; @@ -158,21 +156,21 @@ private: public: std::string unique_id, representation_id, type; - IfcGeom::Transformation transformation; - std::vector vertices; - std::vector normals; + ifcopenshell::geometry::Transformation transformation; + std::vector vertices; + std::vector normals; std::vector faces; std::vector edges; std::vector material_ids; - std::vector materials; + std::vector materials; std::vector material_references; - std::vector uvs; - std::vector*> parents_; + std::vector uvs; + std::vector parents_; - DeferredObject(const std::string& unique_id, const std::string& representation_id, const std::string& type, const IfcGeom::Transformation& transformation, - const std::vector& vertices, const std::vector& normals, const std::vector& faces, - const std::vector& edges, const std::vector& material_ids, const std::vector& materials, - const std::vector& material_references, const std::vector& uvs) + DeferredObject(const std::string& unique_id, const std::string& representation_id, const std::string& type, const ifcopenshell::geometry::Transformation& transformation, + const std::vector& vertices, const std::vector& normals, const std::vector& faces, + const std::vector& edges, const std::vector& material_ids, const std::vector& materials, + const std::vector& material_references, const std::vector& uvs) : unique_id(unique_id) , representation_id(representation_id) , type(type) @@ -187,8 +185,8 @@ private: , uvs(uvs) {} - std::vector*>& parents() { return parents_; } - const std::vector*>& parents() const { return parents_; } + std::vector& parents() { return parents_; } + const std::vector& parents() const { return parents_; } }; COLLADABU::NativeString filename; COLLADASW::StreamWriter stream; @@ -211,7 +209,7 @@ private: std::vector deferreds; virtual ~ColladaExporter() {} void startDocument(const std::string& unit_name, float unit_magnitude); - void write(const IfcGeom::TriangulationElement* o); + void write(const ifcopenshell::geometry::TriangulationElement* o); void endDocument(); }; ColladaExporter exporter; @@ -229,8 +227,8 @@ public: } bool ready(); void writeHeader(); - void write(const IfcGeom::TriangulationElement* o); - void write(const IfcGeom::NativeElement* /*o*/) {} + void write(const ifcopenshell::geometry::TriangulationElement* o); + void write(const ifcopenshell::geometry::NativeElement* /*o*/) {} void finalize(); bool isTesselated() const { return true; } void setUnitNameAndMagnitude(const std::string& name, float magnitude) { @@ -239,7 +237,7 @@ public: } void setFile(IfcParse::IfcFile*) {} - std::string object_id(const IfcGeom::Element* o) /*override*/; + std::string object_id(const ifcopenshell::geometry::Element* o) /*override*/; private: static std::string differentiateSlabTypes(const IfcUtil::IfcBaseEntity* slab); diff --git a/src/serializers/GeometrySerializer.h b/src/serializers/GeometrySerializer.h index ff50154f29..3eec952cc7 100644 --- a/src/serializers/GeometrySerializer.h +++ b/src/serializers/GeometrySerializer.h @@ -27,29 +27,29 @@ typedef float real_t; #endif #include "../serializers/Serializer.h" -#include "../ifcgeom/schema_agnostic/IfcGeomIterator.h" #include "../ifcgeom/schema_agnostic/IfcGeomElement.h" +#include "../ifcgeom/settings.h" -class SerializerSettings : public IfcGeom::IteratorSettings +class SerializerSettings : public ifcopenshell::geometry::settings { public: enum Setting { /// Use entity names instead of unique IDs for naming elements. /// Applicable for OBJ, DAE, and SVG output. - USE_ELEMENT_NAMES = 1 << (IfcGeom::IteratorSettings::NUM_SETTINGS + 1), + USE_ELEMENT_NAMES = 1 << (ifcopenshell::geometry::settings::NUM_SETTINGS + 1), /// Use entity GUIDs instead of unique IDs for naming elements. /// Applicable for OBJ, DAE, and SVG output. - USE_ELEMENT_GUIDS = 1 << (IfcGeom::IteratorSettings::NUM_SETTINGS + 2), + USE_ELEMENT_GUIDS = 1 << (ifcopenshell::geometry::settings::NUM_SETTINGS + 2), /// Use material names instead of unique IDs for naming materials. /// Applicable for OBJ and DAE output. - USE_MATERIAL_NAMES = 1 << (IfcGeom::IteratorSettings::NUM_SETTINGS + 3), + USE_MATERIAL_NAMES = 1 << (ifcopenshell::geometry::settings::NUM_SETTINGS + 3), /// Use element types instead of unique IDs for naming elements. /// Applicable for DAE output. - USE_ELEMENT_TYPES = 1 << (IfcGeom::IteratorSettings::NUM_SETTINGS + 4), + USE_ELEMENT_TYPES = 1 << (ifcopenshell::geometry::settings::NUM_SETTINGS + 4), /// Order the elements using their IfcBuildingStorey parent /// Applicable for DAE output - USE_ELEMENT_HIERARCHY = 1 << (IfcGeom::IteratorSettings::NUM_SETTINGS + 5), + USE_ELEMENT_HIERARCHY = 1 << (ifcopenshell::geometry::settings::NUM_SETTINGS + 5), /// Number of different setting flags. NUM_SETTINGS = 5 }; @@ -76,15 +76,15 @@ public: virtual ~GeometrySerializer() {} virtual bool isTesselated() const = 0; - virtual void write(const IfcGeom::TriangulationElement* o) = 0; - virtual void write(const IfcGeom::NativeElement* o) = 0; + virtual void write(const ifcopenshell::geometry::TriangulationElement* o) = 0; + virtual void write(const ifcopenshell::geometry::NativeElement* o) = 0; virtual void setUnitNameAndMagnitude(const std::string& name, float magnitude) = 0; const SerializerSettings& settings() const { return settings_; } SerializerSettings& settings() { return settings_; } /// Returns ID for the object depending on the used setting. - virtual std::string object_id(const IfcGeom::Element* o) + virtual std::string object_id(const ifcopenshell::geometry::Element* o) { if (settings_.get(SerializerSettings::USE_ELEMENT_GUIDS)) return o->guid(); if (settings_.get(SerializerSettings::USE_ELEMENT_NAMES)) return o->name(); diff --git a/src/serializers/OpenCascadeBasedSerializer.h b/src/serializers/OpenCascadeBasedSerializer.h index bb92b4fd1c..81b8bbd126 100644 --- a/src/serializers/OpenCascadeBasedSerializer.h +++ b/src/serializers/OpenCascadeBasedSerializer.h @@ -20,7 +20,6 @@ #ifndef OPENCASCADEBASEDSERIALIZER_H #define OPENCASCADEBASEDSERIALIZER_H -#include "../ifcgeom/schema_agnostic/IfcGeomIterator.h" #include "../ifcgeom/schema_agnostic/opencascade/OpenCascadeConversionResult.h" #include "../serializers/GeometrySerializer.h" diff --git a/src/serializers/schema_dependent/XmlSerializer.cpp b/src/serializers/schema_dependent/XmlSerializer.cpp index d47c184555..dbe310d538 100644 --- a/src/serializers/schema_dependent/XmlSerializer.cpp +++ b/src/serializers/schema_dependent/XmlSerializer.cpp @@ -28,7 +28,7 @@ #include "../../ifcparse/IfcSIPrefix.h" #include "../../ifcparse/utils.h" -#include "../../ifcgeom/kernels/opencascade/IfcGeom.h" +#include "../../ifcgeom/abstract_mapping.h" using boost::property_tree::ptree; @@ -57,7 +57,7 @@ std::map POSTFIX_SCHEMA(argument_name_map); // Format an IFC attribute and maybe returns as string. Only literal scalar // values are converted. Things like entity instances and lists are omitted. -boost::optional format_attribute(const Argument* argument, IfcUtil::ArgumentType argument_type, const std::string& argument_name) { +boost::optional format_attribute(ifcopenshell::geometry::abstract_mapping* mapping, const Argument* argument, IfcUtil::ArgumentType argument_type, const std::string& argument_name) { boost::optional value; // Hard-code lat-lon as it represents an array @@ -106,7 +106,7 @@ boost::optional format_attribute(const Argument* argument, IfcUtil: IfcUtil::IfcBaseClass* e = *argument; if (!e->declaration().as_entity()) { IfcUtil::IfcBaseType* f = (IfcUtil::IfcBaseType*) e; - value = format_attribute(f->data().getArgument(0), f->data().getArgument(0)->type(), argument_name); + value = format_attribute(mapping, f->data().getArgument(0), f->data().getArgument(0)->type(), argument_name); } else if (e->declaration().is(IfcSchema::IfcSIUnit::Class()) || e->declaration().is(IfcSchema::IfcConversionBasedUnit::Class())) { // Some string concatenation to have a unit name as a XML attribute. @@ -125,21 +125,21 @@ boost::optional format_attribute(const Argument* argument, IfcUtil: value = unit_name; } else if (e->declaration().is(IfcSchema::IfcLocalPlacement::Class())) { - IfcSchema::IfcLocalPlacement* placement = e->as(); - gp_Trsf trsf; - IfcGeom::POSTFIX_SCHEMA(Kernel) kernel; - - if (kernel.convert(placement, trsf)) { - std::stringstream stream; - for (int i = 1; i < 5; ++i) { - for (int j = 1; j < 4; ++j) { - const double trsf_value = trsf.Value(j, i); - stream << trsf_value << " "; - } - stream << ((i == 4) ? "1" : "0 "); + auto placement = mapping->map(e); + auto matrix = (ifcopenshell::geometry::taxonomy::matrix4*) placement; + + std::stringstream stream; + for (int i = 0; i < 16; ++i) { + const double trsf_value = matrix->components[i]; + stream << trsf_value; + if (i != 15) { + stream << " "; } - value = stream.str(); - } + } + value = stream.str(); + + delete mapping; + delete placement; } break; } default: @@ -149,7 +149,7 @@ boost::optional format_attribute(const Argument* argument, IfcUtil: } // Appends to a node with possibly existing attributes -ptree& format_entity_instance(IfcUtil::IfcBaseEntity* instance, ptree& child, ptree& tree, bool as_link = false) { +ptree& format_entity_instance(ifcopenshell::geometry::abstract_mapping* mapping, IfcUtil::IfcBaseEntity* instance, ptree& child, ptree& tree, bool as_link = false) { const unsigned n = instance->declaration().attribute_count(); for (unsigned i = 0; i < n; ++i) { try { @@ -172,11 +172,9 @@ ptree& format_entity_instance(IfcUtil::IfcBaseEntity* instance, ptree& child, pt const std::string qualified_name = instance->declaration().name() + "." + argument_name; boost::optional value; try { - value = format_attribute(argument, argument_type, qualified_name); + value = format_attribute(mapping, argument, argument_type, qualified_name); } catch (const std::exception& e) { Logger::Error(e); - } catch (const Standard_ConstructionError& e) { - Logger::Error(e.GetMessageString(), instance); } if (value) { @@ -196,9 +194,9 @@ ptree& format_entity_instance(IfcUtil::IfcBaseEntity* instance, ptree& child, pt // Formats an entity instances as a ptree node, and insert into the DOM. Recurses // over the entity attributes and writes them as xml attributes of the node. -ptree& format_entity_instance(IfcUtil::IfcBaseEntity* instance, ptree& tree, bool as_link = false) { +ptree& format_entity_instance(ifcopenshell::geometry::abstract_mapping* mapping, IfcUtil::IfcBaseEntity* instance, ptree& tree, bool as_link = false) { ptree child; - return format_entity_instance(instance, child, tree, as_link); + return format_entity_instance(mapping, instance, child, tree, as_link); } std::string qualify_unrooted_instance(IfcUtil::IfcBaseClass* inst) { @@ -208,11 +206,11 @@ std::string qualify_unrooted_instance(IfcUtil::IfcBaseClass* inst) { // A function to be called recursively. Template specialization is used // to descend into decomposition, containment and property relationships. template -ptree& descend(A* instance, ptree& tree) { +ptree& descend(ifcopenshell::geometry::abstract_mapping* mapping, A* instance, ptree& tree) { if (instance->declaration().is(IfcSchema::IfcObjectDefinition::Class())) { - return descend(instance->template as(), tree); + return descend(mapping, instance->template as(), tree); } else { - return format_entity_instance(instance, tree); + return format_entity_instance(mapping, instance, tree); } } @@ -232,8 +230,8 @@ typename V::list::ptr get_related(T* t, F f, G g) { // Descends into the tree by recursing into IfcRelContainedInSpatialStructure, // IfcRelDecomposes, IfcRelDefinesByType, IfcRelDefinesByProperties relations. template <> -ptree& descend(IfcSchema::IfcObjectDefinition* product, ptree& tree) { - ptree& child = format_entity_instance(product, tree); +ptree& descend(ifcopenshell::geometry::abstract_mapping* mapping, IfcSchema::IfcObjectDefinition* product, ptree& tree) { + ptree& child = format_entity_instance(mapping, product, tree); if (product->declaration().is(IfcSchema::IfcSpatialStructureElement::Class())) { IfcSchema::IfcSpatialStructureElement* structure = (IfcSchema::IfcSpatialStructureElement*) product; @@ -243,7 +241,7 @@ ptree& descend(IfcSchema::IfcObjectDefinition* product, ptree& tree) { (structure, &IfcSchema::IfcSpatialStructureElement::ContainsElements, &IfcSchema::IfcRelContainedInSpatialStructure::RelatedElements); for (IfcSchema::IfcObjectDefinition::list::it it = elements->begin(); it != elements->end(); ++it) { - descend(*it, child); + descend(mapping, *it, child); } } @@ -253,7 +251,7 @@ ptree& descend(IfcSchema::IfcObjectDefinition* product, ptree& tree) { element, &IfcSchema::IfcElement::HasOpenings, &IfcSchema::IfcRelVoidsElement::RelatedOpeningElement); for (IfcSchema::IfcOpeningElement::list::it it = openings->begin(); it != openings->end(); ++it) { - descend(*it, child); + descend(mapping, *it, child); } } @@ -269,7 +267,7 @@ ptree& descend(IfcSchema::IfcObjectDefinition* product, ptree& tree) { for (IfcSchema::IfcObjectDefinition::list::it it = structures->begin(); it != structures->end(); ++it) { IfcSchema::IfcObjectDefinition* ob = *it; - descend(ob, child); + descend(mapping, ob, child); } if (product->declaration().is(IfcSchema::IfcObject::Class())) { @@ -282,13 +280,13 @@ ptree& descend(IfcSchema::IfcObjectDefinition* product, ptree& tree) { for (IfcSchema::IfcPropertySetDefinition::list::it it = property_sets->begin(); it != property_sets->end(); ++it) { IfcSchema::IfcPropertySetDefinition* pset = *it; if (pset->declaration().is(IfcSchema::IfcPropertySet::Class())) { - format_entity_instance(pset, child, true); + format_entity_instance(mapping, pset, child, true); } if (pset->declaration().is(IfcSchema::IfcElementQuantity::Class())) { - format_entity_instance(pset, child, true); + format_entity_instance(mapping, pset, child, true); } if (pset->declaration().is(IfcSchema::IfcElementQuantity::Class())) { - format_entity_instance(pset, child, true); + format_entity_instance(mapping, pset, child, true); } } @@ -304,19 +302,19 @@ ptree& descend(IfcSchema::IfcObjectDefinition* product, ptree& tree) { for (IfcSchema::IfcTypeObject::list::it it = types->begin(); it != types->end(); ++it) { IfcSchema::IfcTypeObject* type = *it; - format_entity_instance(type, child, true); + format_entity_instance(mapping, type, child, true); } } if (product->declaration().is(IfcSchema::IfcProduct::Class())) { - std::map layers = IfcGeom::Kernel::get_layers(product); + std::map layers = mapping->get_layers(product); for (std::map::const_iterator it = layers.begin(); it != layers.end(); ++it) { // IfcPresentationLayerAssignments don't have GUIDs (only optional Identifier) so use name as the ID. // Note that the IfcPresentationLayerAssignment passed here doesn't really matter as as_link is true // for the format_entity_instance() call. ptree node; node.put(".xlink:href", "#" + it->first); - format_entity_instance(it->second, node, child, true); + format_entity_instance(mapping, it->second, node, child, true); } IfcSchema::IfcRelAssociates::list::ptr associations = product->HasAssociations(); @@ -325,7 +323,7 @@ ptree& descend(IfcSchema::IfcObjectDefinition* product, ptree& tree) { IfcSchema::IfcMaterialSelect* mat = (*it)->as()->RelatingMaterial(); ptree node; node.put(".xlink:href", "#" + qualify_unrooted_instance(mat)); - format_entity_instance((IfcUtil::IfcBaseEntity*) mat, node, child, true); + format_entity_instance(mapping, (IfcUtil::IfcBaseEntity*) mat, node, child, true); } } } @@ -334,26 +332,26 @@ ptree& descend(IfcSchema::IfcObjectDefinition* product, ptree& tree) { } // Format IfcProperty instances and insert into the DOM. IfcComplexProperties are flattened out. -void format_properties(IfcSchema::IfcProperty::list::ptr properties, ptree& node) { +void format_properties(ifcopenshell::geometry::abstract_mapping* mapping, IfcSchema::IfcProperty::list::ptr properties, ptree& node) { for (IfcSchema::IfcProperty::list::it it = properties->begin(); it != properties->end(); ++it) { IfcSchema::IfcProperty* p = *it; if (p->declaration().is(IfcSchema::IfcComplexProperty::Class())) { IfcSchema::IfcComplexProperty* complex = (IfcSchema::IfcComplexProperty*) p; - format_properties(complex->HasProperties(), node); + format_properties(mapping, complex->HasProperties(), node); } else { - format_entity_instance(p, node); + format_entity_instance(mapping, p, node); } } } // Format IfcElementQuantity instances and insert into the DOM. -void format_quantities(IfcSchema::IfcPhysicalQuantity::list::ptr quantities, ptree& node) { +void format_quantities(ifcopenshell::geometry::abstract_mapping* mapping, IfcSchema::IfcPhysicalQuantity::list::ptr quantities, ptree& node) { for (IfcSchema::IfcPhysicalQuantity::list::it it = quantities->begin(); it != quantities->end(); ++it) { IfcSchema::IfcPhysicalQuantity* p = *it; - ptree& node2 = format_entity_instance(p, node); + ptree& node2 = format_entity_instance(mapping, p, node); if (p->declaration().is(IfcSchema::IfcPhysicalComplexQuantity::Class())) { IfcSchema::IfcPhysicalComplexQuantity* complex = (IfcSchema::IfcPhysicalComplexQuantity*)p; - format_quantities(complex->HasQuantities(), node2); + format_quantities(mapping, complex->HasQuantities(), node2); } } } @@ -435,22 +433,22 @@ void POSTFIX_SCHEMA(XmlSerializer)::finalize() { } // Descend into the decomposition structure of the IFC file. - descend(project, decomposition); + descend(mapping_, project, decomposition); // Write all property sets and values as XML nodes. IfcSchema::IfcPropertySet::list::ptr psets = file->instances_by_type(); for (IfcSchema::IfcPropertySet::list::it it = psets->begin(); it != psets->end(); ++it) { IfcSchema::IfcPropertySet* pset = *it; - ptree& node = format_entity_instance(pset, properties); - format_properties(pset->HasProperties(), node); + ptree& node = format_entity_instance(mapping_, pset, properties); + format_properties(mapping_, pset->HasProperties(), node); } // Write all quantities and values as XML nodes. IfcSchema::IfcElementQuantity::list::ptr qtosets = file->instances_by_type(); for (IfcSchema::IfcElementQuantity::list::it it = qtosets->begin(); it != qtosets->end(); ++it) { IfcSchema::IfcElementQuantity* qto = *it; - ptree& node = format_entity_instance(qto, quantities); - format_quantities(qto->Quantities(), node); + ptree& node = format_entity_instance(mapping_, qto, quantities); + format_quantities(mapping_, qto->Quantities(), node); } @@ -458,7 +456,7 @@ void POSTFIX_SCHEMA(XmlSerializer)::finalize() { IfcSchema::IfcTypeObject::list::ptr type_objects = file->instances_by_type(); for (IfcSchema::IfcTypeObject::list::it it = type_objects->begin(); it != type_objects->end(); ++it) { IfcSchema::IfcTypeObject* type_object = *it; - ptree& node = descend(type_object, types); + ptree& node = descend(mapping_, type_object, types); // ptree& node = format_entity_instance(type_object, types); if (type_object->hasHasPropertySets()) { @@ -466,7 +464,7 @@ void POSTFIX_SCHEMA(XmlSerializer)::finalize() { for (IfcSchema::IfcPropertySetDefinition::list::it jt = property_sets->begin(); jt != property_sets->end(); ++jt) { IfcSchema::IfcPropertySetDefinition* pset = *jt; if (pset->declaration().is(IfcSchema::IfcPropertySet::Class())) { - format_entity_instance(pset, node, true); + format_entity_instance(mapping_, pset, node, true); } } } @@ -477,10 +475,10 @@ void POSTFIX_SCHEMA(XmlSerializer)::finalize() { for (IfcEntityList::it it = unit_assignments->begin(); it != unit_assignments->end(); ++it) { if ((*it)->declaration().is(IfcSchema::IfcNamedUnit::Class())) { IfcSchema::IfcNamedUnit* named_unit = (*it)->as(); - ptree& node = format_entity_instance(named_unit, units); + ptree& node = format_entity_instance(mapping, named_unit, units); node.put(".SI_equivalent", IfcParse::get_SI_equivalent(named_unit)); } else if ((*it)->declaration().is(IfcSchema::IfcMonetaryUnit::Class())) { - format_entity_instance((*it)->as(), units); + format_entity_instance(mapping_, (*it)->as(), units); } } @@ -495,7 +493,7 @@ void POSTFIX_SCHEMA(XmlSerializer)::finalize() { layer_names.insert(name); ptree node; node.put(".id", name); - format_entity_instance(*it, node, layers); + format_entity_instance(mapping_, *it, node, layers); } } @@ -521,16 +519,16 @@ void POSTFIX_SCHEMA(XmlSerializer)::finalize() { if ((*jt)->hasMaterial()) { subnode.put(".Name", (*jt)->Material()->Name()); } - format_entity_instance(*jt, subnode, node); + format_entity_instance(mapping_, *jt, subnode, node); } } else if (mat->as()) { IfcSchema::IfcMaterial::list::ptr mats = mat->as()->Materials(); for (IfcSchema::IfcMaterial::list::it jt = mats->begin(); jt != mats->end(); ++jt) { ptree subnode; - format_entity_instance(*jt, subnode, node); + format_entity_instance(mapping_, *jt, subnode, node); } } - format_entity_instance((IfcUtil::IfcBaseEntity*) mat, node, materials); + format_entity_instance(mapping_, (IfcUtil::IfcBaseEntity*) mat, node, materials); } } diff --git a/src/serializers/schema_dependent/XmlSerializer.h b/src/serializers/schema_dependent/XmlSerializer.h index 9e55d7f1f6..2141576e41 100644 --- a/src/serializers/schema_dependent/XmlSerializer.h +++ b/src/serializers/schema_dependent/XmlSerializer.h @@ -20,6 +20,7 @@ #ifndef XMLSERIALIZERIMPL_H #define XMLSERIALIZERIMPL_H +#include "../../ifcgeom/abstract_mapping.h" #include "../../ifcparse/macros.h" #include "../../serializers/XmlSerializer.h" @@ -29,10 +30,12 @@ class POSTFIX_SCHEMA(XmlSerializer) : public XmlSerializer { private: IfcParse::IfcFile* file; + ifcopenshell::geometry::abstract_mapping* mapping_; public: POSTFIX_SCHEMA(XmlSerializer)(IfcParse::IfcFile* file, const std::string& xml_filename) : XmlSerializer(0, "") + , mapping_(ifcopenshell::geometry::impl::mapping_implementations().construct(file)) { this->file = file; this->xml_filename = xml_filename; diff --git a/win/build-deps.cmd b/win/build-deps.cmd index b09ed33d47..1805fb5180 100644 --- a/win/build-deps.cmd +++ b/win/build-deps.cmd @@ -157,6 +157,8 @@ set OCE_VERSION=OCE-0.18 set PYTHON_VERSION=3.4.3 set SWIG_VERSION=3.0.12 +goto :Eigen + :: Note all of the dependencies have appropriate label so that user can easily skip something if wanted :: by modifying this file and using goto. :Boost @@ -459,6 +461,15 @@ IF NOT %ERRORLEVEL%==0 GOTO :Error call :InstallCMakeProject "%DEPENDENCY_DIR%\%BUILD_DIR%" %BUILD_CFG% IF NOT %ERRORLEVEL%==0 GOTO :Error +:Eigen +set DEPENDENCY_NAME=eigen +call :DownloadFile http://bitbucket.org/eigen/eigen/get/3.3.7.zip "%DEPS_DIR%" eigen-eigen-323c052e1731.zip +IF NOT %ERRORLEVEL%==0 GOTO :Error +call :ExtractArchive eigen-eigen-323c052e1731.zip "%DEPS_DIR%" "%DEPS_DIR%\eigen" +IF NOT %ERRORLEVEL%==0 GOTO :Error +IF NOT EXIST "%INSTALL_DIR%\Eigen\Eigen". mkdir "%INSTALL_DIR%\Eigen\Eigen" +robocopy /MIR "%DEPS_DIR%\eigen-eigen-323c052e1731\Eigen" "%INSTALL_DIR%\Eigen\Eigen" + :Successful echo. call "%~dp0\utils\cecho.cmd" 0 10 "%PROJECT_NAME% dependencies built." diff --git a/win/run-cmake.bat b/win/run-cmake.bat index 058aa3498a..f091990988 100755 --- a/win/run-cmake.bat +++ b/win/run-cmake.bat @@ -86,6 +86,7 @@ set MPFR_INCLUDE_DIR=%INSTALL_DIR%\mpfr set MPFR_LIBRARY_DIR=%INSTALL_DIR%\mpfr set VOXEL_INCLUDE_DIR=%INSTALL_DIR%\voxel\include set VOXEL_LIBRARY_DIR=%INSTALL_DIR%\voxel\lib +set EIGEN_DIR=%INSTALL_DIR%\Eigen echo. call cecho.cmd 0 10 "Script configuration:" @@ -115,6 +116,7 @@ echo MPFR_INCLUDE_DIR = %MPFR_INCLUDE_DIR% echo MPFR_LIBRARY_DIR = %MPFR_LIBRARY_DIR% echo VOXEL_INCLUDE_DIR = %VOXEL_INCLUDE_DIR% echo VOXEL_LIBRARY_DIR = %VOXEL_LIBRARY_DIR% +echo EIGEN_DIR = %EIGEN_DIR% echo. echo CMAKE_INSTALL_PREFIX = %CMAKE_INSTALL_PREFIX% echo.