MeshFix integration

This commit is contained in:
PavelMikus 2022-05-31 16:04:44 +02:00
parent d4dc4bb8f6
commit e3541bb4fe
18 changed files with 162 additions and 142 deletions

View File

@ -1,17 +1,3 @@
################################################################################
# General Informations
################################################################################
cmake_minimum_required(VERSION 3.0)
project(MeshFix)
################################################################################
if(NOT CMAKE_BUILD_TYPE)
message(STATUS "No build type selected, default to Release")
set(CMAKE_BUILD_TYPE "Release")
endif()
################################################################################
set(SOURCES

View File

@ -145,6 +145,12 @@ inline void p_swap(void **a, void **b) {void *t = *a; *a = *b; *b = t;}
/////////////////////////////////////////////////////////////////////////////////////////////
class Data {
public:
virtual ~Data() = default;
};
} //namespace T_MESH
#endif //_BASICS_H

View File

@ -39,7 +39,7 @@ namespace T_MESH
//! Base class type for nodes of non-oriented graphs
class graphNode
class graphNode : public Data
{
public:
@ -62,7 +62,7 @@ class graphNode
//! Base class type for edges of non-oriented graphs
class graphEdge
class graphEdge : public Data
{
public:

View File

@ -45,6 +45,6 @@
namespace T_MESH
{
extern void jqsort(void *v[], int numels, int (*comp)(const void *, const void *));
extern void jqsort(Data *v[], int numels, int (*comp)(const Data *, const Data *));
} //namespace T_MESH

View File

@ -32,6 +32,7 @@
#define _JLIST_H
#include <stdio.h>
#include "basics.h"
namespace T_MESH
{
@ -40,19 +41,18 @@ namespace T_MESH
//! Generic node of a doubly likned list.
class Node
{
friend class List; // This is to make methods in 'List' able to modify n_prev and n_next
public :
void *data; //!< Actual data stored in the node
Data *data; //!< Actual data stored in the node
//! Creates an isolated node storing 'd'
Node(const void *d) {data=(void *)d; n_prev=n_next=NULL;}
Node(const void *d) {data=(Data *)d; n_prev=n_next=NULL;}
//! Creates a new node storing 'd' and links it to a previous node 'p' and to a next one 'n'.
Node(const Node *p, const void *d, const Node *n);
Node(const Node *p, const Data *d, const Node *n);
~Node(); //!< Standard destructor
inline Node *prev() const {return n_prev;} //!< Returns the previous node in the list, possibly NULL
@ -68,7 +68,7 @@ class Node
//! Doubly linked list.
class List
class List : public Data
{
protected :
@ -82,10 +82,10 @@ class List
List() {l_head = l_tail = NULL; l_numels = 0;}
//! Creates a list containing an element 'd' (singleton)
List(const void *d) {l_head = l_tail = new Node(d); l_numels = 1;}
List(const Data *d) {l_head = l_tail = new Node(d); l_numels = 1;}
//! Creates a list out of an array 'd' made of 'n' elements.
List(const void **d, int n);
List(const Data **d, int n);
//! Creates a duplicated list.
List(List& l) {l_head = l_tail = NULL; l_numels = 0; appendList(&l);}
@ -100,12 +100,12 @@ class List
Node *tail() const {return l_tail;} //!< Gets the last node, NULL if empty. \n O(1).
int numels() const {return l_numels;} //!< Gets the number of elements. \n O(1).
void appendHead(const void *d); //!< Appends a new node storing 'd' to the head. \n O(1).
void appendTail(const void *d); //!< Appends a new node storing 'd' to the tail. \n O(1).
void insertAfter(Node *n, const void *d); //! Inserts a new node storing 'd' right after 'n'. \n O(1).
void appendHead(const Data *d); //!< Appends a new node storing 'd' to the head. \n O(1).
void appendTail(const Data *d); //!< Appends a new node storing 'd' to the tail. \n O(1).
void insertAfter(Node *n, const Data *d); //! Inserts a new node storing 'd' right after 'n'. \n O(1).
//! Deletes and removes the node containing 'd'. Returns its position, 0 if 'd' is not in the list. \n O(numels()).
int removeNode(const void *d);
int removeNode(const Data *d);
//! Deletes and i'th node (starting from 0). Returns 0 if the list has less than i+1 nodes. \n O(numels()).
int removeNode(int i);
@ -125,11 +125,12 @@ class List
//! Moves node 'n' from this list to the end of 'l'. \n O(1).
void moveNodeTo(Node *n, List *l);
void *popHead(); //!< Deletes and removes the first node. Returns its data. \n O(1).
void *popTail(); //!< Deletes and removes the last node. Returns its data. \n O(1).
Data *popHead(); //!< Deletes and removes the first node. Returns its data. \n O(1).
Data *popTail(); //!< Deletes and removes the last node. Returns its data. \n O(1).
//! Deletes and removes the node 'n' from the list and frees data memory. \n O(1).
// NOTE: The following is not true after refactoring, void* has been replaced with Data*
//! Warning. This method uses the free() function to to dispose the memory space
//! used by the data stored in the node. This means that such data should have
//! been allocated through malloc(), calloc() or realloc(), and not through the
@ -143,13 +144,13 @@ class List
//! Deletes and removes the node storing 'd' and frees the memory occupied by 'd' itself. \n O(numels()).
//! Warning. Read the comment for the method 'freeCell()'
void freeNode(void *d);
void freeNode(Data *d);
//! Returns the node storing 'd'. NULL if not found. \n O(numels()).
Node *containsNode(const void *d) const;
Node *containsNode(const Data *d) const;
//! Replaces old_n with new_n. The Node containing new_n is returned. \n O(numels()).
Node *replaceNode(const void *old_n, const void *new_n);
Node *replaceNode(const Data *old_n, const Data *new_n);
//! Deletes and removes all the nodes and frees data memory. \n O(numels()).
@ -158,7 +159,7 @@ class List
void removeNodes(); //!< Deletes and removes all the nodes. \n O(numels()).
void **toArray() const; //!< Creates an array out of the list. \n O(numels()).
Data **toArray() const; //!< Creates an array out of the list. \n O(numels()).
//! Sorts the list using 'comp' as comparison function for two elements. \n O(numels()^2).
@ -167,7 +168,7 @@ class List
//! the need to have a guaranteed O(NlogN) complexity, it is possible to implement a heap
//! based on the 'abstractHeap' class. See the documentation of the standard 'qsort' library
//! function for details on the prototype of the comparison function 'comp'.
int sort(int (*comp)(const void *, const void *));
int sort(int (*comp)(const Data *, const Data *));
};
//! Convenience macro to scan the nodes of a list.

View File

@ -68,7 +68,7 @@ PM_Rational orient2D(const PM_Rational& px, const PM_Rational& py, const PM_Rati
//! of our version of the epsilon geometry for robust computation.
class Point
class Point : public Data
{
public :
coord x,y,z; //!< Coordinates
@ -285,7 +285,7 @@ class Point
};
//! Lexycographic comparison to be used with jqsort() or abstractHeap.
int xyzCompare(const void *p1, const void *p2);
int xyzCompare(const Data *p1, const Data *p2);
//! Static point with DBL_MAX coordinates.
extern const Point INFINITE_POINT;

View File

@ -38,7 +38,7 @@ namespace T_MESH
#define DI_MAX_NUMBER_OF_CELLS 10000
#define DI_EPSILON_POINT Point(1.0e-9, 1.0e-9, 1.0e-9)
class di_cell
class di_cell : public Data
{
public:
Point mp, Mp;

View File

@ -48,7 +48,7 @@ namespace T_MESH
//! assigning up to 256 different states to the edge.
class Edge
class Edge : public Data
{
public :
@ -237,14 +237,14 @@ class Edge
};
//! Edge comparison based on length to be used with jqsort() or abstractHeap.
int edgeCompare(const void *a, const void *b);
int edgeCompare(const Data *a, const Data *b);
//! Lexycographic edge comparison to be used with jqsort() or abstractHeap.
int lexEdgeCompare(const void *a, const void *b);
int lexEdgeCompare(const Data *a, const Data *b);
//! Vertex-based edge comparison for qsort.
//! Duplicated edges are contiguous in this sorting.
int vtxEdgeCompare(const void *a, const void *b);
int vtxEdgeCompare(const Data *a, const Data *b);
} //namespace T_MESH

View File

@ -42,7 +42,7 @@ namespace T_MESH
//
///////////////////////////////////////////////////////////////
class mc_ints
class mc_ints : public Data
{
public:
@ -54,7 +54,7 @@ class mc_ints
mc_ints(coord a, unsigned char b, Triangle *s) { ic = a; sg = b; v = NULL; source = s; }
~mc_ints() { if (v) delete(v); }
static int compare(const void *e1, const void *e2);
static int compare(const Data *e1, const Data *e2);
};
@ -64,7 +64,7 @@ class mc_ints
//
///////////////////////////////////////////////////////////////
class mc_cell
class mc_cell : public Data
{
public:
int x,y,z; // Coordinates (i.e. cell's position)
@ -94,7 +94,7 @@ private:
//
///////////////////////////////////////////////////////////////
class mc_grid
class mc_grid : public Data
{
Point origin; // Origin for normalization
coord norm; // Normalization factor

View File

@ -104,6 +104,7 @@ class Basic_TMesh
Basic_TMesh(const Triangle *t, const bool keep_ref = false);
void init(const Triangle *t, const bool keep_ref = false);
//FIXED:
//! Destructor. Frees the memory allocated for all the mesh elements.
//! Warning! This method uses the freeNodes() method of the class List,
//! which is not guaranteed to work correctly on systems other than

View File

@ -46,7 +46,7 @@ namespace T_MESH
//! assigning up to 256 different states to the edge.
class Triangle
class Triangle : public Data
{
public :

View File

@ -196,7 +196,7 @@ Vertex *Basic_TMesh::checkGeometry()
if (varr == NULL) TMesh::warning("checkGeometry: Not enough memory. Can't check for coincident vertices.\n");
else
{
jqsort((void **)varr, V.numels(), xyzCompare);
jqsort((Data **)varr, V.numels(), xyzCompare);
for (i=0; i<(V.numels()-1); i++)
{
v1 = ((Vertex *)varr[i]);
@ -220,7 +220,7 @@ Vertex *Basic_TMesh::checkGeometry()
if (evarr == NULL) TMesh::warning("checkGeometry: Not enough memory. Can't check for coincident edges.\n");
else
{
jqsort((void **)evarr, E.numels(), lexEdgeCompare);
jqsort((Data **)evarr, E.numels(), lexEdgeCompare);
for (i=0; i<(E.numels()-1); i++)
{
if (!lexEdgeCompare(evarr[i], evarr[i+1]))
@ -377,7 +377,7 @@ bool Basic_TMesh::rebuildConnectivity(bool fixconnectivity) //!< AMF_CHANGE 1.1>
}
for (i=0; i<V.numels(); i++) delete(var[i]);
delete var;
delete [] var;
delete [] triangles;
if(fixconnectivity) return fixConnectivity();

View File

@ -33,7 +33,7 @@
namespace T_MESH
{
int mc_ints::compare(const void *e1, const void *e2)
int mc_ints::compare(const Data *e1, const Data *e2)
{
mc_ints *a = (mc_ints *)e1;
mc_ints *b = (mc_ints *)e2;

View File

@ -35,6 +35,8 @@
#include <algorithm>
#endif
#include "basics.h"
namespace T_MESH
{
@ -68,15 +70,15 @@ void jqsort(void *v[], int numels, int(*comp)(const void *, const void *))
class compobj
{
int(*comp)(const void *, const void *);
int(*comp)(const Data *, const Data *);
public:
compobj(int(*c)(const void *, const void *)) { comp = c; }
compobj(int(*c)(const Data *, const Data *)) { comp = c; }
bool operator()(void *a, void *b) { return (comp(a, b) < 0); }
bool operator()(Data *a, Data *b) { return (comp(a, b) < 0); }
};
void jqsort(void *v[], int numels, int(*comp)(const void *, const void *))
void jqsort(Data *v[], int numels, int(*comp)(const Data *, const Data *))
{
compobj a(comp);
std::sort(v, v + numels, a);

View File

@ -39,9 +39,9 @@ namespace T_MESH
// Create a new node containing 'd', and link it to //
// 'p' on the left (prev) and to 'n' on the right (next). //
Node::Node(const Node *p, const void *d, const Node *n)
Node::Node(const Node *p, const Data *d, const Node *n)
{
data=(void *)d;
data=(Data *)d;
if ((n_prev=(Node *)p) != NULL) n_prev->n_next = this;
if ((n_next=(Node *)n) != NULL) n_next->n_prev = this;
}
@ -58,7 +58,7 @@ Node::~Node()
/////////// Constructor from list ///////////////////
List::List(const void **d, int n)
List::List(const Data **d, int n)
{
l_head = l_tail = NULL; l_numels = 0;
for (int i=0; i<n; i++) appendTail(d[i]);
@ -73,21 +73,21 @@ List::~List()
////////////////// Append an element //////////////////
void List::appendHead(const void *d)
void List::appendHead(const Data *d)
{
l_head = new Node(NULL, d, l_head);
if (l_tail == NULL) l_tail = l_head;
l_numels++;
}
void List::appendTail(const void *d)
void List::appendTail(const Data *d)
{
l_tail = new Node(l_tail, d, NULL);
if (l_head == NULL) l_head = l_tail;
l_numels++;
}
void List::insertAfter(Node *b, const void *d)
void List::insertAfter(Node *b, const Data *d)
{
Node *nn = new Node(b, d, b->next());
if (b == l_tail) l_tail = nn;
@ -141,25 +141,25 @@ void List::moveNodeTo(Node *n, List *l)
//// Removes the first node and returns the corresponding data /////
void *List::popHead()
Data *List::popHead()
{
void *data = (l_head != NULL)?(l_head->data):(NULL);
Data *data = (l_head != NULL)?(l_head->data):(NULL);
if (l_head != NULL) removeCell(l_head);
return data;
}
//// Removes the last node and returns the corresponding data /////
void *List::popTail()
Data *List::popTail()
{
void *data = (l_tail != NULL)?(l_tail->data):(NULL);
Data *data = (l_tail != NULL)?(l_tail->data):(NULL);
if (l_tail != NULL) removeCell(l_tail);
return data;
}
//////////////////// Removes an element //////////////////
int List::removeNode(const void *d)
int List::removeNode(const Data *d)
{
Node *tmp = l_head;
int i=1;
@ -216,19 +216,19 @@ void List::removeCell(Node *n)
void List::freeCell(Node *n)
{
free(n->data);
delete(n->data);
removeCell(n);
}
void List::freeNode(void *d)
void List::freeNode(Data *d)
{
free(d);
delete(d);
removeNode(d);
}
//////////////////// Belonging check /////////////////
Node *List::containsNode(const void *d) const
Node *List::containsNode(const Data *d) const
{
Node *tmp = l_head;
@ -241,10 +241,10 @@ Node *List::containsNode(const void *d) const
//////////////////// Replaces a node /////////////////
Node *List::replaceNode(const void *od, const void *nd)
Node *List::replaceNode(const Data *od, const Data *nd)
{
Node *tmp = containsNode(od);
if (tmp != NULL) {tmp->data = (void *)nd; return tmp;}
if (tmp != NULL) { tmp->data = (Data *)nd; return tmp;}
appendTail(nd);
return l_tail;
}
@ -266,14 +266,14 @@ void List::removeNodes()
///// Conversion to array ///////
void **List::toArray() const
Data **List::toArray() const
{
Node *n = l_head;
int i;
void **array;
Data **array;
if (l_numels == 0) return NULL;
array = (void **)malloc(sizeof(void *)*l_numels);
array = (Data **)malloc(sizeof(Data *)*l_numels);
if (array == NULL) return NULL;
for (i=0; i<l_numels; i++, n=n->n_next) array[i] = n->data;
@ -282,9 +282,9 @@ void **List::toArray() const
///// Sorts the list /////////
int List::sort(int (*comp)(const void *, const void *))
int List::sort(int (*comp)(const Data *, const Data *))
{
void **array;
Data **array;
int ne = l_numels-1;
if (l_numels < 2) return 0;

View File

@ -235,7 +235,7 @@ bool Point::operator<(const Point& s) const
}
// This can be used with jqsort
int xyzCompare(const void *a, const void *b)
int xyzCompare(const Data *a, const Data *b)
{
coord c;

View File

@ -36,7 +36,7 @@ namespace T_MESH
//////// Length-based edge comparison for qsort //////////
int edgeCompare(const void *a, const void *b)
int edgeCompare(const Data *a, const Data *b)
{
coord la = ((Edge *)a)->squaredLength();
coord lb = ((Edge *)b)->squaredLength();
@ -50,7 +50,7 @@ int edgeCompare(const void *a, const void *b)
//////// Lexycographic edge comparison for qsort //////////
int lexEdgeCompare(const void *a, const void *b)
int lexEdgeCompare(const Data *a, const Data *b)
{
Vertex *va1 = ((Edge *)a)->v1;
Vertex *va2 = ((Edge *)a)->v2;
@ -70,7 +70,7 @@ int lexEdgeCompare(const void *a, const void *b)
//////// Vertex-based edge comparison for qsort //////////
int vtxEdgeCompare(const void *a, const void *b)
int vtxEdgeCompare(const Data *a, const Data *b)
{
Vertex *va1 = ((Edge *)a)->v1;
Vertex *va2 = ((Edge *)a)->v2;

View File

@ -33,6 +33,20 @@
namespace Slic3r {
class RepairCanceledException: public std::exception {
public:
const char* what() const throw () {
return "Model repair has been canceled";
}
};
class RepairFailedException: public std::exception {
public:
const char* what() const throw () {
return "Model repair has failed";
}
};
namespace detail {
using namespace T_MESH;
@ -158,6 +172,9 @@ public:
}
for (const auto &face : its.indices) {
if (face.x() == face.y() || face.y() == face.z() || face.z() == face.x()) {
continue;
}
this->CreateIndexedTriangle(tmp, face.x(), face.y(), face.z());
}
@ -188,15 +205,19 @@ public:
return out;
}
bool meshclean_single_iteration(int inner_loops) {
bool meshclean_single_iteration(int inner_loops, const std::atomic<bool> &canceled) {
bool ni, nd;
Triangle *t;
Node *m;
nd = strongDegeneracyRemoval(inner_loops);
if (canceled)
throw RepairCanceledException();
deselectTriangles();
invertSelection();
ni = strongIntersectionRemoval(inner_loops);
if (canceled)
throw RepairCanceledException();
if (ni && nd) {
FOREACHTRIANGLE(t, m)
if (t->isExactlyDegenerate())
@ -225,19 +246,10 @@ private:
}
class RepairCanceledException: public std::exception {
public:
const char* what() const throw () {
return "Model repair has been canceled";
}
};
bool fix_model_by_meshfix(ModelObject &model_object, int volume_idx, wxProgressDialog &progress_dlg,
const wxString &msg_header, std::string &fix_result) {
std::mutex mutex;
std::mutex mtx;
std::condition_variable condition;
std::unique_lock<std::mutex> lock(mutex);
struct Progress {
std::string message;
int percent = 0;
@ -256,8 +268,8 @@ bool fix_model_by_meshfix(ModelObject &model_object, int volume_idx, wxProgressD
// (It seems like wxWidgets initialize the COM contex as single threaded and we need a multi-threaded context).
bool success = false;
size_t ivolume = 0;
auto on_progress = [&mutex, &condition, &ivolume, &volumes, &progress](const char *msg, unsigned prcnt) {
std::lock_guard<std::mutex> lk(mutex);
auto on_progress = [&mtx, &condition, &ivolume, &volumes, &progress](const char *msg, unsigned prcnt) {
std::unique_lock<std::mutex> lock(mtx);
progress.message = msg;
progress.percent = (int) floor((float(prcnt) + float(ivolume) * 100.f) / float(volumes.size()));
progress.updated = true;
@ -269,19 +281,21 @@ bool fix_model_by_meshfix(ModelObject &model_object, int volume_idx, wxProgressD
std::vector<TriangleMesh> meshes_repaired;
meshes_repaired.reserve(volumes.size());
for (ModelVolume *mv : volumes) {
std::vector<indexed_triangle_set> parts = its_split(mv->mesh().its);
for (size_t part_idx = 0; part_idx < parts.size(); ++part_idx) {
detail::Basic_TMesh_Adapter tin { };
on_progress(L("Loading source model"), 0);
if (canceled)
throw RepairCanceledException();
tin.load_indexed_triangle_set(mv->mesh().its);
tin.load_indexed_triangle_set(parts[part_idx]);
tin.boundaries();
on_progress(L("Join closest components"), 10);
if (canceled)
throw RepairCanceledException();
joinClosestComponents(&tin);
tin.deselectTriangles();
tin.boundaries();
// Keep only the largest component (i.e. with most triangles)
on_progress(L("Remove smallest components"), 20);
if (canceled)
@ -289,7 +303,7 @@ bool fix_model_by_meshfix(ModelObject &model_object, int volume_idx, wxProgressD
tin.removeSmallestComponents();
// Fill holes
on_progress(L("Fill holes"), 30);
on_progress(L("Check holes"), 30);
if (canceled)
throw RepairCanceledException();
if (tin.boundaries()) {
@ -310,7 +324,7 @@ bool fix_model_by_meshfix(ModelObject &model_object, int volume_idx, wxProgressD
tin.invertSelection();
bool fixed = false;
while (iteration < 10 && !fixed) { //default constants taken from TMesh library
fixed = tin.meshclean_single_iteration(3);
fixed = tin.meshclean_single_iteration(3, canceled);
on_progress(L("Fixing geometry"), std::min(95, 60 + iteration * 8)); // majority of objects should finish in 4 iterations
if (canceled)
throw RepairCanceledException();
@ -319,12 +333,18 @@ bool fix_model_by_meshfix(ModelObject &model_object, int volume_idx, wxProgressD
}
if (tin.boundaries() || tin.T.numels() == 0) {
meshes_repaired.emplace_back(std::move(mv->mesh().its));
throw Slic3r::RuntimeError(L("Model repair failed"));
throw RepairFailedException();
}
parts[part_idx] = tin.to_indexed_triangle_set();
}
meshes_repaired.emplace_back(std::move(tin.to_indexed_triangle_set()));
for (size_t part_idx = 1; part_idx < parts.size(); ++part_idx) {
its_merge(parts[0], parts[part_idx]);
}
meshes_repaired.emplace_back(std::move(parts[0]));
}
for (size_t i = 0; i < volumes.size(); ++i) {
volumes[i]->set_mesh(std::move(meshes_repaired[i]));
volumes[i]->calculate_convex_hull();
@ -345,7 +365,9 @@ bool fix_model_by_meshfix(ModelObject &model_object, int volume_idx, wxProgressD
on_progress(ex.what(), 100);
}
});
while (!finished) {
std::unique_lock<std::mutex> lock(mtx);
condition.wait_for(lock, std::chrono::milliseconds(250), [&progress] {
return progress.updated;
});
@ -357,6 +379,8 @@ bool fix_model_by_meshfix(ModelObject &model_object, int volume_idx, wxProgressD
progress.updated = false;
}
worker_thread.join();
if (canceled) {
// Nothing to show.
} else if (success) {
@ -364,7 +388,7 @@ bool fix_model_by_meshfix(ModelObject &model_object, int volume_idx, wxProgressD
} else {
fix_result = progress.message;
}
worker_thread.join();
return !canceled;
}