diff --git a/src/Mod/MeshPart/App/Mesher.cpp b/src/Mod/MeshPart/App/Mesher.cpp index 1cf051a96aea..e5088b914133 100644 --- a/src/Mod/MeshPart/App/Mesher.cpp +++ b/src/Mod/MeshPart/App/Mesher.cpp @@ -33,6 +33,7 @@ #include #include #include +#include #include #include "Mesher.h" @@ -123,47 +124,6 @@ int MeshingOutput::sync() namespace MeshPart { -struct Vertex -{ - static const double deflection; - Standard_Real x, y, z; - Standard_Integer i = 0; - mutable MeshCore::MeshPoint p; - - Vertex(Standard_Real X, Standard_Real Y, Standard_Real Z) - : x(X) - , y(Y) - , z(Z) - { - p.x = static_cast(x); - p.y = static_cast(y); - p.z = static_cast(z); - } - - const MeshCore::MeshPoint& toPoint() const - { - return p; - } - - bool operator<(const Vertex& v) const - { - if (fabs(this->x - v.x) >= deflection) { - return this->x < v.x; - } - if (fabs(this->y - v.y) >= deflection) { - return this->y < v.y; - } - if (fabs(this->z - v.z) >= deflection) { - return this->z < v.z; - } - return false; // points are considered to be equal - } -}; - -const double Vertex::deflection = 10 * std::numeric_limits::epsilon(); - -// ---------------------------------------------------------------------------- - class BrepMesh { bool segments; @@ -177,111 +137,52 @@ class BrepMesh Mesh::MeshObject* create(const std::vector& domains) const { - std::map> colorMap; - for (std::size_t i = 0; i < colors.size(); i++) { - colorMap[colors[i]].push_back(i); - } - - bool createSegm = (colors.size() == domains.size()); + std::vector points; + std::vector facets; + Part::BRepMesh mesh; + mesh.getFacesFromDomains(domains, points, facets); MeshCore::MeshFacetArray faces; - std::size_t numTriangles = 0; - for (const auto& it : domains) { - numTriangles += it.facets.size(); + faces.reserve(facets.size()); + std::transform(facets.cbegin(), + facets.cend(), + std::back_inserter(faces), + [](const Part::TopoShape::Facet& face) { + return MeshCore::MeshFacet(face.I1, face.I2, face.I3); + }); + + MeshCore::MeshPointArray verts; + verts.reserve(points.size()); + for (const auto& it : points) { + verts.emplace_back(float(it.x), float(it.y), float(it.z)); } - faces.reserve(numTriangles); - std::set vertices; - Standard_Real x1, y1, z1; - Standard_Real x2, y2, z2; - Standard_Real x3, y3, z3; + MeshCore::MeshKernel kernel; + kernel.Adopt(verts, faces, true); + // mesh segments std::vector> meshSegments; - std::size_t numMeshFaces = 0; - - for (const auto& domain : domains) { - std::size_t numDomainFaces = 0; - for (std::size_t j = 0; j < domain.facets.size(); ++j) { - const Part::TopoShape::Facet& tria = domain.facets[j]; - x1 = domain.points[tria.I1].x; - y1 = domain.points[tria.I1].y; - z1 = domain.points[tria.I1].z; - - x2 = domain.points[tria.I2].x; - y2 = domain.points[tria.I2].y; - z2 = domain.points[tria.I2].z; - - x3 = domain.points[tria.I3].x; - y3 = domain.points[tria.I3].y; - z3 = domain.points[tria.I3].z; - - std::set::iterator it; - MeshCore::MeshFacet face; - - // 1st vertex - Vertex v1(x1, y1, z1); - it = vertices.find(v1); - if (it == vertices.end()) { - v1.i = vertices.size(); - face._aulPoints[0] = v1.i; - vertices.insert(v1); - } - else { - face._aulPoints[0] = it->i; - } - - // 2nd vertex - Vertex v2(x2, y2, z2); - it = vertices.find(v2); - if (it == vertices.end()) { - v2.i = vertices.size(); - face._aulPoints[1] = v2.i; - vertices.insert(v2); - } - else { - face._aulPoints[1] = it->i; - } - - // 3rd vertex - Vertex v3(x3, y3, z3); - it = vertices.find(v3); - if (it == vertices.end()) { - v3.i = vertices.size(); - face._aulPoints[2] = v3.i; - vertices.insert(v3); - } - else { - face._aulPoints[2] = it->i; - } - - // make sure that we don't insert invalid facets - if (face._aulPoints[0] != face._aulPoints[1] - && face._aulPoints[1] != face._aulPoints[2] - && face._aulPoints[2] != face._aulPoints[0]) { - faces.push_back(face); - numDomainFaces++; - } - } - // add a segment for the face - if (createSegm || this->segments) { - std::vector segment(numDomainFaces); - std::generate(segment.begin(), - segment.end(), - Base::iotaGen(numMeshFaces)); - numMeshFaces += numDomainFaces; - meshSegments.push_back(segment); - } + std::map> colorMap; + for (std::size_t i = 0; i < colors.size(); i++) { + colorMap[colors[i]].push_back(i); } - MeshCore::MeshPointArray verts; - verts.resize(vertices.size()); - for (const auto& it : vertices) { - verts[it.i] = it.toPoint(); - } + bool createSegm = (colors.size() == domains.size()); - MeshCore::MeshKernel kernel; - kernel.Adopt(verts, faces, true); + // add a segment for the face + if (createSegm || this->segments) { + auto segments = mesh.createSegments(); + meshSegments.reserve(segments.size()); + std::transform(segments.cbegin(), + segments.cend(), + std::back_inserter(meshSegments), + [](const Part::BRepMesh::Segment& segm) { + std::vector faces; + faces.insert(faces.end(), segm.cbegin(), segm.cend()); + return faces; + }); + } Mesh::MeshObject* meshdata = new Mesh::MeshObject(); meshdata->swap(kernel); diff --git a/src/Mod/Part/App/BRepMesh.cpp b/src/Mod/Part/App/BRepMesh.cpp new file mode 100644 index 000000000000..a13251186f1e --- /dev/null +++ b/src/Mod/Part/App/BRepMesh.cpp @@ -0,0 +1,134 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later + +/*************************************************************************** + * Copyright (c) 2024 Werner Mayer * + * * + * This file is part of FreeCAD. * + * * + * FreeCAD is free software: you can redistribute it and/or modify it * + * under the terms of the GNU Lesser General Public License as * + * published by the Free Software Foundation, either version 2.1 of the * + * License, or (at your option) any later version. * + * * + * FreeCAD is distributed in the hope that it will be useful, but * + * WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * + * Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public * + * License along with FreeCAD. If not, see * + * . * + * * + **************************************************************************/ + + +#include "PreCompiled.h" +#ifndef _PreComp_ +#include +#endif + +#include "BRepMesh.h" +#include + +using namespace Part; + +namespace Part { +struct MeshVertex +{ + Base::Vector3d p; + std::size_t i; + + explicit MeshVertex(const Base::Vector3d& p) + : p(p), i(0) + { + } + + Base::Vector3d toPoint() const + { return p; } + + bool operator < (const MeshVertex &v) const + { + if (fabs ( p.x - v.p.x) >= epsilon) + return p.x < v.p.x; + if (fabs ( p.y - v.p.y) >= epsilon) + return p.y < v.p.y; + if (fabs ( p.z - v.p.z) >= epsilon) + return p.z < v.p.z; + return false; // points are considered to be equal + } + +private: + static const double epsilon; +}; + +const double MeshVertex::epsilon = 10 * std::numeric_limits::epsilon(); + +} + +void BRepMesh::getFacesFromDomains(const std::vector& domains, + std::vector& points, + std::vector& faces) +{ + std::size_t numFaces = 0; + for (const auto& it : domains) { + numFaces += it.facets.size(); + } + faces.reserve(numFaces); + + std::set vertices; + auto addVertex = [&vertices](const Base::Vector3d& pnt, uint32_t& pointIndex) { + MeshVertex vertex(pnt); + vertex.i = vertices.size(); + auto it = vertices.insert(vertex); + pointIndex = it.first->i; + }; + + for (const auto & domain : domains) { + std::size_t numDomainFaces = 0; + for (const Facet& df : domain.facets) { + Facet face; + + // 1st vertex + addVertex(domain.points[df.I1], face.I1); + + // 2nd vertex + addVertex(domain.points[df.I2], face.I2); + + // 3rd vertex + addVertex(domain.points[df.I3], face.I3); + + // make sure that we don't insert invalid facets + if (face.I1 != face.I2 && + face.I2 != face.I3 && + face.I3 != face.I1) { + faces.push_back(face); + numDomainFaces++; + } + } + + domainSizes.push_back(numDomainFaces); + } + + std::vector meshPoints; + meshPoints.resize(vertices.size()); + for (const auto & vertex : vertices) { + meshPoints[vertex.i] = vertex.toPoint(); + } + points.swap(meshPoints); +} + +std::vector BRepMesh::createSegments() const +{ + std::size_t numMeshFaces = 0; + std::vector segm; + for (size_t numDomainFaces : domainSizes) { + Segment segment(numDomainFaces); + std::generate(segment.begin(), + segment.end(), + Base::iotaGen(numMeshFaces)); + numMeshFaces += numDomainFaces; + segm.push_back(segment); + } + + return segm; +} diff --git a/src/Mod/Part/App/BRepMesh.h b/src/Mod/Part/App/BRepMesh.h new file mode 100644 index 000000000000..e5edeaa9febd --- /dev/null +++ b/src/Mod/Part/App/BRepMesh.h @@ -0,0 +1,50 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later + +/*************************************************************************** + * Copyright (c) 2024 Werner Mayer * + * * + * This file is part of FreeCAD. * + * * + * FreeCAD is free software: you can redistribute it and/or modify it * + * under the terms of the GNU Lesser General Public License as * + * published by the Free Software Foundation, either version 2.1 of the * + * License, or (at your option) any later version. * + * * + * FreeCAD is distributed in the hope that it will be useful, but * + * WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * + * Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public * + * License along with FreeCAD. If not, see * + * . * + * * + **************************************************************************/ + +#ifndef PART_BREPMESH_H +#define PART_BREPMESH_H + +#include +#include + +namespace Part { + +class PartExport BRepMesh +{ +public: + using Facet = Data::ComplexGeoData::Facet; + using Domain = Data::ComplexGeoData::Domain; + using Segment = std::vector; + + void getFacesFromDomains(const std::vector& domains, + std::vector& points, + std::vector& faces); + std::vector createSegments() const; + +private: + std::vector domainSizes; +}; + +} + +#endif // PART_BREPMESH_H diff --git a/src/Mod/Part/App/CMakeLists.txt b/src/Mod/Part/App/CMakeLists.txt index 1a85f3b6202c..ab4cd149a97f 100644 --- a/src/Mod/Part/App/CMakeLists.txt +++ b/src/Mod/Part/App/CMakeLists.txt @@ -500,6 +500,8 @@ SET(Part_SRCS Attacher.h AppPart.cpp AppPartPy.cpp + BRepMesh.cpp + BRepMesh.h BRepOffsetAPI_MakeOffsetFix.cpp BRepOffsetAPI_MakeOffsetFix.h BSplineCurveBiArcs.cpp diff --git a/src/Mod/Part/App/TopoShape.cpp b/src/Mod/Part/App/TopoShape.cpp index 883d7b3a7783..25266bf35fa0 100644 --- a/src/Mod/Part/App/TopoShape.cpp +++ b/src/Mod/Part/App/TopoShape.cpp @@ -172,6 +172,7 @@ #include #include "TopoShape.h" +#include "BRepMesh.h" #include "BRepOffsetAPI_MakeOffsetFix.h" #include "CrossSection.h" #include "encodeFilename.h" @@ -3359,118 +3360,12 @@ void TopoShape::getDomains(std::vector& domains) const } } -namespace Part { -struct MeshVertex -{ - Standard_Real x,y,z; - Standard_Integer i; - - MeshVertex(Standard_Real X, Standard_Real Y, Standard_Real Z) - : x(X),y(Y),z(Z),i(0) - { - } - explicit MeshVertex(const Base::Vector3d& p) - : x(p.x),y(p.y),z(p.z),i(0) - { - } - - Base::Vector3d toPoint() const - { return Base::Vector3d(x,y,z); } - - bool operator < (const MeshVertex &v) const - { - if (fabs ( this->x - v.x) >= MESH_MIN_PT_DIST) - return this->x < v.x; - if (fabs ( this->y - v.y) >= MESH_MIN_PT_DIST) - return this->y < v.y; - if (fabs ( this->z - v.z) >= MESH_MIN_PT_DIST) - return this->z < v.z; - return false; // points are considered to be equal - } - -private: - // use the same value as used inside the Mesh module - static const double MESH_MIN_PT_DIST; -}; -} - -const double MeshVertex::MESH_MIN_PT_DIST = 10 * std::numeric_limits::epsilon(); - void TopoShape::getFacesFromDomains(const std::vector& domains, std::vector& points, std::vector& faces) const { - std::set vertices; - Standard_Real x1, y1, z1; - Standard_Real x2, y2, z2; - Standard_Real x3, y3, z3; - - for (const auto & domain : domains) { - for (std::vector::const_iterator jt = domain.facets.begin(); jt != domain.facets.end(); ++jt) { - x1 = domain.points[jt->I1].x; - y1 = domain.points[jt->I1].y; - z1 = domain.points[jt->I1].z; - - x2 = domain.points[jt->I2].x; - y2 = domain.points[jt->I2].y; - z2 = domain.points[jt->I2].z; - - x3 = domain.points[jt->I3].x; - y3 = domain.points[jt->I3].y; - z3 = domain.points[jt->I3].z; - - TopoShape::Facet face; - std::set::iterator vIt; - - // 1st vertex - MeshVertex v1(x1,y1,z1); - vIt = vertices.find(v1); - if (vIt == vertices.end()) { - v1.i = vertices.size(); - face.I1 = v1.i; - vertices.insert(v1); - } - else { - face.I1 = vIt->i; - } - - // 2nd vertex - MeshVertex v2(x2,y2,z2); - vIt = vertices.find(v2); - if (vIt == vertices.end()) { - v2.i = vertices.size(); - face.I2 = v2.i; - vertices.insert(v2); - } - else { - face.I2 = vIt->i; - } - - // 3rd vertex - MeshVertex v3(x3,y3,z3); - vIt = vertices.find(v3); - if (vIt == vertices.end()) { - v3.i = vertices.size(); - face.I3 = v3.i; - vertices.insert(v3); - } - else { - face.I3 = vIt->i; - } - - // make sure that we don't insert invalid facets - if (face.I1 != face.I2 && - face.I2 != face.I3 && - face.I3 != face.I1) - faces.push_back(face); - } - } - - std::vector meshPoints; - meshPoints.resize(vertices.size()); - for (const auto & vertex : vertices) - meshPoints[vertex.i] = vertex.toPoint(); - points.swap(meshPoints); + BRepMesh mesh; + mesh.getFacesFromDomains(domains, points, faces); } double TopoShape::getAccuracy() const