FreeCAD / src /Mod /Mesh /App /Core /Segmentation.cpp
AbdulElahGwaith's picture
Upload folder using huggingface_hub
985c397 verified
// SPDX-License-Identifier: LGPL-2.1-or-later
/***************************************************************************
* Copyright (c) 2012 Imetric 3D GmbH *
* *
* This file is part of the FreeCAD CAx development system. *
* *
* This library is free software; you can redistribute it and/or *
* modify it under the terms of the GNU Library General Public *
* License as published by the Free Software Foundation; either *
* version 2 of the License, or (at your option) any later version. *
* *
* This library 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 Library General Public License for more details. *
* *
* You should have received a copy of the GNU Library General Public *
* License along with this library; see the file COPYING.LIB. If not, *
* write to the Free Software Foundation, Inc., 59 Temple Place, *
* Suite 330, Boston, MA 02111-1307, USA *
* *
***************************************************************************/
#include <algorithm>
#include <cmath>
#include <limits>
#include "Algorithm.h"
#include "Approximation.h"
#include "Segmentation.h"
using namespace MeshCore;
void MeshSurfaceSegment::Initialize(FacetIndex)
{}
bool MeshSurfaceSegment::TestInitialFacet(FacetIndex) const
{
return true;
}
void MeshSurfaceSegment::AddFacet(const MeshFacet&)
{}
void MeshSurfaceSegment::AddSegment(const std::vector<FacetIndex>& segm)
{
if (segm.size() >= minFacets) {
segments.push_back(segm);
}
}
MeshSegment MeshSurfaceSegment::FindSegment(FacetIndex index) const
{
for (const auto& segment : segments) {
if (std::ranges::find(segment, index) != segment.end()) {
return segment;
}
}
return {};
}
// --------------------------------------------------------
MeshDistancePlanarSegment::MeshDistancePlanarSegment(
const MeshKernel& mesh,
unsigned long minFacets,
float tol
)
: MeshDistanceSurfaceSegment(mesh, minFacets, tol)
, fitter(new PlaneFit)
{}
MeshDistancePlanarSegment::~MeshDistancePlanarSegment()
{
delete fitter;
}
void MeshDistancePlanarSegment::Initialize(FacetIndex index)
{
fitter->Clear();
MeshGeomFacet triangle = kernel.GetFacet(index);
basepoint = triangle.GetGravityPoint();
normal = triangle.GetNormal();
fitter->AddPoint(triangle._aclPoints[0]);
fitter->AddPoint(triangle._aclPoints[1]);
fitter->AddPoint(triangle._aclPoints[2]);
}
bool MeshDistancePlanarSegment::TestFacet(const MeshFacet& face) const
{
if (!fitter->Done()) {
fitter->Fit();
}
MeshGeomFacet triangle = kernel.GetFacet(face);
for (auto pnt : triangle._aclPoints) {
if (std::fabs(fitter->GetDistanceToPlane(pnt)) > tolerance) {
return false;
}
}
return true;
}
void MeshDistancePlanarSegment::AddFacet(const MeshFacet& face)
{
MeshGeomFacet triangle = kernel.GetFacet(face);
fitter->AddPoint(triangle.GetGravityPoint());
}
// --------------------------------------------------------
PlaneSurfaceFit::PlaneSurfaceFit()
: fitter(new PlaneFit)
{}
PlaneSurfaceFit::PlaneSurfaceFit(const Base::Vector3f& b, const Base::Vector3f& n)
: basepoint(b)
, normal(n)
, fitter(nullptr)
{}
PlaneSurfaceFit::~PlaneSurfaceFit()
{
delete fitter;
}
void PlaneSurfaceFit::Initialize(const MeshCore::MeshGeomFacet& tria)
{
if (fitter) {
basepoint = tria.GetGravityPoint();
normal = tria.GetNormal();
fitter->Clear();
fitter->AddPoint(tria._aclPoints[0]);
fitter->AddPoint(tria._aclPoints[1]);
fitter->AddPoint(tria._aclPoints[2]);
fitter->Fit();
}
}
bool PlaneSurfaceFit::TestTriangle(const MeshGeomFacet&) const
{
return true;
}
void PlaneSurfaceFit::AddTriangle(const MeshCore::MeshGeomFacet& tria)
{
if (fitter) {
fitter->AddPoint(tria.GetGravityPoint());
}
}
bool PlaneSurfaceFit::Done() const
{
if (!fitter) {
return true;
}
return fitter->Done();
}
float PlaneSurfaceFit::Fit()
{
if (!fitter) {
return 0;
}
return fitter->Fit();
}
float PlaneSurfaceFit::GetDistanceToSurface(const Base::Vector3f& pnt) const
{
if (!fitter) {
return pnt.DistanceToPlane(basepoint, normal);
}
return fitter->GetDistanceToPlane(pnt);
}
std::vector<float> PlaneSurfaceFit::Parameters() const
{
Base::Vector3f base = basepoint;
Base::Vector3f norm = normal;
if (fitter) {
base = fitter->GetBase();
norm = fitter->GetNormal();
}
std::vector<float> c;
c.push_back(base.x);
c.push_back(base.y);
c.push_back(base.z);
c.push_back(norm.x);
c.push_back(norm.y);
c.push_back(norm.z);
return c;
}
// --------------------------------------------------------
CylinderSurfaceFit::CylinderSurfaceFit()
: radius(std::numeric_limits<float>::max())
, fitter(new CylinderFit)
{
axis.Set(0, 0, 0);
}
/*!
* \brief CylinderSurfaceFit::CylinderSurfaceFit
* Set a predefined cylinder. Internal cylinder fits are not done, then.
*/
CylinderSurfaceFit::CylinderSurfaceFit(const Base::Vector3f& b, const Base::Vector3f& a, float r)
: basepoint(b)
, axis(a)
, radius(r)
, fitter(nullptr)
{}
CylinderSurfaceFit::~CylinderSurfaceFit()
{
delete fitter;
}
void CylinderSurfaceFit::Initialize(const MeshCore::MeshGeomFacet& tria)
{
if (fitter) {
fitter->Clear();
fitter->AddPoint(tria._aclPoints[0]);
fitter->AddPoint(tria._aclPoints[1]);
fitter->AddPoint(tria._aclPoints[2]);
}
}
void CylinderSurfaceFit::AddTriangle(const MeshCore::MeshGeomFacet& tria)
{
if (fitter) {
fitter->AddPoint(tria._aclPoints[0]);
fitter->AddPoint(tria._aclPoints[1]);
fitter->AddPoint(tria._aclPoints[2]);
}
}
bool CylinderSurfaceFit::TestTriangle(const MeshGeomFacet& tria) const
{
// This is to filter out triangles whose points lie on the cylinder and
// that whose normals are more or less parallel to the cylinder axis
float dot = axis.Dot(tria.GetNormal());
return std::fabs(dot) < 0.5F;
}
bool CylinderSurfaceFit::Done() const
{
if (fitter) {
return fitter->Done();
}
return true;
}
float CylinderSurfaceFit::Fit()
{
if (!fitter) {
return 0;
}
float fit = fitter->Fit();
if (fit < std::numeric_limits<float>::max()) {
basepoint = fitter->GetBase();
axis = fitter->GetAxis();
radius = fitter->GetRadius();
}
return fit;
}
float CylinderSurfaceFit::GetDistanceToSurface(const Base::Vector3f& pnt) const
{
if (fitter && !fitter->Done()) {
// collect some points
return 0;
}
float dist = pnt.DistanceToLine(basepoint, axis);
return (dist - radius);
}
std::vector<float> CylinderSurfaceFit::Parameters() const
{
Base::Vector3f base = basepoint;
Base::Vector3f norm = axis;
float radval = radius;
if (fitter) {
base = fitter->GetBase();
norm = fitter->GetAxis();
radval = fitter->GetRadius();
}
std::vector<float> c;
c.push_back(base.x);
c.push_back(base.y);
c.push_back(base.z);
c.push_back(norm.x);
c.push_back(norm.y);
c.push_back(norm.z);
c.push_back(radval);
return c;
}
// --------------------------------------------------------
SphereSurfaceFit::SphereSurfaceFit()
: radius(std::numeric_limits<float>::max())
, fitter(new SphereFit)
{
center.Set(0, 0, 0);
}
SphereSurfaceFit::SphereSurfaceFit(const Base::Vector3f& c, float r)
: center(c)
, radius(r)
, fitter(nullptr)
{}
SphereSurfaceFit::~SphereSurfaceFit()
{
delete fitter;
}
void SphereSurfaceFit::Initialize(const MeshCore::MeshGeomFacet& tria)
{
if (fitter) {
fitter->Clear();
fitter->AddPoint(tria._aclPoints[0]);
fitter->AddPoint(tria._aclPoints[1]);
fitter->AddPoint(tria._aclPoints[2]);
}
}
void SphereSurfaceFit::AddTriangle(const MeshCore::MeshGeomFacet& tria)
{
if (fitter) {
fitter->AddPoint(tria._aclPoints[0]);
fitter->AddPoint(tria._aclPoints[1]);
fitter->AddPoint(tria._aclPoints[2]);
}
}
bool SphereSurfaceFit::TestTriangle(const MeshGeomFacet&) const
{
// Already handled by GetDistanceToSurface
return true;
}
bool SphereSurfaceFit::Done() const
{
if (fitter) {
return fitter->Done();
}
return true;
}
float SphereSurfaceFit::Fit()
{
if (!fitter) {
return 0;
}
float fit = fitter->Fit();
if (fit < std::numeric_limits<float>::max()) {
center = fitter->GetCenter();
radius = fitter->GetRadius();
}
return fit;
}
float SphereSurfaceFit::GetDistanceToSurface(const Base::Vector3f& pnt) const
{
float dist = Base::Distance(pnt, center);
return (dist - radius);
}
std::vector<float> SphereSurfaceFit::Parameters() const
{
Base::Vector3f base = center;
float radval = radius;
if (fitter) {
base = fitter->GetCenter();
radval = fitter->GetRadius();
}
std::vector<float> c;
c.push_back(base.x);
c.push_back(base.y);
c.push_back(base.z);
c.push_back(radval);
return c;
}
// --------------------------------------------------------
MeshDistanceGenericSurfaceFitSegment::MeshDistanceGenericSurfaceFitSegment(
AbstractSurfaceFit* fit,
const MeshKernel& mesh,
unsigned long minFacets,
float tol
)
: MeshDistanceSurfaceSegment(mesh, minFacets, tol)
, fitter(fit)
{}
MeshDistanceGenericSurfaceFitSegment::~MeshDistanceGenericSurfaceFitSegment()
{
delete fitter;
}
void MeshDistanceGenericSurfaceFitSegment::Initialize(FacetIndex index)
{
MeshGeomFacet triangle = kernel.GetFacet(index);
fitter->Initialize(triangle);
}
bool MeshDistanceGenericSurfaceFitSegment::TestInitialFacet(FacetIndex index) const
{
MeshGeomFacet triangle = kernel.GetFacet(index);
for (auto pnt : triangle._aclPoints) {
if (std::fabs(fitter->GetDistanceToSurface(pnt)) > tolerance) {
return false;
}
}
return fitter->TestTriangle(triangle);
}
bool MeshDistanceGenericSurfaceFitSegment::TestFacet(const MeshFacet& face) const
{
if (!fitter->Done()) {
fitter->Fit();
}
MeshGeomFacet triangle = kernel.GetFacet(face);
for (auto ptIndex : triangle._aclPoints) {
if (std::fabs(fitter->GetDistanceToSurface(ptIndex)) > tolerance) {
return false;
}
}
return fitter->TestTriangle(triangle);
}
void MeshDistanceGenericSurfaceFitSegment::AddFacet(const MeshFacet& face)
{
MeshGeomFacet triangle = kernel.GetFacet(face);
fitter->AddTriangle(triangle);
}
std::vector<float> MeshDistanceGenericSurfaceFitSegment::Parameters() const
{
return fitter->Parameters();
}
// --------------------------------------------------------
bool MeshCurvaturePlanarSegment::TestFacet(const MeshFacet& rclFacet) const
{
for (PointIndex ptIndex : rclFacet._aulPoints) {
const CurvatureInfo& ci = GetInfo(ptIndex);
if (std::fabs(ci.fMinCurvature) > tolerance) {
return false;
}
if (std::fabs(ci.fMaxCurvature) > tolerance) {
return false;
}
}
return true;
}
bool MeshCurvatureCylindricalSegment::TestFacet(const MeshFacet& rclFacet) const
{
for (PointIndex ptIndex : rclFacet._aulPoints) {
const CurvatureInfo& ci = GetInfo(ptIndex);
float fMax = std::max<float>(std::fabs(ci.fMaxCurvature), std::fabs(ci.fMinCurvature));
float fMin = std::min<float>(std::fabs(ci.fMaxCurvature), std::fabs(ci.fMinCurvature));
if (fMin > toleranceMin) {
return false;
}
if (std::fabs(fMax - curvature) > toleranceMax) {
return false;
}
}
return true;
}
bool MeshCurvatureSphericalSegment::TestFacet(const MeshFacet& rclFacet) const
{
for (PointIndex ptIndex : rclFacet._aulPoints) {
const CurvatureInfo& ci = GetInfo(ptIndex);
if (ci.fMaxCurvature * ci.fMinCurvature < 0) {
return false;
}
float diff {};
diff = std::fabs(ci.fMinCurvature) - curvature;
if (std::fabs(diff) > tolerance) {
return false;
}
diff = std::fabs(ci.fMaxCurvature) - curvature;
if (std::fabs(diff) > tolerance) {
return false;
}
}
return true;
}
bool MeshCurvatureFreeformSegment::TestFacet(const MeshFacet& rclFacet) const
{
for (PointIndex ptIndex : rclFacet._aulPoints) {
const CurvatureInfo& ci = GetInfo(ptIndex);
if (std::fabs(ci.fMinCurvature - c2) > toleranceMin) {
return false;
}
if (std::fabs(ci.fMaxCurvature - c1) > toleranceMax) {
return false;
}
}
return true;
}
// --------------------------------------------------------
MeshSurfaceVisitor::MeshSurfaceVisitor(MeshSurfaceSegment& segm, std::vector<FacetIndex>& indices)
: indices(indices)
, segm(segm)
{}
bool MeshSurfaceVisitor::AllowVisit(
const MeshFacet& face,
const MeshFacet&,
FacetIndex,
unsigned long,
unsigned short
)
{
return segm.TestFacet(face);
}
bool MeshSurfaceVisitor::Visit(const MeshFacet& face, const MeshFacet&, FacetIndex ulFInd, unsigned long)
{
indices.push_back(ulFInd);
segm.AddFacet(face);
return true;
}
// --------------------------------------------------------
void MeshSegmentAlgorithm::FindSegments(std::vector<MeshSurfaceSegmentPtr>& segm)
{
// reset VISIT flags
FacetIndex startFacet {};
MeshCore::MeshAlgorithm cAlgo(myKernel);
cAlgo.ResetFacetFlag(MeshCore::MeshFacet::VISIT);
const MeshCore::MeshFacetArray& rFAry = myKernel.GetFacets();
MeshCore::MeshFacetArray::_TConstIterator iCur = rFAry.begin();
MeshCore::MeshFacetArray::_TConstIterator iBeg = rFAry.begin();
MeshCore::MeshFacetArray::_TConstIterator iEnd = rFAry.end();
// start from the first not visited facet
cAlgo.CountFacetFlag(MeshCore::MeshFacet::VISIT);
std::vector<FacetIndex> resetVisited;
for (auto& it : segm) {
cAlgo.ResetFacetsFlag(resetVisited, MeshCore::MeshFacet::VISIT);
resetVisited.clear();
MeshCore::MeshIsNotFlag<MeshCore::MeshFacet> flag;
iCur = std::find_if(iBeg, iEnd, [flag](const MeshFacet& f) {
return flag(f, MeshFacet::VISIT);
});
if (iCur < iEnd) {
startFacet = iCur - iBeg;
}
else {
startFacet = FACET_INDEX_MAX;
}
while (startFacet != FACET_INDEX_MAX) {
// collect all facets of the same geometry
std::vector<FacetIndex> indices;
it->Initialize(startFacet);
if (it->TestInitialFacet(startFacet)) {
indices.push_back(startFacet);
}
MeshSurfaceVisitor pv(*it, indices);
myKernel.VisitNeighbourFacets(pv, startFacet);
// add or discard the segment
if (indices.size() <= 1) {
resetVisited.push_back(startFacet);
}
else {
it->AddSegment(indices);
}
// search for the next start facet
iCur = std::find_if(iCur, iEnd, [flag](const MeshFacet& f) {
return flag(f, MeshFacet::VISIT);
});
if (iCur < iEnd) {
startFacet = iCur - iBeg;
}
else {
startFacet = FACET_INDEX_MAX;
}
}
}
}