diff --git a/README.html b/README.html
index 6c13281..c163d77 100644
--- a/README.html
+++ b/README.html
@@ -343,7 +343,7 @@
Constant function approximation
Linear function approximation
-If we make make a slightly more appropriate assuming that in the neighborhood
+
If we make make a slightly more appropriate assumption that in the neighborhood
of the \(P_Y(\z₀)\) the surface \(Y\) is a plane, then we can improve this
approximation while keeping \(\f\) linear in \(\z\):
diff --git a/README.md b/README.md
index bce5dc2..ce58753 100644
--- a/README.md
+++ b/README.md
@@ -263,7 +263,7 @@ derived our gradients geometrically.
### Linear function approximation
-If we make make a slightly more appropriate assuming that in the neighborhood
+If we make make a slightly more appropriate assumption that in the neighborhood
of the $P_Y(\z₀)$ the surface $Y$ is a plane, then we can improve this
approximation while keeping $\f$ linear in $\z$:
diff --git a/src/closest_rotation.cpp b/src/closest_rotation.cpp
index 54403c1..c80a674 100644
--- a/src/closest_rotation.cpp
+++ b/src/closest_rotation.cpp
@@ -1,9 +1,17 @@
#include "closest_rotation.h"
+#include
+#include
void closest_rotation(
const Eigen::Matrix3d & M,
Eigen::Matrix3d & R)
{
- // Replace with your code
- R = Eigen::Matrix3d::Identity();
+ Eigen::JacobiSVD svd(M, Eigen::ComputeFullU | Eigen::ComputeFullV);
+ Eigen::Matrix3d U = svd.matrixU();
+ Eigen::Matrix3d V = svd.matrixV();
+ Eigen::Matrix3d Sigma(3,3);
+ Sigma << 1, 0, 0,
+ 0, 1, 0,
+ 0, 0, (U * V.transpose()).determinant();
+ R = U * Sigma * V.transpose();
}
diff --git a/src/hausdorff_lower_bound.cpp b/src/hausdorff_lower_bound.cpp
index 8608964..2034614 100644
--- a/src/hausdorff_lower_bound.cpp
+++ b/src/hausdorff_lower_bound.cpp
@@ -1,4 +1,6 @@
#include "hausdorff_lower_bound.h"
+#include "random_points_on_mesh.h"
+#include "point_mesh_distance.h"
double hausdorff_lower_bound(
const Eigen::MatrixXd & VX,
@@ -7,6 +9,13 @@ double hausdorff_lower_bound(
const Eigen::MatrixXi & FY,
const int n)
{
- // Replace with your code
- return 0;
+ Eigen::MatrixXd X(n, 3);
+ random_points_on_mesh(n, VX, FX, X);
+
+ Eigen::VectorXd D(n);
+ Eigen::MatrixXd P(n, 3);
+ Eigen::MatrixXd N(n, 3);
+ point_mesh_distance(X, VY, FY, D, P, N);
+
+ return D.maxCoeff();
}
diff --git a/src/icp_single_iteration.cpp b/src/icp_single_iteration.cpp
index 1e8fda9..70d3081 100644
--- a/src/icp_single_iteration.cpp
+++ b/src/icp_single_iteration.cpp
@@ -1,4 +1,8 @@
#include "icp_single_iteration.h"
+#include "random_points_on_mesh.h"
+#include "point_mesh_distance.h"
+#include "point_to_point_rigid_matching.h"
+#include "point_to_plane_rigid_matching.h"
void icp_single_iteration(
const Eigen::MatrixXd & VX,
@@ -10,7 +14,15 @@ void icp_single_iteration(
Eigen::Matrix3d & R,
Eigen::RowVector3d & t)
{
- // Replace with your code
- R = Eigen::Matrix3d::Identity();
- t = Eigen::RowVector3d::Zero();
+ Eigen::MatrixXd X;
+ random_points_on_mesh(num_samples, VX, FX, X);
+
+ Eigen::MatrixXd P, N;
+ Eigen::VectorXd D;
+ point_mesh_distance(X, VY, FY, D, P, N);
+
+ if (method == ICP_METHOD_POINT_TO_POINT)
+ point_to_point_rigid_matching(X, P, R, t);
+ else
+ point_to_plane_rigid_matching(X, P, N, R, t);
}
diff --git a/src/point_mesh_distance.cpp b/src/point_mesh_distance.cpp
index 2e6a070..e7c16be 100644
--- a/src/point_mesh_distance.cpp
+++ b/src/point_mesh_distance.cpp
@@ -1,4 +1,6 @@
#include "point_mesh_distance.h"
+#include "point_triangle_distance.h"
+#include
void point_mesh_distance(
const Eigen::MatrixXd & X,
@@ -8,9 +10,33 @@ void point_mesh_distance(
Eigen::MatrixXd & P,
Eigen::MatrixXd & N)
{
- // Replace with your code
- P.resizeLike(X);
- N = Eigen::MatrixXd::Zero(X.rows(),X.cols());
- for(int i = 0;i
+using namespace Eigen;
void point_to_plane_rigid_matching(
const Eigen::MatrixXd & X,
@@ -7,7 +10,31 @@ void point_to_plane_rigid_matching(
Eigen::Matrix3d & R,
Eigen::RowVector3d & t)
{
- // Replace with your code
- R = Eigen::Matrix3d::Identity();
- t = Eigen::RowVector3d::Zero();
+ int k = X.rows();
+ MatrixXd A(3*k, 6);
+ VectorXd a(3*k);
+ MatrixXd D(k, 3*k);
+ VectorXd zeros = VectorXd::Zero(k);
+ VectorXd ones = VectorXd::Ones(k);
+ A << zeros, X.col(2), -X.col(1), ones, zeros, zeros,
+ -X.col(2), zeros, X.col(0), zeros, ones, zeros,
+ X.col(1), -X.col(0), zeros, zeros, zeros, ones;
+ a << X.col(0) - P.col(0),
+ X.col(1) - P.col(1),
+ X.col(2) - P.col(2);
+ D << MatrixXd(N.col(0).asDiagonal()),
+ MatrixXd(N.col(1).asDiagonal()),
+ MatrixXd(N.col(2).asDiagonal());
+
+ A = D * A;
+ a = D * a;
+ VectorXd u = (A.transpose() * A).lu().solve(-A.transpose() * a);
+
+ Matrix3d M;
+ M << 1, u(2), -u(1),
+ -u(2), 1, u(0),
+ u(1), -u(0), 1;
+ closest_rotation(M, R);
+
+ t = u.tail(3);
}
diff --git a/src/point_to_point_rigid_matching.cpp b/src/point_to_point_rigid_matching.cpp
index 079151f..58bcd9d 100644
--- a/src/point_to_point_rigid_matching.cpp
+++ b/src/point_to_point_rigid_matching.cpp
@@ -1,5 +1,7 @@
#include "point_to_point_rigid_matching.h"
-#include
+#include "closest_rotation.h"
+#include
+using namespace Eigen;
void point_to_point_rigid_matching(
const Eigen::MatrixXd & X,
@@ -7,8 +9,36 @@ void point_to_point_rigid_matching(
Eigen::Matrix3d & R,
Eigen::RowVector3d & t)
{
- // Replace with your code
- R = Eigen::Matrix3d::Identity();
- t = Eigen::RowVector3d::Zero();
+ /*
+ // this is the closed-form version of the point-to-point minimizer
+ Eigen::RowVector3d x_h = X.colwise().mean();
+ Eigen::RowVector3d p_h = P.colwise().mean();
+ Eigen::Matrix3d M = (P.rowwise() - p_h).transpose() * (X.rowwise() - x_h);
+ closest_rotation(M, R);
+ t = (p_h.transpose() - R * x_h.transpose()).transpose(); */
+
+ int k = X.rows();
+ MatrixXd A(3*k, 6);
+ VectorXd a(3*k);
+ VectorXd zeros = VectorXd::Zero(k);
+ VectorXd ones = VectorXd::Ones(k);
+ A << zeros, X.col(2), -X.col(1), ones, zeros, zeros,
+ -X.col(2), zeros, X.col(0), zeros, ones, zeros,
+ X.col(1), -X.col(0), zeros, zeros, zeros, ones;
+ a << X.col(0) - P.col(0),
+ X.col(1) - P.col(1),
+ X.col(2) - P.col(2);
+
+ VectorXd u = (A.transpose() * A).lu().solve(-A.transpose() * a);
+
+ Matrix3d M;
+
+ // I am not sure why I needed to flip the signs here
+ M << 1, u(2), -u(1),
+ -u(2), 1, u(0),
+ u(1), -u(0), 1;
+ closest_rotation(M, R);
+
+ t = u.tail(3);
}
diff --git a/src/point_triangle_distance.cpp b/src/point_triangle_distance.cpp
index 6405100..89dd3c3 100644
--- a/src/point_triangle_distance.cpp
+++ b/src/point_triangle_distance.cpp
@@ -1,4 +1,5 @@
#include "point_triangle_distance.h"
+#include
void point_triangle_distance(
const Eigen::RowVector3d & x,
@@ -8,7 +9,38 @@ void point_triangle_distance(
double & d,
Eigen::RowVector3d & p)
{
- // Replace with your code
- d = 0;
- p = a;
+ // obtain the normal to abc
+ Eigen::RowVector3d n = (c-a).cross(c-b);
+ n /= n.norm();
+
+ // obtain a candidate p0 by projecting x onto triangle normal
+ Eigen::RowVector3d p0 = x - (x-a).dot(n) * n;
+
+ bool left_of_ab = (b-a).cross(p0-a).dot(n) >= 0;
+ bool left_of_bc = (c-b).cross(p0-b).dot(n) >= 0;
+ bool left_of_ca = (a-c).cross(p0-c).dot(n) >= 0;
+ if (left_of_ab and left_of_bc and left_of_ca) {
+ p = p0; // p is inside the triangle
+ }
+ else {
+ // obtain three more candidates by projecting p onto triangle edges
+ Eigen::RowVector3d p_ab = a + (p0-a).dot(b-a) * (b-a);
+ Eigen::RowVector3d p_bc = b + (p0-b).dot(c-b) * (c-b);
+ Eigen::RowVector3d p_ca = c + (p0-c).dot(a-c) * (a-c);
+ // set p according to region p0 is in
+ if (left_of_ca && left_of_bc)
+ p = p_ab;
+ else if (left_of_ab && left_of_ca)
+ p = p_bc;
+ else if (left_of_bc && left_of_ab)
+ p = p_ca;
+ else if (!left_of_ca && !left_of_ab)
+ p = a;
+ else if (!left_of_ab && !left_of_bc)
+ p = b;
+ else if (!left_of_bc && !left_of_ca)
+ p = c;
+ }
+
+ d = (x - p).norm();
}
diff --git a/src/random_points_on_mesh.cpp b/src/random_points_on_mesh.cpp
index 6e85d75..aa1029f 100644
--- a/src/random_points_on_mesh.cpp
+++ b/src/random_points_on_mesh.cpp
@@ -1,4 +1,8 @@
#include "random_points_on_mesh.h"
+#include
+#include
+#include
+#include
void random_points_on_mesh(
const int n,
@@ -6,8 +10,47 @@ void random_points_on_mesh(
const Eigen::MatrixXi & F,
Eigen::MatrixXd & X)
{
- // REPLACE WITH YOUR CODE:
X.resize(n,3);
- for(int i = 0;i 0 ? C[i-1].area : 0)};
+ }
+ std::sort(C, C + m,
+ [](tr_area t1, tr_area t2){return t1.area < t2.area;});
+
+ for (int i = 0; i < n; i++) {
+
+ // obtain random numbers between 0 and 1
+ double r = std::rand() / double(RAND_MAX);
+ double alpha = std::rand() / double(RAND_MAX);
+ double beta = std::rand() / double(RAND_MAX);
+
+ // perform binary search to find the first entry C[t] in C > r
+ int t = std::lower_bound(C, C + m, tr_area{.Fi = -1, .area = r},
+ [](tr_area t1, tr_area t2){return t1.area < t2.area;}) - C;
+ Eigen::Vector3d v1 = V.row(F(C[t].Fi, 0));
+ Eigen::Vector3d v2 = V.row(F(C[t].Fi, 1));
+ Eigen::Vector3d v3 = V.row(F(C[t].Fi, 2));
+
+ // pick a random point on the triangle
+ if (alpha + beta > 1) {
+ alpha = 1 - alpha;
+ beta = 1 - beta;
+ }
+ Eigen::Vector3d x = v1 + alpha * (v2 - v1) + beta * (v3 - v1);
+
+ X.row(i) = x;
+ }
}