diff --git a/src/examples/IfcParseExamples.cpp b/src/examples/IfcParseExamples.cpp index 2bdb84957f..3e729553a7 100644 --- a/src/examples/IfcParseExamples.cpp +++ b/src/examples/IfcParseExamples.cpp @@ -29,10 +29,11 @@ int main(int argc, char** argv) { } // Redirect the output (both progress and log) to stdout - Ifc::SetOutput(&std::cout,&std::cout); + Logger::SetOutput(&std::cout,&std::cout); // Parse the IFC file provided in argv[1] - if ( ! Ifc::Init(argv[1]) ) { + IfcParse::IfcFile file; + if ( ! file.Init(argv[1]) ) { std::cout << "Unable to parse .ifc file" << std::endl; return 1; } @@ -53,12 +54,7 @@ int main(int argc, char** argv) { // we need to cast them to IfcWindows. Since these properties // are optional we need to make sure the properties are // defined for the window in question before accessing them. - // - // Since we are accessing properties that represent a length - // measure we can multiply the value by Ifc::LengthUnit, which - // contains the ratio of the unit defined in the IfcUnitAssignment - // to the standard SI Unit, the meter. - IfcBuildingElement::list elements = Ifc::EntitiesByType(); + IfcBuildingElement::list elements = file.EntitiesByType(); std::cout << "Found " << elements->Size() << " elements in " << argv[1] << ":" << std::endl; @@ -71,8 +67,8 @@ int main(int argc, char** argv) { const IfcWindow::ptr window = reinterpret_pointer_cast(element); if ( window->hasOverallWidth() && window->hasOverallHeight() ) { - const float area = window->OverallWidth()*window->OverallHeight() * (Ifc::LengthUnit*Ifc::LengthUnit); - std::cout << "This window has an area of " << area << "m2" << std::endl; + const double area = window->OverallWidth()*window->OverallHeight(); + std::cout << "The area of this window is " << area << std::endl; } } diff --git a/src/ifcgeom/IfcGeom.h b/src/ifcgeom/IfcGeom.h index 9c6b55a917..c750cdd0f2 100644 --- a/src/ifcgeom/IfcGeom.h +++ b/src/ifcgeom/IfcGeom.h @@ -47,16 +47,32 @@ namespace IfcGeom { // Tolerances and settings for various geometrical operations: enum GeomValue { // Specifies the deflection of the mesher + // Default: 0.001m / 1mm GV_DEFLECTION_TOLERANCE, // Specifies the tolerance of the wire builder, most notably for trimmed curves + // Defailt: 0.0001m / 0.1mm GV_WIRE_CREATION_TOLERANCE, // Specifies the minimal area of a face to be included in an IfcConnectedFaceset + // Default: 0.000001m 0.01cm2 GV_MINIMAL_FACE_AREA, // Specifies the treshold distance under which cartesian points are deemed equal + // Default: 0.00001m / 0.01mm GV_POINT_EQUALITY_TOLERANCE, // Specifies maximum number of faces for a shell to be sewed. Sewing shells // that consist of many faces is really detrimental for the performance. - GV_MAX_FACES_TO_SEW + // Default: 1000 + GV_MAX_FACES_TO_SEW, + // By default singular faces have no explicitly defined orientation, to + // force faces to be defined CounterClockWise, set this value greater than zero. + GV_FORCE_CCW_FACE_ORIENTATION, + // The length unit used the creation of TopoDS_Shapes, primarily affects the + // interpretation of IfcCartesianPoints and IfcVector magnitudes + // DefaultL 1.0 + GV_LENGTH_UNIT, + // The plane angle unit used for the creation of TopoDS_Shapes, primarily affects + // the interpretation of IfcParamaterValues of IfcTrimmedCurves + // Default: -1.0 (= not set, fist try degrees, then radians) + GV_PLANEANGLE_UNIT, }; bool convert_wire_to_face(const TopoDS_Wire& wire, TopoDS_Face& face); @@ -80,7 +96,7 @@ namespace IfcGeom { double face_area(const TopoDS_Face& f); void SetValue(GeomValue var, double value); double GetValue(GeomValue var); - + namespace Cache { void Purge(); void PurgeShapeCache(); diff --git a/src/ifcgeom/IfcGeomCurves.cpp b/src/ifcgeom/IfcGeomCurves.cpp index 443b734b49..37b2297b63 100644 --- a/src/ifcgeom/IfcGeomCurves.cpp +++ b/src/ifcgeom/IfcGeomCurves.cpp @@ -78,7 +78,7 @@ #include "../ifcgeom/IfcGeom.h" bool IfcGeom::convert(const Ifc2x3::IfcCircle::ptr l, Handle(Geom_Curve)& curve) { - const double r = l->Radius() * Ifc::LengthUnit; + const double r = l->Radius() * IfcGeom::GetValue(GV_LENGTH_UNIT); if ( r <= 0.0f ) { return false; } gp_Trsf trsf; Ifc2x3::IfcAxis2Placement placement = l->Position(); @@ -94,8 +94,8 @@ bool IfcGeom::convert(const Ifc2x3::IfcCircle::ptr l, Handle(Geom_Curve)& curve) return true; } bool IfcGeom::convert(const Ifc2x3::IfcEllipse::ptr l, Handle(Geom_Curve)& curve) { - double x = l->SemiAxis1() * Ifc::LengthUnit; - double y = l->SemiAxis2() * Ifc::LengthUnit; + double x = l->SemiAxis1() * IfcGeom::GetValue(GV_LENGTH_UNIT); + double y = l->SemiAxis2() * IfcGeom::GetValue(GV_LENGTH_UNIT); if ( x == 0.0f || y == 0.0f || y > x ) { return false; } gp_Trsf trsf; Ifc2x3::IfcAxis2Placement placement = l->Position(); diff --git a/src/ifcgeom/IfcGeomFaces.cpp b/src/ifcgeom/IfcGeomFaces.cpp index 1b7ac98cb6..7a206d7f96 100644 --- a/src/ifcgeom/IfcGeomFaces.cpp +++ b/src/ifcgeom/IfcGeomFaces.cpp @@ -74,6 +74,7 @@ #include #include +#include #include "../ifcgeom/IfcGeom.h" @@ -81,15 +82,15 @@ bool IfcGeom::convert(const Ifc2x3::IfcFace::ptr l, TopoDS_Face& face) { Ifc2x3::IfcFaceBound::list bounds = l->Bounds(); Ifc2x3::IfcFaceBound::it it = bounds->begin(); Ifc2x3::IfcLoop::ptr loop = (*it)->Bound(); - TopoDS_Wire wire; - if ( ! IfcGeom::convert_wire(loop,wire) ) return false; - BRepBuilderAPI_MakeFace mf (wire); + TopoDS_Wire outer_wire; + if ( ! IfcGeom::convert_wire(loop,outer_wire) ) return false; + BRepBuilderAPI_MakeFace mf (outer_wire); BRepBuilderAPI_FaceError er = mf.Error(); if ( er == BRepBuilderAPI_NotPlanar ) { ShapeFix_ShapeTolerance FTol; - FTol.SetTolerance(wire, 0.01, TopAbs_WIRE); + FTol.SetTolerance(outer_wire, 0.01, TopAbs_WIRE); mf.~BRepBuilderAPI_MakeFace(); - new (&mf) BRepBuilderAPI_MakeFace(wire); + new (&mf) BRepBuilderAPI_MakeFace(outer_wire); er = mf.Error(); } if ( er != BRepBuilderAPI_FaceDone ) return false; @@ -116,6 +117,65 @@ bool IfcGeom::convert(const Ifc2x3::IfcFace::ptr l, TopoDS_Face& face) { return false; } } + + if ( IfcGeom::GetValue(GV_FORCE_CCW_FACE_ORIENTATION)>0 ) { + // Check the orientation of the face by comparing the + // normal of the topological surface to the Newell's Method's + // normal. Newell's Method is used for the normal calculation + // as a simple edge cross product can give opposite results + // for a concave face boundary. + // Reference: Graphics Gems III p. 231 + BRepGProp_Face prop(face); + gp_Vec normal_direction; + gp_Pnt center; + double u1,u2,v1,v2; + prop.Bounds(u1,u2,v1,v2); + prop.Normal((u1+u2)/2.0,(v1+v2)/2.0,center,normal_direction); + gp_Dir face_normal1 = gp_Dir(normal_direction.XYZ()); + + double x = 0, y = 0, z = 0; + gp_Pnt current, previous, first; + int n = 0; + // Iterate over the vertices of the outer wire (discarding + // any potential holes) + for ( TopExp_Explorer exp(outer_wire,TopAbs_VERTEX);; exp.Next()) { + unsigned has_more = exp.More(); + if ( has_more ) { + const TopoDS_Vertex& v = TopoDS::Vertex(exp.Current()); + current = BRep_Tool::Pnt(v); + } else { + current = first; + } + if ( n ) { + const double& xn = previous.X(); + const double& yn = previous.Y(); + const double& zn = previous.Z(); + const double& xn1 = current.X(); + const double& yn1 = current.Y(); + const double& zn1 = current.Z(); + x += (yn-yn1)*(zn+zn1); + y += (xn+xn1)*(zn-zn1); + z += (xn-xn1)*(yn+yn1); + } else { + first = current; + } + if ( !has_more ) { + break; + } + previous = current; + ++n; + } + + // If Newell's normal does not point in the same direction + // as the topological face normal the face orientation is + // reversed + gp_Vec face_normal2(x,y,z); + if ( face_normal1.Dot(face_normal2) < 0 ) { + TopAbs_Orientation o = face.Orientation(); + face.Orientation(o == TopAbs_FORWARD ? TopAbs_REVERSED : TopAbs_FORWARD); + } + } + // It might be a good idea to globally discard faces // smaller than a certain treshold value. But for now // only when processing IfcConnectedFacesets the small @@ -145,11 +205,11 @@ bool IfcGeom::convert(const Ifc2x3::IfcArbitraryProfileDefWithVoids::ptr l, Topo return true; } bool IfcGeom::convert(const Ifc2x3::IfcRectangleProfileDef::ptr l, TopoDS_Face& face) { - const double x = l->XDim() / 2.0f * Ifc::LengthUnit; - const double y = l->YDim() / 2.0f * Ifc::LengthUnit; + const double x = l->XDim() / 2.0f * IfcGeom::GetValue(GV_LENGTH_UNIT); + const double y = l->YDim() / 2.0f * IfcGeom::GetValue(GV_LENGTH_UNIT); if ( x == 0.0f || y == 0.0f ) { - Ifc::LogMessage("Notice","Skipping zero sized profile:",l->entity); + Logger::Message(Logger::LOG_NOTICE,"Skipping zero sized profile:",l->entity); return false; } @@ -159,18 +219,18 @@ bool IfcGeom::convert(const Ifc2x3::IfcRectangleProfileDef::ptr l, TopoDS_Face& return IfcGeom::profile_helper(4,coords,0,0,0,trsf2d,face); } bool IfcGeom::convert(const Ifc2x3::IfcIShapeProfileDef::ptr l, TopoDS_Face& face) { - const double x = l->OverallWidth() / 2.0f * Ifc::LengthUnit; - const double y = l->OverallDepth() / 2.0f * Ifc::LengthUnit; - const double d1 = l->WebThickness() / 2.0f * Ifc::LengthUnit; - const double d2 = l->FlangeThickness() * Ifc::LengthUnit; + const double x = l->OverallWidth() / 2.0f * IfcGeom::GetValue(GV_LENGTH_UNIT); + const double y = l->OverallDepth() / 2.0f * IfcGeom::GetValue(GV_LENGTH_UNIT); + const double d1 = l->WebThickness() / 2.0f * IfcGeom::GetValue(GV_LENGTH_UNIT); + const double d2 = l->FlangeThickness() * IfcGeom::GetValue(GV_LENGTH_UNIT); bool doFillet = l->hasFilletRadius(); double f; if ( doFillet ) { - f = l->FilletRadius() * Ifc::LengthUnit; + f = l->FilletRadius() * IfcGeom::GetValue(GV_LENGTH_UNIT); } if ( x == 0.0f || y == 0.0f || d1 == 0.0f || d2 == 0.0f ) { - Ifc::LogMessage("Notice","Skipping zero sized profile:",l->entity); + Logger::Message(Logger::LOG_NOTICE,"Skipping zero sized profile:",l->entity); return false; } @@ -183,19 +243,19 @@ bool IfcGeom::convert(const Ifc2x3::IfcIShapeProfileDef::ptr l, TopoDS_Face& fac return IfcGeom::profile_helper(12,coords,doFillet ? 4 : 0,fillets,radii,trsf2d,face); } bool IfcGeom::convert(const Ifc2x3::IfcCShapeProfileDef::ptr l, TopoDS_Face& face) { - const double x = l->Depth() / 2.0f * Ifc::LengthUnit; - const double y = l->Width() / 2.0f * Ifc::LengthUnit; - const double d1 = l->WallThickness() * Ifc::LengthUnit; - const double d2 = l->Girth() * Ifc::LengthUnit; + const double x = l->Depth() / 2.0f * IfcGeom::GetValue(GV_LENGTH_UNIT); + const double y = l->Width() / 2.0f * IfcGeom::GetValue(GV_LENGTH_UNIT); + const double d1 = l->WallThickness() * IfcGeom::GetValue(GV_LENGTH_UNIT); + const double d2 = l->Girth() * IfcGeom::GetValue(GV_LENGTH_UNIT); bool doFillet = l->hasInternalFilletRadius(); double f1,f2; if ( doFillet ) { - f1 = l->InternalFilletRadius() * Ifc::LengthUnit; + f1 = l->InternalFilletRadius() * IfcGeom::GetValue(GV_LENGTH_UNIT); f2 = f1 + d1; } if ( x == 0.0f || y == 0.0f || d1 == 0.0f || d2 == 0.0f ) { - Ifc::LogMessage("Notice","Skipping zero sized profile:",l->entity); + Logger::Message(Logger::LOG_NOTICE,"Skipping zero sized profile:",l->entity); return false; } @@ -208,21 +268,21 @@ bool IfcGeom::convert(const Ifc2x3::IfcCShapeProfileDef::ptr l, TopoDS_Face& fac return IfcGeom::profile_helper(12,coords,doFillet ? 8 : 0,fillets,radii,trsf2d,face); } bool IfcGeom::convert(const Ifc2x3::IfcLShapeProfileDef::ptr l, TopoDS_Face& face) { - const double y = l->Depth() / 2.0f * Ifc::LengthUnit; - const double x = l->Width() / 2.0f * Ifc::LengthUnit; - const double d = l->Thickness() * Ifc::LengthUnit; + const double y = l->Depth() / 2.0f * IfcGeom::GetValue(GV_LENGTH_UNIT); + const double x = l->Width() / 2.0f * IfcGeom::GetValue(GV_LENGTH_UNIT); + const double d = l->Thickness() * IfcGeom::GetValue(GV_LENGTH_UNIT); bool doEdgeFillet = l->hasEdgeRadius(); bool doFillet = l->hasFilletRadius(); double f1 = 0.0f; double f2 = 0.0f; if (doFillet) { - f1 = l->FilletRadius() * Ifc::LengthUnit; + f1 = l->FilletRadius() * IfcGeom::GetValue(GV_LENGTH_UNIT); } if ( doEdgeFillet) { - f2 = l->EdgeRadius() * Ifc::LengthUnit; + f2 = l->EdgeRadius() * IfcGeom::GetValue(GV_LENGTH_UNIT); } if ( x == 0.0f || y == 0.0f || d == 0.0f ) { - Ifc::LogMessage("Notice","Skipping zero sized profile:",l->entity); + Logger::Message(Logger::LOG_NOTICE,"Skipping zero sized profile:",l->entity); return false; } @@ -235,9 +295,9 @@ bool IfcGeom::convert(const Ifc2x3::IfcLShapeProfileDef::ptr l, TopoDS_Face& fac return IfcGeom::profile_helper(6,coords,doFillet ? 3 : 0,fillets,radii,trsf2d,face); } bool IfcGeom::convert(const Ifc2x3::IfcCircleProfileDef::ptr l, TopoDS_Face& face) { - const double r = l->Radius() * Ifc::LengthUnit; + const double r = l->Radius() * IfcGeom::GetValue(GV_LENGTH_UNIT); if ( r == 0.0f ) { - Ifc::LogMessage("Notice","Skipping zero sized profile:",l->entity); + Logger::Message(Logger::LOG_NOTICE,"Skipping zero sized profile:",l->entity); return false; } @@ -252,11 +312,11 @@ bool IfcGeom::convert(const Ifc2x3::IfcCircleProfileDef::ptr l, TopoDS_Face& fac return IfcGeom::convert_wire_to_face(w,face); } bool IfcGeom::convert(const Ifc2x3::IfcCircleHollowProfileDef::ptr l, TopoDS_Face& face) { - const double r = l->Radius() * Ifc::LengthUnit; - const double t = l->WallThickness() * Ifc::LengthUnit; + const double r = l->Radius() * IfcGeom::GetValue(GV_LENGTH_UNIT); + const double t = l->WallThickness() * IfcGeom::GetValue(GV_LENGTH_UNIT); if ( r == 0.0f || t == 0.0f ) { - Ifc::LogMessage("Notice","Skipping zero sized profile:",l->entity); + Logger::Message(Logger::LOG_NOTICE,"Skipping zero sized profile:",l->entity); return false; } diff --git a/src/ifcgeom/IfcGeomFunctions.cpp b/src/ifcgeom/IfcGeomFunctions.cpp index 58e6b3223a..d727d02549 100644 --- a/src/ifcgeom/IfcGeomFunctions.cpp +++ b/src/ifcgeom/IfcGeomFunctions.cpp @@ -163,7 +163,7 @@ bool IfcGeom::convert_openings(const Ifc2x3::IfcProduct::ptr entity, const Ifc2x const gp_GTrsf& entity_shape_gtrsf = *(it3->first); TopoDS_Shape entity_shape; if ( entity_shape_gtrsf.Form() == gp_Other ) { - Ifc::LogMessage("Warning","Applying non uniform transformation to:",entity->entity); + Logger::Message(Logger::LOG_WARNING,"Applying non uniform transformation to:",entity->entity); entity_shape = BRepBuilderAPI_GTransform(entity_shape_unlocated,entity_shape_gtrsf,true).Shape(); } else { entity_shape = entity_shape_unlocated.Moved(entity_shape_gtrsf.Trsf()); @@ -175,17 +175,17 @@ bool IfcGeom::convert_openings(const Ifc2x3::IfcProduct::ptr entity, const Ifc2x const TopoDS_Shape& opening_shape_unlocated = IfcGeom::ensure_fit_for_subtraction(*(it4->second),opening_shape_solid); const gp_GTrsf& opening_shape_gtrsf = *(it4->first); if ( opening_shape_gtrsf.Form() == gp_Other ) { - Ifc::LogMessage("Warning","Applying non uniform transformation to opening of:",entity->entity); + Logger::Message(Logger::LOG_WARNING,"Applying non uniform transformation to opening of:",entity->entity); } const TopoDS_Shape& opening_shape = opening_shape_gtrsf.Form() == gp_Other ? BRepBuilderAPI_GTransform(opening_shape_unlocated,opening_shape_gtrsf,true).Shape() : opening_shape_unlocated.Moved(opening_shape_gtrsf.Trsf()); double opening_volume, original_shape_volume; - if ( Ifc::Verbosity > 1 ) { + if ( Logger::Verbosity() >= Logger::LOG_WARNING ) { opening_volume = shape_volume(opening_shape); if ( opening_volume <= ALMOST_ZERO ) - Ifc::LogMessage("Warning","Empty opening for:",entity->entity); + Logger::Message(Logger::LOG_WARNING,"Empty opening for:",entity->entity); original_shape_volume = shape_volume(entity_shape); } @@ -198,17 +198,17 @@ bool IfcGeom::convert_openings(const Ifc2x3::IfcProduct::ptr entity, const Ifc2x bool is_valid = analyser.IsValid() != 0; if ( is_valid ) { entity_shape = brep_cut; - if ( Ifc::Verbosity > 1 ) { + if ( Logger::Verbosity() >= Logger::LOG_WARNING ) { const double volume_after_subtraction = shape_volume(entity_shape); if ( ALMOST_THE_SAME(original_shape_volume,volume_after_subtraction) ) - Ifc::LogMessage("Warning","Subtraction yields unchanged volume:",entity->entity); + Logger::Message(Logger::LOG_WARNING,"Subtraction yields unchanged volume:",entity->entity); } } else { - Ifc::LogMessage("Error","Invalid result from subtraction:",entity->entity); + Logger::Message(Logger::LOG_ERROR,"Invalid result from subtraction:",entity->entity); } } else { - Ifc::LogMessage("Error","Failed to process subtraction:",entity->entity); + Logger::Message(Logger::LOG_ERROR,"Failed to process subtraction:",entity->entity); } } @@ -274,7 +274,7 @@ bool IfcGeom::convert_openings_fast(const Ifc2x3::IfcProduct::ptr entity, const const gp_GTrsf& entity_shape_gtrsf = *(it3->first); TopoDS_Shape entity_shape; if ( entity_shape_gtrsf.Form() == gp_Other ) { - Ifc::LogMessage("Warning","Applying non uniform transformation to:",entity->entity); + Logger::Message(Logger::LOG_WARNING,"Applying non uniform transformation to:",entity->entity); entity_shape = BRepBuilderAPI_GTransform(entity_shape_unlocated,entity_shape_gtrsf,true).Shape(); } else { entity_shape = entity_shape_unlocated.Moved(entity_shape_gtrsf.Trsf()); @@ -295,7 +295,7 @@ bool IfcGeom::convert_openings_fast(const Ifc2x3::IfcProduct::ptr entity, const // Apparently processing the boolean operation failed or resulted in an invalid result // in which case the original shape without the subtractions is returned instead // we try convert the openings in the original way, one by one. - Ifc::LogMessage("Warning","Subtracting combined openings compound failed:",entity->entity); + Logger::Message(Logger::LOG_WARNING,"Subtracting combined openings compound failed:",entity->entity); return false; } @@ -429,7 +429,10 @@ static double deflection_tolerance = 0.001; static double wire_creation_tolerance = 0.0001; static double minimal_face_area = 0.000001; static double point_equality_tolerance = 0.00001; -static double max_faces_to_sew = 1000; +static double max_faces_to_sew = -1.0; +static double ifc_length_unit = 1.0; +static double ifc_planeangle_unit = -1.0; +static double force_ccw_face_orientation = -1.0; void IfcGeom::SetValue(GeomValue var, double value) { switch (var) { @@ -448,6 +451,15 @@ void IfcGeom::SetValue(GeomValue var, double value) { case GV_MAX_FACES_TO_SEW: max_faces_to_sew = value; break; + case GV_LENGTH_UNIT: + ifc_length_unit = value; + break; + case GV_PLANEANGLE_UNIT: + ifc_planeangle_unit = value; + break; + case GV_FORCE_CCW_FACE_ORIENTATION: + force_ccw_face_orientation = value; + break; default: assert(!"never reach here"); } @@ -465,6 +477,15 @@ double IfcGeom::GetValue(GeomValue var) { return point_equality_tolerance; case GV_MAX_FACES_TO_SEW: return max_faces_to_sew; + case GV_LENGTH_UNIT: + return ifc_length_unit; + break; + case GV_PLANEANGLE_UNIT: + return ifc_planeangle_unit; + break; + case GV_FORCE_CCW_FACE_ORIENTATION: + return force_ccw_face_orientation; + break; } assert(!"never reach here"); return 0; diff --git a/src/ifcgeom/IfcGeomHelpers.cpp b/src/ifcgeom/IfcGeomHelpers.cpp index acbcf1b363..a229c78957 100644 --- a/src/ifcgeom/IfcGeomHelpers.cpp +++ b/src/ifcgeom/IfcGeomHelpers.cpp @@ -91,9 +91,9 @@ bool IfcGeom::convert(const Ifc2x3::IfcCartesianPoint::ptr l, gp_Pnt& point) { IN_CACHE(IfcCartesianPoint,l,gp_Pnt,point) std::vector xyz = l->Coordinates(); point = gp_Pnt( - xyz.size() ? (xyz[0]*Ifc::LengthUnit) : 0.0f, - xyz.size() > 1 ? (xyz[1]*Ifc::LengthUnit) : 0.0f, - xyz.size() > 2 ? (xyz[2]*Ifc::LengthUnit) : 0.0f + xyz.size() ? (xyz[0]*IfcGeom::GetValue(GV_LENGTH_UNIT)) : 0.0f, + xyz.size() > 1 ? (xyz[1]*IfcGeom::GetValue(GV_LENGTH_UNIT)) : 0.0f, + xyz.size() > 2 ? (xyz[2]*IfcGeom::GetValue(GV_LENGTH_UNIT)) : 0.0f ); CACHE(IfcCartesianPoint,l,point) return true; @@ -113,7 +113,7 @@ bool IfcGeom::convert(const Ifc2x3::IfcVector::ptr l, gp_Vec& v) { IN_CACHE(IfcVector,l,gp_Vec,v) gp_Dir d; IfcGeom::convert(l->Orientation(),d); - v = l->Magnitude() * Ifc::LengthUnit * d; + v = l->Magnitude() * IfcGeom::GetValue(GV_LENGTH_UNIT) * d; CACHE(IfcVector,l,v) return true; } diff --git a/src/ifcgeom/IfcGeomObjects.cpp b/src/ifcgeom/IfcGeomObjects.cpp index 49f3433a9a..f3d931a53a 100644 --- a/src/ifcgeom/IfcGeomObjects.cpp +++ b/src/ifcgeom/IfcGeomObjects.cpp @@ -51,9 +51,9 @@ bool convert_back_units = false; bool use_faster_booleans = false; int IfcGeomObjects::IfcMesh::addvert(const gp_XYZ& p) { - const float X = convert_back_units ? (float) (p.X() / Ifc::LengthUnit) : (float)p.X(); - const float Y = convert_back_units ? (float) (p.Y() / Ifc::LengthUnit) : (float)p.Y(); - const float Z = convert_back_units ? (float) (p.Z() / Ifc::LengthUnit) : (float)p.Z(); + const float X = convert_back_units ? (float) (p.X() / IfcGeom::GetValue(IfcGeom::GV_LENGTH_UNIT)) : (float)p.X(); + const float Y = convert_back_units ? (float) (p.Y() / IfcGeom::GetValue(IfcGeom::GV_LENGTH_UNIT)) : (float)p.Y(); + const float Z = convert_back_units ? (float) (p.Z() / IfcGeom::GetValue(IfcGeom::GV_LENGTH_UNIT)) : (float)p.Z(); int i = (int) verts.size() / 3; if ( weld_vertices ) { const VertKey key = VertKey(X,std::pair(Y,Z)); @@ -71,6 +71,8 @@ int IfcGeomObjects::IfcMesh::addvert(const gp_XYZ& p) { bool use_world_coords = false; bool use_brep_data = false; +static IfcParse::IfcFile* ifc_file = 0; + IfcGeomObjects::IfcMesh::IfcMesh(int i, const IfcGeom::ShapeList& shapes) { id = i; @@ -102,10 +104,10 @@ IfcGeomObjects::IfcMesh::IfcMesh(int i, const IfcGeom::ShapeList& shapes) { // Triangulate the shape try { - //BRepTools::Clean(s); + // BRepTools::Clean(s); BRepMesh::Mesh(s, IfcGeom::GetValue(IfcGeom::GV_DEFLECTION_TOLERANCE)); } catch(...) { - Ifc::LogMessage("Error","Failed to triangulate mesh:",Ifc::EntityById(i)->entity); + Logger::Message(Logger::LOG_ERROR,"Failed to triangulate mesh:",ifc_file->EntityById(i)->entity); continue; } TopExp_Explorer exp; @@ -401,7 +403,7 @@ IfcGeomObjects::IfcGeomObject* _get() { IfcGeom::convert_openings(ifc_product,openings,shapes,trsf,opened_shapes); } } catch(...) { - Ifc::LogMessage("Error","Error processing openings for:",ifc_product->entity); + Logger::Message(Logger::LOG_ERROR,"Error processing openings for:",ifc_product->entity); } if ( use_world_coords ) { for ( IfcGeom::ShapeList::const_iterator it = opened_shapes.begin(); it != opened_shapes.end(); ++ it ) { @@ -452,7 +454,8 @@ bool IfcGeomObjects::Next() { } std::vector returned_objects; bool IfcGeomObjects::CleanUp() { - Ifc::Dispose(); + // TODO: Correctly implement destructor for IfcFile + delete ifc_file; IfcGeom::Cache::Purge(); for ( std::vector::const_iterator it = returned_objects.begin(); it != returned_objects.end(); @@ -465,7 +468,7 @@ bool IfcGeomObjects::CleanUp() { const IfcGeomObjects::IfcObject* IfcGeomObjects::GetObject(int id) { IfcObject* ifc_object = 0; try { - const IfcEntity& ifc_entity = Ifc::EntityById(id); + const IfcParse::IfcEntity& ifc_entity = ifc_file->EntityById(id); if ( ifc_entity->is(Ifc2x3::Type::IfcProduct) ) { Ifc2x3::IfcProduct::ptr ifc_product = reinterpret_pointer_cast(ifc_entity); int parent_id = -1; @@ -488,11 +491,93 @@ const IfcGeomObjects::IfcObject* IfcGeomObjects::GetObject(int id) { const IfcGeomObjects::IfcGeomObject* IfcGeomObjects::Get() { return current_geom_obj; } +double UnitPrefixToValue( Ifc2x3::IfcSIPrefix::IfcSIPrefix v ) { + if ( v == Ifc2x3::IfcSIPrefix::IfcSIPrefix_EXA ) return (double) 1e18; + else if ( v == Ifc2x3::IfcSIPrefix::IfcSIPrefix_PETA ) return (double) 1e15; + else if ( v == Ifc2x3::IfcSIPrefix::IfcSIPrefix_TERA ) return (double) 1e12; + else if ( v == Ifc2x3::IfcSIPrefix::IfcSIPrefix_GIGA ) return (double) 1e9; + else if ( v == Ifc2x3::IfcSIPrefix::IfcSIPrefix_MEGA ) return (double) 1e6; + else if ( v == Ifc2x3::IfcSIPrefix::IfcSIPrefix_KILO ) return (double) 1e3; + else if ( v == Ifc2x3::IfcSIPrefix::IfcSIPrefix_HECTO ) return (double) 1e2; + else if ( v == Ifc2x3::IfcSIPrefix::IfcSIPrefix_DECA ) return (double) 1; + else if ( v == Ifc2x3::IfcSIPrefix::IfcSIPrefix_DECI ) return (double) 1e-1; + else if ( v == Ifc2x3::IfcSIPrefix::IfcSIPrefix_CENTI ) return (double) 1e-2; + else if ( v == Ifc2x3::IfcSIPrefix::IfcSIPrefix_MILLI ) return (double) 1e-3; + else if ( v == Ifc2x3::IfcSIPrefix::IfcSIPrefix_MICRO ) return (double) 1e-6; + else if ( v == Ifc2x3::IfcSIPrefix::IfcSIPrefix_NANO ) return (double) 1e-9; + else if ( v == Ifc2x3::IfcSIPrefix::IfcSIPrefix_PICO ) return (double) 1e-12; + else if ( v == Ifc2x3::IfcSIPrefix::IfcSIPrefix_FEMTO ) return (double) 1e-15; + else if ( v == Ifc2x3::IfcSIPrefix::IfcSIPrefix_ATTO ) return (double) 1e-18; + else return 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); + + Ifc2x3::IfcUnitAssignment::list unit_assignments = ifc_file->EntitiesByType(); + IfcUtil::IfcAbstractSelect::list units = IfcUtil::IfcAbstractSelect::list(); + if ( unit_assignments->Size() ) { + Ifc2x3::IfcUnitAssignment::ptr unit_assignment = *unit_assignments->begin(); + units = unit_assignment->Units(); + } + 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. + Ifc2x3::IfcExtrudedAreaSolid::list extrusions = ifc_file->EntitiesByType(); + if ( ! extrusions->Size() ) return; + double max_height = -1.0f; + for ( Ifc2x3::IfcExtrudedAreaSolid::it it = extrusions->begin(); it != extrusions->end(); ++ it ) { + const double depth = (*it)->Depth(); + if ( depth > max_height ) max_height = depth; + } + if ( max_height > 100.0f ) IfcGeom::SetValue(IfcGeom::GV_LENGTH_UNIT,0.001); + return; + } + try { + for ( IfcUtil::IfcAbstractSelect::it it = units->begin(); it != units->end(); ++ it ) { + const IfcUtil::IfcAbstractSelect::ptr base = *it; + Ifc2x3::IfcSIUnit::ptr unit = Ifc2x3::IfcSIUnit::ptr(); + double value = 1.0f; + if ( base->is(Ifc2x3::Type::IfcConversionBasedUnit) ) { + const Ifc2x3::IfcConversionBasedUnit::ptr u = reinterpret_pointer_cast(base); + const Ifc2x3::IfcMeasureWithUnit::ptr u2 = u->ConversionFactor(); + Ifc2x3::IfcUnit u3 = u2->UnitComponent(); + if ( u3->is(Ifc2x3::Type::IfcSIUnit) ) { + unit = (Ifc2x3::IfcSIUnit*) u3; + } + Ifc2x3::IfcValue v = u2->ValueComponent(); + IfcUtil::IfcArgumentSelect* v2 = (IfcUtil::IfcArgumentSelect*) v; + const double f = *v2->wrappedValue(); + value *= f; + } else if ( base->is(Ifc2x3::Type::IfcSIUnit) ) { + unit = reinterpret_pointer_cast(base); + } + if ( unit ) { + if ( unit->hasPrefix() ) { + value *= UnitPrefixToValue(unit->Prefix()); + } + Ifc2x3::IfcUnitEnum::IfcUnitEnum type = unit->UnitType(); + if ( type == Ifc2x3::IfcUnitEnum::IfcUnit_LENGTHUNIT ) { + IfcGeom::SetValue(IfcGeom::GV_LENGTH_UNIT,value); + } else if ( type == Ifc2x3::IfcUnitEnum::IfcUnit_PLANEANGLEUNIT ) { + IfcGeom::SetValue(IfcGeom::GV_PLANEANGLE_UNIT,value); + } + } + } + } catch ( IfcException ex ) { + Logger::Message(Logger::LOG_ERROR,ex.what()); + } +} + bool IfcGeomObjects::Init(const std::string fn) { return IfcGeomObjects::Init(fn, 0, 0); } bool _Init() { - shapereps = Ifc::EntitiesByType(); + IfcGeomObjects::InitUnits(); + + shapereps = ifc_file->EntitiesByType(); if ( ! shapereps ) return false; outer = shapereps->begin(); @@ -506,18 +591,21 @@ bool _Init() { return true; } bool IfcGeomObjects::Init(const std::string fn, std::ostream* log1, std::ostream* log2) { - Ifc::SetOutput(log1,log2); - if ( !Ifc::Init(fn) ) return false; + Logger::SetOutput(log1,log2); + ifc_file = new IfcParse::IfcFile(); + if ( !ifc_file->Init(fn) ) return false; return _Init(); } bool IfcGeomObjects::Init(std::istream& f, int len, std::ostream* log1, std::ostream* log2) { - Ifc::SetOutput(log1,log2); - if ( !Ifc::Init(f, len) ) return false; + Logger::SetOutput(log1,log2); + ifc_file = new IfcParse::IfcFile(); + if ( !ifc_file->Init(f, len) ) return false; return _Init(); } bool IfcGeomObjects::Init(void* data, int len) { - Ifc::SetOutput(0,0); - if ( !Ifc::Init(data, len) ) return false; + Logger::SetOutput(0,0); + ifc_file = new IfcParse::IfcFile(); + if ( !ifc_file->Init(data, len) ) return false; return _Init(); } void IfcGeomObjects::Settings(int setting, bool value) { @@ -538,7 +626,10 @@ void IfcGeomObjects::Settings(int setting, bool value) { use_faster_booleans = value; break; case SEW_SHELLS: - Ifc::SewShells = value; + IfcGeom::SetValue(IfcGeom::GV_MAX_FACES_TO_SEW,value ? 1000 : -1); + break; + case IfcGeomObjects::FORCE_CCW_FACE_ORIENTATION: + IfcGeom::SetValue(IfcGeom::GV_FORCE_CCW_FACE_ORIENTATION,value ? 1 : -1); break; } } @@ -546,5 +637,5 @@ int IfcGeomObjects::Progress() { return 100 * done / total; } std::string IfcGeomObjects::GetLog() { - return Ifc::GetLog(); + return Logger::GetLog(); } diff --git a/src/ifcgeom/IfcGeomObjects.h b/src/ifcgeom/IfcGeomObjects.h index 451ac9b0ae..623b174b4b 100644 --- a/src/ifcgeom/IfcGeomObjects.h +++ b/src/ifcgeom/IfcGeomObjects.h @@ -94,6 +94,9 @@ namespace IfcGeomObjects { // Specifies whether to compose IfcOpeningElements into a single compound // in order to speed up the processing of opening subtractions. const int FASTER_BOOLEANS = 6; + // By default singular faces have no explicitly defined orientation, to + // force faces to be defined CounterClockWise set this to true. + const int FORCE_CCW_FACE_ORIENTATION = 7; // End of settings enumeration. @@ -143,6 +146,7 @@ namespace IfcGeomObjects { IfcGeomObject(int my_id, int p_id, const std::string& n, const std::string& t, const std::string& g, const gp_Trsf& trsf, IfcMesh* m); }; + void InitUnits(); bool Init(const std::string fn); bool Init(void* data, int len); bool Init(const std::string fn, std::ostream* log1= 0, std::ostream* log2= 0); diff --git a/src/ifcgeom/IfcGeomShapes.cpp b/src/ifcgeom/IfcGeomShapes.cpp index d06a329828..dccb1a5147 100644 --- a/src/ifcgeom/IfcGeomShapes.cpp +++ b/src/ifcgeom/IfcGeomShapes.cpp @@ -81,7 +81,7 @@ bool IfcGeom::convert(const Ifc2x3::IfcExtrudedAreaSolid::ptr l, TopoDS_Shape& shape) { TopoDS_Face face; if ( ! IfcGeom::convert_face(l->SweptArea(),face) ) return false; - const double height = l->Depth() * Ifc::LengthUnit; + const double height = l->Depth() * IfcGeom::GetValue(GV_LENGTH_UNIT); gp_Trsf trsf; IfcGeom::convert(l->Position(),trsf); @@ -158,11 +158,11 @@ bool IfcGeom::convert(const Ifc2x3::IfcBooleanClippingResult::ptr l, TopoDS_Shap const double first_operand_volume = shape_volume(s1); if ( first_operand_volume <= ALMOST_ZERO ) - Ifc::LogMessage("Warning","Empty solid for:",l->FirstOperand()->entity); + Logger::Message(Logger::LOG_WARNING,"Empty solid for:",l->FirstOperand()->entity); if ( !IfcGeom::convert_shape(l->SecondOperand(),s2) ) { shape = s1; - Ifc::LogMessage("Error","Failed to convert SecondOperand of:",l->entity); + Logger::Message(Logger::LOG_ERROR,"Failed to convert SecondOperand of:",l->entity); return true; } @@ -176,7 +176,7 @@ bool IfcGeom::convert(const Ifc2x3::IfcBooleanClippingResult::ptr l, TopoDS_Shap if ( ! is_halfspace ) { const double second_operand_volume = shape_volume(s2); if ( second_operand_volume <= ALMOST_ZERO ) - Ifc::LogMessage("Warning","Empty solid for:",operand2->entity); + Logger::Message(Logger::LOG_WARNING,"Empty solid for:",operand2->entity); } bool valid_cut = false; @@ -211,7 +211,7 @@ bool IfcGeom::convert(const Ifc2x3::IfcBooleanClippingResult::ptr l, TopoDS_Shap if ( is_valid ) { shape = result; valid_cut = true; - Ifc::LogMessage("Warning","Slightly nudged the SecondOperand of:",l->entity); + Logger::Message(Logger::LOG_WARNING,"Slightly nudged the SecondOperand of:",l->entity); } } } @@ -281,9 +281,9 @@ bool IfcGeom::convert(const Ifc2x3::IfcBooleanClippingResult::ptr l, TopoDS_Shap if ( valid_cut ) { const double volume_after_subtraction = shape_volume(shape); if ( ALMOST_THE_SAME(first_operand_volume,volume_after_subtraction) ) - Ifc::LogMessage("Warning","Subtraction yields unchanged volume:",l->entity); + Logger::Message(Logger::LOG_WARNING,"Subtraction yields unchanged volume:",l->entity); } else { - Ifc::LogMessage("Error","Failed to process subtraction:",l->entity); + Logger::Message(Logger::LOG_ERROR,"Failed to process subtraction:",l->entity); shape = s1; } @@ -294,7 +294,7 @@ bool IfcGeom::convert(const Ifc2x3::IfcConnectedFaceSet::ptr l, TopoDS_Shape& sh Ifc2x3::IfcFace::list faces = l->CfsFaces(); bool facesAdded = false; const unsigned int num_faces = faces->Size(); - if ( Ifc::SewShells && num_faces < GetValue(GV_MAX_FACES_TO_SEW) ) { + if ( num_faces < GetValue(GV_MAX_FACES_TO_SEW) ) { BRepOffsetAPI_Sewing builder; builder.SetTolerance(GetValue(GV_POINT_EQUALITY_TOLERANCE)); builder.SetMaxTolerance(GetValue(GV_POINT_EQUALITY_TOLERANCE)); @@ -305,7 +305,7 @@ bool IfcGeom::convert(const Ifc2x3::IfcConnectedFaceSet::ptr l, TopoDS_Shape& sh builder.Add(face); facesAdded = true; } else { - Ifc::LogMessage("Warning","Invalid face:",(*it)->entity); + Logger::Message(Logger::LOG_WARNING,"Invalid face:",(*it)->entity); } } if ( ! facesAdded ) return false; @@ -326,7 +326,7 @@ bool IfcGeom::convert(const Ifc2x3::IfcConnectedFaceSet::ptr l, TopoDS_Shape& sh builder.Add(compound,face); facesAdded = true; } else { - Ifc::LogMessage("Warning","Invalid face:",(*it)->entity); + Logger::Message(Logger::LOG_WARNING,"Invalid face:",(*it)->entity); } } if ( ! facesAdded ) return false; diff --git a/src/ifcgeom/IfcGeomWires.cpp b/src/ifcgeom/IfcGeomWires.cpp index 692b7922ca..3850b8c9bc 100644 --- a/src/ifcgeom/IfcGeomWires.cpp +++ b/src/ifcgeom/IfcGeomWires.cpp @@ -83,62 +83,58 @@ #include "../ifcgeom/IfcGeom.h" bool IfcGeom::convert(const Ifc2x3::IfcCompositeCurve::ptr l, TopoDS_Wire& wire) { - if ( ! Ifc::hasPlaneAngleUnit ) { - Ifc::LogMessage("Warning","Creating a composite curve without unit information:",l->entity); - - // Temporarily pretend we do have unit information - Ifc::hasPlaneAngleUnit = true; + if ( IfcGeom::GetValue(GV_PLANEANGLE_UNIT)>0 ) { + Logger::Message(Logger::LOG_WARNING,"Creating a composite curve without unit information:",l->entity); - bool succes_radians = false; + // Temporarily pretend we do have unit information + IfcGeom::SetValue(GV_PLANEANGLE_UNIT,1.0); + + bool succes_radians = false; bool succes_degrees = false; bool use_radians = false; bool use_degrees = false; - - // First try radians - Ifc::PlaneAngleUnit = 1.0f; - TopoDS_Wire wire_radians, wire_degrees; + + // First try radians + TopoDS_Wire wire_radians, wire_degrees; try { succes_radians = IfcGeom::convert(l,wire_radians); } catch (...) {} - - // Now try degrees - Ifc::PlaneAngleUnit = 0.0174532925199433f; + + // Now try degrees + IfcGeom::SetValue(GV_PLANEANGLE_UNIT,0.0174532925199433); try { succes_degrees = IfcGeom::convert(l,wire_degrees); } catch (...) {} // Restore to unknown unit state - Ifc::PlaneAngleUnit = 1.0f; - Ifc::hasPlaneAngleUnit = false; + IfcGeom::SetValue(GV_PLANEANGLE_UNIT,-1.0); - if ( succes_degrees && ! succes_radians ) { + if ( succes_degrees && ! succes_radians ) { use_degrees = true; } else if ( succes_radians && ! succes_degrees ) { - use_radians = true; - } else if ( succes_radians && succes_degrees ) { - if ( wire_degrees.Closed() && ! wire_radians.Closed() ) { - use_degrees = true; - } else if ( wire_radians.Closed() && ! wire_degrees.Closed() ) { - use_radians = true; - } else { - // No heuristic left to prefer the one over the other, - // apparently both variants are equally succesful. - // The curve might be composed of only straight segments. - // Let's go with the wire created using radians as that - // at least is a SI unit. - use_radians = true; - } - } - - if ( use_radians ) { - Ifc::LogMessage("Notice","Used radians to create composite curve"); - wire = wire_radians; - } else if ( use_degrees ) { - Ifc::LogMessage("Notice","Used degrees to create composite curve"); - wire = wire_degrees; + use_radians = true; + } else if ( succes_radians && succes_degrees ) { + if ( wire_degrees.Closed() && ! wire_radians.Closed() ) { + use_degrees = true; + } else if ( wire_radians.Closed() && ! wire_degrees.Closed() ) { + use_radians = true; + } else { + // No heuristic left to prefer the one over the other, + // apparently both variants are equally succesful. + // The curve might be composed of only straight segments. + // Let's go with the wire created using radians as that + // at least is a SI unit. + use_radians = true; + } } - return succes_radians || succes_degrees; + if ( use_radians ) { + Logger::Message(Logger::LOG_NOTICE,"Used radians to create composite curve"); + wire = wire_radians; + } else if ( use_degrees ) { + Logger::Message(Logger::LOG_NOTICE,"Used degrees to create composite curve"); + wire = wire_degrees; + } } Ifc2x3::IfcCompositeCurveSegment::list segments = l->Segments(); BRepBuilderAPI_MakeWire w; @@ -147,7 +143,7 @@ bool IfcGeom::convert(const Ifc2x3::IfcCompositeCurve::ptr l, TopoDS_Wire& wire) const Ifc2x3::IfcCurve::ptr curve = (*it)->ParentCurve(); TopoDS_Wire wire2; if ( ! IfcGeom::convert_wire(curve,wire2) ) { - Ifc::LogMessage("Error","Failed to convert curve:",curve->entity); + Logger::Message(Logger::LOG_ERROR,"Failed to convert curve:",curve->entity); continue; } if ( ! (*it)->SameSense() ) wire2.Reverse(); @@ -166,7 +162,7 @@ bool IfcGeom::convert(const Ifc2x3::IfcCompositeCurve::ptr l, TopoDS_Wire& wire) w.Add(wire2); //last_vertex = w.Vertex(); if ( w.Error() != BRepBuilderAPI_WireDone ) { - Ifc::LogMessage("Error","Failed to join curve segments:",l->entity); + Logger::Message(Logger::LOG_ERROR,"Failed to join curve segments:",l->entity); return false; } } @@ -176,7 +172,7 @@ bool IfcGeom::convert(const Ifc2x3::IfcCompositeCurve::ptr l, TopoDS_Wire& wire) bool IfcGeom::convert(const Ifc2x3::IfcTrimmedCurve::ptr l, TopoDS_Wire& wire) { Ifc2x3::IfcCurve::ptr basis_curve = l->BasisCurve(); bool isConic = basis_curve->is(Ifc2x3::Type::IfcConic); - double parameterFactor = isConic ? Ifc::PlaneAngleUnit : Ifc::LengthUnit; + double parameterFactor = isConic ? IfcGeom::GetValue(GV_PLANEANGLE_UNIT) : IfcGeom::GetValue(GV_LENGTH_UNIT); Handle(Geom_Curve) curve; if ( ! IfcGeom::convert_curve(basis_curve,curve) ) return false; bool trim_cartesian = l->MasterRepresentation() == Ifc2x3::IfcTrimmingPreference::IfcTrimmingPreference_CARTESIAN; @@ -209,7 +205,7 @@ bool IfcGeom::convert(const Ifc2x3::IfcTrimmedCurve::ptr l, TopoDS_Wire& wire) { BRepBuilderAPI_EdgeError err = e.Error(); if ( err == BRepBuilderAPI_PointProjectionFailed ) { w.Add(BRepBuilderAPI_MakeEdge(sense_agreement ? pnt1 : pnt2,sense_agreement ? pnt2 : pnt1)); - Ifc::LogMessage("Warning","Point projection failed for:",l->entity); + Logger::Message(Logger::LOG_WARNING,"Point projection failed for:",l->entity); } } else { w.Add(e.Edge()); diff --git a/src/ifcgeom/IfcRegister.cpp b/src/ifcgeom/IfcRegister.cpp index 19c56127ca..07e2f63f67 100644 --- a/src/ifcgeom/IfcRegister.cpp +++ b/src/ifcgeom/IfcRegister.cpp @@ -33,7 +33,7 @@ using namespace IfcUtil; bool IfcGeom::convert_shapes(const IfcBaseClass* l, ShapeList& r) { #include "IfcRegisterConvertShapes.h" - Ifc::LogMessage("Error","No operation defined for:",l->entity); + Logger::Message(Logger::LOG_ERROR,"No operation defined for:",l->entity); return false; } bool IfcGeom::is_shape_collection(const IfcBaseClass* l) { @@ -45,21 +45,21 @@ const TopoDS_Shape* IfcGeom::convert_shape(const IfcBaseClass* l, TopoDS_Shape& std::map::const_iterator it = Cache::Shape.find(id); if ( it != Cache::Shape.end() ) { r = it->second; return &(it->second); } #include "IfcRegisterConvertShape.h" - Ifc::LogMessage("Error","No operation defined for:",l->entity); + Logger::Message(Logger::LOG_ERROR,"No operation defined for:",l->entity); return 0; } bool IfcGeom::convert_wire(const IfcBaseClass* l, TopoDS_Wire& r) { #include "IfcRegisterConvertWire.h" - Ifc::LogMessage("Error","No operation defined for:",l->entity); + Logger::Message(Logger::LOG_ERROR,"No operation defined for:",l->entity); return false; } bool IfcGeom::convert_face(const IfcBaseClass* l, TopoDS_Face& r) { #include "IfcRegisterConvertFace.h" - Ifc::LogMessage("Error","No operation defined for:",l->entity); + Logger::Message(Logger::LOG_ERROR,"No operation defined for:",l->entity); return false; } bool IfcGeom::convert_curve(const IfcBaseClass* l, Handle(Geom_Curve)& r) { #include "IfcRegisterConvertCurve.h" - Ifc::LogMessage("Error","No operation defined for:",l->entity); + Logger::Message(Logger::LOG_ERROR,"No operation defined for:",l->entity); return false; } \ No newline at end of file diff --git a/src/ifcgeom/IfcRegister.h b/src/ifcgeom/IfcRegister.h index 9d6149960a..0e1d064d07 100644 --- a/src/ifcgeom/IfcRegister.h +++ b/src/ifcgeom/IfcRegister.h @@ -38,6 +38,7 @@ #include #include +#include "../ifcparse/IfcUtil.h" #include "../ifcparse/IfcParse.h" using namespace Ifc2x3; diff --git a/src/ifcgeom/IfcRegisterConvertShape.h b/src/ifcgeom/IfcRegisterConvertShape.h index cc68abf227..c84f5fa19d 100644 --- a/src/ifcgeom/IfcRegisterConvertShape.h +++ b/src/ifcgeom/IfcRegisterConvertShape.h @@ -7,7 +7,7 @@ return &(Cache::Shape[id]); \ } \ } catch(...) { } \ - Ifc::LogMessage("Error","Failed to convert:",l->entity); \ + Logger::Message(Logger::LOG_ERROR,"Failed to convert:",l->entity); \ return false; \ } #include "IfcRegisterDef.h" diff --git a/src/ifcgeom/IfcRegisterConvertShapes.h b/src/ifcgeom/IfcRegisterConvertShapes.h index 0762609895..962cccb203 100644 --- a/src/ifcgeom/IfcRegisterConvertShapes.h +++ b/src/ifcgeom/IfcRegisterConvertShapes.h @@ -4,7 +4,7 @@ try { \ return IfcGeom::convert((T*)l,r); \ } catch (...) { } \ - Ifc::LogMessage("Error","Failed to convert:",l->entity); \ + Logger::Message(Logger::LOG_ERROR,"Failed to convert:",l->entity); \ return false; \ } #include "IfcRegisterDef.h" diff --git a/src/ifcparse/IfcCharacterDecoder.cpp b/src/ifcparse/IfcCharacterDecoder.cpp index 5100c084ab..4f869ddb0f 100644 --- a/src/ifcparse/IfcCharacterDecoder.cpp +++ b/src/ifcparse/IfcCharacterDecoder.cpp @@ -1,4 +1,4 @@ -/******************************************************************************** +/******************************************************************************** * * * This file is part of IfcOpenShell. * * * @@ -87,7 +87,7 @@ void IfcCharacterDecoder::addChar(std::stringstream& s,const UChar32& ch) { s.put(substitution_character); #endif } -IfcCharacterDecoder::IfcCharacterDecoder(IfcParse::File* f) { +IfcCharacterDecoder::IfcCharacterDecoder(IfcParse::IfcSpfStream* f) { file = f; #ifdef HAVE_ICU if (destination) ucnv_close(destination); diff --git a/src/ifcparse/IfcCharacterDecoder.h b/src/ifcparse/IfcCharacterDecoder.h index 57a7d76072..e9e8c94dbd 100644 --- a/src/ifcparse/IfcCharacterDecoder.h +++ b/src/ifcparse/IfcCharacterDecoder.h @@ -42,7 +42,7 @@ namespace IfcParse { class IfcCharacterDecoder { private: - IfcParse::File* file; + IfcParse::IfcSpfStream* file; #ifdef HAVE_ICU static UConverter* destination; static UConverter* converter; @@ -65,7 +65,7 @@ namespace IfcParse { #else static char substitution_character; #endif - IfcCharacterDecoder(IfcParse::File* file); + IfcCharacterDecoder(IfcParse::IfcSpfStream* file); ~IfcCharacterDecoder(); void dryRun(); operator std::string(); diff --git a/src/ifcparse/IfcFile.h b/src/ifcparse/IfcFile.h index 204687025b..df6021b37b 100644 --- a/src/ifcparse/IfcFile.h +++ b/src/ifcparse/IfcFile.h @@ -39,14 +39,14 @@ //const int BUF_SIZE = (128 * 1024 * 1024); namespace IfcParse { - /// The File class represents a ISO 10303-21 IFC-SPF file in memory. + /// The IfcSpfStream class represents a ISO 10303-21 IFC-SPF file in memory. /// The file is interpreted as a sequence of tokens which are lazily /// interpreted only when requested. If the size of the file is /// larger than BUF_SIZE, the file is split into seperate pages, of /// which only one is simultaneously kept in memory, for files /// that define their entities not in a sequential nature, this is /// detrimental for the performance of the parser. - class File { + class IfcSpfStream { private: std::ifstream stream; char* buffer; @@ -61,9 +61,9 @@ namespace IfcParse { bool valid; bool eof; unsigned int size; - File(const std::string& fn); - File(std::istream& f, int len); - File(void* data, int len); + IfcSpfStream(const std::string& fn); + IfcSpfStream(std::istream& f, int len); + IfcSpfStream(void* data, int len); /// Returns the character at the cursor char Peek(); /// Returns the character at specified offset diff --git a/src/ifcparse/IfcParse.cpp b/src/ifcparse/IfcParse.cpp index e20c2a0215..4e647a5a7c 100644 --- a/src/ifcparse/IfcParse.cpp +++ b/src/ifcparse/IfcParse.cpp @@ -1,4 +1,4 @@ -/******************************************************************************** +/******************************************************************************** * * * This file is part of IfcOpenShell. * * * @@ -33,7 +33,7 @@ using namespace IfcParse; // // Opens the file, gets the filesize and reads a chunk in memory // -File::File(const std::string& fn) { +IfcSpfStream::IfcSpfStream(const std::string& fn) { eof = false; stream.open(fn.c_str(),std::ios_base::binary); if ( ! stream.good() ) { @@ -56,7 +56,7 @@ File::File(const std::string& fn) { ReadBuffer(false); } -File::File(std::istream& f, int l) { +IfcSpfStream::IfcSpfStream(std::istream& f, int l) { eof = false; size = l; #ifdef BUF_SIZE @@ -70,7 +70,7 @@ File::File(std::istream& f, int l) { len = l; } -File::File(void* data, int l) { +IfcSpfStream::IfcSpfStream(void* data, int l) { eof = false; size = l; #ifdef BUF_SIZE @@ -83,15 +83,17 @@ File::File(void* data, int l) { len = l; } -void File::Close() { - stream.close(); +void IfcSpfStream::Close() { +#ifdef BUF_SIZE + if ( paging ) stream.close(); +#endif delete[] buffer; } // // Reads a chunk of BUF_SIZE in memory and increments cursor if requested // -void File::ReadBuffer(bool inc) { +void IfcSpfStream::ReadBuffer(bool inc) { #ifdef BUF_SIZE if ( inc ) { offset += len; @@ -108,12 +110,17 @@ void File::ReadBuffer(bool inc) { len = (unsigned int) stream.gcount(); eof = len == 0; ptr = 0; +#ifdef BUF_SIZE + if (!paging) stream.close(); +#else + stream.close(); +#endif } // // Seeks an arbitrary position in the file // -void File::Seek(unsigned int o) { +void IfcSpfStream::Seek(unsigned int o) { #ifdef BUF_SIZE if ( !paging ) { #endif @@ -135,14 +142,14 @@ void File::Seek(unsigned int o) { // // Returns the character at the cursor // -char File::Peek() { +char IfcSpfStream::Peek() { return buffer[ptr]; } // // Returns the character at specified offset // -char File::Read(unsigned int o) { +char IfcSpfStream::Read(unsigned int o) { #ifdef BUF_SIZE if ( ! paging ) { #endif @@ -161,7 +168,7 @@ char File::Read(unsigned int o) { // // Returns the cursor position // -unsigned int File::Tell() { +unsigned int IfcSpfStream::Tell() { #ifdef BUF_SIZE return offset + ptr; #else @@ -172,7 +179,7 @@ unsigned int File::Tell() { // // Increments cursor and reads new chunk if necessary // -void File::Inc() { +void IfcSpfStream::Inc() { if ( ++ptr == len ) { #ifdef BUF_SIZE if ( paging ) ReadBuffer(); @@ -184,13 +191,14 @@ void File::Inc() { } #endif } - const char current = File::Peek(); - if ( current == '\n' || current == '\r' ) File::Inc(); + const char current = IfcSpfStream::Peek(); + if ( current == '\n' || current == '\r' ) IfcSpfStream::Inc(); } -Tokens::Tokens(IfcParse::File *f) { +Tokens::Tokens(IfcParse::IfcSpfStream *s, IfcParse::IfcFile* f) { file = f; - decoder = new IfcCharacterDecoder(f); + stream = s; + decoder = new IfcCharacterDecoder(s); } Tokens::~Tokens() { @@ -202,38 +210,38 @@ Tokens::~Tokens() { // Token Tokens::Next() { - if ( file->eof ) return TokenPtr(); + if ( stream->eof ) return TokenPtr(); char c; // Trim whitespace - while ( !file->eof ) { - c = file->Peek(); - if ( (c == ' ' || c == '\r' || c == '\n' || c == '\t' ) ) file->Inc(); + while ( !stream->eof ) { + c = stream->Peek(); + if ( (c == ' ' || c == '\r' || c == '\n' || c == '\t' ) ) stream->Inc(); else break; } - if ( file->eof ) return TokenPtr(); - unsigned int pos = file->Tell(); + if ( stream->eof ) return TokenPtr(); + unsigned int pos = stream->Tell(); bool inString = false; bool inComment = false; // If the cursor is at [()=,;$*] we know token consists of single char if ( c == '(' || c == ')' || c == '=' || c == ',' || c == ';' || c == '$' || c == '*' ) { - file->Inc(); + stream->Inc(); return TokenPtr(c); } int len = 0; char p = 0; - while ( ! file->eof ) { + while ( ! stream->eof ) { // Read character and increment pointer if not starting a new token - char c = file->Peek(); + char c = stream->Peek(); if ( len && (!inString || inComment) && (c == '(' || c == ')' || c == '=' || c == ',' || c == ';' ) ) break; - file->Inc(); + stream->Inc(); // Skip whitespace if not in comment or string if ( !inComment && !inString && (c == ' ' || c == '\r' || c == '\n' || c == '\t' ) ) continue; @@ -247,7 +255,7 @@ Token Tokens::Next() { p = c; } - if ( len ) return TokenPtr(pos); + if ( len ) return TokenPtr(this,pos); else return TokenPtr(); } @@ -256,18 +264,18 @@ Token Tokens::Next() { // Omits whitespace and comments // std::string Tokens::TokenString(unsigned int offset) { - const bool was_eof = file->eof; - unsigned int old_offset = file->Tell(); - file->Seek(offset); + const bool was_eof = stream->eof; + unsigned int old_offset = stream->Tell(); + stream->Seek(offset); bool inString = false; bool inComment = false; std::string buffer; buffer.reserve(128); char p = 0; - while ( ! file->eof ) { - char c = file->Peek(); + while ( ! stream->eof ) { + char c = stream->Peek(); if ( buffer.size() && (!inString || inComment) && (c == '(' || c == ')' || c == '=' || c == ',' || c == ';' ) ) break; - file->Inc(); + stream->Inc(); if ( !inComment && !inString && (c == ' ' || c == '\r' || c == '\n' || c == '\t' ) ) continue; if ( !inComment ) buffer.push_back(c); if ( inComment && p == '*' && c == '/' ) inComment = false; @@ -275,8 +283,8 @@ std::string Tokens::TokenString(unsigned int offset) { else if ( !inComment && c == '\'' ) return *decoder; p = c; } - if ( was_eof ) file->eof = true; - else file->Seek(old_offset); + if ( was_eof ) stream->eof = true; + else stream->Seek(old_offset); return buffer; } @@ -284,33 +292,32 @@ std::string Tokens::TokenString(unsigned int offset) { // Functions for creating Tokens from an arbitary file offset. // The first 4 bits are reserved for Tokens of type ()=,;$* // -Token IfcParse::TokenPtr(unsigned int offset) { return offset + 128; } -Token IfcParse::TokenPtr(char c) { return c; } -Token IfcParse::TokenPtr() { return 0; } +Token IfcParse::TokenPtr(Tokens* tokens, unsigned int offset) { return Token(tokens,offset); } +Token IfcParse::TokenPtr(char c) { return Token((Tokens*)0,(unsigned) c); } +Token IfcParse::TokenPtr() { return Token((Tokens*)0,0); } // // Functions to convert Tokens to binary data // -unsigned int TokenFunc::Offset(Token t) { return t - 128; } -bool TokenFunc::startsWith(Token t, char c) { - return Ifc::file->Read(Offset(t)) == c; +bool TokenFunc::startsWith(const Token& t, char c) { + return t.first->stream->Read(t.second) == c; } -bool TokenFunc::isOperator(Token t, char op) { - return (t < 128) && (!op || op == t); +bool TokenFunc::isOperator(const Token& t, char op) { + return (!t.first) && (!op || op == t.second); } -bool TokenFunc::isIdentifier(Token t) { +bool TokenFunc::isIdentifier(const Token& t) { return ! isOperator(t) && startsWith(t, '#'); } -bool TokenFunc::isString(Token t) { +bool TokenFunc::isString(const Token& t) { return ! isOperator(t) && startsWith(t, '\''); } -bool TokenFunc::isEnumeration(Token t) { +bool TokenFunc::isEnumeration(const Token& t) { return ! isOperator(t) && startsWith(t, '.'); } -bool TokenFunc::isDatatype(Token t) { +bool TokenFunc::isDatatype(const Token& t) { return ! isOperator(t) && startsWith(t, 'I'); } -int TokenFunc::asInt(Token t) { +int TokenFunc::asInt(const Token& t) { const std::string str = asString(t); // In case of an ENTITY_INSTANCE_NAME skip the leading # const char* start = str.c_str() + (isIdentifier(t) ? 1 : 0); @@ -319,31 +326,31 @@ int TokenFunc::asInt(Token t) { if ( start == end ) throw IfcException("Token is not an integer or identifier"); return (int) result; } -bool TokenFunc::asBool(Token t) { +bool TokenFunc::asBool(const Token& t) { const std::string str = asString(t); return str == "T"; } -double TokenFunc::asFloat(Token t) { +double TokenFunc::asFloat(const Token& t) { const std::string str = asString(t); return (double) atof(str.c_str()); } -std::string TokenFunc::asString(Token t) { +std::string TokenFunc::asString(const Token& t) { if ( isOperator(t,'$') ) return ""; else if ( isOperator(t) ) throw IfcException("Token is not a string"); - std::string str = Ifc::tokens->TokenString(t - 128); + std::string str = t.first->TokenString(t.second); return isString(t) || isEnumeration(t) ? str.substr(1,str.size()-2) : str; } -std::string TokenFunc::toString(Token t) { - if ( isOperator(t) ) return std::string ( (char*) &t, 1 ); - else return Ifc::tokens->TokenString(t - 128); +std::string TokenFunc::toString(const Token& t) { + if ( isOperator(t) ) return std::string ( (char*) &t.second , 1 ); + else return t.first->TokenString(t.second); } -TokenArgument::TokenArgument(Token t) { +TokenArgument::TokenArgument(const Token& t) { token = t; } -EntityArgument::EntityArgument(Ifc2x3::Type::Enum ty, Token t) { +EntityArgument::EntityArgument(Ifc2x3::Type::Enum ty, const Token& t) { entity = new IfcUtil::IfcArgumentSelect(ty,new TokenArgument(t)); } @@ -352,10 +359,11 @@ EntityArgument::EntityArgument(Ifc2x3::Type::Enum ty, Token t) { // Aditionally, stores the ids (i.e. #[\d]+) in a vector // ArgumentList::ArgumentList(Tokens* t, std::vector& ids) { - while( Token next = t->Next() ) { - if ( TokenFunc::isOperator(next,',') ) continue; - if ( TokenFunc::isOperator(next,')') ) break; - if ( TokenFunc::isOperator(next,'(') ) Push( new ArgumentList(t,ids) ); + Token next = t->Next(); + while( next.second || next.first ) { + if ( TokenFunc::isOperator(next,',') ) {} + else if ( TokenFunc::isOperator(next,')') ) break; + else if ( TokenFunc::isOperator(next,'(') ) Push( new ArgumentList(t,ids) ); else { if ( TokenFunc::isIdentifier(next) ) ids.push_back(TokenFunc::asInt(next)); if ( TokenFunc::isDatatype(next) ) { @@ -363,13 +371,14 @@ ArgumentList::ArgumentList(Tokens* t, std::vector& ids) { try { Push ( new EntityArgument(Ifc2x3::Type::FromString(TokenFunc::asString(next)),t->Next()) ); } catch ( IfcException& e ) { - Ifc::LogMessage("Error",e.what()); + Logger::Message(Logger::LOG_ERROR,e.what()); } t->Next(); } else { Push ( new TokenArgument(next) ); } } + next = t->Next(); } } @@ -454,11 +463,7 @@ TokenArgument::operator std::string() const { return TokenFunc::asString(token); TokenArgument::operator std::vector() const { throw IfcException("Argument is not a list of floats"); } TokenArgument::operator std::vector() const { throw IfcException("Argument is not a list of ints"); } TokenArgument::operator std::vector() const { throw IfcException("Argument is not a list of strings"); } -TokenArgument::operator IfcUtil::IfcSchemaEntity() const { return Ifc::EntityById(TokenFunc::asInt(token)); } -/*TokenArgument::operator IfcUtil::IfcAbstractSelect::ptr() const { -//TODO Fix memory leak -return new IfcUtil::IfcEntitySelect(*this); -}*/ +TokenArgument::operator IfcUtil::IfcSchemaEntity() const { return token.first->file->EntityById(TokenFunc::asInt(token)); } TokenArgument::operator IfcEntities() const { throw IfcException("Argument is not a list of entities"); } unsigned int TokenArgument::Size() const { return 1; } ArgumentPtr TokenArgument::operator [] (unsigned int i) const { throw IfcException("Argument is not a list of arguments"); } @@ -496,19 +501,21 @@ EntityArgument::~EntityArgument() { delete entity; } // // Reads an Entity from the list of Tokens // -Entity::Entity(unsigned int i, Tokens* t) { - Token datatype = t->Next(); +Entity::Entity(unsigned int i, IfcFile* f) { //: file(f) { + file = f; + Token datatype = f->tokens->Next(); if ( ! TokenFunc::isDatatype(datatype)) throw IfcException("Unexpected token while parsing entity"); _type = Ifc2x3::Type::FromString(TokenFunc::asString(datatype)); _id = i; args = ArgumentPtr(); - offset = TokenFunc::Offset(datatype); + offset = datatype.second; } // // Reads an Entity from the list of Tokens at the specified offset in the file // -Entity::Entity(unsigned int i, Tokens* t, unsigned int o) { +Entity::Entity(unsigned int i, IfcFile* f, unsigned int o) { // : file(f) { + file = f; std::vector ids; _id = i; offset = o; @@ -539,16 +546,16 @@ unsigned int Entity::getArgumentCount() { // void Entity::Load(std::vector& ids, bool seek) { if ( seek ) { - Ifc::file->Seek(offset); - Token datatype = Ifc::tokens->Next(); + file->tokens->stream->Seek(offset); + Token datatype = file->tokens->Next(); if ( ! TokenFunc::isDatatype(datatype)) throw IfcException("Unexpected token while parsing entity"); _type = Ifc2x3::Type::FromString(TokenFunc::asString(datatype)); } - Token open = Ifc::tokens->Next(); - args = new ArgumentList(Ifc::tokens, ids); - unsigned int old_offset = Ifc::file->Tell(); - Token semilocon = Ifc::tokens->Next(); - if ( ! TokenFunc::isOperator(semilocon,';') ) Ifc::file->Seek(old_offset); + Token open = file->tokens->Next(); + args = new ArgumentList(file->tokens, ids); + unsigned int old_offset = file->tokens->stream->Tell(); + Token semilocon = file->tokens->Next(); + if ( ! TokenFunc::isOperator(semilocon,';') ) file->tokens->stream->Seek(old_offset); } Ifc2x3::Type::Enum Entity::type() const { @@ -589,7 +596,7 @@ Entity::~Entity() { // IfcEntities Entity::getInverse(Ifc2x3::Type::Enum c) { IfcEntities l = IfcEntities(new IfcEntityList()); - IfcEntities all = Ifc::EntitiesByReference(_id); + IfcEntities all = file->EntitiesByReference(_id); if ( ! all ) return l; for( IfcEntityList::it it = all->begin(); it != all->end();++ it ) { if ( c == Ifc2x3::Type::ALL || (*it)->is(c) ) { @@ -612,44 +619,55 @@ IfcEntities Entity::getInverse(Ifc2x3::Type::Enum c, int i, const std::string& a bool Entity::is(Ifc2x3::Type::Enum v) const { return _type == v; } unsigned int Entity::id() { return _id; } +IfcFile::IfcFile() { + file = 0; + lastId = 0; + tokens = 0; + MaxId = 0; +} + // // Parses the IFC file in fn // Creates the maps // Gets the unit definitins from the file // -bool Ifc::Init(const std::string& fn) { - return Ifc::Init(new File(fn)); +bool IfcFile::Init(const std::string& fn) { + return IfcFile::Init(new IfcSpfStream(fn)); } -bool Ifc::Init(std::istream& f, int len) { - return Ifc::Init(new File(f,len)); +bool IfcFile::Init(std::istream& f, int len) { + return IfcFile::Init(new IfcSpfStream(f,len)); } -bool Ifc::Init(void* data, int len) { - return Ifc::Init(new File(data,len)); +bool IfcFile::Init(void* data, int len) { + return IfcFile::Init(new IfcSpfStream(data,len)); } -bool Ifc::Init(IfcParse::File* f) { +bool IfcFile::Init(IfcParse::IfcSpfStream* f) { Ifc2x3::InitStringMap(); file = f; if ( ! file->valid ) return false; - tokens = new Tokens (file); - Token token = 0; - Token previous = 0; + tokens = new Tokens (file,this); + Token token = TokenPtr(); + Token previous = TokenPtr(); unsigned int currentId = 0; lastId = 0; int x = 0; EntityPtr e; IfcUtil::IfcSchemaEntity entity = 0; - if ( log1 ) (*log1) << "Scanning file..." << std::endl; + Logger::Status("Scanning file..."); while ( ! file->eof ) { if ( currentId ) { try { - e = new Entity(currentId,tokens); + e = new Entity(currentId,this); entity = Ifc2x3::SchemaEntity(e); } catch (IfcException ex) { currentId = 0; - Ifc::LogMessage("Error",ex.what()); + Logger::Message(Logger::LOG_ERROR,ex.what()); continue; } - if ( log1 && !((++x)%1000) ) (*log1) << "\r#" << currentId << " " << std::flush; + // Update the status after every 1000 instances parsed + if ( !((++x)%1000) ) { + std::stringstream ss; ss << "\r#" << currentId; + Logger::Status(ss.str()); + } if ( entity->is(Ifc2x3::Type::IfcRoot) ) { Ifc2x3::IfcRoot::ptr ifc_root = (Ifc2x3::IfcRoot::ptr) entity; try { @@ -657,11 +675,11 @@ bool Ifc::Init(IfcParse::File* f) { if ( byguid.find(guid) != byguid.end() ) { std::stringstream ss; ss << "Overwriting entity with guid " << guid; - Ifc::LogMessage("Warning",ss.str()); + Logger::Message(Logger::LOG_WARNING,ss.str()); } byguid[guid] = ifc_root; } catch (IfcException ex) { - Ifc::LogMessage("Error",ex.what()); + Logger::Message(Logger::LOG_ERROR,ex.what()); } } Ifc2x3::Type::Enum ty = entity->type(); @@ -677,16 +695,16 @@ bool Ifc::Init(IfcParse::File* f) { if ( byid.find(currentId) != byid.end() ) { std::stringstream ss; ss << "Overwriting entity with id " << currentId; - Ifc::LogMessage("Warning",ss.str()); + Logger::Message(Logger::LOG_WARNING,ss.str()); } byid[currentId] = entity; currentId = 0; } else { try { token = tokens->Next(); } - catch (... ) { token = 0; } + catch (... ) { token = TokenPtr(); } } - if ( ! token ) break; - if ( previous && TokenFunc::isIdentifier(previous) ) { + if ( ! (token.second || token.first) ) break; + if ( (previous.second || previous.first) && TokenFunc::isIdentifier(previous) ) { int id = TokenFunc::asInt(previous); if ( TokenFunc::isOperator(token,'=') ) { currentId = id; @@ -701,89 +719,32 @@ bool Ifc::Init(IfcParse::File* f) { } previous = token; } - - if ( log1 ) (*log1) << "\rDone scanning file " << std::endl; - - hasPlaneAngleUnit = false; - Ifc2x3::IfcUnitAssignment::list unit_assignments = EntitiesByType(); - IfcUtil::IfcAbstractSelect::list units = IfcUtil::IfcAbstractSelect::list(); - if ( unit_assignments->Size() ) { - Ifc2x3::IfcUnitAssignment::ptr unit_assignment = *unit_assignments->begin(); - units = unit_assignment->Units(); - } - 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. - Ifc2x3::IfcExtrudedAreaSolid::list extrusions = EntitiesByType(); - if ( ! extrusions->Size() ) return true; - double max_height = -1.0f; - for ( Ifc2x3::IfcExtrudedAreaSolid::it it = extrusions->begin(); it != extrusions->end(); ++ it ) { - const double depth = (*it)->Depth(); - if ( depth > max_height ) max_height = depth; - } - if ( max_height > 100.0f ) Ifc::LengthUnit = 0.001f; - return true; - } - try { - for ( IfcUtil::IfcAbstractSelect::it it = units->begin(); it != units->end(); ++ it ) { - const IfcUtil::IfcAbstractSelect::ptr base = *it; - Ifc2x3::IfcSIUnit::ptr unit = Ifc2x3::IfcSIUnit::ptr(); - double value = 1.0f; - if ( base->is(Ifc2x3::Type::IfcConversionBasedUnit) ) { - const Ifc2x3::IfcConversionBasedUnit::ptr u = reinterpret_pointer_cast(base); - const Ifc2x3::IfcMeasureWithUnit::ptr u2 = u->ConversionFactor(); - Ifc2x3::IfcUnit u3 = u2->UnitComponent(); - if ( u3->is(Ifc2x3::Type::IfcSIUnit) ) { - unit = (Ifc2x3::IfcSIUnit*) u3; - } - Ifc2x3::IfcValue v = u2->ValueComponent(); - IfcUtil::IfcArgumentSelect* v2 = (IfcUtil::IfcArgumentSelect*) v; - const double f = *v2->wrappedValue(); - value *= f; - } else if ( base->is(Ifc2x3::Type::IfcSIUnit) ) { - unit = reinterpret_pointer_cast(base); - } - if ( unit ) { - if ( unit->hasPrefix() ) { - value *= UnitPrefixToValue(unit->Prefix()); - } - Ifc2x3::IfcUnitEnum::IfcUnitEnum type = unit->UnitType(); - if ( type == Ifc2x3::IfcUnitEnum::IfcUnit_LENGTHUNIT ) { - Ifc::LengthUnit = value; - } else if ( type == Ifc2x3::IfcUnitEnum::IfcUnit_PLANEANGLEUNIT ) { - Ifc::PlaneAngleUnit = value; - Ifc::hasPlaneAngleUnit = true; - } - } - } - } catch ( IfcException ex ) { - Ifc::LogMessage("Error",ex.what()); - } + Logger::Status("\rDone scanning file "); return true; } -IfcEntities Ifc::EntitiesByType(Ifc2x3::Type::Enum t) { +IfcEntities IfcFile::EntitiesByType(Ifc2x3::Type::Enum t) { MapEntitiesByType::const_iterator it = bytype.find(t); return (it == bytype.end()) ? IfcEntities() : it->second; } -IfcEntities Ifc::EntitiesByReference(int t) { +IfcEntities IfcFile::EntitiesByReference(int t) { MapEntitiesByRef::const_iterator it = byref.find(t); return (it == byref.end()) ? IfcEntities() : it->second; } -IfcUtil::IfcSchemaEntity Ifc::EntityById(int id) { +IfcUtil::IfcSchemaEntity IfcFile::EntityById(int id) { MapEntityById::const_iterator it = byid.find(id); if ( it == byid.end() ) { MapOffsetById::const_iterator it2 = offsets.find(id); if ( it2 == offsets.end() ) throw IfcException("Entity not found"); const unsigned int offset = (*it2).second; - EntityPtr e = EntityPtr(new Entity(id,Ifc::tokens,offset)); + EntityPtr e = EntityPtr(new Entity(id,this,offset)); IfcUtil::IfcSchemaEntity entity = Ifc2x3::SchemaEntity(e); byid[id] = entity; return entity; } return it->second; } -Ifc2x3::IfcRoot::ptr Ifc::EntityByGuid(const std::string& guid) { +Ifc2x3::IfcRoot::ptr IfcFile::EntityByGuid(const std::string& guid) { MapEntityByGuid::const_iterator it = byguid.find(guid); if ( it == byguid.end() ) { throw IfcException("Entity not found"); @@ -796,77 +757,19 @@ IfcException::IfcException(std::string e) { error = e; } IfcException::~IfcException() throw () {} const char* IfcException::what() const throw() { return error.c_str(); } -void Ifc::Dispose() { +// FIXME: Test destructor to delete entity and arg allocations +IfcFile::~IfcFile() { for( MapEntityById::const_iterator it = byid.begin(); it != byid.end(); ++ it ) { delete it->second->entity; delete it->second; } - bytype.clear(); - byid.clear(); - byref.clear(); - file->Close(); delete file; delete tokens; - offsets.clear(); - log_stream.str(""); } -double UnitPrefixToValue( Ifc2x3::IfcSIPrefix::IfcSIPrefix v ) { - if ( v == Ifc2x3::IfcSIPrefix::IfcSIPrefix_EXA ) return (double) 1e18; - else if ( v == Ifc2x3::IfcSIPrefix::IfcSIPrefix_PETA ) return (double) 1e15; - else if ( v == Ifc2x3::IfcSIPrefix::IfcSIPrefix_TERA ) return (double) 1e12; - else if ( v == Ifc2x3::IfcSIPrefix::IfcSIPrefix_GIGA ) return (double) 1e9; - else if ( v == Ifc2x3::IfcSIPrefix::IfcSIPrefix_MEGA ) return (double) 1e6; - else if ( v == Ifc2x3::IfcSIPrefix::IfcSIPrefix_KILO ) return (double) 1e3; - else if ( v == Ifc2x3::IfcSIPrefix::IfcSIPrefix_HECTO ) return (double) 1e2; - else if ( v == Ifc2x3::IfcSIPrefix::IfcSIPrefix_DECA ) return (double) 1; - else if ( v == Ifc2x3::IfcSIPrefix::IfcSIPrefix_DECI ) return (double) 1e-1; - else if ( v == Ifc2x3::IfcSIPrefix::IfcSIPrefix_CENTI ) return (double) 1e-2; - else if ( v == Ifc2x3::IfcSIPrefix::IfcSIPrefix_MILLI ) return (double) 1e-3; - else if ( v == Ifc2x3::IfcSIPrefix::IfcSIPrefix_MICRO ) return (double) 1e-6; - else if ( v == Ifc2x3::IfcSIPrefix::IfcSIPrefix_NANO ) return (double) 1e-9; - else if ( v == Ifc2x3::IfcSIPrefix::IfcSIPrefix_PICO ) return (double) 1e-12; - else if ( v == Ifc2x3::IfcSIPrefix::IfcSIPrefix_FEMTO ) return (double) 1e-15; - else if ( v == Ifc2x3::IfcSIPrefix::IfcSIPrefix_ATTO ) return (double) 1e-18; - else return 1.0f; -} -void Ifc::SetOutput(std::ostream* l1, std::ostream* l2) { - log1 = l1; - log2 = l2; - if ( ! log2 ) { - log2 = &log_stream; - } -} -void Ifc::LogMessage(const std::string& type, const std::string& message, const IfcAbstractEntityPtr entity) { - if ( log2 ) { - (*log2) << "[" << type << "] " << message << std::endl; - if ( entity ) (*log2) << entity->toString() << std::endl; - } -} -std::string Ifc::GetLog() { - return log_stream.str(); -} -MapEntityById::const_iterator Ifc::First() { +MapEntityById::const_iterator IfcFile::begin() const { return byid.begin(); } -MapEntityById::const_iterator Ifc::Last() { +MapEntityById::const_iterator IfcFile::end() const { return byid.end(); } - -File* Ifc::file = 0; -std::ostream* Ifc::log1 = 0; -std::ostream* Ifc::log2 = 0; -unsigned int Ifc::lastId = 0; -Tokens* Ifc::tokens = 0; -double Ifc::LengthUnit = 1.0f; -double Ifc::PlaneAngleUnit = 1.0f; -bool Ifc::hasPlaneAngleUnit = false; -bool Ifc::SewShells = false; -int Ifc::CircleSegments = 32; -MapEntitiesByType Ifc::bytype; -MapEntityById Ifc::byid; -MapEntityByGuid Ifc::byguid; -MapEntitiesByRef Ifc::byref; -MapOffsetById Ifc::offsets; -std::stringstream Ifc::log_stream; -int Ifc::Verbosity = 2; diff --git a/src/ifcparse/IfcParse.h b/src/ifcparse/IfcParse.h index ec00f27394..19bde845f0 100644 --- a/src/ifcparse/IfcParse.h +++ b/src/ifcparse/IfcParse.h @@ -1,4 +1,4 @@ -/******************************************************************************** +/******************************************************************************** * * * This file is part of IfcOpenShell. * * * @@ -47,56 +47,59 @@ namespace IfcParse { class Entity; class Entities; + class IfcFile; typedef Entity* EntityPtr; typedef SHARED_PTR EntitiesPtr; - typedef unsigned int Token; + class Tokens; + typedef std::pair Token; /// Provides functions to convert Tokens to binary data /// Tokens are merely offsets to where they can be read in the file class TokenFunc { private: - static bool startsWith(Token t, char c); + static bool startsWith(const Token& t, char c); public: /// Returns the offset at which the token is read from the file - static unsigned int Offset(Token t); + // static unsigned int Offset(const Token& t); /// Returns whether the token can be interpreted as a string - static bool isString(Token t); + static bool isString(const Token& t); /// Returns whether the token can be interpreted as an identifier - static bool isIdentifier(Token t); + static bool isIdentifier(const Token& t); /// Returns whether the token can be interpreted as an syntactical operator - static bool isOperator(Token t, char op = 0); + static bool isOperator(const Token& t, char op = 0); /// Returns whether the token can be interpreted as an enumerated value - static bool isEnumeration(Token t); + static bool isEnumeration(const Token& t); /// Returns whether the token can be interpreted as an datatype name - static bool isDatatype(Token t); + static bool isDatatype(const Token& t); /// Returns the token interpreted as an integer - static int asInt(Token t); + static int asInt(const Token& t); /// Returns the token interpreted as an boolean (.T. or .F.) - static bool asBool(Token t); + static bool asBool(const Token& t); /// Returns the token as a floating point number - static double asFloat(Token t); + static double asFloat(const Token& t); /// Returns the token as a string (without the dot or apostrophe) - static std::string asString(Token t); + static std::string asString(const Token& t); /// Returns a string representation of the token (including the dot or apostrophe) - static std::string toString(Token t); + static std::string toString(const Token& t); }; // // Functions for creating Tokens from an arbitary file offset // The first 4 bits are reserved for Tokens of type ()=,;$* // - Token TokenPtr(unsigned int offset); + Token TokenPtr(Tokens* tokens, unsigned int offset); Token TokenPtr(char c); Token TokenPtr(); - /// A stream of tokens to be read from a File. + /// A stream of tokens to be read from a IfcSpfStream. class Tokens { private: - File* file; IfcCharacterDecoder* decoder; public: - Tokens(File* f); + IfcSpfStream* stream; + IfcFile* file; + Tokens(IfcSpfStream* s, IfcFile* f); Token Next(); ~Tokens(); std::string TokenString(unsigned int offset); @@ -136,7 +139,7 @@ namespace IfcParse { public: Token token; - TokenArgument(Token t); + TokenArgument(const Token& t); operator int() const; operator bool() const; operator double() const; @@ -160,7 +163,7 @@ namespace IfcParse { private: IfcUtil::IfcArgumentSelect* entity; public: - EntityArgument(Ifc2x3::Type::Enum ty, Token t); + EntityArgument(Ifc2x3::Type::Enum ty, const Token& t); ~EntityArgument(); operator int() const; operator bool() const; @@ -183,15 +186,16 @@ namespace IfcParse { /// ============================ class Entity : public IfcAbstractEntity { private: + //IfcFile* file; ArgumentPtr args; Ifc2x3::Type::Enum _type; public: - /// The EXPRESS ENTITY_NAME + /// The EXPRESS ENTITY_INSTANCE_NAME unsigned int _id; /// The offset at which the entity is read unsigned int offset; - Entity(unsigned int i, Tokens* t); - Entity(unsigned int i, Tokens* t, unsigned int o); + Entity(unsigned int i, IfcFile* t); + Entity(unsigned int i, IfcFile* t, unsigned int o); ~Entity(); IfcEntities getInverse(Ifc2x3::Type::Enum c = Ifc2x3::Type::ALL); IfcEntities getInverse(Ifc2x3::Type::Enum c, int i, const std::string& a); @@ -204,10 +208,9 @@ namespace IfcParse { bool is(Ifc2x3::Type::Enum v) const; unsigned int id(); }; -} typedef IfcUtil::IfcSchemaEntity IfcEntity; -typedef IfcEntities IfcEntities; +//typedef IfcEntities IfcEntities; typedef std::map MapEntitiesByType; typedef std::map MapEntityById; typedef std::map MapEntityByGuid; @@ -216,65 +219,56 @@ typedef std::map MapOffsetById; /// This class provides several static convenience functions and variables /// and provide access to the entities in an IFC file -class Ifc { +class IfcFile { private: - static MapEntityById byid; - static MapEntitiesByType bytype; - static MapEntitiesByRef byref; - static MapEntityByGuid byguid; - static MapOffsetById offsets; - static unsigned int lastId; - static std::ostream* log1; - static std::ostream* log2; - static std::stringstream log_stream; + MapEntityById byid; + MapEntitiesByType bytype; + MapEntitiesByRef byref; + MapEntityByGuid byguid; + MapOffsetById offsets; + unsigned int lastId; + unsigned int MaxId; public: - /// Returns the first entity in the file, this probably is the entity with the lowest id (EXPRESS ENTITY_NAME) - static MapEntityById::const_iterator First(); - /// Returns the last entity in the file, this probably is the entity with the highes id (EXPRESS ENTITY_NAME) - static MapEntityById::const_iterator Last(); - /// Determines to what stream respectively progress and errors are logged - static void SetOutput(std::ostream* l1, std::ostream* l2); - /// Log a message to the output stream - static void LogMessage(const std::string& type, const std::string& message, const IfcAbstractEntityPtr entity=0); - static IfcParse::File* file; - static IfcParse::Tokens* tokens; + typedef MapEntityById::const_iterator const_iterator; + IfcFile(); + ~IfcFile(); + /// Returns the first entity in the file, this probably is the entity with the lowest id (EXPRESS ENTITY_INSTANCE_NAME) + const_iterator begin() const; + /// Returns the last entity in the file, this probably is the entity with the highes id (EXPRESS ENTITY_INSTANCE_NAME) + const_iterator end() const; + IfcParse::IfcSpfStream* file; + IfcParse::Tokens* tokens; /// Returns all entities in the file that match the template argument. /// NOTE: This also returns subtypes of the requested type, for example: /// IfcWall will also return IfcWallStandardCase entities template - static typename T::list EntitiesByType() { + typename T::list EntitiesByType() { IfcEntities e = EntitiesByType(T::Class()); typename T::list l ( new IfcTemplatedEntityList() ); if ( e && e->Size() ) - for ( IfcEntityList::it it = e->begin(); it != e->end(); ++ it ) { - l->push(reinterpret_pointer_cast(*it)); - } + for ( IfcEntityList::it it = e->begin(); it != e->end(); ++ it ) { + l->push(reinterpret_pointer_cast(*it)); + } return l; } /// Returns all entities in the file that match the positional argument. /// NOTE: This also returns subtypes of the requested type, for example: /// IfcWall will also return IfcWallStandardCase entities - static IfcEntities EntitiesByType(Ifc2x3::Type::Enum t); + IfcEntities EntitiesByType(Ifc2x3::Type::Enum t); /// Returns all entities in the file that reference the id - static IfcEntities EntitiesByReference(int id); + IfcEntities EntitiesByReference(int id); /// Returns the entity with the specified id - static IfcEntity EntityById(int id); + IfcEntity EntityById(int id); /// Returns the entity with the specified GlobalId - static Ifc2x3::IfcRoot::ptr EntityByGuid(const std::string& guid); - static bool Init(const std::string& fn); - static bool Init(std::istream& fn, int len); - static bool Init(void* data, int len); - static bool Init(IfcParse::File* f); - static std::string GetLog(); - static void Dispose(); - static bool hasPlaneAngleUnit; - static bool SewShells; - static double LengthUnit; - static double PlaneAngleUnit; - static int CircleSegments; - static int Verbosity; + Ifc2x3::IfcRoot::ptr EntityByGuid(const std::string& guid); + bool Init(const std::string& fn); + bool Init(std::istream& fn, int len); + bool Init(void* data, int len); + bool Init(IfcParse::IfcSpfStream* f); }; double UnitPrefixToValue( Ifc2x3::IfcSIPrefix::IfcSIPrefix v ); +} + #endif diff --git a/src/ifcparse/IfcUtil.cpp b/src/ifcparse/IfcUtil.cpp index bf14440b37..6493e4f1d3 100644 --- a/src/ifcparse/IfcUtil.cpp +++ b/src/ifcparse/IfcUtil.cpp @@ -62,3 +62,35 @@ IfcUtil::IfcArgumentSelect::IfcArgumentSelect(Ifc2x3::Type::Enum t, ArgumentPtr ArgumentPtr IfcUtil::IfcArgumentSelect::wrappedValue() { return arg; } bool IfcUtil::IfcArgumentSelect::isSimpleType() { return true; } IfcUtil::IfcArgumentSelect::~IfcArgumentSelect() { delete arg; } + +void Logger::SetOutput(std::ostream* l1, std::ostream* l2) { + log1 = l1; + log2 = l2; + if ( ! log2 ) { + log2 = &log_stream; + } +} +void Logger::Message(Logger::Severity type, const std::string& message, const IfcAbstractEntityPtr entity) { + if ( log2 && type >= verbosity ) { + (*log2) << "[" << severity_strings[type] << "] " << message << std::endl; + if ( entity ) (*log2) << entity->toString() << std::endl; + } +} +void Logger::Status(const std::string& message, bool new_line) { + if ( log1 ) { + (*log1) << message; + if ( new_line ) (*log1) << std::endl; + else (*log1) << std::flush; + } +} +std::string Logger::GetLog() { + return log_stream.str(); +} +void Logger::Verbosity(Logger::Severity v) { verbosity = v; } +Logger::Severity Logger::Verbosity() { return verbosity; } + +std::ostream* Logger::log1 = 0; +std::ostream* Logger::log2 = 0; +std::stringstream Logger::log_stream; +Logger::Severity Logger::verbosity = Logger::LOG_NOTICE; +char* Logger::severity_strings[] = { "Notice","Warning","Error" }; \ No newline at end of file diff --git a/src/ifcparse/IfcUtil.h b/src/ifcparse/IfcUtil.h index 408b901034..b5214805cb 100644 --- a/src/ifcparse/IfcUtil.h +++ b/src/ifcparse/IfcUtil.h @@ -22,6 +22,7 @@ #include #include +#include #include "../ifcparse/SharedPointer.h" #include "../ifcparse/Ifc2x3enum.h" @@ -132,9 +133,14 @@ namespace IfcUtil { }; } +namespace IfcParse { + class IfcFile; +} + class Argument { -protected: public: + //void* file; +//public: virtual operator int() const = 0; virtual operator bool() const = 0; virtual operator double() const = 0; @@ -154,6 +160,7 @@ public: class IfcAbstractEntity { public: + IfcParse::IfcFile* file; virtual IfcEntities getInverse(Ifc2x3::Type::Enum c = Ifc2x3::Type::ALL) = 0; virtual IfcEntities getInverse(Ifc2x3::Type::Enum c, int i, const std::string& a) = 0; virtual std::string datatype() = 0; @@ -166,4 +173,25 @@ public: virtual unsigned int id() = 0; }; +class Logger { +public: + typedef enum { LOG_NOTICE, LOG_WARNING, LOG_ERROR } Severity; +private: + static std::ostream* log1; + static std::ostream* log2; + static std::stringstream log_stream; + static Severity verbosity; + static char* severity_strings[]; +public: + /// Determines to what stream respectively progress and errors are logged + static void SetOutput(std::ostream* l1, std::ostream* l2); + /// Determines the types of log messages to get logged + static void Verbosity(Severity v); + static Severity Verbosity(); + /// Log a message to the output stream + static void Message(Severity type, const std::string& message, const IfcAbstractEntityPtr entity=0); + static void Status(const std::string& message, bool new_line=true); + static std::string Logger::GetLog(); +}; + #endif diff --git a/src/ifcwrap/Interface.h b/src/ifcwrap/Interface.h index f75512371a..aa13f393d6 100644 --- a/src/ifcwrap/Interface.h +++ b/src/ifcwrap/Interface.h @@ -24,6 +24,8 @@ namespace IfcGeomObjects { const int CONVERT_BACK_UNITS = 3; const int USE_BREP_DATA = 4; const int SEW_SHELLS = 5; + const int FASTER_BOOLEANS = 6; + const int FORCE_CCW_FACE_ORIENTATION = 7; class IfcMesh { public: