diff --git a/src/ifcgeom/IfcGeom.h b/src/ifcgeom/IfcGeom.h index 4f64f80ded..3a5be5d41f 100644 --- a/src/ifcgeom/IfcGeom.h +++ b/src/ifcgeom/IfcGeom.h @@ -82,6 +82,7 @@ namespace IfcGeom { const int DISABLE_OPENING_SUBTRACTIONS = 1 << 0; const int DISABLE_OBJECT_PLACEMENT = 1 << 1; const int SEW_SHELLS = 1 << 2; + const int CONVERT_TO_METERS = 1 << 3; 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); @@ -107,7 +108,8 @@ namespace IfcGeom { void apply_tolerance(TopoDS_Shape& s, double t); void SetValue(GeomValue var, double value); double GetValue(GeomValue var); - std::string create_brep_data(Ifc2x3::IfcProduct* s, unsigned int settings); + std::string create_brep_data(IfcSchema::IfcProduct* s, unsigned int settings); + void initialize_units_and_precision(IfcSchema::IfcProject* proj, double& unit_magnitude, std::string& unit_name); bool fill_nonmanifold_wires_with_planar_faces(TopoDS_Shape& shape); IfcSchema::IfcProductDefinitionShape* tesselate(TopoDS_Shape& shape, double deflection, IfcEntities es); diff --git a/src/ifcgeom/IfcGeomFunctions.cpp b/src/ifcgeom/IfcGeomFunctions.cpp index 1f5e387200..4c786fa37c 100644 --- a/src/ifcgeom/IfcGeomFunctions.cpp +++ b/src/ifcgeom/IfcGeomFunctions.cpp @@ -690,10 +690,11 @@ bool IfcGeom::fill_nonmanifold_wires_with_planar_faces(TopoDS_Shape& shape) { return true; } -std::string IfcGeom::create_brep_data(Ifc2x3::IfcProduct* ifc_product, unsigned int settings) { +std::string IfcGeom::create_brep_data(IfcSchema::IfcProduct* ifc_product, unsigned int settings) { const bool no_openings = !!(settings & DISABLE_OPENING_SUBTRACTIONS); const bool no_placement = !!(settings & DISABLE_OBJECT_PLACEMENT); const bool sew_shells = !!(settings & SEW_SHELLS); + const bool convert_to_meters = !!(settings & CONVERT_TO_METERS); const double old_max_faces_to_sew = GetValue(GV_MAX_FACES_TO_SEW); const double inf = std::numeric_limits::infinity(); @@ -703,13 +704,13 @@ std::string IfcGeom::create_brep_data(Ifc2x3::IfcProduct* ifc_product, unsigned if (ifc_product->hasRepresentation()) { - Ifc2x3::IfcProductRepresentation* prod_rep = ifc_product->Representation(); - Ifc2x3::IfcRepresentation::list li = prod_rep->Representations(); - Ifc2x3::IfcShapeRepresentation* shape_rep = 0; - for (Ifc2x3::IfcRepresentation::it i = li->begin(); i != li->end(); ++i) { + IfcSchema::IfcProductRepresentation* prod_rep = ifc_product->Representation(); + IfcSchema::IfcRepresentation::list li = prod_rep->Representations(); + IfcSchema::IfcShapeRepresentation* shape_rep = 0; + for (IfcSchema::IfcRepresentation::it i = li->begin(); i != li->end(); ++i) { const std::string representation_identifier = (*i)->RepresentationIdentifier(); - if ((*i)->is(Ifc2x3::Type::IfcShapeRepresentation) && (representation_identifier == "Body" || representation_identifier == "Facetation")) { - shape_rep = (Ifc2x3::IfcShapeRepresentation*) *i; + if ((*i)->is(IfcSchema::Type::IfcShapeRepresentation) && (representation_identifier == "Body" || representation_identifier == "Facetation")) { + shape_rep = (IfcSchema::IfcShapeRepresentation*) *i; break; } } @@ -781,6 +782,11 @@ std::string IfcGeom::create_brep_data(Ifc2x3::IfcProduct* ifc_product, unsigned BRepBuilderAPI_GTransform(s,trsf,true).Shape(); builder.Add(compound,moved_shape); } + if (GetValue(GV_LENGTH_UNIT) != 1. && !convert_to_meters) { + gp_Trsf length_unit_scale; + length_unit_scale.SetScaleFactor(1. / GetValue(GV_LENGTH_UNIT)); + compound.Move(length_unit_scale); + } std::stringstream sstream; BRepTools::Write(compound,sstream); brep_data = sstream.str(); @@ -791,4 +797,124 @@ std::string IfcGeom::create_brep_data(Ifc2x3::IfcProduct* ifc_product, unsigned SetValue(GV_MAX_FACES_TO_SEW, old_max_faces_to_sew); return brep_data; +} + +double UnitPrefixToValue( IfcSchema::IfcSIPrefix::IfcSIPrefix v ) { + if ( v == IfcSchema::IfcSIPrefix::IfcSIPrefix_EXA ) return (double) 1e18; + else if ( v == IfcSchema::IfcSIPrefix::IfcSIPrefix_PETA ) return (double) 1e15; + else if ( v == IfcSchema::IfcSIPrefix::IfcSIPrefix_TERA ) return (double) 1e12; + else if ( v == IfcSchema::IfcSIPrefix::IfcSIPrefix_GIGA ) return (double) 1e9; + else if ( v == IfcSchema::IfcSIPrefix::IfcSIPrefix_MEGA ) return (double) 1e6; + else if ( v == IfcSchema::IfcSIPrefix::IfcSIPrefix_KILO ) return (double) 1e3; + else if ( v == IfcSchema::IfcSIPrefix::IfcSIPrefix_HECTO ) return (double) 1e2; + else if ( v == IfcSchema::IfcSIPrefix::IfcSIPrefix_DECA ) return (double) 1; + else if ( v == IfcSchema::IfcSIPrefix::IfcSIPrefix_DECI ) return (double) 1e-1; + else if ( v == IfcSchema::IfcSIPrefix::IfcSIPrefix_CENTI ) return (double) 1e-2; + else if ( v == IfcSchema::IfcSIPrefix::IfcSIPrefix_MILLI ) return (double) 1e-3; + else if ( v == IfcSchema::IfcSIPrefix::IfcSIPrefix_MICRO ) return (double) 1e-6; + else if ( v == IfcSchema::IfcSIPrefix::IfcSIPrefix_NANO ) return (double) 1e-9; + else if ( v == IfcSchema::IfcSIPrefix::IfcSIPrefix_PICO ) return (double) 1e-12; + else if ( v == IfcSchema::IfcSIPrefix::IfcSIPrefix_FEMTO ) return (double) 1e-15; + else if ( v == IfcSchema::IfcSIPrefix::IfcSIPrefix_ATTO ) return (double) 1e-18; + else return 1.0f; +} + +void IfcGeom::initialize_units_and_precision(IfcSchema::IfcProject* proj, double& unit_magnitude, std::string& unit_name) { + + // IfcOpenShell measures internally in meters, therefore a conversion factor + // is obtained based on the length unit in the file. + + // Set default units, set length to meters, angles to undefined + IfcGeom::SetValue(IfcGeom::GV_LENGTH_UNIT,1.0); + IfcGeom::SetValue(IfcGeom::GV_PLANEANGLE_UNIT,-1.0); + + IfcUtil::IfcAbstractSelect::list units = IfcUtil::IfcAbstractSelect::list(); + try { + IfcSchema::IfcUnitAssignment* unit_assignment = proj->UnitsInContext(); + units = unit_assignment->Units(); + } catch (const IfcParse::IfcException&) {} + + try { + for ( IfcUtil::IfcAbstractSelect::it it = units->begin(); it != units->end(); ++ it ) { + std::string current_unit_name = ""; + const IfcUtil::IfcAbstractSelect::ptr base = *it; + IfcSchema::IfcSIUnit::ptr unit = IfcSchema::IfcSIUnit::ptr(); + double value = 1.0f; + if ( base->is(IfcSchema::Type::IfcConversionBasedUnit) ) { + const IfcSchema::IfcConversionBasedUnit::ptr u = reinterpret_pointer_cast(base); + current_unit_name = u->Name(); + const IfcSchema::IfcMeasureWithUnit::ptr u2 = u->ConversionFactor(); + IfcSchema::IfcUnit u3 = u2->UnitComponent(); + if ( u3->is(IfcSchema::Type::IfcSIUnit) ) { + unit = (IfcSchema::IfcSIUnit*) u3; + } + IfcSchema::IfcValue v = u2->ValueComponent(); + const double f = *((IfcUtil::IfcBaseEntity*)v)->entity->getArgument(0); + value *= f; + } else if ( base->is(IfcSchema::Type::IfcSIUnit) ) { + unit = reinterpret_pointer_cast(base); + } + if ( unit ) { + if ( unit->hasPrefix() ) { + value *= UnitPrefixToValue(unit->Prefix()); + } + IfcSchema::IfcUnitEnum::IfcUnitEnum type = unit->UnitType(); + if ( type == IfcSchema::IfcUnitEnum::IfcUnit_LENGTHUNIT ) { + IfcGeom::SetValue(IfcGeom::GV_LENGTH_UNIT,value); + if (current_unit_name.empty()) { + if (unit->hasPrefix()) { + current_unit_name = IfcSchema::IfcSIPrefix::ToString(unit->Prefix()); + } + current_unit_name += IfcSchema::IfcSIUnitName::ToString(unit->Name()); + } + unit_magnitude = value; + unit_name = current_unit_name; + } else if ( type == IfcSchema::IfcUnitEnum::IfcUnit_PLANEANGLEUNIT ) { + IfcGeom::SetValue(IfcGeom::GV_PLANEANGLE_UNIT,value); + } + } + } + } catch (const IfcParse::IfcException& ex) { + std::stringstream ss; + ss << "Failed to determine unit information '" << ex.what() << "'"; + Logger::Message(Logger::LOG_ERROR, ss.str()); + } + + // Boolean operations rely on a precision value for vertex and face coincedence. This + // value is obtained from the IfcGeometricRepresentationContexts in the file. + + // Set an initial guess in case reading from file fails. + IfcGeom::SetValue(IfcGeom::GV_PRECISION, 0.00001); + + try { + IfcSchema::IfcRepresentationContext::list rep_contexts = proj->RepresentationContexts(); + // Currently, IfcGeometricRepresentationContext aren't used as much as they should be + // in the evaluation of shape representations, hence, we try to find the one with the + // lowest precision. Typically, a value of 1e-5 is encountered. This value is applied + // to all TopoDS_Shapes generated by one of the IfcGeom::convert() functions. + // TODO: Many of the empirically found tolerances should probably be substituted by + // one that is defined in the model file. + double lowest_precision_encountered = std::numeric_limits::infinity(); + bool any_precision_encountered = false; + for (IfcSchema::IfcRepresentationContext::it it = rep_contexts->begin(); it != rep_contexts->end(); ++it) { + if ((*it)->is(IfcSchema::Type::IfcGeometricRepresentationContext)) { + IfcSchema::IfcGeometricRepresentationContext* rep_context = (IfcSchema::IfcGeometricRepresentationContext*)*it; + if (rep_context->is(IfcSchema::Type::IfcGeometricRepresentationSubContext)) continue; + if (rep_context->hasPrecision()) { + const double precision = rep_context->Precision(); + if (precision < lowest_precision_encountered) { + any_precision_encountered = true; + lowest_precision_encountered = precision; + } + } + } + } + if (any_precision_encountered) { + IfcGeom::SetValue(IfcGeom::GV_PRECISION, lowest_precision_encountered); + } + } catch (const IfcParse::IfcException& ex) { + std::stringstream ss; + ss << "Failed to determine precision value '" << ex.what() << "'"; + Logger::Message(Logger::LOG_ERROR, ss.str()); + } } \ No newline at end of file diff --git a/src/ifcgeom/IfcGeomObjects.cpp b/src/ifcgeom/IfcGeomObjects.cpp index 0695139c82..11246a14a3 100644 --- a/src/ifcgeom/IfcGeomObjects.cpp +++ b/src/ifcgeom/IfcGeomObjects.cpp @@ -587,140 +587,16 @@ const IfcGeomObjects::IfcGeomBrepDataObject* IfcGeomObjects::GetBrepData() { } return current_brep_data_obj; } -double UnitPrefixToValue( IfcSchema::IfcSIPrefix::IfcSIPrefix v ) { - if ( v == IfcSchema::IfcSIPrefix::IfcSIPrefix_EXA ) return (double) 1e18; - else if ( v == IfcSchema::IfcSIPrefix::IfcSIPrefix_PETA ) return (double) 1e15; - else if ( v == IfcSchema::IfcSIPrefix::IfcSIPrefix_TERA ) return (double) 1e12; - else if ( v == IfcSchema::IfcSIPrefix::IfcSIPrefix_GIGA ) return (double) 1e9; - else if ( v == IfcSchema::IfcSIPrefix::IfcSIPrefix_MEGA ) return (double) 1e6; - else if ( v == IfcSchema::IfcSIPrefix::IfcSIPrefix_KILO ) return (double) 1e3; - else if ( v == IfcSchema::IfcSIPrefix::IfcSIPrefix_HECTO ) return (double) 1e2; - else if ( v == IfcSchema::IfcSIPrefix::IfcSIPrefix_DECA ) return (double) 1; - else if ( v == IfcSchema::IfcSIPrefix::IfcSIPrefix_DECI ) return (double) 1e-1; - else if ( v == IfcSchema::IfcSIPrefix::IfcSIPrefix_CENTI ) return (double) 1e-2; - else if ( v == IfcSchema::IfcSIPrefix::IfcSIPrefix_MILLI ) return (double) 1e-3; - else if ( v == IfcSchema::IfcSIPrefix::IfcSIPrefix_MICRO ) return (double) 1e-6; - else if ( v == IfcSchema::IfcSIPrefix::IfcSIPrefix_NANO ) return (double) 1e-9; - else if ( v == IfcSchema::IfcSIPrefix::IfcSIPrefix_PICO ) return (double) 1e-12; - else if ( v == IfcSchema::IfcSIPrefix::IfcSIPrefix_FEMTO ) return (double) 1e-15; - else if ( v == IfcSchema::IfcSIPrefix::IfcSIPrefix_ATTO ) return (double) 1e-18; - else return 1.0f; -} - -void IfcGeomObjects::InitPrecision() { - IfcGeom::SetValue(IfcGeom::GV_PRECISION, 0.00001); - - try { - IfcSchema::IfcGeometricRepresentationContext::list rep_contexts = ifc_file->EntitiesByType(); - // Currently, IfcGeometricRepresentationContext aren't used as much as they should be - // in the evaluation of shape representations, hence, we try to find the one with the - // lowest precision. Typically, a value of 1e-5 is encountered. This value is applied - // to all TopoDS_Shapes generated by one of the IfcGeom::convert() functions. - // TODO: Many of the empirically found tolerances should probably be substituted by - // one that is defined in the model file. - double lowest_precision_encountered = std::numeric_limits::infinity(); - bool any_precision_encountered = false; - for (IfcSchema::IfcGeometricRepresentationContext::it it = rep_contexts->begin(); it != rep_contexts->end(); ++it) { - IfcSchema::IfcGeometricRepresentationContext* rep_context = *it; - if (rep_context->is(IfcSchema::Type::IfcGeometricRepresentationSubContext)) continue; - if (rep_context->hasPrecision()) { - const double precision = rep_context->Precision(); - if (precision < lowest_precision_encountered) { - any_precision_encountered = true; - lowest_precision_encountered = precision; - } - } - } - if (any_precision_encountered) { - IfcGeom::SetValue(IfcGeom::GV_PRECISION, lowest_precision_encountered); - } - } catch (const IfcParse::IfcException& ex) { - std::stringstream ss; - ss << "Failed to determine precision value '" << ex.what() << "'"; - Logger::Message(Logger::LOG_ERROR, ss.str()); - } -} static std::string unit_name = "METER"; static float unit_magnitude = 1.0f; void IfcGeomObjects::InitUnits() { - // Set default units, set length to meters, angles to undefined - IfcGeom::SetValue(IfcGeom::GV_LENGTH_UNIT,1.0); - IfcGeom::SetValue(IfcGeom::GV_PLANEANGLE_UNIT,-1.0); - - IfcSchema::IfcUnitAssignment::list unit_assignments = ifc_file->EntitiesByType(); - IfcUtil::IfcAbstractSelect::list units = IfcUtil::IfcAbstractSelect::list(); - try { - if ( unit_assignments->Size() ) { - IfcSchema::IfcUnitAssignment::ptr unit_assignment = *unit_assignments->begin(); - units = unit_assignment->Units(); - } - } catch (const IfcParse::IfcException&) {} - - if (!units) { - // No units eh... Since tolerances and deflection are specified internally in meters - // we will try to find another indication of the model size. - IfcSchema::IfcExtrudedAreaSolid::list extrusions = ifc_file->EntitiesByType(); - if ( ! extrusions->Size() ) return; - double max_height = -1.0f; - for ( IfcSchema::IfcExtrudedAreaSolid::it it = extrusions->begin(); it != extrusions->end(); ++ it ) { - try { - const double depth = (*it)->Depth(); - if ( depth > max_height ) max_height = depth; - } catch (const IfcParse::IfcException&) {} - } - if ( max_height > 100.0f ) { - IfcGeom::SetValue(IfcGeom::GV_LENGTH_UNIT,0.001); - Logger::Message(Logger::LOG_NOTICE, "Guessed length unit to be in millimeters based on extrusion depth"); - } - return; - } - - try { - for ( IfcUtil::IfcAbstractSelect::it it = units->begin(); it != units->end(); ++ it ) { - std::string current_unit_name = ""; - const IfcUtil::IfcAbstractSelect::ptr base = *it; - IfcSchema::IfcSIUnit::ptr unit = IfcSchema::IfcSIUnit::ptr(); - double value = 1.0f; - if ( base->is(IfcSchema::Type::IfcConversionBasedUnit) ) { - const IfcSchema::IfcConversionBasedUnit::ptr u = reinterpret_pointer_cast(base); - current_unit_name = u->Name(); - const IfcSchema::IfcMeasureWithUnit::ptr u2 = u->ConversionFactor(); - IfcSchema::IfcUnit u3 = u2->UnitComponent(); - if ( u3->is(IfcSchema::Type::IfcSIUnit) ) { - unit = (IfcSchema::IfcSIUnit*) u3; - } - IfcSchema::IfcValue v = u2->ValueComponent(); - const double f = *((IfcUtil::IfcBaseEntity*)v)->entity->getArgument(0); - value *= f; - } else if ( base->is(IfcSchema::Type::IfcSIUnit) ) { - unit = reinterpret_pointer_cast(base); - } - if ( unit ) { - if ( unit->hasPrefix() ) { - value *= UnitPrefixToValue(unit->Prefix()); - } - IfcSchema::IfcUnitEnum::IfcUnitEnum type = unit->UnitType(); - if ( type == IfcSchema::IfcUnitEnum::IfcUnit_LENGTHUNIT ) { - IfcGeom::SetValue(IfcGeom::GV_LENGTH_UNIT,value); - if (current_unit_name.empty()) { - if (unit->hasPrefix()) { - current_unit_name = IfcSchema::IfcSIPrefix::ToString(unit->Prefix()); - } - current_unit_name += IfcSchema::IfcSIUnitName::ToString(unit->Name()); - } - unit_magnitude = value; - unit_name = current_unit_name; - } else if ( type == IfcSchema::IfcUnitEnum::IfcUnit_PLANEANGLEUNIT ) { - IfcGeom::SetValue(IfcGeom::GV_PLANEANGLE_UNIT,value); - } - } - } - } catch (const IfcParse::IfcException& ex) { - std::stringstream ss; - ss << "Failed to determine unit information '" << ex.what() << "'"; - Logger::Message(Logger::LOG_ERROR, ss.str()); + IfcSchema::IfcProject::list projects = ifc_file->EntitiesByType(); + if (projects->Size() == 1) { + double unit_magnitude_double; + IfcGeom::initialize_units_and_precision(*projects->begin(), unit_magnitude_double, unit_name); + unit_magnitude = static_cast(unit_magnitude_double); } } @@ -729,7 +605,6 @@ bool IfcGeomObjects::Init(const std::string fn) { } bool _Init() { IfcGeomObjects::InitUnits(); - IfcGeomObjects::InitPrecision(); shapereps = ifc_file->EntitiesByType(); if ( ! shapereps ) return false; diff --git a/src/ifcwrap/Interface.h b/src/ifcwrap/Interface.h index 5c41f0489f..5944c3db43 100644 --- a/src/ifcwrap/Interface.h +++ b/src/ifcwrap/Interface.h @@ -99,6 +99,12 @@ namespace IfcParse { IfcParse::IfcFile* open(const std::string& s) { IfcParse::IfcFile* f = new IfcParse::IfcFile(); f->Init(s); + IfcEntities projects = f->EntitiesByType("IfcProject"); + if (projects->Size() == 1) { + double unit_magnitude; + std::string unit_name; + IfcGeom::initialize_units_and_precision((IfcSchema::IfcProject*)*projects->begin(), unit_magnitude, unit_name); + } return f; } @@ -115,6 +121,7 @@ namespace IfcParse { const int DISABLE_OPENING_SUBTRACTIONS = 1 << 0; const int DISABLE_OBJECT_PLACEMENT = 1 << 1; const int SEW_SHELLS = 1 << 2; + const int CONVERT_TO_METERS = 1 << 3; } std::ostream& operator<< (std::ostream& os, const IfcParse::IfcFile& f); \ No newline at end of file diff --git a/src/ifcwrap/ifc.py b/src/ifcwrap/ifc.py index 6849192942..be63a4d02b 100644 --- a/src/ifcwrap/ifc.py +++ b/src/ifcwrap/ifc.py @@ -104,3 +104,4 @@ def clean(): return ifc_wrapper.clean() DISABLE_OPENING_SUBTRACTIONS = ifc_wrapper.DISABLE_OPENING_SUBTRACTIONS DISABLE_OBJECT_PLACEMENT = ifc_wrapper.DISABLE_OBJECT_PLACEMENT SEW_SHELLS = ifc_wrapper.SEW_SHELLS +CONVERT_TO_METERS = ifc_wrapper.CONVERT_TO_METERS