add btFileUtils::toLower to convert a string (char*) to lower case

URDF import demo: add COLLADA .dae file support
add FiniteElementMethod demo, extracted from the OpenTissue library (under the zlib license)
don't crash if loading an invalid STL file
add comparison with Assimp for COLLADA file loading (disabled by default, to avoid library dependency)
Gwen: disable some flags that make the build incompatible
This commit is contained in:
erwin coumans
2014-10-29 13:18:34 -07:00
parent aaaf8dc4e2
commit 4b665fa82b
139 changed files with 17704 additions and 46 deletions

View File

@@ -0,0 +1,74 @@
#ifndef OPENTISSUE_CORE_CONTAINERS_T4MESH_T4MESH_CORE_ACCESS_H
#define OPENTISSUE_CORE_CONTAINERS_T4MESH_T4MESH_CORE_ACCESS_H
//
// OpenTissue Template Library
// - A generic toolbox for physics-based modeling and simulation.
// Copyright (C) 2008 Department of Computer Science, University of Copenhagen.
//
// OTTL is licensed under zlib: http://opensource.org/licenses/zlib-license.php
//
#include <OpenTissue/configuration.h>
namespace OpenTissue
{
namespace t4mesh
{
class t4mesh_core_access
{
public:
template< typename feature_type, typename index_type>
static void set_index(feature_type & f, index_type idx)
{
f.set_index(idx);
}
template< typename feature_type, typename mesh_type>
static void set_owner(feature_type & f, mesh_type * owner)
{
f.set_owner(owner);
}
template< typename tetrahedron_type, typename index_type>
static void set_node0(tetrahedron_type & tetrahedron, index_type idx)
{
tetrahedron.set_node0(idx);
}
template< typename tetrahedron_type, typename index_type>
static void set_node1(tetrahedron_type & tetrahedron, index_type idx)
{
tetrahedron.set_node1(idx);
}
template< typename tetrahedron_type, typename index_type>
static void set_node2(tetrahedron_type & tetrahedron, index_type idx)
{
tetrahedron.set_node2(idx);
}
template< typename tetrahedron_type, typename index_type>
static void set_node3(tetrahedron_type & tetrahedron, index_type idx)
{
tetrahedron.set_node3(idx);
}
template< typename node_type, typename index_type>
static void tetrahedra_push_back(node_type & node, index_type idx)
{
node.tetrahedra_push_back(idx);
}
template< typename node_type, typename index_type>
static void tetrahedra_remove(node_type & node, index_type idx)
{
node.tetrahedra_remove(idx);
}
};
} // namespace trimesh
} // namespace OpenTissue
//OPENTISSUE_CORE_CONTAINERS_T4MESH_T4MESH_CORE_ACCESS_H
#endif

View File

@@ -0,0 +1,70 @@
#ifndef OPENTISSUE_CORE_CONTAINERS_T4MESH_T4MESH_DEFAULT_POINT_CONTAINER_H
#define OPENTISSUE_CORE_CONTAINERS_T4MESH_T4MESH_DEFAULT_POINT_CONTAINER_H
//
// OpenTissue Template Library
// - A generic toolbox for physics-based modeling and simulation.
// Copyright (C) 2008 Department of Computer Science, University of Copenhagen.
//
// OTTL is licensed under zlib: http://opensource.org/licenses/zlib-license.php
//
#include <OpenTissue/configuration.h>
namespace OpenTissue
{
namespace t4mesh
{
/**
* Default Point Container.
* This utility class can be used to make the coordniates of the nodes in
* a tetrahedra mesh appear as a point container, i.e. as though the coordinates
* are stored as
*
* std::vector<vector3_type> coordinates;
*
* and accesses as
*
* coordinates[node->idx()]
*
* instead of
*
* node->m_coord
*
* Many algoritms in OpenTissue have been implemented in such a way that they
* do not rely on nodes to have a m_coord member. Instead coordinates are passed
* as point containers. This utility make it convenient to use these algorithms
* on nodes where coordinates are stored in m_coord member.
*
*/
template<typename M>
struct default_point_container
{
typedef M mesh_type;
typedef typename mesh_type::math_types math_types;
typedef typename math_types::vector3_type value_type;
mesh_type * m_mesh;
default_point_container(mesh_type * mesh) : m_mesh(mesh) {}
value_type & operator[] (unsigned int const & idx)
{
return m_mesh->node(idx)->m_coord;
}
value_type const & operator[] (unsigned int const & idx)const
{
return m_mesh->node(idx)->m_coord;
}
void clear(){}
size_t size() const {return m_mesh->size_nodes();}
void resize(size_t){}
};
} // namespace t4mesh
} // namespace OpenTissue
//OPENTISSUE_CORE_CONTAINERS_T4MESH_T4MESH_DEFAULT_POINT_CONTAINER_H
#endif

View File

@@ -0,0 +1,38 @@
#ifndef OPENTISSUE_CORE_CONTAINERS_T4MESH_DEFAULT_TRAITS_H
#define OPENTISSUE_CORE_CONTAINERS_T4MESH_DEFAULT_TRAITS_H
//
// OpenTissue Template Library
// - A generic toolbox for physics-based modeling and simulation.
// Copyright (C) 2008 Department of Computer Science, University of Copenhagen.
//
// OTTL is licensed under zlib: http://opensource.org/licenses/zlib-license.php
//
#include <OpenTissue/configuration.h>
namespace OpenTissue
{
namespace t4mesh
{
template<typename math_types>
class DefaultNodeTraits
{
public:
typedef typename math_types::vector3_type vector3_type;
typedef typename math_types::real_type real_type;
vector3_type m_coord; ///< Default Coordinate of tetramesh node.
};
class DefaultTetrahedronTraits { };
class DefaultT4EdgeTraits { };
class DefaultT4FaceTraits { };
} // namespace t4mesh
} // namespace OpenTissue
//OPENTISSUE_CORE_CONTAINERS_T4MESH_DEFAULT_TRAITS_H
#endif

View File

