Part: Add class BRepMesh

This refactors the code of TopoShape::getFacesFromDomains and the the private class BrepMesh of the MeshPart module
to reduce code duplication.
This commit is contained in:
wmayer
2024-03-05 18:03:01 +01:00
committed by wwmayer
parent da36c0c1f3
commit 0d37ea2629
5 changed files with 228 additions and 246 deletions

View File

@@ -33,6 +33,7 @@
#include <Base/Console.h>
#include <Base/Tools.h>
#include <Mod/Mesh/App/Mesh.h>
#include <Mod/Part/App/BRepMesh.h>
#include <Mod/Part/App/TopoShape.h>
#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<float>(x);
p.y = static_cast<float>(y);
p.z = static_cast<float>(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<double>::epsilon();
// ----------------------------------------------------------------------------
class BrepMesh
{
bool segments;
@@ -177,6 +137,32 @@ public:
Mesh::MeshObject* create(const std::vector<Part::TopoShape::Domain>& domains) const
{
std::vector<Base::Vector3d> points;
std::vector<Part::TopoShape::Facet> facets;
Part::BRepMesh mesh;
mesh.getFacesFromDomains(domains, points, facets);
MeshCore::MeshFacetArray faces;
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));
}
MeshCore::MeshKernel kernel;
kernel.Adopt(verts, faces, true);
// mesh segments
std::vector<std::vector<MeshCore::FacetIndex>> meshSegments;
std::map<uint32_t, std::vector<std::size_t>> colorMap;
for (std::size_t i = 0; i < colors.size(); i++) {
colorMap[colors[i]].push_back(i);
@@ -184,104 +170,19 @@ public:
bool createSegm = (colors.size() == domains.size());
MeshCore::MeshFacetArray faces;
std::size_t numTriangles = 0;
for (const auto& it : domains) {
numTriangles += it.facets.size();
// 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<MeshCore::FacetIndex> faces;
faces.insert(faces.end(), segm.cbegin(), segm.cend());
return faces;
});
}
faces.reserve(numTriangles);
std::set<Vertex> vertices;
Standard_Real x1, y1, z1;
Standard_Real x2, y2, z2;
Standard_Real x3, y3, z3;
std::vector<std::vector<MeshCore::FacetIndex>> 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<Vertex>::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<MeshCore::FacetIndex> segment(numDomainFaces);
std::generate(segment.begin(),
segment.end(),
Base::iotaGen<MeshCore::FacetIndex>(numMeshFaces));
numMeshFaces += numDomainFaces;
meshSegments.push_back(segment);
}
}
MeshCore::MeshPointArray verts;
verts.resize(vertices.size());
for (const auto& it : vertices) {
verts[it.i] = it.toPoint();
}
MeshCore::MeshKernel kernel;
kernel.Adopt(verts, faces, true);
Mesh::MeshObject* meshdata = new Mesh::MeshObject();
meshdata->swap(kernel);

View File

@@ -0,0 +1,134 @@
// SPDX-License-Identifier: LGPL-2.1-or-later
/***************************************************************************
* Copyright (c) 2024 Werner Mayer <wmayer[at]users.sourceforge.net> *
* *
* 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 *
* <https://www.gnu.org/licenses/>. *
* *
**************************************************************************/
#include "PreCompiled.h"
#ifndef _PreComp_
#include <algorithm>
#endif
#include "BRepMesh.h"
#include <Base/Tools.h>
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<double>::epsilon();
}
void BRepMesh::getFacesFromDomains(const std::vector<Domain>& domains,
std::vector<Base::Vector3d>& points,
std::vector<Facet>& faces)
{
std::size_t numFaces = 0;
for (const auto& it : domains) {
numFaces += it.facets.size();
}
faces.reserve(numFaces);
std::set<MeshVertex> 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<Base::Vector3d> meshPoints;
meshPoints.resize(vertices.size());
for (const auto & vertex : vertices) {
meshPoints[vertex.i] = vertex.toPoint();
}
points.swap(meshPoints);
}
std::vector<BRepMesh::Segment> BRepMesh::createSegments() const
{
std::size_t numMeshFaces = 0;
std::vector<Segment> segm;
for (size_t numDomainFaces : domainSizes) {
Segment segment(numDomainFaces);
std::generate(segment.begin(),
segment.end(),
Base::iotaGen<std::size_t>(numMeshFaces));
numMeshFaces += numDomainFaces;
segm.push_back(segment);
}
return segm;
}

View File

@@ -0,0 +1,50 @@
// SPDX-License-Identifier: LGPL-2.1-or-later
/***************************************************************************
* Copyright (c) 2024 Werner Mayer <wmayer[at]users.sourceforge.net> *
* *
* 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 *
* <https://www.gnu.org/licenses/>. *
* *
**************************************************************************/
#ifndef PART_BREPMESH_H
#define PART_BREPMESH_H
#include <Mod/Part/PartGlobal.h>
#include <App/ComplexGeoData.h>
namespace Part {
class PartExport BRepMesh
{
public:
using Facet = Data::ComplexGeoData::Facet;
using Domain = Data::ComplexGeoData::Domain;
using Segment = std::vector<std::size_t>;
void getFacesFromDomains(const std::vector<Domain>& domains,
std::vector<Base::Vector3d>& points,
std::vector<Facet>& faces);
std::vector<Segment> createSegments() const;
private:
std::vector<std::size_t> domainSizes;
};
}
#endif // PART_BREPMESH_H

View File

@@ -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

View File

@@ -172,6 +172,7 @@
#include <Base/Writer.h>
#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<Domain>& 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<double>::epsilon();
void TopoShape::getFacesFromDomains(const std::vector<Domain>& domains,
std::vector<Base::Vector3d>& points,
std::vector<Facet>& faces) const
{
std::set<MeshVertex> vertices;
Standard_Real x1, y1, z1;
Standard_Real x2, y2, z2;
Standard_Real x3, y3, z3;
for (const auto & domain : domains) {
for (std::vector<Facet>::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<MeshVertex>::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<Base::Vector3d> 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