diff --git a/.gitignore b/.gitignore index f7b2047913..69f95db0c8 100644 --- a/.gitignore +++ b/.gitignore @@ -9,4 +9,9 @@ __pycache__ # Visual Studio Code files .vscode +# Mac metadata .DS_Store +# Conversion results +/test/input/*.obj +/test/input/*.mtl +/test/input/*.tmp \ No newline at end of file diff --git a/src/ifcgeom/kernels/cgal/CgalConversionFunctions.cpp b/src/ifcgeom/kernels/cgal/CgalConversionFunctions.cpp index 30e441be04..f290b24061 100644 --- a/src/ifcgeom/kernels/cgal/CgalConversionFunctions.cpp +++ b/src/ifcgeom/kernels/cgal/CgalConversionFunctions.cpp @@ -49,6 +49,15 @@ bool IfcGeom::CgalKernel::convert(const IfcSchema::IfcDirection* l, cgal_directi return true; } +bool IfcGeom::CgalKernel::convert(const IfcSchema::IfcVector* l, cgal_vector_t& v) { +// IN_CACHE(IfcVector,l,cgal_vector_t,v) + cgal_direction_t d; + IfcGeom::CgalKernel::convert(l->Orientation(),d); + v = l->Magnitude() * getValue(GV_LENGTH_UNIT) * d; +// CACHE(IfcVector,l,v) + return true; +} + bool IfcGeom::CgalKernel::convert(const IfcSchema::IfcPlane* pln, cgal_plane_t& plane) { // IN_CACHE(IfcPlane,pln,gp_Pln,plane) IfcSchema::IfcAxis2Placement3D* l = pln->Position(); diff --git a/src/ifcgeom/kernels/cgal/CgalEntityMapping.h b/src/ifcgeom/kernels/cgal/CgalEntityMapping.h index c2f0825b72..80a6cae20a 100644 --- a/src/ifcgeom/kernels/cgal/CgalEntityMapping.h +++ b/src/ifcgeom/kernels/cgal/CgalEntityMapping.h @@ -72,9 +72,15 @@ WIRE(IfcOrientedEdge); WIRE(IfcPolyLoop); WIRE(IfcPolyline); WIRE(IfcCompositeCurve); +WIRE(IfcTrimmedCurve); + +CURVE(IfcCircle); +CURVE(IfcEllipse); +CURVE(IfcLine); CLASS(IfcCartesianPoint,cgal_point_t); CLASS(IfcDirection,cgal_direction_t); +CLASS(IfcVector,cgal_vector_t); CLASS(IfcPlane,cgal_plane_t); CLASS(IfcAxis2Placement2D,cgal_placement_t); CLASS(IfcAxis2Placement3D,cgal_placement_t); diff --git a/src/ifcgeom/kernels/cgal/CgalIfcGeomCurves.cpp b/src/ifcgeom/kernels/cgal/CgalIfcGeomCurves.cpp new file mode 100644 index 0000000000..536bc74b66 --- /dev/null +++ b/src/ifcgeom/kernels/cgal/CgalIfcGeomCurves.cpp @@ -0,0 +1,75 @@ +#include "CgalKernel.h" + +bool IfcGeom::CgalKernel::convert(const IfcSchema::IfcCircle* l, cgal_curve_t& curve) { + const double r = l->Radius() * getValue(GV_LENGTH_UNIT); + if ( r < ALMOST_ZERO ) { + Logger::Message(Logger::LOG_ERROR, "Radius not greater than zero for:", l->entity); + return false; + } + cgal_placement_t trsf; + IfcSchema::IfcAxis2Placement* placement = l->Position(); + if (placement->is(IfcSchema::Type::IfcAxis2Placement3D)) { + IfcGeom::CgalKernel::convert((IfcSchema::IfcAxis2Placement3D*)placement,trsf); + } else { + cgal_placement_t trsf2d; + IfcGeom::CgalKernel::convert((IfcSchema::IfcAxis2Placement2D*)placement,trsf2d); + trsf = trsf2d; + } + + const int segments = 12; + + curve = cgal_curve_t(); + for (int current_segment = 0; current_segment < segments; ++current_segment) { + double current_angle = current_segment*2.0*3.141592653589793/((double)segments); + curve.push_back(Kernel::Point_3(r*cos(current_angle), r*sin(current_angle), 0)); + } + + for (auto &vertex: curve) { + vertex = vertex.transform(trsf); + } + + return true; +} + +bool IfcGeom::CgalKernel::convert(const IfcSchema::IfcEllipse* l, cgal_curve_t& curve) { + double x = l->SemiAxis1() * getValue(GV_LENGTH_UNIT); + double y = l->SemiAxis2() * getValue(GV_LENGTH_UNIT); + if (x < ALMOST_ZERO || y < ALMOST_ZERO) { + Logger::Message(Logger::LOG_ERROR, "Radius not greater than zero for:", l->entity); + return false; + } + cgal_placement_t trsf; + IfcSchema::IfcAxis2Placement* placement = l->Position(); + if (placement->is(IfcSchema::Type::IfcAxis2Placement3D)) { + convert((IfcSchema::IfcAxis2Placement3D*)placement,trsf); + } else { + cgal_placement_t trsf2d; + convert((IfcSchema::IfcAxis2Placement2D*)placement,trsf2d); + trsf = trsf2d; + } + + const int segments = 12; + + curve = cgal_curve_t(); + for (int current_segment = 0; current_segment < segments; ++current_segment) { + double current_angle = current_segment*2.0*3.141592653589793/((double)segments); + curve.push_back(Kernel::Point_3(x*cos(current_angle), y*sin(current_angle), 0)); + } + + for (auto &vertex: curve) { + vertex = vertex.transform(trsf); + } + + return true; +} + +bool IfcGeom::CgalKernel::convert(const IfcSchema::IfcLine* l, cgal_curve_t& curve) { + cgal_point_t pnt; + cgal_direction_t vec; + convert(l->Pnt(),pnt); + convert(l->Dir(),vec); + curve = cgal_curve_t(); + curve.push_back(pnt); + curve.push_back(pnt+vec); + return true; +} diff --git a/src/ifcgeom/kernels/cgal/CgalIfcGeomWires.cpp b/src/ifcgeom/kernels/cgal/CgalIfcGeomWires.cpp index 72ac78fa2d..c6832de476 100644 --- a/src/ifcgeom/kernels/cgal/CgalIfcGeomWires.cpp +++ b/src/ifcgeom/kernels/cgal/CgalIfcGeomWires.cpp @@ -175,3 +175,161 @@ bool IfcGeom::CgalKernel::convert(const IfcSchema::IfcCompositeCurve* l, cgal_wi wire = w; return true; } + +// TODO: Project points to closest point in curve? +bool IfcGeom::CgalKernel::convert(const IfcSchema::IfcTrimmedCurve* l, cgal_wire_t& wire) { + IfcSchema::IfcCurve* basis_curve = l->BasisCurve(); + bool isConic = basis_curve->is(IfcSchema::Type::IfcConic); + double parameterFactor = isConic ? getValue(GV_PLANEANGLE_UNIT) : getValue(GV_LENGTH_UNIT); + cgal_curve_t curve; + if ( !convert_curve(basis_curve,curve) ) return false; + bool trim_cartesian = l->MasterRepresentation() == IfcSchema::IfcTrimmingPreference::IfcTrimmingPreference_CARTESIAN; + IfcEntityList::ptr trims1 = l->Trim1(); + IfcEntityList::ptr trims2 = l->Trim2(); + unsigned sense_agreement = l->SenseAgreement() ? 0 : 1; + double flts[2]; + cgal_point_t pnts[2]; + bool has_flts[2] = {false,false}; + bool has_pnts[2] = {false,false}; + cgal_wire_t w; + for ( IfcEntityList::it it = trims1->begin(); it != trims1->end(); it ++ ) { + IfcUtil::IfcBaseClass* i = *it; + if ( i->is(IfcSchema::Type::IfcCartesianPoint) ) { + IfcGeom::CgalKernel::convert((IfcSchema::IfcCartesianPoint*)i, pnts[sense_agreement] ); + has_pnts[sense_agreement] = true; + } else if ( i->is(IfcSchema::Type::IfcParameterValue) ) { + const double value = *((IfcSchema::IfcParameterValue*)i); + flts[sense_agreement] = value * parameterFactor; + has_flts[sense_agreement] = true; + } + } + for ( IfcEntityList::it it = trims2->begin(); it != trims2->end(); it ++ ) { + IfcUtil::IfcBaseClass* i = *it; + if ( i->is(IfcSchema::Type::IfcCartesianPoint) ) { + IfcGeom::CgalKernel::convert((IfcSchema::IfcCartesianPoint*)i, pnts[1-sense_agreement] ); + has_pnts[1-sense_agreement] = true; + } else if ( i->is(IfcSchema::Type::IfcParameterValue) ) { + const double value = *((IfcSchema::IfcParameterValue*)i); + flts[1-sense_agreement] = value * parameterFactor; + has_flts[1-sense_agreement] = true; + } + } + trim_cartesian &= has_pnts[0] && has_pnts[1]; + bool trim_cartesian_failed = !trim_cartesian; + if ( trim_cartesian ) { + if ( CGAL::squared_distance(pnts[0], pnts[1]) < getValue(GV_WIRE_CREATION_TOLERANCE)*getValue(GV_WIRE_CREATION_TOLERANCE) ) { + Logger::Message(Logger::LOG_WARNING,"Skipping segment with length below tolerance level:",l->entity); + return false; + } + if (l->SenseAgreement()) { + bool found = false; + int loops_to_go = 2; + std::vector::const_iterator point = curve.begin(); + do { + if (!found) { + if (CGAL::squared_distance(*point, pnts[0]) < getValue(GV_WIRE_CREATION_TOLERANCE)*getValue(GV_WIRE_CREATION_TOLERANCE)) { + found = true; + w.push_back(*point); + } + } else { + w.push_back(*point); + if (CGAL::squared_distance(*point, pnts[1]) < getValue(GV_WIRE_CREATION_TOLERANCE)*getValue(GV_WIRE_CREATION_TOLERANCE)) { + break; + } + } ++point; + if (point == curve.end()) { + point = curve.begin(); + --loops_to_go; + } + } while (point != curve.begin() && loops_to_go > 0); + } else { + bool found = false; + int loops_to_go = 2; + std::vector::const_reverse_iterator point = curve.rbegin(); + do { + if (!found) { + if (CGAL::squared_distance(*point, pnts[0]) < getValue(GV_WIRE_CREATION_TOLERANCE)*getValue(GV_WIRE_CREATION_TOLERANCE)) { + found = true; + w.push_back(*point); + } + } else { + w.push_back(*point); + if (CGAL::squared_distance(*point, pnts[1]) < getValue(GV_WIRE_CREATION_TOLERANCE)*getValue(GV_WIRE_CREATION_TOLERANCE)) { + break; + } + } ++point; + if (point == curve.rend() && loops_to_go > 0) point = curve.rbegin(); + } while (point != curve.rbegin()); + } + } + if ( (!trim_cartesian || trim_cartesian_failed) && (has_flts[0] && has_flts[1]) ) { + // The Geom_Line is constructed from a gp_Pnt and gp_Dir, whereas the IfcLine + // is defined by an IfcCartesianPoint and an IfcVector with Magnitude. Because + // the vector is normalised when passed to Geom_Line constructor the magnitude + // needs to be factored in with the IfcParameterValue here. + if ( basis_curve->is(IfcSchema::Type::IfcLine) ) { + IfcSchema::IfcLine* line = static_cast(basis_curve); + const double magnitude = line->Dir()->Magnitude(); + flts[0] *= magnitude; flts[1] *= magnitude; + } + if ( basis_curve->is(IfcSchema::Type::IfcEllipse) ) { + IfcSchema::IfcEllipse* ellipse = static_cast(basis_curve); + double x = ellipse->SemiAxis1() * getValue(GV_LENGTH_UNIT); + double y = ellipse->SemiAxis2() * getValue(GV_LENGTH_UNIT); + const bool rotated = y > x; + if (rotated) { + flts[0] -= M_PI / 2.; + flts[1] -= M_PI / 2.; + } + } + if ( isConic && ALMOST_THE_SAME(fmod(flts[1]-flts[0],M_PI*2.),0.) ) { + for (auto &point: curve) w.push_back(point); + } else { + if (l->SenseAgreement()) { + bool found = false; + int loops_to_go = 2; + std::vector::const_iterator point = curve.begin(); + do { + if (!found) { + if (CGAL::squared_distance(*point, pnts[0]) < getValue(GV_WIRE_CREATION_TOLERANCE)*getValue(GV_WIRE_CREATION_TOLERANCE)) { + found = true; + w.push_back(*point); + } + } else { + w.push_back(*point); + if (CGAL::squared_distance(*point, pnts[1]) < getValue(GV_WIRE_CREATION_TOLERANCE)*getValue(GV_WIRE_CREATION_TOLERANCE)) { + break; + } + } ++point; + if (point == curve.end()) { + point = curve.begin(); + --loops_to_go; + } + } while (point != curve.begin() && loops_to_go > 0); + } else { + bool found = false; + int loops_to_go = 2; + std::vector::const_reverse_iterator point = curve.rbegin(); + do { + if (!found) { + if (CGAL::squared_distance(*point, pnts[0]) < getValue(GV_WIRE_CREATION_TOLERANCE)*getValue(GV_WIRE_CREATION_TOLERANCE)) { + found = true; + w.push_back(*point); + } + } else { + w.push_back(*point); + if (CGAL::squared_distance(*point, pnts[1]) < getValue(GV_WIRE_CREATION_TOLERANCE)*getValue(GV_WIRE_CREATION_TOLERANCE)) { + break; + } + } ++point; + if (point == curve.rend() && loops_to_go > 0) point = curve.rbegin(); + } while (point != curve.rbegin()); + } + } + } else if ( trim_cartesian_failed && (has_pnts[0] && has_pnts[1]) ) { + w.push_back(pnts[0]); + w.push_back(pnts[1]); + } + wire = w; + return true; +} diff --git a/src/ifcgeom/kernels/cgal/CgalKernel.h b/src/ifcgeom/kernels/cgal/CgalKernel.h index e6658f547f..f63404d129 100644 --- a/src/ifcgeom/kernels/cgal/CgalKernel.h +++ b/src/ifcgeom/kernels/cgal/CgalKernel.h @@ -54,6 +54,7 @@ typedef CGAL::Exact_predicates_exact_constructions_kernel Kernel; typedef Kernel::Aff_transformation_3 cgal_placement_t; typedef Kernel::Point_3 cgal_point_t; typedef Kernel::Vector_3 cgal_direction_t; +typedef Kernel::Vector_3 cgal_vector_t; typedef Kernel::Plane_3 cgal_plane_t; typedef std::vector cgal_curve_t; typedef std::vector cgal_wire_t;