@@ -0,0 +1,153 @@
#ifndef OPENTISSUE_CORE_CONTAINERS_T4MESH_T4BOUNDARY_FACES_H
#define OPENTISSUE_CORE_CONTAINERS_T4MESH_T4BOUNDARY_FACES_H
//
// OpenTissue Template Library
// - A generic toolbox for physics-based modeling and simulation.
// Copyright (C) 2008 Department of Computer Science, University of Copenhagen.
//
// OTTL is licensed under zlib: http://opensource.org/licenses/zlib-license.php
//
#include <OpenTissue/configuration.h>
#include <list>
#include <cassert>
namespace OpenTissue
{
namespace t4mesh
{
/**
* t4mesh Face.
*/
template<typename M, typename F>
class T4Face : public F
{
public:
typedef M mesh_type;
typedef F face_traits;
typedef T4Face<M,F> face_type;
typedef typename mesh_type::index_type index_type;
protected:
index_type m_idx0; ///< First node index.
index_type m_idx1; ///< Second node index.
index_type m_idx2; ///< Third node index.
public:
T4Face()
: m_idx0(-1)
, m_idx1(-1)
, m_idx2(-1)
{}
T4Face(index_type const & index0, index_type const & index1, index_type const & index2)
: m_idx0(index0)
, m_idx1(index1)
, m_idx2(index2)
{}
public:
const index_type & idx0() const { return m_idx0; }
const index_type & idx1() const { return m_idx1; }
const index_type & idx2() const { return m_idx2; }
};
/**
* Tetrahedra Mesh Boundary Faces.
* Note that if subsequent changes are made to the t4mesh are not reflected in
* the faces stored in this class. Meaning that a new face-queury
* must be initiated everytime the t4mesh changes its topology.
*
* Example of usage:
*
* typedef t4mesh<...> MyMeshType;
* MyMeshType mymesh;
* ...
* class MyFaceTraits : public DefaultFaceTraits
* {
* public:
* Color m_color;
* ...
* };
* typedef T4BoundaryFaces<MyMeshType,MyFaceTraits> MyBoundaryFaces;
* MyBoundaryFaces bounday(mymesh);
* for(MyBoundaryFaces::face_iterator face=boundary.begin();face!=boundary.end();++face)
* {
* face->m_color = Color::white;
* std::cout << face->idx0() << std::endl;
* }
*/
template<class M,typename F>
class T4BoundaryFaces
{
public:
typedef M mesh_type;
typedef F face_traits;
typedef T4Face<M,F> face_type;
typedef std::list<face_type> face_container;
typedef typename face_container::iterator face_iterator;
typedef typename face_container::const_iterator const_face_iterator;
protected:
face_container m_faces; ///< Container of extrated boundary faces.
public:
face_iterator begin() { return m_faces.begin(); }
face_iterator end() { return m_faces.end(); }
const_face_iterator begin() const { return m_faces.begin(); }
const_face_iterator end() const { return m_faces.end(); }
public:
/**
* Default Constructor.
* Constructs an empty set of boundary faces.
*/
T4BoundaryFaces()
: m_faces()
{}
/**
* Specialezed Constructor
* Traverses the tetrahedral mesh, and extracts all boundary faces. A boundary
* face is a face that only have one neighboring tetrahedron. A face inside
* the tetrahedral mesh will have exactly two neighboring tetrahedra.
*
* Face node indices are given in CCW order.
*
* @param mesh The tetrahedral mesh from which boundary
* faces are extracted.
*/
T4BoundaryFaces(mesh_type & mesh)
: m_faces()
{
typename mesh_type::tetrahedron_iterator tetrahedron;
for( tetrahedron = mesh.tetrahedron_begin(); tetrahedron != mesh.tetrahedron_end(); ++tetrahedron)
{
if(tetrahedron->jkm()==mesh.tetrahedron_end())
m_faces.push_back(face_type(tetrahedron->j()->idx(),tetrahedron->k()->idx(),tetrahedron->m()->idx()));
if(tetrahedron->ijm()==mesh.tetrahedron_end())
m_faces.push_back(face_type(tetrahedron->i()->idx(),tetrahedron->j()->idx(),tetrahedron->m()->idx()));
if(tetrahedron->kim()==mesh.tetrahedron_end())
m_faces.push_back(face_type(tetrahedron->k()->idx(),tetrahedron->i()->idx(),tetrahedron->m()->idx()));
if(tetrahedron->ikj()==mesh.tetrahedron_end())
m_faces.push_back(face_type(tetrahedron->i()->idx(),tetrahedron->k()->idx(),tetrahedron->j()->idx()));
}
}
};
} // namespace t4mesh
} // namespace OpenTissue
//OPENTISSUE_CORE_CONTAINERS_T4MESH_T4BOUNDARY_FACES_H
#endif

View File

