/* * Copyright (C) 2023 Christopher J. Howard * * This file is part of Antkeeper source code. * * Antkeeper source code is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * Antkeeper source code is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Antkeeper source code. If not, see . */ #include #include namespace geom { mesh::mesh(const mesh& other) { *this = other; } mesh::~mesh() { clear(); } mesh& mesh::operator=(const mesh& other) { // Clear the mesh clear(); // Resize vertices, edges, and faces vertices.resize(other.vertices.size()); edges.resize(other.edges.size()); faces.resize(other.faces.size()); // Allocate vertices for (std::size_t i = 0; i < vertices.size(); ++i) vertices[i] = new vertex(); // Allocate edges for (std::size_t i = 0; i < edges.size(); ++i) { edges[i] = new edge(); edges[i]->symmetric = new edge(); edges[i]->symmetric->symmetric = edges[i]; } // Allocate faces for (std::size_t i = 0; i < faces.size(); ++i) faces[i] = new face(); // Copy vertices for (std::size_t i = 0; i < vertices.size(); ++i) { vertex* va = vertices[i]; const vertex* vb = other.vertices[i]; va->index = vb->index; va->position = vb->position; va->edge = nullptr; if (vb->edge) { va->edge = edges[vb->edge->index]; if (vb->edge != other.edges[vb->edge->index]) va->edge = va->edge->symmetric; } } // Copy edges for (std::size_t i = 0; i < edges.size(); ++i) { edge* ea = edges[i]; const edge* eb = other.edges[i]; for (std::size_t j = 0; j < 2; ++j) { ea->index = eb->index; ea->vertex = vertices[eb->vertex->index]; ea->face = nullptr; if (eb->face) ea->face = faces[eb->face->index]; ea->previous = edges[eb->previous->index]; if (eb->previous != other.edges[eb->previous->index]) ea->previous = ea->previous->symmetric; ea->next = edges[eb->next->index]; if (eb->next != other.edges[eb->next->index]) ea->next = ea->next->symmetric; ea = ea->symmetric; eb = eb->symmetric; } } // Copy faces for (std::size_t i = 0; i < faces.size(); ++i) { face* fa = faces[i]; const face* fb = other.faces[i]; fa->index = fb->index; fa->edge = edges[fb->edge->index]; if (fb->edge != other.edges[fb->edge->index]) fa->edge = fa->edge->symmetric; } return *this; } void mesh::clear() noexcept { // Deallocate vertices for (mesh::vertex* vertex: vertices) delete vertex; // Deallocate edges for (mesh::edge* edge: edges) { delete edge->symmetric; delete edge; } // Deallocate faces for (mesh::face* face: faces) delete face; vertices.clear(); edges.clear(); faces.clear(); } mesh::vertex* mesh::add_vertex(const float3& position) { mesh::vertex* vertex = new mesh::vertex { vertices.size(), nullptr, position }; vertices.push_back(vertex); return vertex; } mesh::edge* mesh::add_edge(mesh::vertex* a, mesh::vertex* b) { mesh::edge* ab = new mesh::edge(); mesh::edge* ba = new mesh::edge(); ab->index = edges.size(); ab->vertex = a; ab->face = nullptr; ab->previous = ba; ab->next = ba; ab->symmetric = ba; ba->index = edges.size(); ba->vertex = b; ba->face = nullptr; ba->previous = ab; ba->next = ab; ba->symmetric = ab; if (!a->edge) { a->edge = ab; } else { mesh::edge* a_in = find_free_incident(a); mesh::edge* a_out = a_in->next; a_in->next = ab; ab->previous = a_in; ba->next = a_out; a_out->previous = ba; } if (!b->edge) { b->edge = ba; } else { mesh::edge* b_in = find_free_incident(b); mesh::edge* b_out = b_in->next; b_in->next = ba; ba->previous = b_in; ab->next = b_out; b_out->previous = ab; } // Add edge edges.push_back(ab); return ab; } mesh::face* mesh::add_face(const loop& loop) { if (loop.empty()) { throw std::runtime_error("Empty edge loop"); } // Validate edge loop for (std::size_t i = 0; i < loop.size(); ++i) { mesh::edge* current = loop[i]; mesh::edge* next = loop[(i + 1) % loop.size()]; if (current->symmetric->vertex != next->vertex) { // Disconnected edge loop throw std::runtime_error("Disconnected edge loop"); } if (current->face) { // This edge already has a face throw std::runtime_error("Non-manifold mesh 1"); } } // Make edges adjacent for (std::size_t i = 0; i < loop.size(); ++i) { if (!make_adjacent(loop[i], loop[(i + 1) % loop.size()])) { throw std::runtime_error("Non-manifold mesh 2"); } } // Create face mesh::face* face = new mesh::face(); face->edge = loop[0]; face->index = faces.size(); // Add face faces.push_back(face); // Connect edges to the face for (mesh::edge* edge: loop) { edge->face = face; } return face; } void mesh::remove_face(mesh::face* face) { // Nullify pointers to this face mesh::edge* edge = face->edge; do { edge->face = nullptr; edge = edge->next; } while (edge != face->edge); // Adjust indices of faces after this face for (std::size_t i = face->index + 1; i < faces.size(); ++i) { --faces[i]->index; } // Remove face from the faces vector faces.erase(faces.begin() + face->index); // Deallocate face delete face; } void mesh::remove_edge(mesh::edge* edge) { mesh::edge* ab = edge; mesh::edge* ba = edge->symmetric; mesh::vertex* a = ab->vertex; mesh::edge* a_in = ab->previous; mesh::edge* a_out = ba->next; mesh::vertex* b = ba->vertex; mesh::edge* b_in = ba->previous; mesh::edge* b_out = ab->next; // Remove dependent faces if (ab->face) remove_face(ab->face); if (ba->face) remove_face(ba->face); // Re-link edges if (a->edge == ab) a->edge = (a_out == ab) ? nullptr : a_out; if (b->edge == ba) b->edge = (b_out == ba) ? nullptr : b_out; a_in->next = a_out; a_out->previous = a_in; b_in->next = b_out; b_out->previous = b_in; // Adjust indices of edges after this edge for (std::size_t i = edge->index + 1; i < edges.size(); ++i) { --edges[i]->index; --edges[i]->symmetric->index; } // Remove edge from the edges vector edges.erase(edges.begin() + edge->index); // Deallocate edge delete edge->symmetric; delete edge; } void mesh::remove_vertex(mesh::vertex* vertex) { // Remove connected edges if (vertex->edge) { mesh::edge* current = nullptr; mesh::edge* next = vertex->edge; do { current = next; next = next->symmetric->next; if (next == current) { next = next->symmetric->next; } remove_edge(current); } while (current != next); } // Adjust indices of vertices after this vertex for (std::size_t i = vertex->index + 1; i < vertices.size(); ++i) { --vertices[i]->index; } // Remove vertex from the vertices vector vertices.erase(vertices.begin() + vertex->index); // Deallocate vertex delete vertex; } mesh::edge* mesh::find_free_incident(mesh::vertex* vertex) const { mesh::edge* begin = vertex->edge->symmetric; mesh::edge* current = begin; do { if (!current->face) { return current; } current = current->next->symmetric; } while (current != begin); return nullptr; } mesh::edge* mesh::find_free_incident(mesh::edge* start_edge, mesh::edge* end_edge) const { if (start_edge == end_edge) { return nullptr; } mesh::edge* current = start_edge; do { if (!current->face) { return current; } current = current->next->symmetric; } while (current != end_edge); return nullptr; } bool mesh::make_adjacent(mesh::edge* in, mesh::edge* out) { if (in->next == out) { return true; } mesh::edge* b = in->next; mesh::edge* d = out->previous; mesh::edge* g = find_free_incident(out->symmetric, in); if (!g) { return false; } mesh::edge* h = g->next; in->next = out; out->previous = in; g->next = b; b->previous = g; d->next = h; h->previous = d; return true; } } // namespace geom