diff --git a/src/Mod/Mesh/App/Core/Builder.cpp b/src/Mod/Mesh/App/Core/Builder.cpp index 7dfd2acab6..ec7f82e5e3 100644 --- a/src/Mod/Mesh/App/Core/Builder.cpp +++ b/src/Mod/Mesh/App/Core/Builder.cpp @@ -32,6 +32,8 @@ #include "Builder.h" #include "MeshKernel.h" +#include "Functional.h" +#include 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 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& 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(), threads); + + QVector indices(ulCtPts); + + size_t vertex_count = 0; + for (QVector::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::iterator v = verts.begin(); v != verts.end(); ++v) { + rPoints.push_back(MeshPoint(v->x, v->y, v->z)); + } + + _meshKernel.Adopt(rPoints, rFacets, true); +} diff --git a/src/Mod/Mesh/App/Core/Builder.h b/src/Mod/Mesh/App/Core/Builder.h index ae663a8c02..e308b80693 100644 --- a/src/Mod/Mesh/App/Core/Builder.h +++ b/src/Mod/Mesh/App/Core/Builder.h @@ -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 diff --git a/src/Mod/Mesh/App/Core/Evaluation.cpp b/src/Mod/Mesh/App/Core/Evaluation.cpp index 56f77bc153..31562bbe91 100644 --- a/src/Mod/Mesh/App/Core/Evaluation.cpp +++ b/src/Mod/Mesh/App/Core/Evaluation.cpp @@ -39,6 +39,7 @@ #include "Helpers.h" #include "Grid.h" #include "TopoAlgorithm.h" +#include "Functional.h" #include #include @@ -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; diff --git a/src/Mod/Mesh/App/Core/Functional.h b/src/Mod/Mesh/App/Core/Functional.h new file mode 100644 index 0000000000..f2e5f80522 --- /dev/null +++ b/src/Mod/Mesh/App/Core/Functional.h @@ -0,0 +1,64 @@ +/*************************************************************************** + * Copyright (c) 2018 Werner Mayer * + * * + * 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 +#include +#include +#include + +namespace MeshCore +{ + template + 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 future = QtConcurrent::run(parallel_sort, begin, mid, comp, threads / 2); + std::sort(mid, end, comp); + future.waitForFinished(); + } + else + { + QFuture a = QtConcurrent::run(parallel_sort, begin, mid, comp, threads / 2); + QFuture b = QtConcurrent::run(parallel_sort, mid, end, comp, threads / 2); + a.waitForFinished(); + b.waitForFinished(); + } + std::inplace_merge(begin, mid, end, comp); + } + } + +} // namespace MeshCore + + +#endif // MESH_FUNCTIONAL_H diff --git a/src/Mod/Mesh/App/Core/MeshIO.cpp b/src/Mod/Mesh/App/Core/MeshIO.cpp index c4797808e7..30b25f3d72 100644 --- a/src/Mod/Mesh/App/Core/MeshIO.cpp +++ b/src/Mod/Mesh/App/Core/MeshIO.cpp @@ -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(meshPoints.size()); + i2 = std::atoi(what[2].first); + i2 = i2 > 0 ? i2-1 : i2+static_cast(meshPoints.size()); + i3 = std::atoi(what[3].first); + i3 = i3 > 0 ? i3-1 : i3+static_cast(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 " << 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 { diff --git a/src/Mod/Mesh/App/Core/MeshIO.h b/src/Mod/Mesh/App/Core/MeshIO.h index 582b00c5ad..b5d10d7d09 100644 --- a/src/Mod/Mesh/App/Core/MeshIO.h +++ b/src/Mod/Mesh/App/Core/MeshIO.h @@ -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. */ diff --git a/src/Mod/Mesh/App/Mesh.cpp b/src/Mod/Mesh/App/Mesh.cpp index f04f0ac44f..a941efde40 100644 --- a/src/Mod/Mesh/App/Mesh.cpp +++ b/src/Mod/Mesh/App/Mesh.cpp @@ -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 diff --git a/src/Mod/Mesh/App/MeshPyImp.cpp b/src/Mod/Mesh/App/MeshPyImp.cpp index 1a8f4a5bd5..dcdacee715 100644 --- a/src/Mod/Mesh/App/MeshPyImp.cpp +++ b/src/Mod/Mesh/App/MeshPyImp.cpp @@ -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; diff --git a/src/Mod/Mesh/Init.py b/src/Mod/Mesh/Init.py index 2b1476a9ca..0b9b0fcd8f 100644 --- a/src/Mod/Mesh/Init.py +++ b/src/Mod/Mesh/Init.py @@ -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")