@@ -0,0 +1,219 @@
#ifndef OPENTISSUE_CORE_CONTAINERS_T4MESH_T4EDGES_H
#define OPENTISSUE_CORE_CONTAINERS_T4MESH_T4EDGES_H
//
// OpenTissue Template Library
// - A generic toolbox for physics-based modeling and simulation.
// Copyright (C) 2008 Department of Computer Science, University of Copenhagen.
//
// OTTL is licensed under zlib: http://opensource.org/licenses/zlib-license.php
//
#include <OpenTissue/configuration.h>
#include <list>
#include <cassert>
namespace OpenTissue
{
namespace t4mesh
{
/**
* t4mesh Edge.
* An edge is uniquely defined by the indices of its two end nodes.
* The order of indices do not matter.
*/
template<typename M, typename E>
class T4Edge : public E
{
public:
typedef M mesh_type;
typedef E edge_traits;
typedef T4Edge<M,E> edge_type;
typedef typename mesh_type::index_type index_type;
protected:
index_type m_idxA; ///< Index to first node.
index_type m_idxB; ///< Index to second node.
public:
T4Edge()
: m_idxA(-1)
, m_idxB(-1)
{}
T4Edge(index_type const & indexA, index_type const & indexB)
: m_idxA(indexA)
, m_idxB(indexB)
{}
public:
index_type const & idxA() const { return m_idxA; }
index_type const & idxB() const { return m_idxB; }
bool operator==(edge_type const & edge) const
{
if(m_idxA==edge.idxA() && m_idxB==edge.idxB())
return true;
if(m_idxB==edge.idxA() && m_idxA==edge.idxB())
return true;
return false;
}
bool operator!=(edge_type const & edge)const{ return !((*this)==edge); }
};
/**
* Tetrahedra Mesh Edges.
* Edges are not represented explicitly in a t4mesh, only nodes and tetrahedra
* are representated. Thus this class extracts all unique edges from a t4mesh, by
* traversing it and generating explicit edges.
*
* Note that if subsequent changes are made to the t4mesh, the edge changes are
* not reflected by the edges stored in this class. Meaning that a new edge-queury
* must be initiated everytime the t4mesh changes its topology.
*
*/
template<class M, typename E >
class T4Edges
{
public:
typedef M mesh_type;
typedef E edge_traits;
typedef T4Edge<M,E> edge_type;
typedef typename mesh_type::index_type index_type;
typedef std::list<edge_type> edge_container;
typedef typename edge_container::iterator edge_iterator;
typedef typename edge_container::const_iterator const_edge_iterator;
protected:
typedef enum{white,grey,black} color_type;
typedef std::vector<color_type> color_container;
typedef typename mesh_type::node_type node_type;
typedef typename node_type::tetrahedron_circulator tetrahedron_type;
typedef std::list<node_type*> work_queue;
protected:
edge_container m_edges;
public:
edge_iterator begin() { return m_edges.begin(); }
edge_iterator end() { return m_edges.end(); }
const_edge_iterator begin() const { return m_edges.begin(); }
const_edge_iterator end() const { return m_edges.end(); }
protected:
/**
* Internally used functor, needed for visiting neighboring
* nodes and extracting un-seen edges.
*/
struct VisitT4Edge
{
void visit(
index_type & idxA
, index_type & idxB
, work_queue & work
, color_container & colors
, edge_container & edges
, mesh_type & mesh
)
{
if(idxA==idxB)//--- self-loop, ignore it
return;
//std::cout << "\tvisting:" <<idxB << " : color = " << colors[idxB] << std::endl;
if(colors[idxB]==white)
{
colors[idxB] = grey;
work.push_back(&(*(mesh.node(idxB))));
}
if(colors[idxB]!=black)
{
edges.push_back(edge_type(idxA,idxB));
}
}
};
public:
T4Edges(){}
/**
* Specialized Constructor.
* This constructor traverses the specified mesh and extracts all edges.
*/
T4Edges(mesh_type & mesh)
{
index_type idxA,idxB;
color_container colors(mesh.size_nodes());
std::fill(colors.begin(),colors.end(),white);
node_type * node = &(*(mesh.node(0)));
colors[0]=grey;
work_queue work;
work.push_back(node);
while(!work.empty())
{
node = work.back();
work.pop_back();
idxA = node->idx();
assert(colors[idxA] == grey || !"T4Edges(mesh): Encounted non-greay node");
//std::cout << idxA << " : color = " << colors[idxA] << std::endl;
std::list<index_type> neighbors;
for(tetrahedron_type T = node->begin();T!=node->end();++T)
{
idxB = T->i()->idx();
if(idxB != idxA)
neighbors.push_back(idxB);
idxB = T->j()->idx();
if(idxB != idxA)
neighbors.push_back(idxB);
idxB = T->k()->idx();
if(idxB != idxA)
neighbors.push_back(idxB);
idxB = T->m()->idx();
if(idxB != idxA)
neighbors.push_back(idxB);
}
neighbors.sort();
neighbors.unique();
for(typename std::list<index_type>::iterator n = neighbors.begin();n!=neighbors.end();++n)
VisitT4Edge().visit(idxA, *n ,work,colors,m_edges,mesh);
//for(tetrahedron_type T = node->begin();T!=node->end();++T)
//{
// idxB = T->i()->idx();
// VisitT4Edge().visit(idxA,idxB,work,colors,m_edges,mesh);
// idxB = T->j()->idx();
// VisitT4Edge().visit(idxA,idxB,work,colors,m_edges,mesh);
// idxB = T->k()->idx();
// VisitT4Edge().visit(idxA,idxB,work,colors,m_edges,mesh);
// idxB = T->m()->idx();
// VisitT4Edge().visit(idxA,idxB,work,colors,m_edges,mesh);
//}
colors[idxA] = black;
}
};
};
} // namespace t4mesh
} // namespace OpenTissue
//OPENTISSUE_CORE_CONTAINERS_T4MESH_T4EDGES_H
#endif

View File

@@ -0,0 +1,202 @@
#ifndef OPENTISSUE_CORE_CONTAINERS_T4MESH_T4MESH_H
#define OPENTISSUE_CORE_CONTAINERS_T4MESH_T4MESH_H
//
// OpenTissue Template Library
// - A generic toolbox for physics-based modeling and simulation.
// Copyright (C) 2008 Department of Computer Science, University of Copenhagen.
//
// OTTL is licensed under zlib: http://opensource.org/licenses/zlib-license.php
//
#include <OpenTissue/configuration.h>
#include <OpenTissue/core/containers/t4mesh/t4mesh_t4node.h>
#include <OpenTissue/core/containers/t4mesh/t4mesh_t4tetrahedron.h>
#include <OpenTissue/core/containers/t4mesh/t4mesh_core_access.h>
#include <OpenTissue/core/math/math_constants.h>
#include <vector>
#include <list>
#include <cassert>
namespace OpenTissue
{
namespace t4mesh
{
namespace detail
{
/**
* Basic (Simple) Tetrahedra Mesh.
*
* This tetrahedra mesh data structure is designed specially for
* two purposes: It should maintain a valid topology of the mesh
* at all times, that is the connectivity of nodes and tetrahedra
* are always valid.
*
* The other purpose is to make sure that the global indexing of
* nodes (0..N-1) and tetrahedra (0..T-1) always is a compact range
* starting from zero to the maximum number minus one.
*
* Obviously removing entities (nodes or tetrahedra) alters the global
* index ranges, thus end users can not trust previously stored indices
* of entities in their own apps.
*
* The mesh takes three template arguments. The first specifies the
* math_types used in the mesh. The following two arguments are node
* traits and tetrahedron traits respectively.
*/
template<
typename M
, typename N
, typename T
>
class T4Mesh
{
public:
typedef M math_types;
typedef N node_traits;
typedef T tetrahedron_traits;
typedef T4Mesh<M,N,T> mesh_type;
typedef T4Node<mesh_type> node_type;
typedef T4Tetrahedron<mesh_type> tetrahedron_type;
typedef size_t index_type;
/**
* Undefined index value.
*
* @return An index value that means that the index value is undefined. The
* largest possible value of the index type is used for this purpose.
*/
static index_type const & undefined()
{
static index_type value = FLT_MAX;//math::detail::highest<index_type>();
return value;
}
protected:
typedef std::vector< node_type > node_container;
typedef std::vector< tetrahedron_type > tetrahedra_container;
public:
node_container m_nodes; ///< Internal node storage.
tetrahedra_container m_tetrahedra; ///< Internal tetrahedra storage.
public:
public:
T4Mesh()
: m_nodes()
, m_tetrahedra()
{}
T4Mesh( T4Mesh const & cpy)
{
*this = cpy;
}
public:
void clear()
{
m_nodes.clear();
m_tetrahedra.clear();
}
public:
/**
* Add New Node.
* New node will have index value equal to number of nodes prior to insertion.
*
* @return A iterator to the new node
*/
void insert()
{
m_nodes.push_back( node_type() );
node_type & nd = m_nodes.back();
t4mesh_core_access::set_index( nd, this->m_nodes.size()-1 );
t4mesh_core_access::set_owner( nd, this );
}
/**
* Overloaded insert method for tetrahedron, support index-based insertion.
*
* This is a bit slower than using the iterator-based insertion method directly.
* But it makes it easier to code....
*
*/
void insert(
index_type i,
index_type j,
index_type k,
index_type m
)
{
// assert(find(i,j,k,m)==tetrahedron_end() || !"T4Mesh::insert(): Tetrahedron already exists in mesh");
m_tetrahedra.push_back( tetrahedron_type() );
tetrahedron_type & t = m_tetrahedra.back();
t4mesh_core_access::set_index( t, m_tetrahedra.size() - 1 );
t4mesh_core_access::set_owner( t, this );
t4mesh_core_access::set_node0( t, i );
t4mesh_core_access::set_node1( t, j );
t4mesh_core_access::set_node2( t, k );
t4mesh_core_access::set_node3( t, m );
t4mesh_core_access::tetrahedra_push_back( m_nodes[i], t.idx() );
t4mesh_core_access::tetrahedra_push_back( m_nodes[j], t.idx() );
t4mesh_core_access::tetrahedra_push_back( m_nodes[k], t.idx() );
t4mesh_core_access::tetrahedra_push_back( m_nodes[m], t.idx() );
//return tetrahedron_iterator(m_tetrahedra, t.idx());
}
/**
* Erase Tetrahedron at specified Position.
*
* @param where
*
* @return
*/
index_type erase(index_type& where)
{
index_type last = m_tetrahedra.size()-1;
if (where != last)
{
this->swap(where,last);
}
this->unlink(last);
//--- This might be a bit stupid, it would
//--- proberly be better to keep track of last unused
//--- entry and only resize vector when there is no
//--- more space
m_tetrahedra.pop_back();
return where;
}
protected:
protected:
};
} // namespace detail
} // namespace t4mesh
} // namespace OpenTissue
//OPENTISSUE_CORE_CONTAINERS_T4MESH_T4MESH_H
#endif

