// Description: Half-edge structure
#ifndef PASTELGEOMETRY_HALFMESH_H
#define PASTELGEOMETRY_HALFMESH_H
#include "pastel/sys/mytypes.h"
#include "pastel/sys/ensure.h"
#include "pastel/sys/generic/class.h"
#include "pastel/sys/list.h"
#include "pastel/geometry/halfmesh/halfmesh_concepts.h"
#include "pastel/geometry/halfmesh/halfmesh_fwd.h"
#include "pastel/geometry/halfmesh/halfmesh_vertex.h"
#include "pastel/geometry/halfmesh/halfmesh_half.h"
#include "pastel/geometry/halfmesh/halfmesh_edge.h"
#include "pastel/geometry/halfmesh/halfmesh_polygon.h"
#include <boost/operators.hpp>
#include <vector>
#include <unordered_set>
namespace Pastel
{
//! Half-edge structure
template <
typename Settings,
template <typename> class Customization_>
class HalfMesh
: public Customization_<Settings>
{
public:
using Customization = Customization_<Settings>;
using Fwd = HalfMesh_Fwd<Settings>;
PASTEL_FWD(Vertex);
PASTEL_FWD(Half);
PASTEL_FWD(Edge);
PASTEL_FWD(Polygon);
PASTEL_FWD(VertexData_Class);
PASTEL_FWD(HalfData_Class);
PASTEL_FWD(EdgeData_Class);
PASTEL_FWD(PolygonData_Class);
static constexpr bool MultipleEdges = Fwd::MultipleEdges;
static constexpr bool Loops = Fwd::Loops;
PASTEL_FWD(VertexSet);
PASTEL_FWD(Vertex_Iterator);
PASTEL_FWD(Vertex_ConstIterator);
PASTEL_FWD(HalfSet);
PASTEL_FWD(Half_Iterator);
PASTEL_FWD(Half_ConstIterator);
PASTEL_FWD(EdgeSet);
PASTEL_FWD(Edge_Iterator);
PASTEL_FWD(Edge_ConstIterator);
PASTEL_FWD(PolygonSet);
PASTEL_FWD(Polygon_Iterator);
PASTEL_FWD(Polygon_ConstIterator);
PASTEL_FWD(InsertEdge_Return);
PASTEL_FWD(InsertEdge_Return_Pair);
template <typename Range, typename To>
struct IsConvertible
{
static constexpr bool value =
std::is_convertible<decltype(*std::begin(std::declval<Range>())), To>::value;
};
//! Constructs an empty mesh.
/*!
Time complexity: O(1)
Exception safety: strong
*/
HalfMesh()
: vertexSet_()
, halfSet_()
, edgeSet_()
, polygonSet_()
{
}
//! Move-constructs from another mesh.
/*!
Time complexity: O(1)
Exception safety: strong
*/
HalfMesh(HalfMesh&& that)
: HalfMesh()
{
swap(that);
}
//! Copy-constructs from another mesh.
/*!
Time complexity: O(vertices() + edges() + polygons())
Exception safety: strong
*/
HalfMesh(const HalfMesh& that);
//! Destructs the mesh.
/*!
Exception safety:
nothrow
*/
~HalfMesh()
{
clear();
}
//! Assigns from another mesh.
/*!
Time complexity:
O(1), if moved,
O(vertices() + edges() + polygons()), otherwise.
Exception safety: strong
*/
HalfMesh& operator=(HalfMesh that)
{
swap(that);
return *this;
}
//! Swaps two half-edge meshes.
/*!
Time complexity: O(1)
Exception safety: nothrow
*/
void swap(HalfMesh& that)
{
Customization::swap(that);
vertexSet_.swap(that.vertexSet_);
halfSet_.swap(that.halfSet_);
edgeSet_.swap(that.edgeSet_);
polygonSet_.swap(that.polygonSet_);
}
//! Remove all data of the mesh.
/*!
Time complexity: O(vertices() + edges() + polygons())
Exception safety: nothrow
*/
void clear()
{
this->onClear();
vertexSet_.clear();
halfSet_.clear();
edgeSet_.clear();
polygonSet_.clear();
}
// Iterators
PASTEL_ITERATOR_FUNCTIONS_PREFIX(Vertex_, vertexBegin, vertexSet_.begin());
PASTEL_ITERATOR_FUNCTIONS_PREFIX(Vertex_, vertexEnd, vertexSet_.end());
PASTEL_ITERATOR_FUNCTIONS_PREFIX(Vertex_, vertexLast, std::prev(vertexEnd()));
PASTEL_ITERATOR_FUNCTIONS_PREFIX(Half_, halfBegin, halfSet_.begin());
PASTEL_ITERATOR_FUNCTIONS_PREFIX(Half_, halfEnd, halfSet_.end());
PASTEL_ITERATOR_FUNCTIONS_PREFIX(Half_, halfLast, std::prev(halfEnd()));
PASTEL_ITERATOR_FUNCTIONS_PREFIX(Edge_, edgeBegin, edgeSet_.begin());
PASTEL_ITERATOR_FUNCTIONS_PREFIX(Edge_, edgeEnd, edgeSet_.end());
PASTEL_ITERATOR_FUNCTIONS_PREFIX(Edge_, edgeLast, std::prev(edgeEnd()));
PASTEL_ITERATOR_FUNCTIONS_PREFIX(Polygon_, polygonBegin, polygonSet_.begin());
PASTEL_ITERATOR_FUNCTIONS_PREFIX(Polygon_, polygonEnd, polygonSet_.end());
PASTEL_ITERATOR_FUNCTIONS_PREFIX(Polygon_, polygonLast, std::prev(polygonEnd()));
//! Searches for a half-edge from 'from' to 'to'.
/*!
Time complexity:
O(n)
where
n is the number of edges around 'from'.
Exception safety:
nothrow
*/
Half_ConstIterator findHalf(
const Vertex_ConstIterator& from,
const Vertex_ConstIterator& to) const;
Half_Iterator findHalf(
const Vertex_ConstIterator& from,
const Vertex_ConstIterator& to)
{
return cast(addConst(*this).findHalf(from, to));
}
//! Makes 'in' and 'out' oriented neighbours.
/*!
in, out:
The half-edges to make adjacent.
Returns:
Whether the half-edges could be made adjacent
(sometimes this is not possible, meaning it
would lead to a non-representable non-manifold
configuration).
Preconditions:
!in.empty()
!out.empty()
in.destination() == out.origin()
in.left().empty()
out.left().empty()
Postconditions:
If returns true, in.next() == out
and out.previous() == in.
Otherwise, nothing is changed.
Time complexity:
O(edges around out.origin())
Exception safety:
nothrow
*/
bool makeAdjacent(
const Half_ConstIterator& in,
const Half_ConstIterator& out);
//! Inserts an isolated vertex.
/*!
Time complexity: O(1)
Exception safety: strong
*/
template <typename... Type>
Vertex_Iterator insertVertex(
Type&&... data);
//! Removes a vertex and all its neighbouring edges.
/*!
Time complexity:
Let E = {e[i]} be the set of edges connected to the
vertex to be removed. Let P = {p[i] = e[i].left()}.
Then the complexity is
sum_i O(edges in p[i]) + |E| O(log(edges())) on average.
Exception safety:
nothrow
*/
Vertex_Iterator removeVertex(
const Vertex_ConstIterator& vertex);
//! Casts away the constness of an iterator.
/*!
Time complexity: O(1)
Exception safety: nothrow
*/
Vertex_Iterator cast(
const Vertex_ConstIterator& that)
{
return vertexSet_.cast(that);
}
//! Returns the number of vertices.
/*!
Time complexity:
constant
Exception safety:
nothrow
*/
integer vertices() const
{
return vertexSet_.size();
}
//! Casts away the constness of an iterator.
/*!
Time complexity: O(1)
Exception safety: nothrow
*/
Half_Iterator cast(
const Half_ConstIterator& that)
{
return halfSet_.cast(that);
}
//! Inserts an edge between two vertices.
/*!
from, to:
The vertices between which to insert the new edge.
data:
The edge-data.
returns:
If multiple edges are allowed between two vertices,
then an edge-iterator-boolean pair, where the boolean
tells whether an edge was created.
If multiple edges are not allowed between two vertices,
then an edge-iterator to the created edge, or to a
possibly existing edge.
The returned edge-iterator will be empty in the cases
where the edge can not be represented by the half-edge
structure.
Preconditions:
from.isNormal()
to.isNormal()
Time complexity:
O(edges around 'from') +
O(edges around 'to')
Exception safety:
strong
*/
template <typename... Type>
InsertEdge_Return insertEdge(
const Vertex_ConstIterator& from,
const Vertex_ConstIterator& to,
Type&&... data);
//! Removes an edge and all its neighbouring polygons.
/*!
Time complexity:
O(edges in the left polygon) +
O(edges in the right polygon)
Exception safety:
nothrow
*/
Edge_Iterator removeEdge(
const Edge_ConstIterator& edge);
//! Casts away the constness of an iterator.
/*!
Time complexity: O(1)
Exception safety: nothrow
*/
Edge_Iterator cast(
const Edge_ConstIterator& that)
{
return edgeSet_.cast(that);
}
//! Returns the number of edges.
/*!
Time complexity: O(1)
Exception safety: nothrow
*/
integer edges() const
{
return edgeSet_.size();
}
//! Inserts a polygon to the given boundary loop.
/*!
Returns:
The added polygon if successful,
otherwise an empty polygon.
Preconditions:
For all i: !halfLoop[i].empty()
A polygon can be added if the following conditions hold:
For all i: halfLoop[i].destination() == halfLoop[(i + 1) % halfLoop.size()].origin()
For all i: halfLoop[i].left().empty()
For all i: halfLoop[i].origin().free()
Time complexity:
sum_i O(edges around halfLoop[i].origin()) on average
Exception safety:
strong
*/
template <
typename Half_Range,
typename... Type>
auto insertPolygon(
const Half_Range& halfSet,
Type&&... data)
-> EnableIf<IsConvertible<Half_Range, Half_ConstIterator>, Polygon_Iterator>;
//! Inserts a polygon to the given vertex loop.
/*!
Time complexity:
sum_i O(edges around vertexLoop[i]) +
O(vertexLoop.size()) on average
Exception safety:
strong
*/
template <
typename Vertex_Range,
typename... Type>
auto insertPolygon(
const Vertex_Range& vertexSet,
Type&&... data)
-> EnableIf<IsConvertible<Vertex_Range, Vertex_ConstIterator>, Polygon_Iterator>;
//! Removes a polygon.
/*!
Time complexity:
O(edges in the polygon) on average
Exception safety:
nothrow
*/
Polygon_Iterator removePolygon(
const Polygon_ConstIterator& polygon);
//! Casts away the constness of an iterator.
/*!
Time complexity: O(1)
Exception safety: nothrow
*/
Polygon_Iterator cast(
const Polygon_ConstIterator& that)
{
return polygonSet_.cast(that);
}
//! Returns the number of polygons.
/*!
Time complexity: O(1)
Exception safety: nothrow
*/
integer polygons() const
{
return polygonSet_.size();
}
//! Merges polygons across the half-edge.
/*!
Time complexity: O(n)
where
n is the number of vertices in half.righ().
Exception safety: nothrow
The half, half.pair(), and half.edge() are removed.
The half.left() is extended, and half.right() is
removed (unless half.left() == half.right()).
returns:
half.left()
*/
auto merge(const Half_ConstIterator& half)
-> Polygon_Iterator;
private:
//! Detaches a half-edge from its origin.
/*!
Time complexity: O(1)
Exception safety: nothrow
*/
void detachHalf(
const Half_ConstIterator& half);
//! Detaches a polygon.
/*!
Time complexity: O(n)
where
n is the number of half-edges in the polygon.
Exception safety: nothrow
*/
void detachPolygon(
const Polygon_ConstIterator& polygon);
//! Link a polygon to a loop of half-edges.
/*!
Time complexity: O(n)
where
n is the number of half-edges in the loop
Exception safety: nothrow
*/
void linkPolygon(
const Half_ConstIterator& half,
const Polygon_ConstIterator& left);
Half_ConstIterator findFreeIncident(
const Vertex_ConstIterator& vertex) const;
Half_Iterator findFreeIncident(
const Vertex_ConstIterator& vertex)
{
return cast(addConst(*this).findFreeIncident(vertex));
}
Half_ConstIterator findFreeIncident(
const Vertex_ConstIterator& vertex,
const Half_ConstIterator& startingFrom,
const Half_ConstIterator& andBefore) const;
Half_Iterator findFreeIncident(
const Vertex_ConstIterator& vertex,
const Half_ConstIterator& startingFrom,
const Half_ConstIterator& andBefore)
{
return cast(addConst(*this).findFreeIncident(
vertex, startingFrom, andBefore));
}
struct PairTag {};
struct SingleTag {};
InsertEdge_Return insertEdgeReturn(
const Edge_Iterator& edge,
bool created) const
{
using Tag = typename std::conditional<
MultipleEdges, SingleTag, PairTag>::type;
return insertEdgeReturn(edge, created, Tag());
}
InsertEdge_Return_Pair insertEdgeReturn(
const Edge_Iterator& edge,
bool created,
PairTag) const
{
return InsertEdge_Return_Pair(edge, created);
}
Edge_Iterator insertEdgeReturn(
const Edge_Iterator& edge,
bool created,
SingleTag) const
{
return edge;
}
Edge_Iterator insertEdgeEdge(const InsertEdge_Return_Pair& edgeReturn) const
{
return edgeReturn.first;
}
Edge_Iterator insertEdgeEdge(const Edge_Iterator& edgeReturn) const
{
return edgeReturn;
}
bool insertEdgeCreated(const InsertEdge_Return_Pair& edgeReturn) const
{
return edgeReturn.second;
}
bool insertEdgeCreated(const Edge_Iterator& edgeReturn) const
{
return true;
}
VertexSet vertexSet_;
HalfSet halfSet_;
EdgeSet edgeSet_;
PolygonSet polygonSet_;
};
}
namespace Pastel
{
template <
typename VertexData_ = void,
typename HalfData_ = void,
typename EdgeData_ = void,
typename PolygonData_ = void,
bool MultipleEdges_ = true,
bool Loops_ = true>
class HalfMesh_Settings
{
public:
using VertexData = VertexData_;
using HalfData = HalfData_;
using EdgeData = EdgeData_;
using PolygonData = PolygonData_;
static constexpr bool MultipleEdges = MultipleEdges_;
static constexpr bool Loops = Loops_;
};
template <
typename VertexData_ = void,
typename HalfData_ = void,
typename EdgeData_ = void,
typename PolygonData_ = void,
bool MultipleEdges_ = true,
bool Loops_ = true,
template <typename> class Customization_ = Empty_HalfMesh_Customization>
using HalfEdge = HalfMesh<HalfMesh_Settings<
VertexData_, HalfData_, EdgeData_, PolygonData_,
MultipleEdges_, Loops_>, Customization_>;
}
#include "pastel/geometry/halfmesh/halfmesh.hpp"
#include "pastel/geometry/halfmesh/halfmesh_detach_half.hpp"
#include "pastel/geometry/halfmesh/halfmesh_find.hpp"
#include "pastel/geometry/halfmesh/halfmesh_merge_half.hpp"
#include "pastel/geometry/halfmesh/halfmesh_remove_edge.hpp"
#include "pastel/geometry/halfmesh/halfmesh_remove_polygon.hpp"
#include "pastel/geometry/halfmesh/halfmesh_remove_vertex.hpp"
#include "pastel/geometry/halfmesh/halfmesh_insert_edge.hpp"
#include "pastel/geometry/halfmesh/halfmesh_insert_polygon.hpp"
#include "pastel/geometry/halfmesh/halfmesh_insert_vertex.hpp"
#include "pastel/geometry/halfmesh/halfmesh_invariants.h"
#include "pastel/geometry/halfmesh/halfmesh_stream.h"
#endif