Mesh improvements:

+ support of Simple Model Format (smf)
+ improve reading of STL files (reduce by ~70%)
This commit is contained in:
wmayer
2018-07-26 13:19:35 +02:00
parent 98af9aabd9
commit 6cc1524a0d
9 changed files with 363 additions and 2 deletions

View File

@@ -32,6 +32,8 @@
#include "Builder.h"
#include "MeshKernel.h"
#include "Functional.h"
#include <QVector>
using namespace MeshCore;
@@ -253,3 +255,108 @@ void MeshBuilder::Finish (bool freeMemory)
_meshKernel.RecalcBoundBox();
}
// ----------------------------------------------------------------------------
struct MeshFastBuilder::Private {
struct Vertex
{
Vertex() : x(0), y(0), z(0), i(0) {}
Vertex(float x, float y, float z) : x(x), y(y), z(z), i(0) {}
float x, y, z;
size_t i;
bool operator!=(const Vertex& rhs) const
{
return x != rhs.x || y != rhs.y || z != rhs.z;
}
bool operator<(const Vertex& rhs) const
{
if (x != rhs.x) return x < rhs.x;
else if (y != rhs.y) return y < rhs.y;
else if (z != rhs.z) return z < rhs.z;
else return false;
}
};
// Hint: Using a QVector instead of std::vector is a bit faster
QVector<Vertex> verts;
};
MeshFastBuilder::MeshFastBuilder(MeshKernel &rclM) : _meshKernel(rclM), p(new Private)
{
}
MeshFastBuilder::~MeshFastBuilder(void)
{
delete p;
}
void MeshFastBuilder::Initialize (unsigned long ctFacets)
{
p->verts.reserve(ctFacets * 3);
}
void MeshFastBuilder::AddFacet (const Base::Vector3f* facetPoints)
{
Private::Vertex v;
for (int i=0; i<3; i++) {
v.x = facetPoints[i].x;
v.y = facetPoints[i].y;
v.z = facetPoints[i].z;
p->verts.push_back(v);
}
}
void MeshFastBuilder::AddFacet (const MeshGeomFacet& facetPoints)
{
Private::Vertex v;
for (int i=0; i<3; i++) {
v.x = facetPoints._aclPoints[i].x;
v.y = facetPoints._aclPoints[i].y;
v.z = facetPoints._aclPoints[i].z;
p->verts.push_back(v);
}
}
void MeshFastBuilder::Finish ()
{
QVector<Private::Vertex>& verts = p->verts;
size_t ulCtPts = verts.size();
for (size_t i=0; i < ulCtPts; ++i) {
verts[i].i = i;
}
//std::sort(verts.begin(), verts.end());
int threads = std::max(1, QThread::idealThreadCount());
MeshCore::parallel_sort(verts.begin(), verts.end(), std::less<Private::Vertex>(), threads);
QVector<unsigned long> indices(ulCtPts);
size_t vertex_count = 0;
for (QVector<Private::Vertex>::iterator v = verts.begin(); v != verts.end(); ++v) {
if (!vertex_count || *v != verts[vertex_count-1])
verts[vertex_count++] = *v;
indices[v->i] = vertex_count - 1;
}
size_t ulCt = verts.size()/3;
MeshFacetArray rFacets(ulCt);
for (size_t i=0; i < ulCt; ++i) {
rFacets[i]._aulPoints[0] = indices[3*i];
rFacets[i]._aulPoints[1] = indices[3*i + 1];
rFacets[i]._aulPoints[2] = indices[3*i + 2];
}
verts.resize(vertex_count);
MeshPointArray rPoints;
rPoints.reserve(vertex_count);
for (QVector<Private::Vertex>::iterator v = verts.begin(); v != verts.end(); ++v) {
rPoints.push_back(MeshPoint(v->x, v->y, v->z));
}
_meshKernel.Adopt(rPoints, rFacets, true);
}

View File

@@ -164,6 +164,52 @@ private:
float _fSaveTolerance;
};
/**
* Class for creating the mesh structure by adding facets. Building the structure needs 3 steps:
* 1. initializing
* 2. adding the facets
* 3. finishing
* \code
* // Sample Code for building a mesh structure
* MeshFastBuilder builder(someMeshReference);
* builder.Initialize(numberOfFacets);
* ...
* for (...)
* builder.AddFacet(...);
* ...
* builder.Finish();
* \endcode
* @author Werner Mayer
*/
class MeshExport MeshFastBuilder
{
private:
MeshKernel& _meshKernel;
public:
MeshFastBuilder(MeshKernel &rclM);
~MeshFastBuilder(void);
/** Initializes the class. Must be done before adding facets
* @param ctFacets count of facets.
*/
void Initialize (unsigned long ctFacets);
/** Add new facet
*/
void AddFacet (const Base::Vector3f* facetPoints);
/** Add new facet
*/
void AddFacet (const MeshGeomFacet& facetPoints);
/** Finishes building up the mesh structure. Must be done after adding facets.
*/
void Finish ();
private:
struct Private;
Private* p;
};
} // namespace MeshCore
#endif