View File

@@ -0,0 +1,138 @@
#ifndef OPENTISSUE_CORE_CONTAINERS_T4MESH_T4NODE_H
#define OPENTISSUE_CORE_CONTAINERS_T4MESH_T4NODE_H
//
// OpenTissue Template Library
// - A generic toolbox for physics-based modeling and simulation.
// Copyright (C) 2008 Department of Computer Science, University of Copenhagen.
//
// OTTL is licensed under zlib: http://opensource.org/licenses/zlib-license.php
//
#include <OpenTissue/configuration.h>
#include <list>
#include <cassert>
namespace OpenTissue
{
namespace t4mesh
{
template< typename mesh_type_>
class T4Node : public mesh_type_::node_traits
{
public:
typedef mesh_type_ mesh_type;
typedef typename mesh_type::node_type node_type;
typedef typename mesh_type::tetrahedron_type tetrahedron_type;
typedef typename mesh_type::index_type index_type;
typedef std::list<index_type> tetrahedra_index_container;
typedef typename tetrahedra_index_container::iterator tetrahedra_idx_iterator;
protected:
index_type m_idx; ///< Global index of node
mesh_type * m_owner; ///< Pointer to mesh which the node belongs to.
tetrahedra_index_container m_tetrahedra; ///< Indices of tetrahedra this node is part of.
private:
friend class t4mesh_core_access;
void set_index(index_type idx) { m_idx = idx; }
void set_owner(mesh_type * owner) { m_owner = owner; }
void tetrahedra_push_back(index_type idx) { m_tetrahedra.push_back(idx); }
void tetrahedra_remove(index_type idx) { m_tetrahedra.remove(idx); }
public:
class tetrahedron_circulator
{
private:
node_type * m_node; ///< A pointer to the node
tetrahedra_idx_iterator m_it; ///< An ``local'' iterator to the tetrahedron index, indicating current tetrahedron of this iterator.
public:
tetrahedron_circulator()
: m_node( 0 )
, m_it( 0 )
{}
tetrahedron_circulator( node_type * node, tetrahedra_idx_iterator pos)
: m_node( node )
, m_it( pos )
{
assert(m_node || !"tetrahedron_circulator() : node was null");
assert(m_node->m_owner || !"tetrahedron_circulator(..) : owner was null");
}
bool operator== ( tetrahedron_circulator const & other ) const
{
return ( m_node == other.m_node && m_it == other.m_it );
}
bool operator!= ( tetrahedron_circulator const & other ) const
{
return !( *this == other);
}
tetrahedron_type & operator*()
{
assert(m_node || !"tetrahedron_circulator::* : node was null");
assert(m_node->m_owner || !"tetrahedron_circulator::* : owner was null");
return *(m_node->m_owner->tetrahedron(*m_it));
}
tetrahedron_type * operator->()
{
assert(m_node || !"tetrahedron_circulator::-> : node was null");
assert(m_node->m_owner || !"tetrahedron_circulator::-> : owner was null");
return &(*(m_node->m_owner->tetrahedron(*m_it)));
}
tetrahedron_circulator & operator++()
{
assert(m_node || !"tetrahedron_circulator::++ : node was null");
assert(m_node->m_owner || !"tetrahedron_circulator::++ : owner was null");
++m_it;
return *this;
}
};
tetrahedron_circulator begin() { return tetrahedron_circulator( this, m_tetrahedra.begin() ); }
tetrahedron_circulator end() { return tetrahedron_circulator( this, m_tetrahedra.end() ); }
/**
* Get Number of Tetrahedra.
*
* @return The number of tetrahedra that this node is part of. If
* zero it means the node is an isolated node.
*/
size_t size_tetrahedra() const { return m_tetrahedra.size(); }
bool isolated() const { return m_tetrahedra.empty(); }
public:
T4Node()
: m_idx( mesh_type::undefined() )
, m_owner(0)
, m_tetrahedra()
{}
public:
index_type idx() const { return m_idx; }
mesh_type * owner() { return m_owner; }
mesh_type const * owner() const { return m_owner; }
};
} // namespace t4mesh
} // namespace OpenTissue
//OPENTISSUE_CORE_CONTAINERS_T4MESH_T4NODE_H
#endif

