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..7c0d961 100644
--- a/src/closest_rotation.cpp
+++ b/src/closest_rotation.cpp
@@ -1,4 +1,7 @@
#include "closest_rotation.h"
+#include
+#include
+#include
void closest_rotation(
const Eigen::Matrix3d & M,
@@ -6,4 +9,8 @@ void closest_rotation(
{
// Replace with your code
R = Eigen::Matrix3d::Identity();
+ Eigen::JacobiSVD svd(M, Eigen::ComputeFullU | Eigen::ComputeFullV);
+ Eigen::Matrix3d Omega = (svd.matrixU() * svd.matrixV().transpose());
+ R(2,2) = Omega.determinant();
+ R = svd.matrixU() * R * svd.matrixV().transpose();
}
diff --git a/src/hausdorff_lower_bound.cpp b/src/hausdorff_lower_bound.cpp
index 8608964..638a3b8 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,
@@ -8,5 +10,16 @@ double hausdorff_lower_bound(
const int n)
{
// Replace with your code
- return 0;
+ // Sample points from X mesh
+ Eigen::MatrixXd sample_P;
+ random_points_on_mesh(n, VX, FX, sample_P);
+
+ // Figure out their closest points to the Y mesh
+ Eigen::VectorXd D;
+ Eigen::MatrixXd P;
+ Eigen::MatrixXd N;
+ point_mesh_distance(sample_P, VY, FY, D, P, N);
+
+ // compute max D for the hausdorff lb
+ return D.maxCoeff();
}
diff --git a/src/icp_single_iteration.cpp b/src/icp_single_iteration.cpp
index 1e8fda9..39d8c10 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,
@@ -11,6 +15,23 @@ void icp_single_iteration(
Eigen::RowVector3d & t)
{
// Replace with your code
- R = Eigen::Matrix3d::Identity();
- t = Eigen::RowVector3d::Zero();
+
+ // Sample source mesh
+ Eigen::MatrixXd X;
+ random_points_on_mesh(num_samples, VX, FX, X);
+
+ // Project sample points onto mesh Y
+ Eigen::VectorXd D;
+ Eigen::MatrixXd Py;
+ Eigen::MatrixXd Ny;
+ point_mesh_distance(X, VY, FY, D, Py, Ny);
+
+ // Do one iteration of the chosen method to obtain an R and a t
+ if (method == ICP_METHOD_POINT_TO_POINT) {
+ point_to_point_rigid_matching(X, Py, R, t);
+ }
+
+ else if (method == ICP_METHOD_POINT_TO_PLANE) {
+ point_to_plane_rigid_matching(X, Py, Ny, R, t);
+ }
}
diff --git a/src/point_mesh_distance.cpp b/src/point_mesh_distance.cpp
index 2e6a070..e874be7 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 "igl/per_face_normals.h"
void point_mesh_distance(
const Eigen::MatrixXd & X,
@@ -9,8 +11,36 @@ void point_mesh_distance(
Eigen::MatrixXd & N)
{
// Replace with your code
+ D.resize(X.rows());
P.resizeLike(X);
+ Eigen::MatrixXd NY = Eigen::MatrixXd::Zero(FY.rows(), 3);
+ igl::per_face_normals(VY, FY, Eigen::Vector3d::Zero(), NY);
N = Eigen::MatrixXd::Zero(X.rows(),X.cols());
- for(int i = 0;i
void point_to_plane_rigid_matching(
const Eigen::MatrixXd & X,
@@ -7,7 +9,45 @@ void point_to_plane_rigid_matching(
Eigen::Matrix3d & R,
Eigen::RowVector3d & t)
{
- // Replace with your code
- R = Eigen::Matrix3d::Identity();
- t = Eigen::RowVector3d::Zero();
+ // Construct matrix A containing points
+ Eigen::MatrixXd A = Eigen::MatrixXd::Zero(3 * X.rows(), 6);
+ Eigen::VectorXd b(3 * X.rows());
+ A.block(0, 1, X.rows(), 1) = -X.col(2);
+ A.block(0, 2, X.rows(), 1) = X.col(1);
+ A.block(0, 3, X.rows(), 1) = Eigen::MatrixXd::Constant(X.rows(), 1, 1);
+ A.block(X.rows(), 0, X.rows(), 1) = X.col(2);
+ A.block(X.rows(), 2, X.rows(), 1) = -X.col(0);
+ A.block(X.rows(), 4, X.rows(), 1) = Eigen::MatrixXd::Constant(X.rows(), 1, 1);
+ A.block(2 * X.rows(), 0, X.rows(), 1) = -X.col(1);
+ A.block(2 * X.rows(), 1, X.rows(), 1) = X.col(0);
+ A.block(2 * X.rows(), 5, X.rows(), 1) = Eigen::MatrixXd::Constant(X.rows(), 1, 1);
+
+ // Construct matrix D of normals
+ Eigen::MatrixXd D(X.rows(), 3 * X.rows());
+ Eigen::MatrixXd D_1 = N.col(0).asDiagonal();
+ Eigen::MatrixXd D_2 = N.col(1).asDiagonal();
+ Eigen::MatrixXd D_3 = N.col(2).asDiagonal();
+ D.block(0, 0, X.rows(), X.rows()) = D_1;
+ D.block(0, X.rows(), X.rows(), X.rows()) = D_2;
+ D.block(0, 2 * X.rows(), X.rows(), X.rows()) = D_3;
+
+ // Construct vector P -X
+ b << P.col(0) - X.col(0),
+ P.col(1) - X.col(1),
+ P.col(2) - X.col(2);
+
+ // Solve for the least squares solution of ||DAx - D(P-X)||^2.
+ A = D * A;
+ b = D * b;
+ Eigen::MatrixXd L = A.transpose() * A;
+ Eigen::VectorXd r = A.transpose() * b;
+ Eigen::VectorXd u = L.fullPivLu().solve(r);
+
+ // Extract R and t from solution of optimization problem
+ Eigen::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..f4ee8f6 100644
--- a/src/point_to_point_rigid_matching.cpp
+++ b/src/point_to_point_rigid_matching.cpp
@@ -1,5 +1,6 @@
#include "point_to_point_rigid_matching.h"
-#include
+#include "closest_rotation.h"
+#include
void point_to_point_rigid_matching(
const Eigen::MatrixXd & X,
@@ -8,7 +9,36 @@ void point_to_point_rigid_matching(
Eigen::RowVector3d & t)
{
// Replace with your code
- R = Eigen::Matrix3d::Identity();
- t = Eigen::RowVector3d::Zero();
+
+ // Construct matrix A and vector P - X
+ Eigen::MatrixXd A = Eigen::MatrixXd::Zero(3 * X.rows(), 6);
+ Eigen::VectorXd b(3 * X.rows());
+ A.block(0, 1, X.rows(), 1) = -X.col(2);
+ A.block(0, 2, X.rows(), 1) = X.col(1);
+ A.block(0, 3, X.rows(), 1) = Eigen::MatrixXd::Constant(X.rows(), 1, 1);
+ A.block(X.rows(), 0, X.rows(), 1) = X.col(2);
+ A.block(X.rows(), 2, X.rows(), 1) = -X.col(0);
+ A.block(X.rows(), 4, X.rows(), 1) = Eigen::MatrixXd::Constant(X.rows(), 1, 1);
+ A.block(2 * X.rows(), 0, X.rows(), 1) = -X.col(1);
+ A.block(2 * X.rows(), 1, X.rows(), 1) = X.col(0);
+ A.block(2 * X.rows(), 5, X.rows(), 1) = Eigen::MatrixXd::Constant(X.rows(), 1, 1);
+
+ b << P.col(0) - X.col(0),
+ P.col(1) - X.col(1),
+ P.col(2) - X.col(2);
+
+ // Solve for the least squares solution u, which minimizes ||Au - (P - X)||^2.
+ Eigen::MatrixXd L = A.transpose() * A;
+ b = A.transpose() * b;
+ Eigen::VectorXd u = L.fullPivLu().solve(b);
+
+ // Extract the new R and t
+ Eigen::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_triangle_distance.cpp b/src/point_triangle_distance.cpp
index 6405100..b78375e 100644
--- a/src/point_triangle_distance.cpp
+++ b/src/point_triangle_distance.cpp
@@ -9,6 +9,49 @@ void point_triangle_distance(
Eigen::RowVector3d & p)
{
// Replace with your code
- d = 0;
- p = a;
+
+ // Project on plane spanned by triangle and see if it lies in it
+ // Consulted formula at https://math.stackexchange.com/questions/544946/determine-if-projection-of-3d-point-onto-plane-is-within-a-triangle
+
+ // Project x on plane containing the triangle
+ Eigen::RowVector3d u = b - a;
+ Eigen::RowVector3d v = c - a;
+ Eigen::RowVector3d tri_norm = u.cross(v);
+ tri_norm = tri_norm / tri_norm.norm();
+ Eigen::RowVector3d w = x - a;
+ double gamma = (u.cross(w)).dot(tri_norm);
+ double beta = (w.cross(v)).dot(tri_norm);
+ double alpha = 1 - gamma - beta;
+
+ // if projection lies outside triangle, project on different lines of the triangle
+
+ if (alpha > 1 || alpha < 0 || beta > 1 || beta < 0 || gamma > 0 || gamma < 1) {
+ // consulted formula at https://en.wikipedia.org/wiki/Distance_from_a_point_to_a_line
+ double proj_ab = (a - x).dot(u) / (u.norm() * u.norm());
+ double proj_ac = (a - x).dot(v) / (v.norm() * v.norm());
+ double proj_bc = (b - x).dot(v-u) / ((v-u).norm() * (v-u).norm());
+ proj_ab = std::min(std::max(proj_ab, 0.0), 1.0);
+ proj_ac = std::min(std::max(proj_ac, 0.0), 1.0);
+ proj_bc = std::min(std::max(proj_bc, 0.0), 1.0);
+ double dist_ab = ((a-x) - (proj_ab * u)).norm();
+ double dist_ac = ((a-x) - (proj_ac * v)).norm();
+ double dist_bc = ((b-x) - (proj_bc * (v-u))).norm();
+ double min_dist = std::min(std::min(dist_ab, dist_ac), dist_bc);
+ if (min_dist == dist_ac) {
+ d = dist_ac;
+ p = proj_ab * u + a;
+ }
+ if (min_dist == dist_ac) {
+ d = dist_ac;
+ p = proj_ac * v + a;
+ }
+ if (min_dist == dist_bc) {
+ d = dist_bc;
+ p = proj_bc * (v-u) + b;
+ }
+ }
+ else {
+ p = alpha * a + beta * b + gamma * c;
+ d = (x - p).norm();
+ }
}
diff --git a/src/random_points_on_mesh.cpp b/src/random_points_on_mesh.cpp
index 6e85d75..af6abeb 100644
--- a/src/random_points_on_mesh.cpp
+++ b/src/random_points_on_mesh.cpp
@@ -1,4 +1,7 @@
#include "random_points_on_mesh.h"
+#include
+#include
+#include
void random_points_on_mesh(
const int n,
@@ -8,6 +11,40 @@ void random_points_on_mesh(
{
// REPLACE WITH YOUR CODE:
X.resize(n,3);
- for(int i = 0;i distribution(0.0,1.0);
+
+ for(int i = 0;i 1) {
+ alpha = 1 - alpha;
+ beta = 1 - beta;
+ }
+
+ // Linear search on cum_A to find the index of face;
+ int idx = 0;
+ while (cum_A(idx) < gamma) {
+ idx++;
+ }
+
+ // Sample point from V(idx)
+ Eigen::Vector3i Tri = F.row(idx);
+ Eigen::Vector3d v = V.row(Tri(0));
+ Eigen::Vector3d w = V.row(Tri(1)) - V.row(Tri(0));
+ Eigen::Vector3d u = V.row(Tri(2)) - V.row(Tri(0));
+ Eigen::Vector3d point = v + alpha * u + beta * w;
+ X.row(i) = point;
+ }
}