1
// SPDX-License-Identifier: LGPL-2.1-or-later
3
/***************************************************************************
4
* Copyright (c) 2024 Werner Mayer <wmayer[at]users.sourceforge.net> *
6
* This file is part of FreeCAD. *
8
* FreeCAD is free software: you can redistribute it and/or modify it *
9
* under the terms of the GNU Lesser General Public License as *
10
* published by the Free Software Foundation, either version 2.1 of the *
11
* License, or (at your option) any later version. *
13
* FreeCAD is distributed in the hope that it will be useful, but *
14
* WITHOUT ANY WARRANTY; without even the implied warranty of *
15
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
16
* Lesser General Public License for more details. *
18
* You should have received a copy of the GNU Lesser General Public *
19
* License along with FreeCAD. If not, see *
20
* <https://www.gnu.org/licenses/>. *
22
**************************************************************************/
25
#include "PreCompiled.h"
28
#include <Precision.hxx>
32
#include <Base/Tools.h>
42
explicit MeshVertex(const Base::Vector3d& p)
47
Base::Vector3d toPoint() const
52
bool operator < (const MeshVertex &v) const
72
using Facet = BRepMesh::Facet;
74
MergeVertex(std::vector<Base::Vector3d> points,
75
std::vector<Facet> faces,
77
: points{std::move(points)}
78
, faces{std::move(faces)}
79
, tolerance{tolerance}
85
bool hasDuplicatedPoints() const
87
return duplicatedPoints > 0;
90
void mergeDuplicatedPoints()
92
if (!hasDuplicatedPoints()) {
97
auto degreeMap = getPointDegrees();
98
decrementPointIndex(degreeMap);
99
removeUnusedPoints(degreeMap);
103
std::vector<Base::Vector3d> getPoints() const
108
std::vector<Facet> getFacets() const
116
// by default map point index to itself
117
mapPointIndex.resize(points.size());
118
std::generate(mapPointIndex.begin(),
120
Base::iotaGen<std::size_t>(0));
125
mapPointIndex.clear();
126
duplicatedPoints = 0;
131
using VertexIterator = std::vector<Base::Vector3d>::const_iterator;
133
double tol3d = tolerance;
134
auto vertexLess = [tol3d](const VertexIterator& v1,
135
const VertexIterator& v2)
137
if (fabs(v1->x - v2->x) >= tol3d) {
138
return v1->x < v2->x;
140
if (fabs(v1->y - v2->y) >= tol3d) {
141
return v1->y < v2->y;
143
if (fabs(v1->z - v2->z) >= tol3d) {
144
return v1->z < v2->z;
146
return false; // points are considered to be equal
148
auto vertexEqual = [&](const VertexIterator& v1,
149
const VertexIterator& v2)
151
if (vertexLess(v1, v2)) {
154
if (vertexLess(v2, v1)) {
160
std::vector<VertexIterator> vertices;
161
vertices.reserve(points.size());
162
for (auto it = points.cbegin(); it != points.cend(); ++it) {
163
vertices.push_back(it);
166
std::sort(vertices.begin(), vertices.end(), vertexLess);
168
auto next = vertices.begin();
169
while (next != vertices.end()) {
170
next = std::adjacent_find(next, vertices.end(), vertexEqual);
171
if (next != vertices.end()) {
173
std::size_t first_index = *first - points.begin();
175
while (next != vertices.end() && vertexEqual(*first, *next)) {
176
std::size_t next_index = *next - points.begin();
177
mapPointIndex[next_index] = first_index;
185
void redirectPointIndex()
187
for (auto& face : faces) {
188
face.I1 = int(mapPointIndex[face.I1]);
189
face.I2 = int(mapPointIndex[face.I2]);
190
face.I3 = int(mapPointIndex[face.I3]);
194
std::vector<std::size_t> getPointDegrees() const
196
std::vector<std::size_t> degreeMap;
197
degreeMap.resize(points.size());
198
for (const auto& face : faces) {
199
degreeMap[face.I1]++;
200
degreeMap[face.I2]++;
201
degreeMap[face.I3]++;
207
void decrementPointIndex(const std::vector<std::size_t>& degreeMap)
209
std::vector<std::size_t> decrements;
210
decrements.resize(points.size());
212
std::size_t decr = 0;
213
for (std::size_t pos = 0; pos < points.size(); pos++) {
214
decrements[pos] = decr;
215
if (degreeMap[pos] == 0) {
220
for (auto& face : faces) {
221
face.I1 -= int(decrements[face.I1]);
222
face.I2 -= int(decrements[face.I2]);
223
face.I3 -= int(decrements[face.I3]);
227
void removeUnusedPoints(const std::vector<std::size_t>& degreeMap)
229
// remove unreferenced points
230
std::vector<Base::Vector3d> new_points;
231
new_points.reserve(points.size() - duplicatedPoints);
232
for (std::size_t pos = 0; pos < points.size(); ++pos) {
233
if (degreeMap[pos] > 0) {
234
new_points.push_back(points[pos]);
238
points.swap(new_points);
242
std::vector<Base::Vector3d> points;
243
std::vector<Facet> faces;
244
double tolerance = 0.0;
245
std::size_t duplicatedPoints = 0;
246
std::vector<std::size_t> mapPointIndex;
251
void BRepMesh::getFacesFromDomains(const std::vector<Domain>& domains,
252
std::vector<Base::Vector3d>& points,
253
std::vector<Facet>& faces)
255
std::size_t numFaces = 0;
256
for (const auto& it : domains) {
257
numFaces += it.facets.size();
259
faces.reserve(numFaces);
261
std::set<MeshVertex> vertices;
262
auto addVertex = [&vertices](const Base::Vector3d& pnt, uint32_t& pointIndex) {
263
MeshVertex vertex(pnt);
264
vertex.i = vertices.size();
265
auto it = vertices.insert(vertex);
266
pointIndex = it.first->i;
269
for (const auto & domain : domains) {
270
std::size_t numDomainFaces = 0;
271
for (const Facet& df : domain.facets) {
275
addVertex(domain.points[df.I1], face.I1);
278
addVertex(domain.points[df.I2], face.I2);
281
addVertex(domain.points[df.I3], face.I3);
283
// make sure that we don't insert invalid facets
284
if (face.I1 != face.I2 &&
285
face.I2 != face.I3 &&
286
face.I3 != face.I1) {
287
faces.push_back(face);
292
domainSizes.push_back(numDomainFaces);
295
std::vector<Base::Vector3d> meshPoints;
296
meshPoints.resize(vertices.size());
297
for (const auto & vertex : vertices) {
298
meshPoints[vertex.i] = vertex.toPoint();
300
points.swap(meshPoints);
302
MergeVertex merge(points, faces, Precision::Confusion());
303
if (merge.hasDuplicatedPoints()) {
304
merge.mergeDuplicatedPoints();
305
points = merge.getPoints();
306
faces = merge.getFacets();
310
std::vector<BRepMesh::Segment> BRepMesh::createSegments() const
312
std::size_t numMeshFaces = 0;
313
std::vector<Segment> segm;
314
for (size_t numDomainFaces : domainSizes) {
315
Segment segment(numDomainFaces);
316
std::generate(segment.begin(),
318
Base::iotaGen<std::size_t>(numMeshFaces));
319
numMeshFaces += numDomainFaces;
320
segm.push_back(segment);