View File

@@ -0,0 +1,105 @@
#ifndef OPENTISSUE_CORE_CONTAINERS_T4MESH_T4TETRAHEDRON_H
#define OPENTISSUE_CORE_CONTAINERS_T4MESH_T4TETRAHEDRON_H
//
// OpenTissue Template Library
// - A generic toolbox for physics-based modeling and simulation.
// Copyright (C) 2008 Department of Computer Science, University of Copenhagen.
//
// OTTL is licensed under zlib: http://opensource.org/licenses/zlib-license.php
//
#include <OpenTissue/configuration.h>
#include <cassert>
namespace OpenTissue
{
namespace t4mesh
{
template< typename mesh_type_>
class T4Tetrahedron : public mesh_type_::tetrahedron_traits
{
public:
typedef mesh_type_ mesh_type;
typedef typename mesh_type::node_type node_type;
typedef typename mesh_type::tetrahedron_type tetrahedron_type;
typedef typename mesh_type::index_type index_type;
index_type m_idx; ///< Global index of tetrahedron
mesh_type * m_owner; ///< Pointer to mesh which the node belongs to.
index_type m_nodes[4]; ///< Global index of node i,j,k and m
private:
friend class t4mesh_core_access;
void set_index(index_type idx) { m_idx = idx; }
void set_owner(mesh_type * owner) { m_owner = owner; }
void set_node0(index_type idx) { m_nodes[0] = idx; }
void set_node1(index_type idx) { m_nodes[1] = idx; }
void set_node2(index_type idx) { m_nodes[2] = idx; }
void set_node3(index_type idx) { m_nodes[3] = idx; }
public:
#ifdef TODO_ERWIN
T4Tetrahedron()
: m_idx( mesh_type::undefined() )
, m_owner(0)
{
this->m_nodes.assign( mesh_type::undefined() );
}
#endif
public:
node_type* node(index_type idx)
{
return &m_owner->m_nodes[m_nodes[idx]];
}
const node_type* node(index_type idx) const
{
return &m_owner->m_nodes[m_nodes[idx]];
}
node_type* i()
{
return node(0);
}
node_type* j()
{
return node(1);
}
node_type* k()
{
return node(2);
}
node_type* m()
{
return node(3);
}
index_type idx() const { return m_idx; }
index_type node_idx(index_type const & local_idx) const
{
assert(0<=local_idx);
assert(local_idx<=3);
return m_nodes[local_idx];
}
mesh_type * owner() { return m_owner; }
mesh_type const * owner() const { return m_owner; }
};
} // namespace t4mesh
} // namespace OpenTissue
//OPENTISSUE_CORE_CONTAINERS_T4MESH_T4TETRAHEDRON_H
#endif

View File

@@ -0,0 +1,114 @@
#ifndef OPENTISSUE_CORE_CONTAINERS_T4MESH_UTIL_T4MESH_BLOCK_GENERATOR_H
#define OPENTISSUE_CORE_CONTAINERS_T4MESH_UTIL_T4MESH_BLOCK_GENERATOR_H
//
// OpenTissue Template Library
// - A generic toolbox for physics-based modeling and simulation.
// Copyright (C) 2008 Department of Computer Science, University of Copenhagen.
//
// OTTL is licensed under zlib: http://opensource.org/licenses/zlib-license.php
//
#include <OpenTissue/configuration.h>
namespace OpenTissue
{
namespace t4mesh
{
/**
* t4mesh Block Generator.
*
* @param I The number of blocks along x axis.
* @param J The number of blocks along y axis.
* @param K The number of blocks along z axis.
* @param block_width The edgelength of the blocks along x-axis.
* @param block_height The edgelength of the blocks along x-axis.
* @param block_depth The edgelength of the blocks along x-axis.
* @param mesh A generic t4mesh, which upon return holds the generated mesh.
*/
template < typename real_type, typename t4mesh_type >
void generate_blocks(
unsigned int const & I
, unsigned int const & J
, unsigned int const & K
, real_type const & block_width
, real_type const & block_height
, real_type const & block_depth
, t4mesh_type & mesh
)
{
mesh.clear();
unsigned int numVertices = (I + 1) * (J + 1) * (K + 1);
for (unsigned int i=0; i<numVertices; ++i)
mesh.insert();
int node_it = 0;
for (unsigned int x = 0; x <= I; ++x)
{
for (unsigned int y = 0; y <= J; ++y)
{
for (unsigned int z = 0; z <= K; ++z)
{
mesh.m_nodes[node_it].m_coord(0) = block_width*x;
mesh.m_nodes[node_it].m_coord(2) = block_depth*y;
mesh.m_nodes[node_it].m_coord(1) = block_height*z;
++node_it;
}
}
}
for (unsigned int i = 0; i < I; ++i)
{
for (unsigned int j = 0; j < J; ++j)
{
for (unsigned int k = 0; k < K; ++k)
{
// For each block, the 8 corners are numerated as:
// 4*-----*7
// /| /|
// / | / |
// 5*-----*6 |
// | 0*--|--*3
// | / | /
// |/ |/
// 1*-----*2
int p0 = (i * (J + 1) + j) * (K + 1) + k;
int p1 = p0 + 1;
int p3 = ((i + 1) * (J + 1) + j) * (K + 1) + k;
int p2 = p3 + 1;
int p7 = ((i + 1) * (J + 1) + (j + 1)) * (K + 1) + k;
int p6 = p7 + 1;
int p4 = (i * (J + 1) + (j + 1)) * (K + 1) + k;
int p5 = p4 + 1;
// Ensure that neighboring tetras are sharing faces
if ((i + j + k) % 2 == 1)
{
mesh.insert(p1,p2,p6,p3);
mesh.insert(p3,p6,p4,p7);
mesh.insert(p1,p4,p6,p5);
mesh.insert(p1,p3,p4,p0);
mesh.insert(p1,p6,p4,p3);
}
else
{
mesh.insert(p2,p0,p5,p1);
mesh.insert(p2,p7,p0,p3);
mesh.insert(p2,p5,p7,p6);
mesh.insert(p0,p7,p5,p4);
mesh.insert(p2,p0,p7,p5);
}
}
}
}
};
} // namespace t4mesh
} // namespace OpenTissue
//OPENTISSUE_CORE_CONTAINERS_T4MESH_UTIL_T4MESH_BLOCK_GENERATOR_H
#endif

