Commit 1b68796e authored by Philip Trettner's avatar Philip Trettner
Browse files

rotate kinda works

parent 644f1cbc
......@@ -22,4 +22,6 @@ Best used with glm and glow.
* opposite edges (from vertex)
* cotangens weights etc.
* smoothing
* make handle.<primitives>() contain only valid ones and provide an all_<primitives>() version
\ No newline at end of file
* make handle.<primitives>() contain only valid ones and provide an all_<primitives>() version
* boundary iterators
* _copy versions of topological operations that copy attributes
\ No newline at end of file
......@@ -189,6 +189,9 @@ void Mesh::assert_consistency() const
assert(h.vertex_from().outgoing_halfedges().contains(h));
assert(h.edge().halfedgeA() == h || h.edge().halfedgeB() == h);
assert(h.next().vertex_from() == h.vertex_to());
assert(h.prev().vertex_to() == h.vertex_from());
}
// check vertex consistencies
......
......@@ -140,6 +140,11 @@ private:
/// Does NOT invalidate iterators!
vertex_index add_vertex();
/// Allocates a new face
face_index alloc_face();
/// Allocates a new edge
edge_index alloc_edge();
/// Adds a face consisting of N vertices
/// The vertices must already be sorted in CCW order
/// (note: trying to add already existing halfedges triggers assertions)
......@@ -155,6 +160,22 @@ private:
/// same as add_or_get_edge but returns the apattrriate half-edge
halfedge_index add_or_get_halfedge(vertex_index v_from, vertex_index v_to);
/// splits a face
vertex_index face_split(face_index f);
/// splits an edge
vertex_index edge_split(edge_index f);
/// splits a half-edge
vertex_index halfedge_split(halfedge_index f);
/// rotates an edge to next
void edge_rotate_next(edge_index e);
/// rotates an edge to prev
void edge_rotate_prev(edge_index e);
/// rotates a half-edge to next
void halfedge_rotate_next(halfedge_index h);
/// rotates a half-edge to prev
void halfedge_rotate_prev(halfedge_index h);
/// removes a face (actually sets the removed status)
/// modifies all adjacent vertices so that they correctly report is_boundary true
void remove_face(face_index f_idx);
......
......@@ -17,6 +17,36 @@ inline vertex_index Mesh::add_vertex()
return idx;
}
inline face_index Mesh::alloc_face()
{
auto idx = face_index((int)mFaces.size());
mFaces.push_back(face_info());
// notify attributes
auto fCnt = (int)mFaces.size();
for (auto p = mFaceAttrs; p; p = p->mNextAttribute)
p->resize(fCnt, false);
return idx;
}
inline edge_index Mesh::alloc_edge()
{
auto idx = edge_index((int)mHalfedges.size() >> 1);
mHalfedges.push_back(halfedge_info());
mHalfedges.push_back(halfedge_info());
// notify attributes
auto hCnt = (int)mHalfedges.size();
auto eCnt = (int)mHalfedges.size() >> 1;
for (auto p = mEdgeAttrs; p; p = p->mNextAttribute)
p->resize(eCnt, false);
for (auto p = mHalfedgeAttrs; p; p = p->mNextAttribute)
p->resize(hCnt, false);
return idx;
}
inline face_index Mesh::add_face(const vertex_handle *v_handles, int vcnt)
{
mFaceInsertCache.resize(vcnt);
......@@ -500,6 +530,284 @@ inline halfedge_index Mesh::prev_valid_idx_from(halfedge_index idx) const
return {}; // invalid
}
inline vertex_index Mesh::face_split(face_index f)
{
// TODO: can be made more performant
auto h_begin = face(f).halfedge;
// remove face
remove_face(f);
// add vertex
vertex_index vs[3];
vs[0] = add_vertex();
// add triangles
auto h = h_begin;
do
{
vs[1] = from_vertex_of(h);
vs[2] = to_vertex_of(h);
add_face(vs, 3);
h = halfedge(h).next_halfedge;
} while (h != h_begin);
return vs[0];
}
inline vertex_index Mesh::edge_split(edge_index f)
{
// remove edge
// add vertex
// add two new edges
assert(0 && "not implemented");
return {};
}
inline vertex_index Mesh::halfedge_split(halfedge_index f)
{
// add vertex
// add new half-edge
assert(0 && "not implemented");
return {};
}
inline void Mesh::edge_rotate_next(edge_index e)
{
assert(!handle_of(e).is_boundary() && "does not work on boundaries");
assert(handle_of(e).vertexA().adjacent_vertices().size() > 2 && "does not work on valence <= 2 vertices");
assert(handle_of(e).vertexB().adjacent_vertices().size() > 2 && "does not work on valence <= 2 vertices");
auto h0 = halfedge_of(e, 0);
auto h1 = halfedge_of(e, 1);
auto &h0_ref = halfedge(h0);
auto &h1_ref = halfedge(h1);
auto h0_next = h0_ref.next_halfedge;
auto h0_prev = h0_ref.prev_halfedge;
auto h1_next = h1_ref.next_halfedge;
auto h1_prev = h1_ref.prev_halfedge;
auto &h0_next_ref = halfedge(h0_next);
auto &h0_prev_ref = halfedge(h0_prev);
auto &h1_next_ref = halfedge(h1_next);
auto &h1_prev_ref = halfedge(h1_prev);
auto h0_next_next = h0_next_ref.next_halfedge;
auto &h0_next_next_ref = halfedge(h0_next_next);
auto h1_next_next = h1_next_ref.next_halfedge;
auto &h1_next_next_ref = halfedge(h1_next_next);
// fix vertices
auto &v0 = vertex(h0_ref.to_vertex);
if (v0.outgoing_halfedge == h1)
v0.outgoing_halfedge = h0_next;
auto &v1 = vertex(h1_ref.to_vertex);
if (v1.outgoing_halfedge == h0)
v1.outgoing_halfedge = h1_next;
// fix faces
face(h0_ref.face).halfedge = h0;
face(h1_ref.face).halfedge = h1;
// fix half-edges
h0_ref.to_vertex = h0_next_ref.to_vertex;
h1_ref.to_vertex = h1_next_ref.to_vertex;
h0_next_ref.face = h1_ref.face;
h1_next_ref.face = h0_ref.face;
// move to next
h1_prev_ref.next_halfedge = h0_next;
h0_next_ref.prev_halfedge = h1_prev;
h0_prev_ref.next_halfedge = h1_next;
h1_next_ref.prev_halfedge = h0_prev;
h0_next_ref.next_halfedge = h1;
h1_ref.prev_halfedge = h0_next;
h1_next_ref.next_halfedge = h0;
h0_ref.prev_halfedge = h1_next;
h0_ref.next_halfedge = h0_next_next;
h0_next_next_ref.prev_halfedge = h0;
h1_ref.next_halfedge = h1_next_next;
h1_next_next_ref.prev_halfedge = h1;
// fix boundary state
fix_boundary_state_of(h0_ref.face);
fix_boundary_state_of(h1_ref.face);
}
inline void Mesh::edge_rotate_prev(edge_index e)
{
assert(!handle_of(e).is_boundary() && "does not work on boundaries");
assert(handle_of(e).vertexA().adjacent_vertices().size() > 2 && "does not work on valence <= 2 vertices");
assert(handle_of(e).vertexB().adjacent_vertices().size() > 2 && "does not work on valence <= 2 vertices");
auto h0 = halfedge_of(e, 0);
auto h1 = halfedge_of(e, 1);
auto &h0_ref = halfedge(h0);
auto &h1_ref = halfedge(h1);
auto h0_next = h0_ref.next_halfedge;
auto h0_prev = h0_ref.prev_halfedge;
auto h1_next = h1_ref.next_halfedge;
auto h1_prev = h1_ref.prev_halfedge;
auto &h0_next_ref = halfedge(h0_next);
auto &h0_prev_ref = halfedge(h0_prev);
auto &h1_next_ref = halfedge(h1_next);
auto &h1_prev_ref = halfedge(h1_prev);
auto h0_prev_prev = h0_prev_ref.prev_halfedge;
auto &h0_prev_prev_ref = halfedge(h0_prev_prev);
auto h1_prev_prev = h1_prev_ref.prev_halfedge;
auto &h1_prev_prev_ref = halfedge(h1_prev_prev);
// fix vertex
auto &v0 = vertex(h0_ref.to_vertex);
if (v0.outgoing_halfedge == h1)
v0.outgoing_halfedge = h0_next;
auto &v1 = vertex(h1_ref.to_vertex);
if (v1.outgoing_halfedge == h0)
v1.outgoing_halfedge = h1_next;
// fix faces
face(h0_ref.face).halfedge = h0;
face(h1_ref.face).halfedge = h1;
// fix half-edge
h1_ref.to_vertex = h0_prev_prev_ref.to_vertex;
h0_ref.to_vertex = h1_prev_prev_ref.to_vertex;
h0_prev_ref.face = h1_ref.face;
h1_prev_ref.face = h0_ref.face;
// move to next
h0_prev_ref.next_halfedge = h1_next;
h1_next_ref.prev_halfedge = h0_prev;
h1_prev_ref.next_halfedge = h0_next;
h0_next_ref.prev_halfedge = h1_prev;
h1_ref.next_halfedge = h0_prev;
h0_prev_ref.prev_halfedge = h1;
h0_ref.next_halfedge = h1_prev;
h1_prev_ref.prev_halfedge = h0;
h0_prev_prev_ref.next_halfedge = h0;
h0_ref.prev_halfedge = h0_prev_prev;
h1_prev_prev_ref.next_halfedge = h1;
h1_ref.prev_halfedge = h1_prev_prev;
// fix boundary state
fix_boundary_state_of(h0_ref.face);
fix_boundary_state_of(h1_ref.face);
}
inline void Mesh::halfedge_rotate_next(halfedge_index h)
{
assert(handle_of(h).next().next().next() != h && "does not work for triangles");
assert(!handle_of(h).edge().is_boundary() && "does not work on boundaries");
assert(handle_of(h).vertex_to().adjacent_vertices().size() > 2 && "does not work on valence <= 2 vertices");
auto h0 = h;
auto h1 = opposite(h);
auto &h0_ref = halfedge(h0);
auto &h1_ref = halfedge(h1);
auto h0_next = h0_ref.next_halfedge;
auto h1_prev = h1_ref.prev_halfedge;
auto &h0_next_ref = halfedge(h0_next);
auto &h1_prev_ref = halfedge(h1_prev);
auto h0_next_next = h0_next_ref.next_halfedge;
auto &h0_next_next_ref = halfedge(h0_next_next);
// fix vertex
auto &v = vertex(h0_ref.to_vertex);
if (v.outgoing_halfedge == h1)
v.outgoing_halfedge = h0_next;
// fix faces
face(h0_ref.face).halfedge = h0;
face(h1_ref.face).halfedge = h1;
// fix half-edges
h0_ref.to_vertex = h0_next_ref.to_vertex;
h0_next_ref.face = h1_ref.face;
// move to next
h1_prev_ref.next_halfedge = h0_next;
h0_next_ref.prev_halfedge = h1_prev;
h0_next_ref.next_halfedge = h1;
h1_ref.prev_halfedge = h0_next;
h0_ref.next_halfedge = h0_next_next;
h0_next_next_ref.prev_halfedge = h0;
// fix boundary state
fix_boundary_state_of(h0_ref.face);
fix_boundary_state_of(h1_ref.face);
}
inline void Mesh::halfedge_rotate_prev(halfedge_index h)
{
assert(handle_of(h).prev().prev().prev() != h && "does not work for triangles");
assert(!handle_of(h).edge().is_boundary() && "does not work on boundaries");
assert(handle_of(h).vertex_from().adjacent_vertices().size() > 2 && "does not work on valence <= 2 vertices");
auto h0 = h;
auto h1 = opposite(h);
auto &h0_ref = halfedge(h0);
auto &h1_ref = halfedge(h1);
auto h0_prev = h0_ref.prev_halfedge;
auto h1_next = h1_ref.next_halfedge;
auto &h0_prev_ref = halfedge(h0_prev);
auto &h1_next_ref = halfedge(h1_next);
auto h0_prev_prev = h0_prev_ref.prev_halfedge;
auto &h0_prev_prev_ref = halfedge(h0_prev_prev);
// fix vertex
auto &v = vertex(h1_ref.to_vertex);
if (v.outgoing_halfedge == h0)
v.outgoing_halfedge = h1_next;
// fix faces
face(h0_ref.face).halfedge = h0;
face(h1_ref.face).halfedge = h1;
// fix half-edge
h1_ref.to_vertex = h0_prev_prev_ref.to_vertex;
h0_prev_ref.face = h1_ref.face;
// move to next
h0_prev_ref.next_halfedge = h1_next;
h1_next_ref.prev_halfedge = h0_prev;
h1_ref.next_halfedge = h0_prev;
h0_prev_ref.prev_halfedge = h1;
h0_prev_prev_ref.next_halfedge = h0;
h0_ref.prev_halfedge = h0_prev_prev;
// fix boundary state
fix_boundary_state_of(h0_ref.face);
fix_boundary_state_of(h1_ref.face);
}
inline void Mesh::compactify()
{
if (is_compact())
......
......@@ -245,6 +245,27 @@ auto smart_range<this_t, ElementT>::to_map(FuncT &&f) const -> std::map<ElementT
return m;
}
template <class this_t, class element_handle>
int primitive_ring<this_t, element_handle>::size() const
{
auto cnt = 0;
for (auto v : *static_cast<this_t const *>(this))
{
(void)v; // unused
cnt++;
}
return cnt;
}
template <class this_t, class tag>
bool primitive_ring<this_t, tag>::contains(handle v) const
{
for (auto v2 : *static_cast<this_t const *>(this))
if (v == v2)
return true;
return false;
}
template <class mesh_ptr, class tag, class iterator>
int smart_collection<mesh_ptr, tag, iterator>::size() const
{
......@@ -272,7 +293,7 @@ typename primitive<tag>::template attribute<PropT> smart_collection<mesh_ptr, ta
template <class mesh_ptr, class tag, class iterator>
template <class FuncT, class PropT>
typename primitive<tag>::template attribute<PropT> smart_collection<mesh_ptr, tag, iterator>::make_attribute(FuncT &&f, PropT const& def_value) const
typename primitive<tag>::template attribute<PropT> smart_collection<mesh_ptr, tag, iterator>::make_attribute(FuncT &&f, PropT const &def_value) const
{
auto attr = make_attribute_default<PropT>(def_value);
for (auto h : *this)
......@@ -502,14 +523,68 @@ face_handle face_collection<iterator>::add(const halfedge_handle (&half_loop)[N]
}
template <class iterator>
edge_handle edge_collection<iterator>::add_or_get(vertex_handle v_from, vertex_handle v_to)
edge_handle edge_collection<iterator>::add_or_get(vertex_handle v_from, vertex_handle v_to) const
{
return this->mesh->handle_of(this->mesh->add_or_get_edge(v_from.idx, v_to.idx));
}
template <class iterator>
halfedge_handle halfedge_collection<iterator>::add_or_get(vertex_handle v_from, vertex_handle v_to)
halfedge_handle halfedge_collection<iterator>::add_or_get(vertex_handle v_from, vertex_handle v_to) const
{
return this->mesh->handle_of(this->mesh->add_or_get_halfedge(v_from.idx, v_to.idx));
}
template <class iterator>
edge_handle edge_collection<iterator>::find(vertex_handle v_from, vertex_handle v_to) const
{
return this->mesh->handle_of(this->mesh->edge_of(this->mesh->find_halfedge(v_from.idx, v_to.idx)));
}
template <class iterator>
halfedge_handle halfedge_collection<iterator>::find(vertex_handle v_from, vertex_handle v_to) const
{
return this->mesh->handle_of(this->mesh->find_halfedge(v_from.idx, v_to.idx));
}
template <class iterator>
vertex_handle face_collection<iterator>::split(face_handle f) const
{
return this->mesh->handle_of(this->mesh->face_split(f.idx));
}
template <class iterator>
vertex_handle edge_collection<iterator>::split(edge_handle e) const
{
return this->mesh->handle_of(this->mesh->edge_split(e.idx));
}
template <class iterator>
void edge_collection<iterator>::rotate_next(edge_handle e) const
{
this->mesh->edge_rotate_next(e.idx);
}
template <class iterator>
void edge_collection<iterator>::rotate_prev(edge_handle e) const
{
this->mesh->edge_rotate_prev(e.idx);
}
template <class iterator>
vertex_handle halfedge_collection<iterator>::split(halfedge_handle h) const
{
return this->mesh->handle_of(this->mesh->halfedge_split(h.idx));
}
template <class iterator>
void halfedge_collection<iterator>::rotate_next(halfedge_handle h) const
{
this->mesh->halfedge_rotate_next(h.idx);
}
template <class iterator>
void halfedge_collection<iterator>::rotate_prev(halfedge_handle h) const
{
this->mesh->halfedge_rotate_prev(h.idx);
}
}
......@@ -99,7 +99,7 @@ struct smart_range
/// converts this range to a map containing {v, f(v)} entries
template <class FuncT>
auto to_map(FuncT&& f) const -> std::map<ElementT, tmp::decayed_result_type_of<FuncT, ElementT>>;
// TODO: (requires new ranges)
// - filter (or where?)
// - map
......@@ -180,6 +180,11 @@ struct face_collection : smart_collection<Mesh*, face_tag, iterator>
face_handle add(std::vector<halfedge_handle> const& half_loop) const;
face_handle add(halfedge_handle const* half_loop, int vcnt) const;
/// Splits a face by inserting a vertex (which is returned) and creating triangles towards it
/// Preserves half-edge attributes
/// The face itself is deleted and multiple new ones are created
vertex_handle split(face_handle f) const;
/// Removes a face (adjacent edges and vertices are NOT removed)
/// (marks it as removed, compactify mesh to actually remove it)
void remove(face_handle f) const;
......@@ -192,9 +197,25 @@ struct edge_collection : smart_collection<Mesh*, edge_tag, iterator>
{
/// Adds an edge between two existing, distinct vertices
/// if edge already exists, returns it
edge_handle add_or_get(vertex_handle v_from, vertex_handle v_to);
// TODO: find
edge_handle add_or_get(vertex_handle v_from, vertex_handle v_to) const;
/// Returns the edge handle between two vertices (invalid if not found)
/// O(valence) computation
edge_handle find(vertex_handle v_from, vertex_handle v_to) const;
/// Splits this edge in half by inserting a vertex (which is returned)
/// Preserves face attributes
/// The edge itself is deleted and two new ones are created
vertex_handle split(edge_handle e) const;
/// Moves both half-edges vertices to their next half-edge vertex
/// Equivalent to an edge flip if both faces are triangular
/// Preserves all attributes
/// NOTE: does not work on boundaries!
/// TODO: image
void rotate_next(edge_handle e) const;
/// Same as rotate_next but with the previous half-edge
void rotate_prev(edge_handle e) const;
/// Removes an edge (and both adjacent faces, vertices are NOT removed)
/// (marks them as removed, compactify mesh to actually remove them)
......@@ -209,9 +230,26 @@ struct halfedge_collection : smart_collection<Mesh*, halfedge_tag, iterator>
/// Adds an half-edge between two existing, distinct vertices
/// if half-edge already exists, returns it
/// (always adds opposite half-edge as well)
halfedge_handle add_or_get(vertex_handle v_from, vertex_handle v_to);
// TODO: find
halfedge_handle add_or_get(vertex_handle v_from, vertex_handle v_to) const;
/// Returns the half-edge handle between two vertices (invalid if not found)
/// O(valence) computation
halfedge_handle find(vertex_handle v_from, vertex_handle v_to) const;
/// Splits this half-edge in half by inserting a vertex (which is returned)
/// Preserves face attributes
/// Contraty to edges().split, the edge is preserved and a single new one is inserted AFTER h
/// (thus h->next() is the newly inserted edge and h->vertex_to() is the returned vertex)
vertex_handle split(halfedge_handle h) const;
/// Moves the to-vertex of this half-edge to the same as the next half-edge
/// Preserves all attributes
/// NOTE: does not work on boundaries!
/// NOTE: does not work on triangles!
/// TODO: image
void rotate_next(halfedge_handle h) const;
/// Same as rotate_next but with the previous half-edge
void rotate_prev(halfedge_handle h) const;
/// Removes the edge and both half-edges belonging to it (and both adjacent faces, vertices are NOT removed)
/// (marks them as removed, compactify mesh to actually remove them)
......@@ -382,28 +420,4 @@ struct vertex_face_ring : vertex_primitive_ring<face_tag, vertex_face_circulator
using vertex_primitive_ring<face_tag, vertex_face_circulator>::vertex_primitive_ring;
};
/// ======== IMPLEMENTATION ========
template <class this_t, class element_handle>
int primitive_ring<this_t, element_handle>::size() const
{
auto cnt = 0;
for (auto v : *static_cast<this_t const*>(this))
{
(void)v; // unused
cnt++;
}
return cnt;
}
template <class this_t, class tag>
bool primitive_ring<this_t, tag>::contains(handle v) const
{
for (auto v2 : *static_cast<this_t const*>(this))
if (v == v2)
return true;
return false;
}
}
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment