Skip to content

Commit 648c71e

Browse files
committed
initial commit
1 parent ee09148 commit 648c71e

File tree

2 files changed

+149
-0
lines changed

2 files changed

+149
-0
lines changed
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
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 <queue>
8+
9+
namespace geometrycentral {
10+
namespace surface {
11+
class Quadric {
12+
public:
13+
Quadric();
14+
Quadric(const Eigen::Matrix3d& A_, const Eigen::Vector3d& b_, double c_);
15+
Quadric(const Quadric& Q1, const Quadric& Q2);
16+
double cost(const Eigen::Vector3d& v);
17+
Eigen::Vector3d optimalPoint();
18+
19+
Quadric operator+=(const Quadric& Q);
20+
21+
protected:
22+
Eigen::Matrix3d A;
23+
Eigen::Vector3d b;
24+
double c;
25+
26+
friend Quadric operator+(const Quadric& Q1, const Quadric& Q2);
27+
};
28+
29+
Quadric operator+(const Quadric& Q1, const Quadric& Q2);
30+
31+
void quadricErrorSimplify(ManifoldSurfaceMesh& mesh, VertexPositionGeometry& geo, double tol = 0.05);
32+
void quadricErrorSimplify(ManifoldSurfaceMesh& mesh, VertexPositionGeometry& geo, double tol = 0.05,
33+
MutationManager& mm);
34+
35+
} // namespace surface
36+
} // namespace geometrycentral
Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
#include "quadric_error_simplification.h"
2+
3+
namespace geometrycentral {
4+
namespace surface {
5+
6+
Quadric::Quadric() {
7+
A = Eigen::Matrix3d::Zero();
8+
b = Eigen::Vector3d::Zero();
9+
c = 0;
10+
}
11+
12+
Quadric::Quadric(const Quadric& Q1, const Quadric& Q2) {
13+
A = Q1.A + Q2.A;
14+
b = Q1.b + Q2.b;
15+
c = Q1.c + Q2.c;
16+
}
17+
18+
Quadric::Quadric(const Eigen::Matrix3d& A_, const Eigen::Vector3d& b_, double c_) : A(A_), b(b_), c(c_) {}
19+
20+
double Quadric::cost(const Eigen::Vector3d& v) { return v.dot(A * v) + 2 * b.dot(v) + c; }
21+
22+
Eigen::Vector3d Quadric::optimalPoint() { return -A.inverse() * b; }
23+
24+
Quadric Quadric::operator+=(const Quadric& Q) {
25+
A += Q.A;
26+
b += Q.b;
27+
c += Q.c;
28+
29+
return *this;
30+
}
31+
32+
Quadric operator+(const Quadric& Q1, const Quadric& Q2) { return Quadric(Q1.A + Q2.A, Q1.b + Q2.b, Q1.c + Q2.c); }
33+
34+
void quadricErrorSimplify(ManifoldSurfaceMesh& mesh, VertexPositionGeometry& geo, double tol) {
35+
MutationManager mm(mesh);
36+
quadricErrorSimplify(mesh, geo, tol, mm);
37+
}
38+
39+
void quadricErrorSimplify(ManifoldSurfaceMesh& mesh, VertexPositionGeometry& geo, double tol, MutationManager& mm) {
40+
VertexData<QES::Quadric> Q(mesh, QES::Quadric());
41+
42+
geo.requireFaceNormals();
43+
44+
for (Face f : mesh.faces()) {
45+
Eigen::Vector3d n = toEigen(geo.faceNormals[f]);
46+
Eigen::Matrix3d M = n * n.transpose();
47+
for (Vertex v : f.adjacentVertices()) {
48+
Eigen::Vector3d q = toEigen(geo.inputVertexPositions[v]);
49+
double d = -n.dot(q);
50+
51+
Q[v] += QES::Quadric(M, d * n, d * d);
52+
}
53+
}
54+
55+
using PotentialEdge = std::tuple<double, Edge>;
56+
57+
auto cmp = [](const PotentialEdge& a, const PotentialEdge& b) -> bool { return std::get<0>(a) > std::get<0>(b); };
58+
59+
std::priority_queue<PotentialEdge, std::vector<PotentialEdge>, decltype(cmp)> edgesToCheck(cmp);
60+
61+
for (Edge e : mesh.edges()) {
62+
QES::Quadric Qe = Q[e.src()] + Q[e.dst()];
63+
Eigen::Vector3d q = Qe.optimalPoint();
64+
double cost = Qe.cost(q);
65+
edgesToCheck.push(std::make_tuple(cost, e));
66+
}
67+
68+
while (!edgesToCheck.empty()) {
69+
PotentialEdge best = edgesToCheck.top();
70+
edgesToCheck.pop();
71+
72+
// Stop when collapse becomes too expensive
73+
double cost = std::get<0>(best);
74+
if (cost > tol) break;
75+
76+
Edge e = std::get<1>(best);
77+
if (!e.isDead()) {
78+
79+
// Get edge quadric
80+
QES::Quadric Qe(Q[e.src()], Q[e.dst()]);
81+
Eigen::Vector3d q = Qe.optimalPoint();
82+
83+
// If either vertex has been collapsed since the edge was pushed
84+
// onto the queue, the old cost was wrong. In that case, give up
85+
if (abs(cost - Qe.cost(q)) > 1e-8) continue;
86+
87+
Vertex v;
88+
try {
89+
v = mm.collapseEdge(e);
90+
} catch (std::runtime_error& e) {
91+
}
92+
93+
// v == Vertex() if collapseEdge threw an exception, or if
94+
// collapseEdge failed and returned Vertex(). In either case, we
95+
// give up
96+
if (v == Vertex()) continue;
97+
geo.vertexPositions[v] = fromEigen(q);
98+
Q[v] = Qe;
99+
100+
for (Edge f : v.adjacentEdges()) {
101+
QES::Quadric Qf(Q[f.src()], Q[f.dst()]);
102+
Eigen::Vector3d q = Qf.optimalPoint();
103+
double cost = Qf.cost(q);
104+
edgesToCheck.push(std::make_tuple(cost, f));
105+
}
106+
}
107+
}
108+
109+
mesh.compress();
110+
return;
111+
}
112+
} // namespace surface
113+
} // namespace geometrycentral

0 commit comments

Comments
 (0)