View File

@@ -0,0 +1,280 @@
#ifndef OPENTISSUE_CORE_CONTAINERS_T4MESH_UTIL_T4MESH_MESH_COUPLING_H
#define OPENTISSUE_CORE_CONTAINERS_T4MESH_UTIL_T4MESH_MESH_COUPLING_H
//
// OpenTissue Template Library
// - A generic toolbox for physics-based modeling and simulation.
// Copyright (C) 2008 Department of Computer Science, University of Copenhagen.
//
// OTTL is licensed under zlib: http://opensource.org/licenses/zlib-license.php
//
#include <OpenTissue/configuration.h>
#include <OpenTissue/collision/spatial_hashing/spatial_hashing.h>
namespace OpenTissue
{
namespace t4mesh
{
namespace mesh_coupling
{
template<typename surface_mesh,typename volume_mesh>
class collision_policy
{
public:
typedef typename surface_mesh::vertex_type vertex_type;
typedef typename volume_mesh::tetrahedron_type tetrahedron_type;
typedef double real_type;
typedef math::Vector3<real_type> point_type;
typedef vertex_type* data_type;
typedef tetrahedron_type query_type;
typedef OpenTissue::spatial_hashing::GridHashFunction hash_function;
typedef OpenTissue::spatial_hashing::Grid< point_type, math::Vector3<int>, data_type, hash_function> hash_grid;
class result_type
{
public:
vertex_type * m_data;
tetrahedron_type * m_query;
real_type m_w0;
real_type m_w1;
real_type m_w2;
real_type m_w3;
};
typedef std::list<result_type> result_container;
public:
point_type position(data_type const & data) const
{
return data->m_coord;
}
point_type min_coord(query_type const & query) const
{
using std::min;
point_type & p0 = query.i()->m_coord;
point_type & p1 = query.j()->m_coord;
point_type & p2 = query.k()->m_coord;
point_type & p3 = query.m()->m_coord;
return min( p0, min( p1 , min( p2, p3) ) );
}
point_type max_coord(query_type const & query) const
{
using std::max;
point_type & p0 = query.i()->m_coord;
point_type & p1 = query.j()->m_coord;
point_type & p2 = query.k()->m_coord;
point_type & p3 = query.m()->m_coord;
return max( p0, max( p1 , max( p2, p3) ) );
}
void reset(result_container & results) { results.clear(); };
void report(data_type const & data, query_type const & query,result_container & results)
{
//--- First we do a quick rejection test. If the vertex is allready reported then simply ignore it!!!
if(data->m_tag == 1)
return;
point_type & pi = query.i()->m_coord;
point_type & pj = query.j()->m_coord;
point_type & pk = query.k()->m_coord;
point_type & pm = query.m()->m_coord;
point_type & p = data->m_coord;
real_type delta = 10e-5;
real_type lower = - delta;
real_type upper = 1.+ delta;
result_type result;
OpenTissue::geometry::barycentric_algebraic(pi,pj,pk,pm,p,result.m_w0,result.m_w1,result.m_w2,result.m_w3);
if(
(result.m_w0>lower)&&(result.m_w0<upper)
&&
(result.m_w1>lower)&&(result.m_w1<upper)
&&
(result.m_w2>lower)&&(result.m_w2<upper)
&&
(result.m_w3>lower)&&(result.m_w3<upper)
)
{
data->m_tag = 1;
result.m_data = const_cast<vertex_type*>( data );
result.m_query = const_cast<tetrahedron_type*>( &query );
results.push_back( result );
return;
}
}
};
/**
* Bind Mesh Surface to t4mesh.
*
*
* @param M A surface mesh that should be bound to a volume mesh.
* @param V A T4Mesh it is implicitly assumed that node traits have a
* real_type and vector3_type (these are defined in the
* default node traits).
*/
template<typename surface_mesh, typename volume_mesh, typename point_container,typename index_container>
void bind_surface (surface_mesh & M,volume_mesh & V,point_container & barycentric,index_container & bind_indices)
{
//---
//--- The main idea behind mesh coupling is to create a multiresolution for the
//--- animataion. The idea is to separate the visual geometry from the geometry
//--- used to compute the dynamics. This allows one to use highly detailed visual
//--- representation of objects, while using a computational low cost coarse volume
//--- mesh for the computing the dynamcis.
//---
//--- A technique for doing this is callled mesh coupling or cartoon meshing. Below
//--- we describe how it is used together with tetrahedral meshes. It is however a
//--- general approach and can be used with other types of geometries, FFD lattices
//--- are probably anohter very common example on mesh coupling.
//---
//--- When using Mesh Coulping the first step is to bind the vertices of the surface
//--- mesh to the tetrahedral elements of the volume mesh.
//---
//--- Here we use a spatial hashing algorithm to find vertex tetrahedron pairs, where
//--- the vertex is embedded inside the tetrahedron. The actual test is done by first
//--- computing the barycentric coordinates of the vertex with respect to a tetrahedron
//--- in question.
//---
//--- If each barycentric coordinate is greather than equal to zero and less than equal
//--- to one then the vertex is embedded in the tetrahedron. In pratice we need to apply
//--- treshold testing to counter numerical precision problems. It may therefor happen
//--- that vertices lying close to a face of a tetrahedron gets reported twice. Once of
//--- the tetrahedron embedding it and once for the neighboring tetetrahedron. The same
//--- happens if the vertex lies exactly on a face.
//---
//--- Therefore, we firstdo a quick rejection test. If the vertex is allready reported
//--- then simply ignore it!!!
//---
//--- Before rendering each frame, we must update the vertex positions to reflect the
//--- underlying deformation of the tetrahedral mesh. This is done using the barycentric
//--- coordinates, such that the new vertex position is given by
//---
//--- c = w0 p0 + w1 p1 + w2 p2 + w3 p3
//---
//--- Where p0, p1 ,p2, and p3 are the nodal coordinates of the tetrahedron which
//--- the vertex was bounded to.
//---
//--- If stiffness warping is used, the element rotation, Re, can be
//--- used to update the undeformed vertex normal, n0, into the deformed
//--- vertex normal, n, by,
//---
//--- n = Re n0
//---
//--- Often a tetrahedra mesh is used with a conservative coverage of the surface mesh.
//--- That means one is guaranteed that all vertices of the surface mesh are embedded
//--- inside one unique tetrahedron. However, mesh coupling can be used in cases where
//--- one only have a partial coverage. The solution is to bind a vertex to the
//--- closest tetrahedron. Eventhough the vertex lies outside the tetrahedra mesh, the
//--- barycentric coordinates extend the deformation of the tetrahedra mesh beyond
//--- its surface.
//---
typedef collision_policy<surface_mesh,volume_mesh> policy;
typedef OpenTissue::spatial_hashing::PointDataQuery<typename policy::hash_grid, policy> point_query_type;
typename policy::result_container results;
point_query_type point_query;
point_query.auto_init_settings(V.tetrahedron_begin(),V.tetrahedron_end());
//--- perform query
mesh::clear_vertex_tags(M);
std::vector<typename surface_mesh::vertex_type*> vertex_ptr_container(M.size_vertices());
unsigned int i = 0;
for(typename surface_mesh::vertex_iterator v= M.vertex_begin();v!=M.vertex_end();++v,++i)
{
vertex_ptr_container[i] = &(*v);
}
point_query(
vertex_ptr_container.begin()
, vertex_ptr_container.end()
, V.tetrahedron_begin()
, V.tetrahedron_end()
, results
, typename point_query_type::all_tag()
);
unsigned int size = static_cast<unsigned int>( results.size() );
barycentric.resize(size);
bind_indices.resize(size);
typename policy::result_container::iterator Rbegin = results.begin();
typename policy::result_container::iterator Rend = results.end();
for(typename policy::result_container::iterator R = Rbegin;R!=Rend;++R)
{
unsigned int i = static_cast<unsigned int>( R->m_data->get_handle().get_idx() );
barycentric[i](0) = R->m_w1;
barycentric[i](1) = R->m_w2;
barycentric[i](2) = R->m_w3;
bind_indices[i] = R->m_query->idx();
}
};
/**
* Update Surface Mesh.
*
*
* @param M A surface mesh that is bound to a volume mesh.
* @param V The volume mesh that the surface is bound to. It
* is implicitly assumed that the node traits define
* a real_type and vector3_type ((these are defined in
* the default node traits).
* @param barycentric
* @param bind_info
*/
template<typename surface_mesh, typename volume_mesh, typename point_container,typename index_container>
void update_surface (
surface_mesh & M,
volume_mesh & V,
point_container const & barycentric,
index_container const & bind_info
)
{
typename surface_mesh::vertex_iterator begin = M.vertex_begin();
typename surface_mesh::vertex_iterator end = M.vertex_end();
typedef typename volume_mesh::tetrahedron_iterator tetrahedron_iterator;
typedef typename volume_mesh::node_type node_type;
typedef typename node_type::real_type real_type;
typedef typename node_type::vector3_type vector3_type;
for( typename surface_mesh::vertex_iterator v = begin; v!=end; ++v)
{
unsigned int i = static_cast<unsigned int>( v->get_handle().get_idx() );
tetrahedron_iterator T = V.tetrahedron( bind_info[i] );
real_type w1 = barycentric[i](0);
real_type w2 = barycentric[i](1);
real_type w3 = barycentric[i](2);
real_type w0 = 1 - w1 - w2 - w3;
vector3_type & p0 = T->i()->m_coord;
vector3_type & p1 = T->j()->m_coord;
vector3_type & p2 = T->k()->m_coord;
vector3_type & p3 = T->m()->m_coord;
v->m_coord = p0*w0 + p1*w1 + p2*w2 + p3*w3;
}
};
} // namespace mesh_coupling
} // namespace t4mesh
} // namespace OpenTissue
//OPENTISSUE_CORE_CONTAINERS_T4MESH_UTIL_T4MESH_MESH_COUPLING_H
#endif

