diff --git a/.gitignore b/.gitignore index c8a6b575ee..f7b2047913 100644 --- a/.gitignore +++ b/.gitignore @@ -9,3 +9,4 @@ __pycache__ # Visual Studio Code files .vscode +.DS_Store diff --git a/src/ifcconvert/ColladaSerializer.cpp b/src/ifcconvert/ColladaSerializer.cpp index c8272aba64..e9108f6e80 100644 --- a/src/ifcconvert/ColladaSerializer.cpp +++ b/src/ifcconvert/ColladaSerializer.cpp @@ -208,7 +208,7 @@ void ColladaSerializer::ColladaExporter::ColladaScene::add( node.addMatrix(matrix_array); COLLADASW::InstanceGeometry instanceGeometry(mSW); instanceGeometry.setUrl ("#" + geom_name); - foreach(std::string material_name, material_ids) { + for (std::string material_name: material_ids) { /// @todo This is done 6 times in this file, try to perform this once and be done with the material naming for the export. collada_id(material_name); COLLADASW::InstanceMaterial material (material_name, "#" + material_name); @@ -276,7 +276,7 @@ bool ColladaSerializer::ColladaExporter::ColladaMaterials::contains(const IfcGeo void ColladaSerializer::ColladaExporter::ColladaMaterials::write() { effects.close(); - foreach(const IfcGeom::Material& material, materials) { + for (const IfcGeom::Material& material: materials) { std::string material_name = (serializer->settings().get(IfcGeom::IteratorSettings::USE_MATERIAL_NAMES) ? material.original_name() : material.name()); std::string material_name_unescaped = material_name; // workaround double-escaping that would occur in addInstanceEffect() @@ -307,7 +307,7 @@ void ColladaSerializer::ColladaExporter::write(const IfcGeom::TriangulationEleme const std::string representation_id = "representation-" + boost::lexical_cast(o->geometry().id()); std::vector material_references; - foreach(const IfcGeom::Material& material, mesh.materials()) { + for (const IfcGeom::Material& material: mesh.materials()) { if (!materials.contains(material)) { materials.add(material); } diff --git a/src/ifcconvert/IfcConvert.cpp b/src/ifcconvert/IfcConvert.cpp index 5b9057a73b..b64e8b508f 100644 --- a/src/ifcconvert/IfcConvert.cpp +++ b/src/ifcconvert/IfcConvert.cpp @@ -202,7 +202,8 @@ int main(int argc, char** argv) { "Applies --include or --exclude also to the decomposition and/or containment (IsDecomposedBy, " "HasOpenings, FillsVoid, ContainedInStructure) of the filtered entity, e.g. " "--include --traverse --names \"Level 1\" includes entity with name \"Level 1\" and all of its children.") - ("kernel", "Geometry kernel to use ('opencascade' or 'cgal'). Defaults to 'cgal'."); +// ("kernel", "Geometry kernel to use ('opencascade' or 'cgal'). Defaults to 'cgal'.") + ("opencascade", "Use opencascade kernel rather than cgal."); std::string bounds; boost::program_options::options_description serializer_options("Serialization options"); @@ -286,10 +287,11 @@ int main(int argc, char** argv) { const bool traverse = vmap.count("traverse") != 0; const bool deflection_tolerance_specified = vmap.count("deflection-tolerance") != 0 ; - if (vmap.count("kernel") == 0) { - std::cerr << "Using default CGAL based kernel" << std::endl; - kernel = "cgal"; - } + if (vmap.count("opencascade") == 1) { + kernel = "opencascade"; + } else { + kernel = "cgal"; + } int bounding_width = -1, bounding_height = -1; if (vmap.count("bounds") == 1) { diff --git a/src/ifcconvert/WavefrontObjSerializer.cpp b/src/ifcconvert/WavefrontObjSerializer.cpp index 54fd609265..52881e858f 100644 --- a/src/ifcconvert/WavefrontObjSerializer.cpp +++ b/src/ifcconvert/WavefrontObjSerializer.cpp @@ -108,6 +108,7 @@ void WaveFrontOBJSerializer::write(const IfcGeom::TriangulationElement* const bool has_uvs = !mesh.uvs().empty(); const bool has_normals = !mesh.normals().empty(); +// std::cout << mesh.faces().size() << " vertices in face mesh" << std::endl; for ( std::vector::const_iterator it = mesh.faces().begin(); it != mesh.faces().end(); ) { const int material_id = *(material_it++); diff --git a/src/ifcconvert/XmlSerializer.cpp b/src/ifcconvert/XmlSerializer.cpp index dcb574e0cd..b6cdec851d 100644 --- a/src/ifcconvert/XmlSerializer.cpp +++ b/src/ifcconvert/XmlSerializer.cpp @@ -341,16 +341,16 @@ void XmlSerializer::finalize() { ptree root, header, units, decomposition, properties, types, layers; // Write the SPF header as XML nodes. - foreach(const std::string& s, file->header().file_description().description()) { + for (const std::string& s: file->header().file_description().description()) { header.add_child("file_description.description", ptree(s)); } - foreach(const std::string& s, file->header().file_name().author()) { + for (const std::string& s: file->header().file_name().author()) { header.add_child("file_name.author", ptree(s)); } - foreach(const std::string& s, file->header().file_name().organization()) { + for (const std::string& s: file->header().file_name().organization()) { header.add_child("file_name.organization", ptree(s)); } - foreach(const std::string& s, file->header().file_schema().schema_identifiers()) { + for (const std::string& s: file->header().file_schema().schema_identifiers()) { header.add_child("file_schema.schema_identifiers", ptree(s)); } header.put("file_description.implementation_level", file->header().file_description().implementation_level()); diff --git a/src/ifcgeom/IfcGeomIterator.h b/src/ifcgeom/IfcGeomIterator.h index 2ada29a2cf..f85c056ae6 100644 --- a/src/ifcgeom/IfcGeomIterator.h +++ b/src/ifcgeom/IfcGeomIterator.h @@ -329,7 +329,7 @@ namespace IfcGeom { void include_entity_names(const std::vector& names) { names_to_include_or_exclude.clear(); - foreach(const std::string &name, names) + for (const std::string &name: names) names_to_include_or_exclude.insert(wildcard_string_to_regex(name)); include_names_in_processing_ = true; } @@ -338,7 +338,7 @@ namespace IfcGeom { void exclude_entity_names(const std::vector& names) { names_to_include_or_exclude.clear(); - foreach(const std::string &name, names) + for (const std::string &name: names) names_to_include_or_exclude.insert(wildcard_string_to_regex(name)); include_names_in_processing_ = false; } @@ -347,7 +347,7 @@ namespace IfcGeom { { // Escape all non-"*?" regex special chars std::string special_chars = "\\^.$|()[]+/"; - foreach(char c, special_chars) { + for (char c: special_chars) { std::string char_str(1, c); boost::replace_all(str, char_str, "\\" + char_str); } @@ -540,7 +540,7 @@ namespace IfcGeom { IfcSchema::IfcProduct* prod = *jt; bool type_found = false; // The set is iterated over to able to filter on subtypes. - foreach(IfcSchema::Type::Enum type, entities_to_include_or_exclude) { + for (IfcSchema::Type::Enum type: entities_to_include_or_exclude) { if (prod->is(type)) { type_found = true; break; @@ -548,7 +548,7 @@ namespace IfcGeom { } if (!type_found && traverse) { - foreach(IfcSchema::Type::Enum type, entities_to_include_or_exclude) { + for (IfcSchema::Type::Enum type: entities_to_include_or_exclude) { IfcSchema::IfcProduct* parent, * current = prod; while ((parent = static_cast(kernel->get_decomposing_entity(current))) != 0) { if (parent->is(type)) { @@ -564,7 +564,7 @@ namespace IfcGeom { } bool name_found = false; - foreach(const boost::regex& r, names_to_include_or_exclude) { + for (const boost::regex& r: names_to_include_or_exclude) { if (prod->hasName() && boost::regex_match(prod->Name(), r)) { name_found = true; break; @@ -572,7 +572,7 @@ namespace IfcGeom { } if (!name_found && traverse) { - foreach(const boost::regex& r, names_to_include_or_exclude) { + for (const boost::regex& r: names_to_include_or_exclude) { IfcSchema::IfcProduct* parent, *current = prod; while ((parent = static_cast(kernel->get_decomposing_entity(current))) != 0) { if (parent->hasName() && boost::regex_match(parent->Name(), r)) { @@ -702,6 +702,14 @@ namespace IfcGeom { try { next_shape_model = create_shape_model_for_next_entity(); + +// std::cout << "trsf" << std::endl; +// IfcGeom::CgalPlacement *trsf = next_shape_model->transformation().data(); +// for (int i = 0; i < 3; ++i) { +// for (int j = 0; j < 4; ++j) { +// std::cout << << " "; +// } std::cout << std::endl; +// } } catch (...) {} if (next_shape_model) { diff --git a/src/ifcgeom/kernels/cgal/CgalConversionFunctions.cpp b/src/ifcgeom/kernels/cgal/CgalConversionFunctions.cpp index 68d9224029..063b4d8abf 100644 --- a/src/ifcgeom/kernels/cgal/CgalConversionFunctions.cpp +++ b/src/ifcgeom/kernels/cgal/CgalConversionFunctions.cpp @@ -23,6 +23,216 @@ bool IfcGeom::CgalKernel::convert(const IfcSchema::IfcRepresentation* l, Convers return part_succes; } -bool IfcGeom::CgalKernel::convert(const IfcSchema::IfcExtrudedAreaSolid*, cgal_shape_t&) { - throw std::runtime_error("Not implemented IfcExtrudedAreaSolid"); +bool IfcGeom::CgalKernel::convert(const IfcSchema::IfcExtrudedAreaSolid *l, cgal_shape_t &shape) { + const double height = l->Depth() * getValue(GV_LENGTH_UNIT); + if (height < getValue(GV_PRECISION)) { + Logger::Message(Logger::LOG_ERROR, "Non-positive extrusion height encountered for:", l->entity); + return false; + } + + cgal_face_t face; + if ( !convert_face(l->SweptArea(),face) ) return false; + + cgal_placement_t trsf; + bool has_position = true; +#ifdef USE_IFC4 + has_position = l->hasPosition(); +#endif + if (has_position) { + IfcGeom::CgalKernel::convert(l->Position(), trsf); + } + + cgal_direction_t dir; + convert(l->ExtrudedDirection(),dir); +// std::cout << "Direction: " << dir << std::endl; + + std::list face_list; + face_list.push_back(face); + + for (std::vector::const_iterator current_vertex = face.outer.begin(); + current_vertex != face.outer.end(); + ++current_vertex) { + std::vector::const_iterator next_vertex = current_vertex; + ++next_vertex; + if (next_vertex == face.outer.end()) { + next_vertex = face.outer.begin(); + } cgal_face_t side_face; + side_face.outer.push_back(*next_vertex); + side_face.outer.push_back(*current_vertex); + side_face.outer.push_back(*current_vertex+height*dir); + side_face.outer.push_back(*next_vertex+height*dir); + face_list.push_back(side_face); + } + + cgal_face_t top_face; + for (std::vector::const_reverse_iterator vertex = face.outer.rbegin(); + vertex != face.outer.rend(); + ++vertex) { + top_face.outer.push_back(*vertex+height*dir); + } face_list.push_back(top_face); + + // Naive creation + cgal_shape_t polyhedron = CGAL::Polyhedron_3(); + PolyhedronBuilder builder(&face_list); + polyhedron.delegate(builder); + + // Stitch edges +// std::cout << "Before: " << polyhedron.size_of_vertices() << " vertices and " << polyhedron.size_of_facets() << " facets" << std::endl; + CGAL::Polygon_mesh_processing::stitch_borders(polyhedron); + if (!CGAL::Polygon_mesh_processing::is_outward_oriented(polyhedron)) { + CGAL::Polygon_mesh_processing::reverse_face_orientations(polyhedron); + } +// std::cout << "After: " << polyhedron.size_of_vertices() << " vertices and " << polyhedron.size_of_facets() << " facets" << std::endl; + + shape = polyhedron; + return true; +} + +bool IfcGeom::CgalKernel::convert(const IfcSchema::IfcCartesianPoint* l, cgal_point_t& point) { + std::vector xyz = l->Coordinates(); + if (xyz.size() == 3) { + point = Kernel::Point_3(xyz.size() ? (xyz[0]*getValue(GV_LENGTH_UNIT)) : 0.0f, + xyz.size() > 1 ? (xyz[1]*getValue(GV_LENGTH_UNIT)) : 0.0f, + xyz.size() > 2 ? (xyz[2]*getValue(GV_LENGTH_UNIT)) : 0.0f); +// std::cout << "Converted Point(" << point << ")" << std::endl; + return true; + } else { + throw std::runtime_error("Point without 3 coordinates"); + } +} + +bool IfcGeom::CgalKernel::convert(const IfcSchema::IfcDirection* l, cgal_direction_t& dir) { +// IN_CACHE(IfcDirection,l,cgal_direction_t,dir) + std::vector xyz = l->DirectionRatios(); + dir = Kernel::Vector_3(xyz.size() ? xyz[0] : 0.0f, + xyz.size() > 1 ? xyz[1] : 0.0f, + xyz.size() > 2 ? xyz[2] : 0.0f); +// CACHE(IfcDirection,l,dir) + return true; +} + +bool IfcGeom::CgalKernel::convert(const IfcSchema::IfcAxis2Placement2D* l, cgal_placement_t& trsf) { + // IN_CACHE(IfcAxis2Placement3D,l,gp_Trsf,trsf) + cgal_point_t o; + cgal_direction_t axis = Kernel::Vector_3(0,0,1); + cgal_direction_t refDirection = Kernel::Vector_3(1,0,0); // TODO: Put identity for now. Check? + IfcGeom::CgalKernel::convert(l->Location(),o); + bool hasRef = l->hasRefDirection(); + if ( hasRef ) IfcGeom::CgalKernel::convert(l->RefDirection(),refDirection); + + // TODO: From Thomas' email. Should be checked. + Kernel::Vector_3 y = CGAL::cross_product(Kernel::Vector_3(0.0, 0.0, 1.0), refDirection); + trsf = Kernel::Aff_transformation_3(refDirection.cartesian(0), y.cartesian(0), 0.0, o.cartesian(0), + refDirection.cartesian(1), y.cartesian(1), 0.0, o.cartesian(1), + 0.0, y.cartesian(2), 1.0, 0.0); + + // CACHE(IfcAxis2Placement3D,l,trsf) + return true; +} + +bool IfcGeom::CgalKernel::convert(const IfcSchema::IfcAxis2Placement3D* l, cgal_placement_t& trsf) { +// IN_CACHE(IfcAxis2Placement3D,l,gp_Trsf,trsf) + cgal_point_t o; + cgal_direction_t axis = Kernel::Vector_3(0,0,1); + cgal_direction_t refDirection = Kernel::Vector_3(1,0,0); // TODO: Put identity for now. Check? + IfcGeom::CgalKernel::convert(l->Location(),o); + bool hasRef = l->hasRefDirection(); + if ( l->hasAxis() ) IfcGeom::CgalKernel::convert(l->Axis(),axis); + if ( hasRef ) IfcGeom::CgalKernel::convert(l->RefDirection(),refDirection); + +// std::cout << "Ref direction: " << refDirection << std::endl; +// std::cout << "Axis: " << axis << std::endl; +// std::cout << "Origin: " << o << std::endl; + + // TODO: From Thomas' email. Should be checked. + Kernel::Vector_3 y = CGAL::cross_product(axis, refDirection); + trsf = Kernel::Aff_transformation_3(refDirection.cartesian(0), y.cartesian(0), axis.cartesian(0), o.cartesian(0), + refDirection.cartesian(1), y.cartesian(1), axis.cartesian(1), o.cartesian(1), + refDirection.cartesian(2), y.cartesian(2), axis.cartesian(2), o.cartesian(2)); + +// for (int i = 0; i < 3; ++i) { +// for (int j = 0; j < 4; ++j) { +// std::cout << trsf.cartesian(i, j) << " "; +// } std::cout << std::endl; +// } + +// CACHE(IfcAxis2Placement3D,l,trsf) + return true; +} + +bool IfcGeom::CgalKernel::convert(const IfcSchema::IfcObjectPlacement* l, cgal_placement_t& trsf) { + // TODO: These macros don't work for the CGAL types. Need to check why. +// IN_CACHE(IfcObjectPlacement,l,cgal_placement_t,trsf) + if ( ! l->is(IfcSchema::Type::IfcLocalPlacement) ) { + Logger::Message(Logger::LOG_ERROR, "Unsupported IfcObjectPlacement:", l->entity); + return false; + } + +// std::cout << "initial trsf (identity?)" << std::endl; +// for (int i = 0; i < 3; ++i) { +// for (int j = 0; j < 4; ++j) { +// std::cout << trsf.cartesian(i, j) << " "; +// } std::cout << std::endl; +// } + + IfcSchema::IfcLocalPlacement* current = (IfcSchema::IfcLocalPlacement*)l; + for (;;) { + cgal_placement_t trsf2; + + IfcSchema::IfcAxis2Placement* relplacement = current->RelativePlacement(); + if ( relplacement->is(IfcSchema::Type::IfcAxis2Placement3D) ) { + IfcGeom::CgalKernel::convert((IfcSchema::IfcAxis2Placement3D*)relplacement,trsf2); + +// std::cout << "trsf2" << std::endl; +// for (int i = 0; i < 3; ++i) { +// for (int j = 0; j < 4; ++j) { +// std::cout << trsf2.cartesian(i, j) << " "; +// } std::cout << std::endl; +// } + + trsf = trsf * trsf2; // TODO: I think it's fine, but maybe should it be the other way around? + +// std::cout << "trsf (after multiplication)" << std::endl; +// for (int i = 0; i < 3; ++i) { +// for (int j = 0; j < 4; ++j) { +// std::cout << trsf.cartesian(i, j) << " "; +// } std::cout << std::endl; +// } + } + if ( current->hasPlacementRelTo() ) { + IfcSchema::IfcObjectPlacement* relto = current->PlacementRelTo(); + if ( relto->is(IfcSchema::Type::IfcLocalPlacement) ) + current = (IfcSchema::IfcLocalPlacement*)current->PlacementRelTo(); + else break; + } else break; + } +// CACHE(IfcObjectPlacement,l,trsf) + return true; +} + +bool IfcGeom::CgalKernel::convert(const IfcSchema::IfcRectangleProfileDef* l, cgal_face_t& face) { + const double x = l->XDim() / 2.0f * getValue(GV_LENGTH_UNIT); + const double y = l->YDim() / 2.0f * getValue(GV_LENGTH_UNIT); + + if ( x < ALMOST_ZERO || y < 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 + if (has_position) { + IfcGeom::CgalKernel::convert(l->Position(), trsf2d); + } + + 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)); + face.outer.push_back(Kernel::Point_3( x, y, 0.0)); + face.outer.push_back(Kernel::Point_3(-x, y, 0.0)); + + return true; } diff --git a/src/ifcgeom/kernels/cgal/CgalConversionResult.cpp b/src/ifcgeom/kernels/cgal/CgalConversionResult.cpp index e2da8ecd97..59225e53f0 100644 --- a/src/ifcgeom/kernels/cgal/CgalConversionResult.cpp +++ b/src/ifcgeom/kernels/cgal/CgalConversionResult.cpp @@ -2,5 +2,47 @@ #include "CgalConversionResult.h" void IfcGeom::CgalShape::Triangulate(const IfcGeom::IteratorSettings & settings, const IfcGeom::ConversionResultPlacement * place, IfcGeom::Representation::Triangulation* t, int surface_style_id) const { - throw std::runtime_error("Not implemented Triangulate()"); + cgal_shape_t s = shape_; + const cgal_placement_t& trsf = dynamic_cast(place)->trsf(); +// std::cout << "Model: " << s.size_of_facets() << " facets and " << s.size_of_vertices() << " vertices" << std::endl; +// std::cout << "Valid: " << s.is_valid() << std::endl; + + // Apply transformation + if (place != NULL) for (auto &vertex: vertices(s)) { + vertex->point() = vertex->point().transform(trsf); + } + + // Triangulate the shape and compute the normals + std::map vertex_normals; + boost::associative_property_map> vertex_normals_map(vertex_normals); + std::map face_normals; + boost::associative_property_map> face_normals_map(face_normals); + if (CGAL::Polygon_mesh_processing::triangulate_faces(s)) { +// std::cout << "Triangulated model: " << s.size_of_facets() << " facets and " << s.size_of_vertices() << " vertices" << std::endl; + } else { + Logger::Message(Logger::LOG_ERROR, "Failed to triangulate shape"); + return; + } + + CGAL::Polygon_mesh_processing::compute_normals(s, vertex_normals_map, face_normals_map); + + // Iterates over the faces of the shape + int num_faces = 0, num_vertices = 0; + for (auto &face: faces(s)) { + CGAL::Polyhedron_3::Halfedge_around_facet_const_circulator current_halfedge = face->facet_begin(); + do { + t->addVertex(surface_style_id, + CGAL::to_double(current_halfedge->vertex()->point().cartesian(0)), + CGAL::to_double(current_halfedge->vertex()->point().cartesian(1)), + CGAL::to_double(current_halfedge->vertex()->point().cartesian(2))); + for (int i = 0; i < 3; ++i) t->normals().push_back(CGAL::to_double(face_normals_map[face].cartesian(i))); + t->faces().push_back(num_vertices); + ++num_vertices; + ++current_halfedge; + } while (current_halfedge != face->facet_begin()); + t->material_ids().push_back(surface_style_id); + ++num_faces; + } + +// std::cout << num_faces << " faces" << std::endl; } diff --git a/src/ifcgeom/kernels/cgal/CgalConversionResult.h b/src/ifcgeom/kernels/cgal/CgalConversionResult.h index 3fb4bb7e67..b02e6e5c95 100644 --- a/src/ifcgeom/kernels/cgal/CgalConversionResult.h +++ b/src/ifcgeom/kernels/cgal/CgalConversionResult.h @@ -1,4 +1,4 @@ -/******************************************************************************** +/******************************************************************************** * * * This file is part of IfcOpenShell. * * * @@ -34,16 +34,17 @@ namespace IfcGeom { operator const cgal_placement_t& () { return trsf_; } virtual double Value(int i, int j) const { - // Get cell from placement as 4x3 matrix as implemented in OCCT. We'll have to check exact semantics. - throw std::runtime_error("Not implemented"); + // TODO: Check +// std::cout << "Getting CgalPlacement with i = " << i << " and j = " << j << std::endl; + return CGAL::to_double(trsf_.cartesian(i-1, j-1)); } virtual void Multiply(const ConversionResultPlacement* other) { - // Multiply matrix as implemented in OCCT. We'll have to check exact semantics. - throw std::runtime_error("Not implemented"); + // TODO: Check + trsf_ = ((CgalPlacement *)other)->trsf_ * trsf_; } virtual void PreMultiply(const ConversionResultPlacement* other) { - // PreMultiply matrix as implemented in OCCT. We'll have to check exact semantics. - throw std::runtime_error("Not implemented"); + // TODO: Check + trsf_ = trsf_ * ((CgalPlacement *)other)->trsf_; } virtual ConversionResultPlacement* clone() const { return new CgalPlacement(trsf_); @@ -51,7 +52,7 @@ namespace IfcGeom { private: cgal_placement_t trsf_; }; - + class CgalShape : public ConversionResultShape { public: CgalShape(const cgal_shape_t& shape) @@ -74,4 +75,4 @@ namespace IfcGeom { } -#endif \ No newline at end of file +#endif diff --git a/src/ifcgeom/kernels/cgal/CgalEntityMapping.cpp b/src/ifcgeom/kernels/cgal/CgalEntityMapping.cpp index f3002292d4..d7c576a4e7 100644 --- a/src/ifcgeom/kernels/cgal/CgalEntityMapping.cpp +++ b/src/ifcgeom/kernels/cgal/CgalEntityMapping.cpp @@ -1,4 +1,4 @@ -/******************************************************************************** +/******************************************************************************** * * * This file is part of IfcOpenShell. * * * @@ -67,7 +67,7 @@ bool IfcGeom::CgalKernel::convert_shape(const IfcBaseClass* l, cgal_shape_t& r) } if ( processed && success ) { - const double precision = getValue(GV_PRECISION); +// const double precision = getValue(GV_PRECISION); // apply_tolerance(r, precision); #ifndef NO_CACHE cache.Shape[id] = r; @@ -81,6 +81,79 @@ bool IfcGeom::CgalKernel::convert_shape(const IfcBaseClass* l, cgal_shape_t& r) return success; } +bool IfcGeom::CgalKernel::convert(const IfcSchema::IfcManifoldSolidBrep* l, ConversionResults& shape) { + cgal_shape_t s; + const SurfaceStyle* collective_style = get_style(l); + if (convert_shape(l->Outer(),s) ) { + const SurfaceStyle* indiv_style = get_style(l->Outer()); + + IfcSchema::IfcClosedShell::list::ptr voids(new IfcSchema::IfcClosedShell::list); + if (l->is(IfcSchema::Type::IfcFacetedBrepWithVoids)) { + voids = l->as()->Voids(); + } +#ifdef USE_IFC4 + if (l->is(IfcSchema::Type::IfcAdvancedBrepWithVoids)) { + voids = l->as()->Voids(); + } +#endif + + for (IfcSchema::IfcClosedShell::list::it it = voids->begin(); it != voids->end(); ++it) { +// TopoDS_Shape s2; +// /// @todo No extensive shapefixing since shells should be disjoint. +// /// @todo Awaiting generalized boolean ops module with appropriate checking +// if (convert_shape(l->Outer(), s2)) { +// s = BRepAlgoAPI_Cut(s, s2).Shape(); +// } + } + + shape.push_back(ConversionResult(new CgalShape(s), indiv_style ? indiv_style : collective_style)); + return true; + } + return false; +} + +bool IfcGeom::CgalKernel::convert(const IfcSchema::IfcConnectedFaceSet* l, cgal_shape_t& shape) { + IfcSchema::IfcFace::list::ptr faces = l->CfsFaces(); + + std::list face_list; + for (IfcSchema::IfcFace::list::it it = faces->begin(); it != faces->end(); ++it) { + bool success = false; + cgal_face_t face; + + try { + success = convert_face(*it, face); + } catch (...) {} + + if (!success) { + Logger::Message(Logger::LOG_WARNING, "Failed to convert face:", (*it)->entity); + continue; + } + +// std::cout << "Face in ConnectedFaceSet: " << std::endl; +// for (auto &point: face.outer) { +// std::cout << "\tPoint(" << point << ")" << std::endl; +// } + + face_list.push_back(face); + } + + // Naive creation + cgal_shape_t polyhedron = CGAL::Polyhedron_3(); + PolyhedronBuilder builder(&face_list); + polyhedron.delegate(builder); + + // Stitch edges +// std::cout << "Before: " << polyhedron.size_of_vertices() << " vertices and " << polyhedron.size_of_facets() << " facets" << std::endl; + CGAL::Polygon_mesh_processing::stitch_borders(polyhedron); + if (!CGAL::Polygon_mesh_processing::is_outward_oriented(polyhedron)) { + CGAL::Polygon_mesh_processing::reverse_face_orientations(polyhedron); + } +// std::cout << "After: " << polyhedron.size_of_vertices() << " vertices and " << polyhedron.size_of_facets() << " facets" << std::endl; + + shape = polyhedron; + return true; +} + bool IfcGeom::CgalKernel::convert_wire(const IfcBaseClass* l, cgal_wire_t& r) { #include "CgalEntityMappingWire.h" Logger::Message(Logger::LOG_ERROR,"No operation defined for:",l->entity); @@ -93,6 +166,94 @@ bool IfcGeom::CgalKernel::convert_face(const IfcBaseClass* l, cgal_face_t& r) { return false; } +bool IfcGeom::CgalKernel::convert(const IfcSchema::IfcFace* l, cgal_face_t& face) { + IfcSchema::IfcFaceBound::list::ptr bounds = l->Bounds(); + + int num_outer_bounds = 0; + + for (IfcSchema::IfcFaceBound::list::it it = bounds->begin(); it != bounds->end(); ++it) { + IfcSchema::IfcFaceBound* bound = *it; + if (bound->is(IfcSchema::Type::IfcFaceOuterBound)) num_outer_bounds ++; + } + + if (num_outer_bounds != 1) { + Logger::Message(Logger::LOG_ERROR, "Invalid configuration of boundaries for:", l->entity); + return false; + } + + cgal_face_t mf; + + for (IfcSchema::IfcFaceBound::list::it it = bounds->begin(); it != bounds->end(); ++it) { + IfcSchema::IfcFaceBound* bound = *it; + IfcSchema::IfcLoop* loop = bound->Bound(); + + const bool is_interior = !bound->is(IfcSchema::Type::IfcFaceOuterBound); + + cgal_wire_t wire; + if (!convert_wire(loop, wire)) { + Logger::Message(Logger::LOG_ERROR, "Failed to process face boundary loop", loop->entity); + return false; + } + + if (!is_interior) { + mf.outer = wire; + } else { + mf.inner.push_back(wire); + } + } + + face = mf; + +// std::cout << "Face: " << std::endl; +// for (auto &point: face.outer) { +// std::cout << "\tPoint(" << point << ")" << std::endl; +// } + + return true; +} + +bool IfcGeom::CgalKernel::convert(const IfcSchema::IfcPolyLoop* l, cgal_wire_t& result) { + IfcSchema::IfcCartesianPoint::list::ptr points = l->Polygon(); + + // Parse and store the points in a sequence + cgal_wire_t polygon = std::vector(); + for(IfcSchema::IfcCartesianPoint::list::it it = points->begin(); it != points->end(); ++ it) { + cgal_point_t pnt; + IfcGeom::CgalKernel::convert(*it, pnt); + polygon.push_back(pnt); + } + + // A loop should consist of at least three vertices + std::size_t original_count = polygon.size(); + if (original_count < 3) { + Logger::Message(Logger::LOG_ERROR, "Not enough edges for:", l->entity); + return false; + } + + // TODO: Remove repeated points (and points that are too close to one another?) +// remove_duplicate_points_from_loop(polygon, true); + + std::size_t count = polygon.size(); + if (original_count - count != 0) { + std::stringstream ss; ss << (original_count - count) << " edges removed for:"; + Logger::Message(Logger::LOG_WARNING, ss.str(), l->entity); + } + + if (count < 3) { + Logger::Message(Logger::LOG_ERROR, "Not enough edges for:", l->entity); + return false; + } + + result = polygon; + +// std::cout << "PolyLoop: " << std::endl; +// for (auto &point: polygon) { +// std::cout << "\tPoint(" << point << ")" << std::endl; +// } + + return true; +} + bool IfcGeom::CgalKernel::convert_curve(const IfcBaseClass* l, cgal_curve_t& r) { #include "CgalEntityMappingCurve.h" Logger::Message(Logger::LOG_ERROR,"No operation defined for:",l->entity); diff --git a/src/ifcgeom/kernels/cgal/CgalEntityMapping.h b/src/ifcgeom/kernels/cgal/CgalEntityMapping.h index eb2ecedf38..b163696066 100644 --- a/src/ifcgeom/kernels/cgal/CgalEntityMapping.h +++ b/src/ifcgeom/kernels/cgal/CgalEntityMapping.h @@ -1,4 +1,4 @@ -/******************************************************************************** +/******************************************************************************** * * * This file is part of IfcOpenShell. * * * @@ -29,7 +29,22 @@ #include "../../../ifcparse/IfcParse.h" SHAPES(IfcRepresentation); +// IfcFacetedBrep included +// IfcAdvancedBrep included +// IfcFacetedBrepWithVoids included +// IfcAdvancedBrepWithVoids included +SHAPES(IfcManifoldSolidBrep); SHAPE(IfcExtrudedAreaSolid); +SHAPE(IfcConnectedFaceSet); + +FACE(IfcFace); +FACE(IfcRectangleProfileDef); + +WIRE(IfcPolyLoop); CLASS(IfcCartesianPoint,cgal_point_t); +CLASS(IfcDirection,cgal_direction_t); +CLASS(IfcAxis2Placement2D,cgal_placement_t); +CLASS(IfcAxis2Placement3D,cgal_placement_t); +CLASS(IfcObjectPlacement,cgal_placement_t); diff --git a/src/ifcgeom/kernels/cgal/CgalKernel.cpp b/src/ifcgeom/kernels/cgal/CgalKernel.cpp index f4f830cbee..e67be1c390 100644 --- a/src/ifcgeom/kernels/cgal/CgalKernel.cpp +++ b/src/ifcgeom/kernels/cgal/CgalKernel.cpp @@ -94,8 +94,15 @@ IfcGeom::NativeElement* IfcGeom::CgalKernel::create_brep_for_representat cgal_placement_t trsf; try { - // convert(product->ObjectPlacement(), trsf); + convert(product->ObjectPlacement(), trsf); } catch (...) {} + +// std::cout << "trsf" << std::endl; +// for (int i = 0; i < 3; ++i) { +// for (int j = 0; j < 4; ++j) { +// std::cout << trsf.cartesian(i, j) << " "; +// } std::cout << std::endl; +// } // Does the IfcElement have any IfcOpenings? // Note that openings for IfcOpeningElements are not processed @@ -107,9 +114,24 @@ IfcGeom::NativeElement* IfcGeom::CgalKernel::create_brep_for_representat if (!settings.get(IfcGeom::IteratorSettings::DISABLE_OPENING_SUBTRACTIONS) && openings && openings->size()) { Logger::Message(Logger::LOG_ERROR, "Not implemented opening subtractions"); } - - shape = new IfcGeom::Representation::Native(element_settings, representation->entity->id(), shapes); - + + if (settings.get(IteratorSettings::USE_WORLD_COORDS)) { + // TODO: OpenCascade code uses opened_shapes. Check why. + 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); + } else { + shape = new IfcGeom::Representation::Native(element_settings, representation->entity->id(), shapes); + } + std::string context_string = ""; if (representation->hasRepresentationIdentifier()) { context_string = representation->RepresentationIdentifier(); @@ -146,7 +168,7 @@ IfcGeom::NativeElement* IfcGeom::CgalKernel::create_brep_for_processed_r cgal_placement_t trsf; try { - // convert(product->ObjectPlacement(), trsf); + convert(product->ObjectPlacement(), trsf); } catch (...) {} std::string context_string = ""; diff --git a/src/ifcgeom/kernels/cgal/CgalKernel.h b/src/ifcgeom/kernels/cgal/CgalKernel.h index a3e7167624..4e82272375 100644 --- a/src/ifcgeom/kernels/cgal/CgalKernel.h +++ b/src/ifcgeom/kernels/cgal/CgalKernel.h @@ -1,4 +1,4 @@ -/******************************************************************************** +/******************************************************************************** * * * This file is part of IfcOpenShell. * * * @@ -37,12 +37,76 @@ if ( it != cache.T.end() ) { e = it->second; return true; } #include "../../../ifcgeom/IfcGeom.h" -typedef void* cgal_shape_t; -typedef void* cgal_face_t; -typedef void* cgal_wire_t; -typedef void* cgal_curve_t; -typedef void* cgal_placement_t; -typedef void* cgal_point_t; +#undef Handle + +#include +#include +#include +#include +#include +#include +#include +#include + +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 std::vector cgal_curve_t; +typedef std::vector cgal_wire_t; + +struct cgal_face_t { + cgal_wire_t outer; + std::vector inner; +}; + +typedef CGAL::Polyhedron_3 cgal_shape_t; +typedef boost::graph_traits>::vertex_descriptor cgal_vertex_descriptor_t; +typedef boost::graph_traits>::face_descriptor cgal_face_descriptor_t; + +struct PolyhedronBuilder : public CGAL::Modifier_base::HalfedgeDS> { +private: + std::list *face_list; +public: + PolyhedronBuilder(std::list *face_list) { + this->face_list = face_list; + } + + void operator()(CGAL::Polyhedron_3::HalfedgeDS &hds) { + std::list points; + std::list> facet_vertices; + CGAL::Polyhedron_incremental_builder_3::HalfedgeDS> builder(hds, true); + + for (auto &face: *face_list) { + facet_vertices.push_back(std::list()); + for (auto &point: face.outer) { + facet_vertices.back().push_back(points.size()); + points.push_back(point); + } + } + + builder.begin_surface(points.size(), facet_vertices.size()); + + for (auto &point: points) { +// std::cout << "Adding point " << point << std::endl; + builder.add_vertex(point); + } + + for (auto &facet: facet_vertices) { + builder.begin_facet(); +// std::cout << "Adding facet "; + for (auto &vertex: facet) { +// std::cout << vertex << " "; + builder.add_vertex_to_facet(vertex); + } +// std::cout << std::endl; + builder.end_facet(); + } + + builder.end_surface(); + } +}; namespace IfcGeom { @@ -90,4 +154,4 @@ namespace IfcGeom { } -#endif \ No newline at end of file +#endif diff --git a/src/ifcparse/IfcUtil.h b/src/ifcparse/IfcUtil.h index 13eeb4a134..8b5259b111 100644 --- a/src/ifcparse/IfcUtil.h +++ b/src/ifcparse/IfcUtil.h @@ -38,7 +38,7 @@ #include #include -#define foreach BOOST_FOREACH +//#define foreach BOOST_FOREACH #define rforeach BOOST_REVERSE_FOREACH class Argument;