diff --git a/src/ifcgeom/kernels/cgal/CgalConversionFunctions.cpp b/src/ifcgeom/kernels/cgal/CgalConversionFunctions.cpp index eb6561c890..30e441be04 100644 --- a/src/ifcgeom/kernels/cgal/CgalConversionFunctions.cpp +++ b/src/ifcgeom/kernels/cgal/CgalConversionFunctions.cpp @@ -54,9 +54,15 @@ bool IfcGeom::CgalKernel::convert(const IfcSchema::IfcPlane* pln, cgal_plane_t& IfcSchema::IfcAxis2Placement3D* l = pln->Position(); cgal_point_t o; cgal_direction_t axis = Kernel::Vector_3(0,0,1); + cgal_direction_t refDirection; IfcGeom::CgalKernel::convert(l->Location(),o); + bool hasRef = l->hasRefDirection(); if ( l->hasAxis() ) IfcGeom::CgalKernel::convert(l->Axis(),axis); - plane = Kernel::Plane_3(o, axis); + if ( hasRef ) IfcGeom::CgalKernel::convert(l->RefDirection(),refDirection); + cgal_plane_t ax3; + if ( hasRef ) ax3 = Kernel::Plane_3(o,o+axis,o+refDirection); + else ax3 = Kernel::Plane_3(o,axis); + plane = ax3; // CACHE(IfcPlane,pln,plane) return true; } diff --git a/src/ifcgeom/kernels/cgal/CgalEntityMapping.h b/src/ifcgeom/kernels/cgal/CgalEntityMapping.h index 371a2b7769..c2f0825b72 100644 --- a/src/ifcgeom/kernels/cgal/CgalEntityMapping.h +++ b/src/ifcgeom/kernels/cgal/CgalEntityMapping.h @@ -63,6 +63,9 @@ FACE(IfcEllipseProfileDef); FACE(IfcCShapeProfileDef); FACE(IfcIShapeProfileDef); FACE(IfcLShapeProfileDef); +FACE(IfcTShapeProfileDef); +FACE(IfcUShapeProfileDef); +FACE(IfcZShapeProfileDef); WIRE(IfcEdgeLoop); WIRE(IfcOrientedEdge); diff --git a/src/ifcgeom/kernels/cgal/CgalIfcGeomFaces.cpp b/src/ifcgeom/kernels/cgal/CgalIfcGeomFaces.cpp index 69e50cde35..0040b65cc3 100644 --- a/src/ifcgeom/kernels/cgal/CgalIfcGeomFaces.cpp +++ b/src/ifcgeom/kernels/cgal/CgalIfcGeomFaces.cpp @@ -655,3 +655,304 @@ bool IfcGeom::CgalKernel::convert(const IfcSchema::IfcIShapeProfileDef* l, cgal_ return true; } + +bool IfcGeom::CgalKernel::convert(const IfcSchema::IfcTShapeProfileDef* l, cgal_face_t& face) { + const bool doFlangeEdgeFillet = l->hasFlangeEdgeRadius(); + const bool doWebEdgeFillet = l->hasWebEdgeRadius(); + const bool doFillet = l->hasFilletRadius(); + const bool hasFlangeSlope = l->hasFlangeSlope(); + const bool hasWebSlope = l->hasWebSlope(); + + const double y = l->Depth() / 2.0f * getValue(GV_LENGTH_UNIT); + const double x = l->FlangeWidth() / 2.0f * getValue(GV_LENGTH_UNIT); + const double d1 = l->WebThickness() * getValue(GV_LENGTH_UNIT); + const double d2 = l->FlangeThickness() * getValue(GV_LENGTH_UNIT); + const double flangeSlope = hasFlangeSlope ? (l->FlangeSlope() * getValue(GV_PLANEANGLE_UNIT)) : 0.; + const double webSlope = hasWebSlope ? (l->WebSlope() * getValue(GV_PLANEANGLE_UNIT)) : 0.; + + if ( x < ALMOST_ZERO || y < ALMOST_ZERO || d1 < ALMOST_ZERO || d2 < ALMOST_ZERO ) { + Logger::Message(Logger::LOG_NOTICE,"Skipping zero sized profile:",l->entity); + return false; + } + + double dy1 = 0.0f; + double dy2 = 0.0f; + double dx1 = 0.0f; + double dx2 = 0.0f; + double f1 = 0.0f; + double f2 = 0.0f; + double f3 = 0.0f; + + if (doFillet) { + f1 = l->FilletRadius() * getValue(GV_LENGTH_UNIT); + } + if (doWebEdgeFillet) { + f2 = l->WebEdgeRadius() * getValue(GV_LENGTH_UNIT); + } + if (doFlangeEdgeFillet) { + f3 = l->FlangeEdgeRadius() * getValue(GV_LENGTH_UNIT); + } + + double xx, xy; + if (hasFlangeSlope) { + dy1 = (x / 2. - d1) * tan(flangeSlope); + dy2 = x / 2. * tan(flangeSlope); + } + if (hasWebSlope) { + dx1 = (y - d2) * tan(webSlope); + dx2 = y * tan(webSlope); + } + if (hasWebSlope || hasFlangeSlope) { + const double x1s = d1/2. - dx2; const double y1s = -y; + const double x1e = d1/2. + dx1; const double y1e = y - d2; + const double x2s = x; const double y2s = y - d2 + dy2; + const double x2e = d1/2.; const double y2e = y - d2 - dy1; + + const double a1 = y1e - y1s; + const double b1 = x1s - x1e; + const double c1 = a1*x1s + b1*y1s; + + const double a2 = y2e - y2s; + const double b2 = x2s - x2e; + const double c2 = a2*x2s + b2*y2s; + + const double det = a1*b2 - a2*b1; + + if (ALMOST_THE_SAME(det, 0.)) { + Logger::Message(Logger::LOG_NOTICE, "Web and flange do not intersect for:",l->entity); + return false; + } + + xx = (b2*c1 - b1*c2) / det; + xy = (a1*c2 - a2*c1) / det; + } else { + xx = d1 / 2; + xy = y - d2; + } + + cgal_placement_t trsf2d; + bool has_position = true; +#ifdef USE_IFC4 + has_position = l->hasPosition(); +#endif + + const int segments = 3; + + face = cgal_face_t(); + if (f2 == 0.0) { + face.outer.push_back(Kernel::Point_3(d1/2.-dx2, -y, 0.0)); + } else { + for (int current_segment = 0; current_segment <= segments; ++current_segment) { + double current_angle = 1.5*3.141592653589793+current_segment*0.5*3.141592653589793/((double)segments); + face.outer.push_back(Kernel::Point_3(d1/2.-dx2-f2+f2*cos(current_angle), -y+f2+f2*sin(current_angle), 0)); + } + } if (f1 == 0.0) { + face.outer.push_back(Kernel::Point_3(xx, xy, 0.0)); + } else { + for (int current_segment = segments; current_segment >= 0; --current_segment) { + double current_angle = 0.5*3.141592653589793+current_segment*0.5*3.141592653589793/((double)segments); + face.outer.push_back(Kernel::Point_3(xx+f1+f1*cos(current_angle), xy-f1+f1*sin(current_angle), 0)); + } + } if (f3 == 0.0) { + face.outer.push_back(Kernel::Point_3(x, y-d2+dy2, 0.0)); + } else { + for (int current_segment = 0; current_segment <= segments; ++current_segment) { + double current_angle = 1.5*3.141592653589793+current_segment*0.5*3.141592653589793/((double)segments); + face.outer.push_back(Kernel::Point_3(x-f3+f3*cos(current_angle), y-d2+dy2+f3+f3*sin(current_angle), 0)); + } + } face.outer.push_back(Kernel::Point_3(x, y, 0.0)); + face.outer.push_back(Kernel::Point_3(-x, y, 0.0)); + if (f3 == 0.0) { + face.outer.push_back(Kernel::Point_3(-x, y-d2+dy2, 0.0)); + } else { + for (int current_segment = 0; current_segment <= segments; ++current_segment) { + double current_angle = 1.0*3.141592653589793+current_segment*0.5*3.141592653589793/((double)segments); + face.outer.push_back(Kernel::Point_3(-x+f3+f3*cos(current_angle), y-d2+dy2+f3+f3*sin(current_angle), 0)); + } + } if (f1 == 0.0) { + face.outer.push_back(Kernel::Point_3(-xx, xy, 0.0)); + } else { + for (int current_segment = segments; current_segment >= 0; --current_segment) { + double current_angle = current_segment*0.5*3.141592653589793/((double)segments); + face.outer.push_back(Kernel::Point_3(-xx-f1+f1*cos(current_angle), xy-f1+f1*sin(current_angle), 0)); + } + } if (f2 == 0.0) { + face.outer.push_back(Kernel::Point_3(-d1/2.+dx2, -y, 0.0)); + } else { + for (int current_segment = 0; current_segment <= segments; ++current_segment) { + double current_angle = 1.0*3.141592653589793+current_segment*0.5*3.141592653589793/((double)segments); + face.outer.push_back(Kernel::Point_3(-d1/2.+dx2+f2+f2*cos(current_angle), -y+f2+f2*sin(current_angle), 0)); + } + } + + if (has_position) { + IfcGeom::CgalKernel::convert(l->Position(), trsf2d); + for (auto &vertex: face.outer) { + vertex = vertex.transform(trsf2d); + } + } + + return true; +} + +bool IfcGeom::CgalKernel::convert(const IfcSchema::IfcUShapeProfileDef* l, cgal_face_t& face) { + const bool doEdgeFillet = l->hasEdgeRadius(); + const bool doFillet = l->hasFilletRadius(); + const bool hasSlope = l->hasFlangeSlope(); + + const double y = l->Depth() / 2.0f * getValue(GV_LENGTH_UNIT); + const double x = l->FlangeWidth() / 2.0f * getValue(GV_LENGTH_UNIT); + const double d1 = l->WebThickness() * getValue(GV_LENGTH_UNIT); + const double d2 = l->FlangeThickness() * getValue(GV_LENGTH_UNIT); + const double slope = hasSlope ? (l->FlangeSlope() * getValue(GV_PLANEANGLE_UNIT)) : 0.; + + double dy1 = 0.0f; + double dy2 = 0.0f; + double f1 = 0.0f; + double f2 = 0.0f; + + if (doFillet) { + f1 = l->FilletRadius() * getValue(GV_LENGTH_UNIT); + } + if (doEdgeFillet) { + f2 = l->EdgeRadius() * getValue(GV_LENGTH_UNIT); + } + + if (hasSlope) { + dy1 = (x - d1) * tan(slope); + dy2 = x * tan(slope); + } + + if ( x < ALMOST_ZERO || y < ALMOST_ZERO || d1 < ALMOST_ZERO || d2 < ALMOST_ZERO ) { + Logger::Message(Logger::LOG_NOTICE,"Skipping zero sized profile:",l->entity); + return false; + } + + cgal_placement_t trsf2d; + bool has_position = true; +#ifdef USE_IFC4 + has_position = l->hasPosition(); +#endif + + const int segments = 3; + + face = cgal_face_t(); + face.outer.push_back(Kernel::Point_3(-x, -y, 0.0)); + face.outer.push_back(Kernel::Point_3(x, -y, 0.0)); + if (f2 == 0.0) { + face.outer.push_back(Kernel::Point_3(x, -y+d2-dy2, 0.0)); + } else { + for (int current_segment = 0; current_segment <= segments; ++current_segment) { + double current_angle = current_segment*0.5*3.141592653589793/((double)segments); + face.outer.push_back(Kernel::Point_3(x-f2+f2*cos(current_angle), -y+d2-dy2-f2+f2*sin(current_angle), 0)); + } + } if (f1 == 0.0) { + face.outer.push_back(Kernel::Point_3(-x+d1, -y+d2+dy1, 0.0)); + } else { + for (int current_segment = segments; current_segment >= 0; --current_segment) { + double current_angle = 1.0*3.141592653589793+current_segment*0.5*3.141592653589793/((double)segments); + face.outer.push_back(Kernel::Point_3(-x+d1+f1+f1*cos(current_angle), -y+d2+dy1+f1+f1*sin(current_angle), 0)); + } + } if (f1 == 0.0) { + face.outer.push_back(Kernel::Point_3(-x+d1, y-d2-dy1, 0.0)); + } else { + for (int current_segment = segments; current_segment >= 0; --current_segment) { + double current_angle = 0.5*3.141592653589793+current_segment*0.5*3.141592653589793/((double)segments); + face.outer.push_back(Kernel::Point_3(-x+d1+f1+f1*cos(current_angle), y-d2-dy1-f1+f1*sin(current_angle), 0)); + } + } if (f2 == 0.0) { + face.outer.push_back(Kernel::Point_3(x,y-d2+dy2, 0.0)); + } else { + for (int current_segment = 0; current_segment <= segments; ++current_segment) { + double current_angle = 1.5*3.141592653589793+current_segment*0.5*3.141592653589793/((double)segments); + face.outer.push_back(Kernel::Point_3(x-f2+f2*cos(current_angle), y-d2+dy2+f2+f2*sin(current_angle), 0)); + } + } face.outer.push_back(Kernel::Point_3(x,y, 0.0)); + face.outer.push_back(Kernel::Point_3(-x,y, 0.0)); + + if (has_position) { + IfcGeom::CgalKernel::convert(l->Position(), trsf2d); + for (auto &vertex: face.outer) { + vertex = vertex.transform(trsf2d); + } + } + + return true; +} + +bool IfcGeom::CgalKernel::convert(const IfcSchema::IfcZShapeProfileDef* l, cgal_face_t& face) { + const double x = l->FlangeWidth() * getValue(GV_LENGTH_UNIT); + const double y = l->Depth() / 2.0f * getValue(GV_LENGTH_UNIT); + const double dx = l->WebThickness() / 2.0f * getValue(GV_LENGTH_UNIT); + const double dy = l->FlangeThickness() * getValue(GV_LENGTH_UNIT); + + bool doFillet = l->hasFilletRadius(); + bool doEdgeFillet = l->hasEdgeRadius(); + + double f1 = 0.; + double f2 = 0.; + + if ( doFillet ) { + f1 = l->FilletRadius() * getValue(GV_LENGTH_UNIT); + } + if ( doEdgeFillet ) { + f2 = l->EdgeRadius() * getValue(GV_LENGTH_UNIT); + } + + if ( x == 0.0f || y == 0.0f || dx == 0.0f || dy == 0.0f ) { + Logger::Message(Logger::LOG_NOTICE,"Skipping zero sized profile:",l->entity); + return false; + } + + cgal_placement_t trsf2d; + bool has_position = true; +#ifdef USE_IFC4 + has_position = l->hasPosition(); +#endif + + const int segments = 3; + + face = cgal_face_t(); + face.outer.push_back(Kernel::Point_3(-dx, -y, 0.0)); + face.outer.push_back(Kernel::Point_3(x, -y, 0.0)); + if (f2 == 0.0) { + face.outer.push_back(Kernel::Point_3(x, -y+dy, 0.0)); + } else { + for (int current_segment = 0; current_segment <= segments; ++current_segment) { + double current_angle = current_segment*0.5*3.141592653589793/((double)segments); + face.outer.push_back(Kernel::Point_3(x-f2+f2*cos(current_angle), -y+dy-f2+f2*sin(current_angle), 0)); + } + } if (f1 == 0.0) { + face.outer.push_back(Kernel::Point_3(dx, -y+dy, 0.0)); + } else { + for (int current_segment = segments; current_segment >= 0; --current_segment) { + double current_angle = 1.0*3.141592653589793+current_segment*0.5*3.141592653589793/((double)segments); + face.outer.push_back(Kernel::Point_3(dx+f1+f1*cos(current_angle), -y+dy+f1+f1*sin(current_angle), 0)); + } + } face.outer.push_back(Kernel::Point_3(dx, y, 0.0)); + face.outer.push_back(Kernel::Point_3(-x, y, 0.0)); + if (f2 == 0.0) { + face.outer.push_back(Kernel::Point_3(-x, y-dy, 0.0)); + } else { + for (int current_segment = 0; current_segment <= segments; ++current_segment) { + double current_angle = 1.0*3.141592653589793+current_segment*0.5*3.141592653589793/((double)segments); + face.outer.push_back(Kernel::Point_3(-x+f2+f2*cos(current_angle), y-dy+f2+f2*sin(current_angle), 0)); + } + } if (f1 == 0.0) { + face.outer.push_back(Kernel::Point_3(-dx, y-dy, 0.0)); + } else { + for (int current_segment = segments; current_segment >= 0; --current_segment) { + double current_angle = current_segment*0.5*3.141592653589793/((double)segments); + face.outer.push_back(Kernel::Point_3(-dx-f1+f1*cos(current_angle), y-dy-f1+f1*sin(current_angle), 0)); + } + } + + if (has_position) { + IfcGeom::CgalKernel::convert(l->Position(), trsf2d); + for (auto &vertex: face.outer) { + vertex = vertex.transform(trsf2d); + } + } + + return true; +} diff --git a/src/ifcgeom/kernels/cgal/CgalKernel.cpp b/src/ifcgeom/kernels/cgal/CgalKernel.cpp index e67be1c390..f111d9bc93 100644 --- a/src/ifcgeom/kernels/cgal/CgalKernel.cpp +++ b/src/ifcgeom/kernels/cgal/CgalKernel.cpp @@ -111,25 +111,24 @@ IfcGeom::NativeElement* IfcGeom::CgalKernel::create_brep_for_representat const std::string product_type = IfcSchema::Type::ToString(product->type()); ElementSettings element_settings(settings, getValue(GV_LENGTH_UNIT), product_type); - if (!settings.get(IfcGeom::IteratorSettings::DISABLE_OPENING_SUBTRACTIONS) && openings && openings->size()) { - Logger::Message(Logger::LOG_ERROR, "Not implemented opening subtractions"); - } - - if (settings.get(IteratorSettings::USE_WORLD_COORDS)) { - // TODO: OpenCascade code uses opened_shapes. Check why. + if (!settings.get(IfcGeom::IteratorSettings::DISABLE_OPENING_SUBTRACTIONS) && openings && openings->size()) { + IfcGeom::ConversionResults opened_shapes; + convert_openings(product,openings,shapes,trsf,opened_shapes); + if (settings.get(IteratorSettings::USE_WORLD_COORDS)) { + for ( IfcGeom::ConversionResults::iterator it = opened_shapes.begin(); it != opened_shapes.end(); ++ it ) { + it->prepend(new CgalPlacement(trsf)); + } + trsf = cgal_placement_t(); + } + shape = new IfcGeom::Representation::Native(element_settings, representation->entity->id(), opened_shapes); + } else if (settings.get(IteratorSettings::USE_WORLD_COORDS)) { for ( IfcGeom::ConversionResults::iterator it = shapes.begin(); it != shapes.end(); ++ it ) { it->prepend(new CgalPlacement(trsf)); } - trsf = Kernel::Aff_transformation_3(); - shape = new IfcGeom::Representation::Native(element_settings, representation->entity->id(), shapes); - } else if (settings.get(IteratorSettings::USE_WORLD_COORDS)) { - for ( IfcGeom::ConversionResults::iterator it = shapes.begin(); it != shapes.end(); ++ it ) { - it->prepend(new CgalPlacement(trsf)); - } - trsf = Kernel::Aff_transformation_3(); - shape = new IfcGeom::Representation::Native(element_settings, representation->entity->id(), shapes); + trsf = cgal_placement_t(); + shape = new IfcGeom::Representation::Native(element_settings, representation->entity->id(), shapes); } else { - shape = new IfcGeom::Representation::Native(element_settings, representation->entity->id(), shapes); + shape = new IfcGeom::Representation::Native(element_settings, representation->entity->id(), shapes); } std::string context_string = ""; @@ -191,3 +190,75 @@ IfcGeom::NativeElement* IfcGeom::CgalKernel::create_brep_for_processed_r brep->geometry_pointer() ); } + +bool IfcGeom::CgalKernel::convert_openings(const IfcSchema::IfcProduct* entity, const IfcSchema::IfcRelVoidsElement::list::ptr& openings, + const IfcGeom::ConversionResults& entity_shapes, const cgal_placement_t& entity_trsf, IfcGeom::ConversionResults& cut_shapes) { + + std::list opening_shapelist; + + for ( IfcSchema::IfcRelVoidsElement::list::it it = openings->begin(); it != openings->end(); ++ it ) { + IfcSchema::IfcRelVoidsElement* v = *it; + IfcSchema::IfcFeatureElementSubtraction* fes = v->RelatedOpeningElement(); + if ( fes->is(IfcSchema::Type::IfcOpeningElement) ) { + if (!fes->hasRepresentation()) continue; + + // Convert the IfcRepresentation of the IfcOpeningElement + cgal_placement_t opening_trsf; + if (fes->hasObjectPlacement()) { + try { + convert(fes->ObjectPlacement(),opening_trsf); + } catch (...) {} + } + + // Move the opening into the coordinate system of the IfcProduct + opening_trsf = opening_trsf * entity_trsf.inverse(); + + IfcSchema::IfcProductRepresentation* prodrep = fes->Representation(); + IfcSchema::IfcRepresentation::list::ptr reps = prodrep->Representations(); + + IfcGeom::ConversionResults opening_shapes; + + for ( IfcSchema::IfcRepresentation::list::it it2 = reps->begin(); it2 != reps->end(); ++ it2 ) { + convert_shapes(*it2,opening_shapes); + } + + for ( unsigned int i = 0; i < opening_shapes.size(); ++ i ) { + cgal_shape_t opening_shape(((CgalShape*)opening_shapes[i].Shape())->shape()); + if (opening_shapes[i].Placement()) { + cgal_placement_t gtrsf = *(CgalPlacement*)opening_shapes[i].Placement(); + gtrsf = gtrsf * opening_trsf; + opening_shape.transform(gtrsf); + } opening_shapelist.push_back(opening_shape); + } + + } + } + + // Iterate over the shapes of the IfcProduct + for ( IfcGeom::ConversionResults::const_iterator it3 = entity_shapes.begin(); it3 != entity_shapes.end(); ++ it3 ) { + const cgal_shape_t& entity_shape_unlocated(((CgalShape*)it3->Shape())->shape()); + cgal_shape_t entity_shape(entity_shape_unlocated); + if (it3->Placement()) { + const cgal_placement_t& entity_shape_gtrsf = *(CgalPlacement*)it3->Placement(); + entity_shape.transform(entity_shape_gtrsf); + } + + cgal_shape_t brep_cut_result(entity_shape); + + for (auto &opening: opening_shapelist) { + brep_cut_result -= opening; + } + + if (brep_cut_result.is_valid()) { + cut_shapes.push_back(IfcGeom::ConversionResult(new CgalShape(brep_cut_result), &it3->Style())); + } else { + // 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. + Logger::Message(Logger::LOG_WARNING, "Subtracting combined openings compound failed:", entity->entity); + return false; + } + + } + return true; +} diff --git a/src/ifcgeom/kernels/cgal/CgalKernel.h b/src/ifcgeom/kernels/cgal/CgalKernel.h index 7a27ae854e..e6658f547f 100644 --- a/src/ifcgeom/kernels/cgal/CgalKernel.h +++ b/src/ifcgeom/kernels/cgal/CgalKernel.h @@ -137,7 +137,7 @@ namespace IfcGeom { void remove_duplicate_points_from_loop(cgal_wire_t& polygon, bool closed, double tol = -1.); - // bool convert_openings(const IfcSchema::IfcProduct* entity, const IfcSchema::IfcRelVoidsElement::list::ptr& openings, const ConversionResults& entity_shapes, const gp_Trsf& entity_trsf, ConversionResults& cut_shapes); + bool convert_openings(const IfcSchema::IfcProduct* entity, const IfcSchema::IfcRelVoidsElement::list::ptr& openings, const ConversionResults& entity_shapes, const cgal_placement_t& entity_trsf, ConversionResults& cut_shapes); void purge_cache() { // Rather hack-ish, but a stopgap solution to keep memory under control