View File

@@ -0,0 +1,48 @@
#ifndef OPENTISSUE_CORE_CONTAINERS_T4MESH_UTIL_T4MESH_TETGEN_CONSTRAINED_DELAUNAY_TRIANGULATION_H
#define OPENTISSUE_CORE_CONTAINERS_T4MESH_UTIL_T4MESH_TETGEN_CONSTRAINED_DELAUNAY_TRIANGULATION_H
//
// OpenTissue Template Library
// - A generic toolbox for physics-based modeling and simulation.
// Copyright (C) 2008 Department of Computer Science, University of Copenhagen.
//
// OTTL is licensed under zlib: http://opensource.org/licenses/zlib-license.php
//
#include <OpenTissue/configuration.h>
#include <OpenTissue/core/containers/t4mesh/util/t4mesh_tetgen_mesh_lofter.h>
namespace OpenTissue
{
namespace t4mesh
{
namespace tetgen
{
/**
* Constrained Delaunay Tetrahedralization Routine.
* - uses TetGen to create the resulting tetrahedal mesh
*
* @param polymesh A poly mesh, which holds a closed two-manifold.
* @param t4mesh A generic t4mesh, which upon return holds the generated tetrahedal mesh.
*
* @return if succesfull then the return value is true otherwise it is false.
*/
template<typename t4mesh_type, typename polymesh_type>
inline bool constrained_delaunay_tetrahedralization(const polymesh_type& polymesh, t4mesh_type & t4mesh)
{
OpenTissue::t4mesh::mesh_lofter_settings config;
config.m_intermediate_file = "tmp";
config.m_quality_ratio = 0.0;
config.m_maximum_volume = 0.0;
config.m_quiet_output = true;
return OpenTissue::t4mesh::mesh_lofter(t4mesh, polymesh, config);
}
} // namespace tetgen
} // namespace t4mesh
} // namespace OpenTissue
//OPENTISSUE_CORE_CONTAINERS_T4MESH_UTIL_T4MESH_TETGEN_CONSTRAINED_DELAUNAY_TRIANGULATION_H
#endif

View File

