Skip to content

Commit b704f0e

Browse files
Merge pull request #99 from MarkGillespie/Remeshing
Simplification & subdivision
2 parents 86b1d22 + 067e370 commit b704f0e

File tree

8 files changed

+523
-29
lines changed

8 files changed

+523
-29
lines changed

include/geometrycentral/surface/mutation_manager.h

Lines changed: 15 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -128,20 +128,23 @@ class MutationManager {
128128
// to `tSplit`.
129129
// In general, both the `tSplit` and the `newVertexPosition` are used; `tSplit` is necessary to allow callbacks to
130130
// interpolate data; if called with either the other will be inferred.
131-
void splitEdge(Edge e, double tSplit);
132-
void splitEdge(Edge e, Vector3 newVertexPosition);
133-
void splitEdge(Edge e, double tSplit, Vector3 newVertexPosition);
131+
// Returns the new halfedge which points away from the new vertex (so he.vertex() is the new vertex), and is the same
132+
// direction as e.halfedge() on the original edge. The halfedge direction of the other part of the new split edge is
133+
// also preserved.
134+
Halfedge splitEdge(Edge e, double tSplit);
135+
Halfedge splitEdge(Edge e, Vector3 newVertexPosition);
136+
Halfedge splitEdge(Edge e, double tSplit, Vector3 newVertexPosition);
134137

135138
// Collapse an edge.
136-
// Returns true if the edge could actually be collapsed.
137-
bool collapseEdge(Edge e, double tCollapse);
138-
bool collapseEdge(Edge e, Vector3 newVertexPosition);
139-
bool collapseEdge(Edge e, double tCollapse, Vector3 newVertexPosition);
140-
141-
// Split a face (i.e. insert a vertex into the face)
142-
void splitFace(Face f, const std::vector<double>& bSplit);
143-
void splitFace(Face f, Vector3 newVertexPosition);
144-
void splitFace(Face f, const std::vector<double>& bSplit, Vector3 newVertexPosition);
139+
// Returns the new vertex if the edge could be collapsed, and Vertex() otherwise
140+
Vertex collapseEdge(Edge e, double tCollapse);
141+
Vertex collapseEdge(Edge e, Vector3 newVertexPosition);
142+
Vertex collapseEdge(Edge e, double tCollapse, Vector3 newVertexPosition);
143+
144+
// Split a face (i.e. insert a vertex into the face), and return the new vertex
145+
Vertex splitFace(Face f, const std::vector<double>& bSplit);
146+
Vertex splitFace(Face f, Vector3 newVertexPosition);
147+
Vertex splitFace(Face f, const std::vector<double>& bSplit, Vector3 newVertexPosition);
145148

146149
// ======================================================
147150
// ======== High-level mutations
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
#pragma once
2+
3+
#include "geometrycentral/surface/manifold_surface_mesh.h"
4+
#include "geometrycentral/surface/mutation_manager.h"
5+
#include "geometrycentral/surface/vertex_position_geometry.h"
6+
7+
#include "geometrycentral/utilities/elementary_geometry.h"
8+
9+
#include <queue>
10+
11+
namespace geometrycentral {
12+
namespace surface {
13+
class Quadric {
14+
public:
15+
Quadric();
16+
Quadric(const Eigen::Matrix3d& A_, const Eigen::Vector3d& b_, double c_);
17+
Quadric(const Quadric& Q1, const Quadric& Q2);
18+
double cost(const Eigen::Vector3d& v);
19+
Eigen::Vector3d optimalPoint();
20+
21+
Quadric operator+=(const Quadric& Q);
22+
23+
protected:
24+
Eigen::Matrix3d A;
25+
Eigen::Vector3d b;
26+
double c;
27+
28+
friend Quadric operator+(const Quadric& Q1, const Quadric& Q2);
29+
};
30+
31+
Quadric operator+(const Quadric& Q1, const Quadric& Q2);
32+
33+
void quadricErrorSimplify(ManifoldSurfaceMesh& mesh, VertexPositionGeometry& geo, double tol = 0.05);
34+
void quadricErrorSimplify(ManifoldSurfaceMesh& mesh, VertexPositionGeometry& geo, double tol, MutationManager& mm);
35+
36+
} // namespace surface
37+
} // namespace geometrycentral
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
#pragma once
2+
3+
#include "geometrycentral/surface/manifold_surface_mesh.h"
4+
#include "geometrycentral/surface/mutation_manager.h"
5+
#include "geometrycentral/surface/vertex_position_geometry.h"
6+
7+
namespace geometrycentral {
8+
namespace surface {
9+
10+
// Subdivide to form quad mesh
11+
// Note: these do not take MutationManager arguments since MutationManager only supports triangular meshes at the moment
12+
void linearSubdivide(ManifoldSurfaceMesh& mesh, VertexPositionGeometry& geo);
13+
void catmullClarkSubdivide(ManifoldSurfaceMesh& mesh, VertexPositionGeometry& geo);
14+
15+
void loopSubdivide(ManifoldSurfaceMesh& mesh, VertexPositionGeometry& geo);
16+
void loopSubdivide(ManifoldSurfaceMesh& mesh, VertexPositionGeometry& geo, MutationManager& mm);
17+
18+
} // namespace surface
19+
} // namespace geometrycentral

src/CMakeLists.txt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,8 @@ SET(SRCS
4747
surface/tufted_laplacian.cpp
4848
surface/flip_geodesics.cpp
4949
surface/transfer_functions.cpp
50+
surface/quadric_error_simplification.cpp
51+
surface/subdivide.cpp
5052
#surface/detect_symmetry.cpp
5153
#surface/mesh_ray_tracer.cpp
5254

@@ -107,13 +109,15 @@ SET(HEADERS
107109
${INCLUDE_ROOT}/surface/mesh_graph_algorithms.h
108110
${INCLUDE_ROOT}/surface/mesh_ray_tracer.h
109111
${INCLUDE_ROOT}/surface/parameterize.h
112+
${INCLUDE_ROOT}/surface/quadric_error_simplification.h
110113
${INCLUDE_ROOT}/surface/rich_surface_mesh_data.h
111114
${INCLUDE_ROOT}/surface/rich_surface_mesh_data.ipp
112115
${INCLUDE_ROOT}/surface/polygon_soup_mesh.h
113116
${INCLUDE_ROOT}/surface/signpost_intrinsic_triangulation.h
114117
${INCLUDE_ROOT}/surface/signpost_intrinsic_triangulation.ipp
115118
${INCLUDE_ROOT}/surface/simple_idt.h
116119
${INCLUDE_ROOT}/surface/simple_polygon_mesh.h
120+
${INCLUDE_ROOT}/surface/subdivide.h
117121
${INCLUDE_ROOT}/surface/surface_centers.h
118122
${INCLUDE_ROOT}/surface/surface_mesh.h
119123
${INCLUDE_ROOT}/surface/surface_mesh.ipp

src/surface/manifold_surface_mesh.cpp

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1263,6 +1263,64 @@ Vertex ManifoldSurfaceMesh::collapseEdgeTriangular(Edge e) {
12631263
modificationTick++;
12641264
}
12651265

1266+
Face ManifoldSurfaceMesh::removeEdge(Edge e) {
1267+
if (e.isBoundary()) {
1268+
throw std::runtime_error("not implemented");
1269+
}
1270+
1271+
// Halfedges/edges/faces that will be removed
1272+
// (except first face)
1273+
std::vector<Halfedge> toRemove{e.halfedge(), e.halfedge().twin()};
1274+
std::vector<Halfedge> ringHalfedges;
1275+
for (Halfedge heStart : toRemove) {
1276+
Halfedge he = heStart.next();
1277+
while (he != heStart) {
1278+
// The one-ring must not contain any other copies of e, or we cannot remove the edge
1279+
if (he.edge() == e) {
1280+
return Face();
1281+
}
1282+
ringHalfedges.push_back(he);
1283+
he = he.next();
1284+
}
1285+
}
1286+
1287+
// If both faces are the same, we cannot remove the edge. This should have been caught above
1288+
if (toRemove[0].face() == toRemove[1].face()) {
1289+
return Face();
1290+
}
1291+
Face keepFace = toRemove[0].face();
1292+
1293+
1294+
// Record these before we break pointers
1295+
Vertex src = e.halfedge().vertex();
1296+
Vertex dst = e.halfedge().twin().vertex();
1297+
Halfedge altSrcHedge = e.halfedge().twin().next();
1298+
Halfedge altDstHedge = e.halfedge().next();
1299+
1300+
// Hook up next and face refs for the halfedges along the ring
1301+
size_t N = ringHalfedges.size();
1302+
for (size_t i = 0; i < N; i++) {
1303+
heNextArr[ringHalfedges[i].getIndex()] = ringHalfedges[(i + 1) % N].getIndex();
1304+
heFaceArr[ringHalfedges[i].getIndex()] = keepFace.getIndex();
1305+
}
1306+
1307+
// only update vHalfedgeArr if needed to avoid disturbing boundary halfedges
1308+
if (src.halfedge().edge() == e) {
1309+
vHalfedgeArr[src.getIndex()] = altSrcHedge.getIndex();
1310+
}
1311+
if (dst.halfedge().edge() == e) {
1312+
vHalfedgeArr[dst.getIndex()] = altDstHedge.getIndex();
1313+
}
1314+
1315+
fHalfedgeArr[keepFace.getIndex()] = ringHalfedges[0].getIndex();
1316+
1317+
// Actually delete all of the elements
1318+
deleteElement(toRemove[1].face());
1319+
deleteEdgeBundle(e);
1320+
1321+
modificationTick++;
1322+
return keepFace;
1323+
}
12661324

12671325

12681326
bool ManifoldSurfaceMesh::removeFaceAlongBoundary(Face f) {

src/surface/mutation_manager.cpp

Lines changed: 21 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -59,16 +59,16 @@ bool MutationManager::flipEdge(Edge e) {
5959
return true;
6060
}
6161

62-
void MutationManager::splitEdge(Edge e, double tSplit) {
62+
Halfedge MutationManager::splitEdge(Edge e, double tSplit) {
6363
Vector3 newPos{0., 0., 0.};
6464
if (geometry) {
6565
VertexData<Vector3>& pos = geometry->vertexPositions;
6666
newPos = (1. - tSplit) * pos[e.halfedge().tailVertex()] + tSplit * pos[e.halfedge().tipVertex()];
6767
}
68-
splitEdge(e, tSplit, newPos);
68+
return splitEdge(e, tSplit, newPos);
6969
}
7070

71-
void MutationManager::splitEdge(Edge e, Vector3 newVertexPosition) {
71+
Halfedge MutationManager::splitEdge(Edge e, Vector3 newVertexPosition) {
7272

7373
double tSplit = -1;
7474
GC_SAFETY_ASSERT(geometry, "must have geometry to split by position");
@@ -80,10 +80,10 @@ void MutationManager::splitEdge(Edge e, Vector3 newVertexPosition) {
8080
tSplit = pointLineSegmentNeaestLocation(newVertexPosition, posTail, posTip);
8181
}
8282

83-
splitEdge(e, tSplit, newVertexPosition);
83+
return splitEdge(e, tSplit, newVertexPosition);
8484
}
8585

86-
void MutationManager::splitEdge(Edge e, double tSplit, Vector3 newVertexPosition) {
86+
Halfedge MutationManager::splitEdge(Edge e, double tSplit, Vector3 newVertexPosition) {
8787

8888
// Invoke before callbacks
8989
for (EdgeSplitPolicy* policy : edgeSplitPolicies) {
@@ -102,11 +102,13 @@ void MutationManager::splitEdge(Edge e, double tSplit, Vector3 newVertexPosition
102102
for (EdgeSplitPolicy* policy : edgeSplitPolicies) {
103103
policy->afterEdgeSplit(newHeFront, newHeBack, tSplit);
104104
}
105+
106+
return newHeFront;
105107
}
106108

107109
// Collapse an edge.
108-
// Returns true if the edge could actually be collapsed.
109-
bool MutationManager::collapseEdge(Edge e, double tCollapse) {
110+
// Returns the new vertex if the edge could be collapsed, and Vertex() otherwise
111+
Vertex MutationManager::collapseEdge(Edge e, double tCollapse) {
110112
Vector3 newPos{0., 0., 0.};
111113
if (geometry) {
112114
// Find the nearest tCoord
@@ -116,7 +118,7 @@ bool MutationManager::collapseEdge(Edge e, double tCollapse) {
116118
return collapseEdge(e, tCollapse, newPos);
117119
}
118120

119-
bool MutationManager::collapseEdge(Edge e, Vector3 newVertexPosition) {
121+
Vertex MutationManager::collapseEdge(Edge e, Vector3 newVertexPosition) {
120122

121123
double tCollapse = -1;
122124
GC_SAFETY_ASSERT(geometry, "must have geometry to split by position");
@@ -130,8 +132,7 @@ bool MutationManager::collapseEdge(Edge e, Vector3 newVertexPosition) {
130132

131133
return collapseEdge(e, tCollapse, newVertexPosition);
132134
}
133-
134-
bool MutationManager::collapseEdge(Edge e, double tCollapse, Vector3 newVertexPosition) {
135+
Vertex MutationManager::collapseEdge(Edge e, double tCollapse, Vector3 newVertexPosition) {
135136

136137
// Invoke before callbacks
137138
// TODO need to handle possiblity that collapse fails -- check before calling
@@ -140,7 +141,7 @@ bool MutationManager::collapseEdge(Edge e, double tCollapse, Vector3 newVertexPo
140141
}
141142

142143
Vertex newV = mesh.collapseEdgeTriangular(e);
143-
if (newV == Vertex()) return false;
144+
if (newV == Vertex()) return Vertex();
144145

145146
if (geometry) {
146147
VertexData<Vector3>& pos = geometry->vertexPositions;
@@ -152,11 +153,11 @@ bool MutationManager::collapseEdge(Edge e, double tCollapse, Vector3 newVertexPo
152153
policy->afterEdgeCollapse(newV, tCollapse);
153154
}
154155

155-
return true;
156+
return newV;
156157
}
157158

158159
// Split a face (i.e. insert a vertex into the face)
159-
void MutationManager::splitFace(Face f, const std::vector<double>& bSplit) {
160+
Vertex MutationManager::splitFace(Face f, const std::vector<double>& bSplit) {
160161
Vector3 newPos = Vector3::zero();
161162
if (geometry) {
162163
size_t iV = 0;
@@ -167,16 +168,17 @@ void MutationManager::splitFace(Face f, const std::vector<double>& bSplit) {
167168
}
168169
}
169170

170-
splitFace(f, bSplit, newPos);
171+
return splitFace(f, bSplit, newPos);
171172
}
172173

173-
void MutationManager::splitFace(Face f, Vector3 newVertexPosition) {
174+
Vertex MutationManager::splitFace(Face f, Vector3 newVertexPosition) {
174175
// TODO
175176
throw std::runtime_error("Face split based on vertex position not implemented yet");
177+
return Vertex();
176178
}
177179

178180

179-
void MutationManager::splitFace(Face f, const std::vector<double>& bSplit, Vector3 newVertexPosition) {
181+
Vertex MutationManager::splitFace(Face f, const std::vector<double>& bSplit, Vector3 newVertexPosition) {
180182
// Invoke before callbacks
181183
for (FaceSplitPolicy* policy : faceSplitPolicies) {
182184
policy->beforeFaceSplit(f, bSplit);
@@ -192,6 +194,8 @@ void MutationManager::splitFace(Face f, const std::vector<double>& bSplit, Vecto
192194
for (FaceSplitPolicy* policy : faceSplitPolicies) {
193195
policy->afterFaceSplit(newV, bSplit);
194196
}
197+
198+
return newV;
195199
}
196200

197201

@@ -290,7 +294,7 @@ MutationPolicyHandle MutationManager::registerPolicy(MutationPolicy* policyObjec
290294
}
291295

292296
void MutationManager::removePolicy(const MutationPolicyHandle& toRemove) {
293-
// Remove from all lists
297+
// Remove from all lists
294298
removeFromVector(vertexRepositionPolicies, toRemove.policy);
295299
removeFromVector(edgeFlipPolicies, toRemove.policy);
296300
removeFromVector(edgeSplitPolicies, toRemove.policy);

0 commit comments

Comments
 (0)