View File

@@ -39,6 +39,7 @@
#include "Helpers.h"
#include "Grid.h"
#include "TopoAlgorithm.h"
#include "Functional.h"
#include <Base/Matrix.h>
#include <Base/Sequencer.h>
@@ -966,7 +967,9 @@ void MeshKernel::RebuildNeighbours (unsigned long index)
}
// sort the edges
std::sort(edges.begin(), edges.end(), Edge_Less());
//std::sort(edges.begin(), edges.end(), Edge_Less());
int threads = std::max(1, QThread::idealThreadCount());
MeshCore::parallel_sort(edges.begin(), edges.end(), Edge_Less(), threads);
unsigned long p0 = ULONG_MAX, p1 = ULONG_MAX;
unsigned long f0 = ULONG_MAX, f1 = ULONG_MAX;

View File

@@ -0,0 +1,64 @@
/***************************************************************************
* Copyright (c) 2018 Werner Mayer <wmayer[at]users.sourceforge.net> *
* *
* 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 *
* *
***************************************************************************/
#ifndef MESH_FUNCTIONAL_H
#define MESH_FUNCTIONAL_H
#include <algorithm>
#include <QtConcurrentRun>
#include <QFuture>
#include <QThread>
namespace MeshCore
{
template <class Iter, class Pred>
static void parallel_sort(Iter begin, Iter end, Pred comp, int threads)
{
if (threads < 2 || end - begin < 2)
{
std::sort(begin, end, comp);
}
else
{
Iter mid = begin + (end - begin) / 2;
if (threads == 2)
{
QFuture<void> future = QtConcurrent::run(parallel_sort<Iter, Pred>, begin, mid, comp, threads / 2);
std::sort(mid, end, comp);
future.waitForFinished();
}
else
{
QFuture<void> a = QtConcurrent::run(parallel_sort<Iter, Pred>, begin, mid, comp, threads / 2);
QFuture<void> b = QtConcurrent::run(parallel_sort<Iter, Pred>, mid, end, comp, threads / 2);
a.waitForFinished();
b.waitForFinished();
}
std::inplace_merge(begin, mid, end, comp);
}
}
} // namespace MeshCore
#endif // MESH_FUNCTIONAL_H

View File