@@ -0,0 +1,128 @@
#ifndef OPENTISSUE_CORE_CONTAINERS_T4MESH_UTIL_T4MESH_TETGEN_MESH_LOFTER_H
#define OPENTISSUE_CORE_CONTAINERS_T4MESH_UTIL_T4MESH_TETGEN_MESH_LOFTER_H
//
// OpenTissue Template Library
// - A generic toolbox for physics-based modeling and simulation.
// Copyright (C) 2008 Department of Computer Science, University of Copenhagen.
//
// OTTL is licensed under zlib: http://opensource.org/licenses/zlib-license.php
//
#include <OpenTissue/configuration.h>
#include <OpenTissue/core/containers/mesh/common/io/mesh_tetgen_write.h>
#include <OpenTissue/core/containers/mesh/polymesh/util/polymesh_is_manifold.h>
#include <OpenTissue/core/containers/t4mesh/io/t4mesh_tetgen_read.h>
#include <TetGen/tetgen.h>
#include <cmath>
#include <string>
#include <sstream>
#include <iostream>
namespace OpenTissue
{
namespace t4mesh
{
struct mesh_lofter_settings
{
mesh_lofter_settings()
: m_quality_ratio(2./*std::sqrt(2.)*/)
, m_maximum_volume(0.)
, m_intermediate_file("")
, m_quiet_output(false)
, m_verify_input(false)
{}
double m_quality_ratio; ///< quality t4mesh is issued if > 0. A minimum radius-edge ratio may be specifyed (default 2.0).
double m_maximum_volume; ///< max volume constraints on t4mesh if > 0.
std::string m_intermediate_file; ///< use intermediate files to/fro tetget if name specified.
bool m_quiet_output; ///< keep output spam as silent as possible, great for RELEASE.
bool m_verify_input; ///< DEBUG: detects plc intersections, i.e. verify "bad" input mesh.
};
/**
* t4mesh_mesh_lofter.
* - uses tetgen to loft/extrude a closed two-manifold polymesh to a generic tetrahedal mesh
*
* @param polymesh A poly mesh, which holds a closed two-manifold.
* @param t4mesh A generic t4mesh, which upon return holds the generated tetrahedal mesh.
* @param settings The settings/configuration for TetGen, can be omitted to use the default settings.
*
*/
// TODO: Implement work-in-mem solution, thus avoid using intermediate files [MKC]
template<typename t4mesh_type, typename polymesh_type>
bool mesh_lofter(t4mesh_type& t4mesh, const polymesh_type& polymesh, const mesh_lofter_settings & settings = mesh_lofter_settings())
{
// local functions are no go in C++, thus we need to wrap them in a local class, tsk!
class local_aux
{
public:
static bool error(const std::string& text)
{
using std::operator<<;
std::cerr << "ERROR [t4mesh_mesh_lofter]:\n- " << text << std::endl;
return false;
}
};
// convenient pointer to use with TetGen
char* tmp_file = settings.m_intermediate_file.size()>0?const_cast<char*>(settings.m_intermediate_file.c_str()):NULL;
// "no tmp file" won't work check
if (!tmp_file)
return local_aux::error("current version only supports using intermediate files");
if (!is_manifold(polymesh))
return local_aux::error("polymesh is not a two-manifold");
if (tmp_file)
if (!tetgen_write(settings.m_intermediate_file+".poly", polymesh))
return local_aux::error("mesh_tetgen_write failed in writing the polymesh file: '"+settings.m_intermediate_file+".poly'");
std::stringstream tmp;
tmp << "p"; // piecewise linear complex (always)
if (settings.m_verify_input)
tmp << "d";
else {
if (settings.m_quality_ratio > 0)
tmp << "q" << settings.m_quality_ratio; // quality
if (settings.m_maximum_volume > 0)
tmp << "a" << settings.m_maximum_volume; // max volume
if (settings.m_quiet_output)
tmp << "Q"; // keep quiet :)
}
std::string txt = tmp.str().c_str();
tetgenbehavior config;
// MKC: setting vars in tetgenbehavior explicitly is too complicated, and requires a deeper knowledge of the system :(
if (!config.parse_commandline(const_cast<char*>(txt.c_str()))) // parsing a "cmd" line will set the correct vars and their dependecies.
return local_aux::error("TetGen parse_commandline failed: '"+txt+"'");
tetgenio in, out;
if (tmp_file)
if (!in.load_poly(tmp_file))
return local_aux::error("TetGen load_poly failed: '"+settings.m_intermediate_file+"'");
// let TetGen do some magic :)
tetrahedralize(&config, &in, &out);
if (out.numberoftetrahedra < 1)
return local_aux::error("TetGen tetrahedralize failed: no tetrahedra generated!");
if (tmp_file) {
out.save_elements(tmp_file);
out.save_nodes(tmp_file);
if (!tetgen_read(settings.m_intermediate_file, t4mesh))
return local_aux::error("t4mesh_tetgen_read failed in reading the data: '"+settings.m_intermediate_file+"'");
}
return true;
}
} // namespace t4mesh
} // namespace OpenTissue
//OPENTISSUE_CORE_CONTAINERS_T4MESH_UTIL_T4MESH_TETGEN_MESH_LOFTER_H
#endif

View File

@@ -0,0 +1,48 @@
#ifndef OPENTISSUE_CORE_CONTAINERS_T4MESH_UTIL_T4MESH_TETGEN_QUALITY_TETRAHEDRALIZATION_H
#define OPENTISSUE_CORE_CONTAINERS_T4MESH_UTIL_T4MESH_TETGEN_QUALITY_TETRAHEDRALIZATION_H
//
// OpenTissue Template Library
// - A generic toolbox for physics-based modeling and simulation.
// Copyright (C) 2008 Department of Computer Science, University of Copenhagen.
//
// OTTL is licensed under zlib: http://opensource.org/licenses/zlib-license.php
//
#include <OpenTissue/configuration.h>
#include <OpenTissue/core/containers/t4mesh/util/t4mesh_tetgen_mesh_lofter.h>
namespace OpenTissue
{
namespace t4mesh
{
namespace tetgen
{
/**
* Quality Tetrahedralization Routine.
* - uses TetGen to create the resulting tetrahedal mesh
*
* @param polymesh A poly mesh, which holds a closed two-manifold.
* @param t4mesh A generic t4mesh, which upon return holds the generated tetrahedal mesh.
*
* @return if succesfull then the return value is true otherwise it is false.
*/
template<typename t4mesh_type, typename polymesh_type>
inline bool quality_tetrahedralization(const polymesh_type& polymesh, t4mesh_type & t4mesh)
{
OpenTissue::t4mesh::mesh_lofter_settings config;
config.m_intermediate_file = "tmp";
config.m_quality_ratio = 2.0;
config.m_maximum_volume = 0.0;
config.m_quiet_output = true;
return OpenTissue::t4mesh::mesh_lofter(t4mesh, polymesh, config);
}
} // namespace tetgen
} // namespace t4mesh
} // namespace OpenTissue
//OPENTISSUE_CORE_CONTAINERS_T4MESH_UTIL_T4MESH_TETGEN_QUALITY_TETRAHEDRALIZATION_H
#endif