@@ -171,6 +171,9 @@ bool MeshInput::LoadAny(const char* FileName)
else if (fi.hasExtension("obj")) {
ok = LoadOBJ( str );
}
else if (fi.hasExtension("smf")) {
ok = LoadSMF( str );
}
else if (fi.hasExtension("off")) {
ok = LoadOFF( str );
}
@@ -200,6 +203,8 @@ bool MeshInput::LoadFormat(std::istream &str, MeshIO::Format fmt)
return LoadBinarySTL(str);
case MeshIO::OBJ:
return LoadOBJ(str);
case MeshIO::SMF:
return LoadSMF(str);
case MeshIO::OFF:
return LoadOFF(str);
case MeshIO::IV:
@@ -444,6 +449,65 @@ bool MeshInput::LoadOBJ (std::istream &rstrIn)
return true;
}
/** Loads an SMF file. */
bool MeshInput::LoadSMF (std::istream &rstrIn)
{
boost::regex rx_p("^v\\s+([-+]?[0-9]*)\\.?([0-9]+([eE][-+]?[0-9]+)?)"
"\\s+([-+]?[0-9]*)\\.?([0-9]+([eE][-+]?[0-9]+)?)"
"\\s+([-+]?[0-9]*)\\.?([0-9]+([eE][-+]?[0-9]+)?)\\s*$");
boost::regex rx_f3("^f\\s+([-+]?[0-9]+)"
"\\s+([-+]?[0-9]+)"
"\\s+([-+]?[0-9]+)\\s*$");
boost::cmatch what;
unsigned long segment=0;
MeshPointArray meshPoints;
MeshFacetArray meshFacets;
std::string line;
float fX, fY, fZ;
int i1=1,i2=1,i3=1;
MeshFacet item;
if (!rstrIn || rstrIn.bad() == true)
return false;
std::streambuf* buf = rstrIn.rdbuf();
if (!buf)
return false;
while (std::getline(rstrIn, line)) {
if (boost::regex_match(line.c_str(), what, rx_p)) {
fX = (float)std::atof(what[1].first);
fY = (float)std::atof(what[4].first);
fZ = (float)std::atof(what[7].first);
meshPoints.push_back(MeshPoint(Base::Vector3f(fX, fY, fZ)));
}
else if (boost::regex_match(line.c_str(), what, rx_f3)) {
// 3-vertex face
i1 = std::atoi(what[1].first);
i1 = i1 > 0 ? i1-1 : i1+static_cast<int>(meshPoints.size());
i2 = std::atoi(what[2].first);
i2 = i2 > 0 ? i2-1 : i2+static_cast<int>(meshPoints.size());
i3 = std::atoi(what[3].first);
i3 = i3 > 0 ? i3-1 : i3+static_cast<int>(meshPoints.size());
item.SetVertices(i1,i2,i3);
item.SetProperty(segment);
meshFacets.push_back(item);
}
}
this->_rclMesh.Clear(); // remove all data before
MeshCleanup meshCleanup(meshPoints,meshFacets);
meshCleanup.RemoveInvalids();
MeshPointFacetAdjacency meshAdj(meshPoints.size(),meshFacets);
meshAdj.SetFacetNeighbourhood();
this->_rclMesh.Adopt(meshPoints,meshFacets);
return true;
}
/** Loads an OFF file. */
bool MeshInput::LoadOFF (std::istream &rstrIn)
{
@@ -1164,7 +1228,11 @@ bool MeshInput::LoadAsciiSTL (std::istream &rstrIn)
// restart from the beginning
buf->pubseekoff(0, std::ios::beg, std::ios::in);
#if 0
MeshBuilder builder(this->_rclMesh);
#else
MeshFastBuilder builder(this->_rclMesh);
#endif
builder.Initialize(ulFacetCt);
ulVertexCt = 0;
@@ -1229,7 +1297,11 @@ bool MeshInput::LoadBinarySTL (std::istream &rstrIn)
if (ulCt > ulFac)
return false;// not a valid STL file
#if 0
MeshBuilder builder(this->_rclMesh);
#else
MeshFastBuilder builder(this->_rclMesh);
#endif
builder.Initialize(ulCt);
for (uint32_t i = 0; i < ulCt; i++) {
@@ -1637,6 +1709,9 @@ MeshIO::Format MeshOutput::GetFormat(const char* FileName)
else if (file.hasExtension("amf")) {
return MeshIO::AMF;
}
else if (file.hasExtension("smf")) {
return MeshIO::SMF;
}
else {
return MeshIO::Undefined;
}
@@ -1688,6 +1763,11 @@ bool MeshOutput::SaveAny(const char* FileName, MeshIO::Format format) const
if (!SaveOBJ(str))
throw Base::FileException("Export of OBJ mesh failed",FileName);
}
else if (fileformat == MeshIO::SMF) {
// write file
if (!SaveSMF(str))
throw Base::FileException("Export of SMF mesh failed",FileName);
}
else if (fileformat == MeshIO::OFF) {
// write file
if (!SaveOFF(str))
@@ -1769,6 +1849,8 @@ bool MeshOutput::SaveFormat(std::ostream &str, MeshIO::Format fmt) const
return SaveBinarySTL(str);
case MeshIO::OBJ:
return SaveOBJ(str);
case MeshIO::SMF:
return SaveSMF(str);
case MeshIO::OFF:
return SaveOFF(str);
case MeshIO::IDTF:
@@ -2114,6 +2196,54 @@ bool MeshOutput::SaveMTL(std::ostream &out) const
return false;
}
/** Saves an SMF file. */
bool MeshOutput::SaveSMF (std::ostream &out) const
{
// http://people.sc.fsu.edu/~jburkardt/data/smf/smf.txt
const MeshPointArray& rPoints = _rclMesh.GetPoints();
const MeshFacetArray& rFacets = _rclMesh.GetFacets();
if (!out || out.bad() == true)
return false;
Base::SequencerLauncher seq("saving...", _rclMesh.CountPoints() + _rclMesh.CountFacets());
// Header
out << "#$SMF 1.0" << std::endl;
out << "#$vertices " << rPoints.size() << std::endl;
out << "#$faces " << rFacets.size() << std::endl;
out << "#" << std::endl;
out << "# Created by FreeCAD <http://www.freecadweb.org>" << std::endl;
out.precision(6);
out.setf(std::ios::fixed | std::ios::showpoint);
// vertices
Base::Vector3f pt;
std::size_t index = 0;
for (MeshPointArray::_TConstIterator it = rPoints.begin(); it != rPoints.end(); ++it, ++index) {
if (this->apply_transform) {
pt = this->_transform * *it;
}
else {
pt.Set(it->x, it->y, it->z);
}
out << "v " << pt.x << " " << pt.y << " " << pt.z << std::endl;
seq.next(true); // allow to cancel
}
// facet indices
for (MeshFacetArray::_TConstIterator it = rFacets.begin(); it != rFacets.end(); ++it) {
out << "f " << it->_aulPoints[0]+1 << " "
<< it->_aulPoints[1]+1 << " "
<< it->_aulPoints[2]+1 << std::endl;
seq.next(true); // allow to cancel
}
return true;
}
/** Saves an OFF file. */
bool MeshOutput::SaveOFF (std::ostream &out) const
{

View File

@@ -56,7 +56,8 @@ namespace MeshIO {
PLY,
APLY,
PY,
AMF
AMF,
SMF
};
enum Binding {
OVERALL,
@@ -109,6 +110,8 @@ public:
bool LoadBinarySTL (std::istream &rstrIn);
/** Loads an OBJ Mesh file. */
bool LoadOBJ (std::istream &rstrIn);
/** Loads an SMF Mesh file. */
bool LoadSMF (std::istream &rstrIn);
/** Loads an OFF Mesh file. */
bool LoadOFF (std::istream &rstrIn);
/** Loads a PLY Mesh file. */
@@ -170,6 +173,8 @@ public:
bool SaveOBJ (std::ostream &rstrOut) const;
/** Saves the materials of an OBJ file. */
bool SaveMTL(std::ostream &rstrOut) const;
/** Saves the mesh object into an SMF file. */
bool SaveSMF (std::ostream &rstrOut) const;
/** Saves the mesh object into an OFF file. */
bool SaveOFF (std::ostream &rstrOut) const;
/** Saves the mesh object into a binary PLY file. */

View File

@@ -458,6 +458,7 @@ void MeshObject::swapKernel(MeshCore::MeshKernel& kernel,
}
}
#if 0
#ifndef FC_DEBUG
try {
MeshCore::MeshEvalNeighbourhood nb(_kernel);
@@ -477,6 +478,7 @@ void MeshObject::swapKernel(MeshCore::MeshKernel& kernel,
Base::Console().Log("Check for defects in mesh data structure failed\n");
}
#endif
#endif
}
void MeshObject::save(std::ostream& out) const

View File

@@ -164,6 +164,7 @@ PyObject* MeshPy::read(PyObject *args, PyObject *kwds)
ext["STL" ] = MeshCore::MeshIO::BSTL;
ext["AST" ] = MeshCore::MeshIO::ASTL;
ext["OBJ" ] = MeshCore::MeshIO::OBJ;
ext["SMF" ] = MeshCore::MeshIO::SMF;
ext["OFF" ] = MeshCore::MeshIO::OFF;
ext["IV" ] = MeshCore::MeshIO::IV;
ext["X3D" ] = MeshCore::MeshIO::X3D;
@@ -212,6 +213,7 @@ PyObject* MeshPy::write(PyObject *args, PyObject *kwds)
ext["STL" ] = MeshCore::MeshIO::BSTL;
ext["AST" ] = MeshCore::MeshIO::ASTL;
ext["OBJ" ] = MeshCore::MeshIO::OBJ;
ext["SMF" ] = MeshCore::MeshIO::SMF;
ext["OFF" ] = MeshCore::MeshIO::OFF;
ext["IDTF"] = MeshCore::MeshIO::IDTF;
ext["MGL" ] = MeshCore::MeshIO::MGL;

View File

@@ -7,6 +7,7 @@ FreeCAD.addImportType("Binary Mesh (*.bms)","Mesh")
FreeCAD.addImportType("Alias Mesh (*.obj)","Mesh")
FreeCAD.addImportType("Object File Format Mesh (*.off)","Mesh")
FreeCAD.addImportType("Stanford Triangle Mesh (*.ply)","Mesh")
FreeCAD.addImportType("Simple Model Format (*.smf)","Mesh")
FreeCAD.addExportType("STL Mesh (*.stl *.ast)", "Mesh")
FreeCAD.addExportType("Binary Mesh (*.bms)","Mesh")
@@ -14,3 +15,4 @@ FreeCAD.addExportType("Alias Mesh (*.obj)","Mesh")
FreeCAD.addExportType("Object File Format Mesh (*.off)","Mesh")
FreeCAD.addExportType("Stanford Triangle Mesh (*.ply)","Mesh")
FreeCAD.addExportType("Additive Manufacturing Format (*.amf)","Mesh")
FreeCAD.addExportType("Simple Model Format (*.smf)","Mesh")