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/main.cpp b/main.cpp
index 3115cc9..807f74d 100644
--- a/main.cpp
+++ b/main.cpp
@@ -20,7 +20,7 @@ int main(int argc, char *argv[])
int num_samples = 100;
bool show_samples = true;
- ICPMethod method = ICP_METHOD_POINT_TO_POINT;
+ ICPMethod method = ICP_METHOD_POINT_TO_PLANE;
igl::viewer::Viewer viewer;
std::cout<
+#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::MatrixXd U = svd.matrixU();
+ Eigen::MatrixXd Vt = svd.matrixV().transpose();
+
+ R << 1, 0, 0,
+ 0, 1, 0,
+ 0, 0, (U * Vt).determinant();
+ R = (U * R) * Vt;
}
diff --git a/src/hausdorff_lower_bound.cpp b/src/hausdorff_lower_bound.cpp
index 8608964..19592fc 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,17 @@ double hausdorff_lower_bound(
const Eigen::MatrixXi & FY,
const int n)
{
- // Replace with your code
- return 0;
+
+ // Sample `n` points from `X`
+ Eigen::MatrixXd X;
+ random_points_on_mesh(n, VX, FX, X);
+
+ // Directed Hausdorff's lower bound is supremum of
+ // point-to-mesh distances for all sampled X
+ // i.e. worst of bests
+ Eigen::VectorXd D;
+ Eigen::MatrixXd P, N;
+ 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..7065fc2 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_plane_rigid_matching.h"
+#include "point_to_point_rigid_matching.h"
void icp_single_iteration(
const Eigen::MatrixXd & VX,
@@ -10,7 +14,24 @@ void icp_single_iteration(
Eigen::Matrix3d & R,
Eigen::RowVector3d & t)
{
- // Replace with your code
- R = Eigen::Matrix3d::Identity();
- t = Eigen::RowVector3d::Zero();
+
+ auto transform = [&R, &t](Eigen::MatrixXd& X) {
+ return (X * R.transpose()).rowwise() + t;
+ };
+
+ // Sample X, project X to mesh Y after transformation
+ // P = P_Y(T(X)) \subset Y
+ Eigen::VectorXd D;
+ Eigen::MatrixXd X, P, N;
+ random_points_on_mesh(num_samples,VX,FX,X);
+ point_mesh_distance(X, VY, FY, D, P, N);
+
+ // Update rigid transformation to match X and P
+ R = Eigen::Matrix3d::Identity();
+ t = Eigen::RowVector3d::Zero();
+ if (method == ICP_METHOD_POINT_TO_POINT) {
+ point_to_point_rigid_matching(X, P, R, t);
+ } else if (method == ICP_METHOD_POINT_TO_PLANE) {
+ 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..3f3ba2c 100644
--- a/src/point_mesh_distance.cpp
+++ b/src/point_mesh_distance.cpp
@@ -1,4 +1,11 @@
#include "point_mesh_distance.h"
+#include "point_triangle_distance.h"
+
+#include
+#include
+#include
+#include
+
void point_mesh_distance(
const Eigen::MatrixXd & X,
@@ -8,9 +15,40 @@ 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::max();
+
+ // Computes closest distance & points from `x` to every triangle `t`
+ // Find triangle that minimizes point-to-mesh distance
+ for (int j = 0; j < ny; ++j) {
+ auto t = FY.row(j);
+ point_triangle_distance(x, VY.row(t(0)), VY.row(t(1)), VY.row(t(2)), d, p);
+ if (d < min_d) {
+ min_d = d;
+ min_idx = j;
+ closest_p = p;
+ }
+ }
+
+ // Saves closest distance, closest point, and normal
+ D(i) = min_d;
+ P.row(i) = closest_p;
+ N.row(i) = NY.row(min_idx);
+ }
}
+
diff --git a/src/point_to_plane_rigid_matching.cpp b/src/point_to_plane_rigid_matching.cpp
index 1978510..3023d91 100644
--- a/src/point_to_plane_rigid_matching.cpp
+++ b/src/point_to_plane_rigid_matching.cpp
@@ -1,4 +1,7 @@
#include "point_to_plane_rigid_matching.h"
+#include "closest_rotation.h"
+
+#include
void point_to_plane_rigid_matching(
const Eigen::MatrixXd & X,
@@ -7,7 +10,39 @@ 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
+ int k = X.rows();
+ Eigen::VectorXd u(6);
+ Eigen::MatrixXd A(3*k, 6);
+ Eigen::MatrixXd b(3*k, 1);
+ Eigen::MatrixXd dN(k, 3*k);
+
+ Eigen::VectorXd zeros = Eigen::VectorXd::Zero(k);
+ Eigen::VectorXd ones = Eigen::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;
+
+ b << X.col(0) - P.col(0),
+ X.col(1) - P.col(1),
+ X.col(2) - P.col(2);
+
+ dN << Eigen::MatrixXd(N.col(0).asDiagonal()),
+ Eigen::MatrixXd(N.col(1).asDiagonal()),
+ Eigen::MatrixXd(N.col(2).asDiagonal());
+
+ A = dN * A;
+ b = dN * b;
+
+ // solve for u
+ u = (A.transpose()*A).inverse() * (-A.transpose()*b);
+
+ // fill 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_to_point_rigid_matching.cpp b/src/point_to_point_rigid_matching.cpp
index 079151f..4c35142 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
void point_to_point_rigid_matching(
const Eigen::MatrixXd & X,
@@ -7,8 +9,32 @@ void point_to_point_rigid_matching(
Eigen::Matrix3d & R,
Eigen::RowVector3d & t)
{
- // Replace with your code
- R = Eigen::Matrix3d::Identity();
- t = Eigen::RowVector3d::Zero();
+ // Construct matrix A
+ int k = X.rows();
+ Eigen::VectorXd u(6);
+ Eigen::MatrixXd A(3*k, 6);
+ Eigen::MatrixXd b(3*k, 1);
+
+ Eigen::VectorXd zeros = Eigen::VectorXd::Zero(k);
+ Eigen::VectorXd ones = Eigen::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;
+
+ b << X.col(0) - P.col(0),
+ X.col(1) - P.col(1),
+ X.col(2) - P.col(2);
+
+ // solve for u
+ u = (A.transpose()*A).inverse() * (-A.transpose()*b);
+
+ // fill 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..093f039 100644
--- a/src/point_triangle_distance.cpp
+++ b/src/point_triangle_distance.cpp
@@ -1,5 +1,7 @@
#include "point_triangle_distance.h"
+#include
+
void point_triangle_distance(
const Eigen::RowVector3d & x,
const Eigen::RowVector3d & a,
@@ -8,7 +10,67 @@ void point_triangle_distance(
double & d,
Eigen::RowVector3d & p)
{
- // Replace with your code
- d = 0;
- p = a;
+ // Reference
+ // http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.479.8237&rep=rep1&type=pdf
+
+
+ // Find point `q` on plane defined by a point `p` and normal `n` {z: (z-p) * n = 0}
+ // such that `q` is the (orthogonally) projected point of `x`
+ auto project = [](
+ const Eigen::RowVector3d& p,
+ const Eigen::RowVector3d& n,
+ const Eigen::RowVector3d& x) {
+ return x - ((x - p).dot(n) / (n.squaredNorm())) * n;
+ };
+
+ auto n = (b-a).cross(c-a);
+ p = project(a, n, x);
+
+ // Find line connecting center of triangle and the vertices
+ auto va = (a-c).normalized() + (a-b).normalized();
+ auto vb = (b-c).normalized() + (b-a).normalized();
+ auto vc = (c-b).normalized() + (c-a).normalized();
+
+ // Find location of projected point `p`
+ // In particular, `p` is closest to edge represented by `uv`
+ auto fa = (va.cross(p-a)).dot(n);
+ auto fb = (vb.cross(p-b)).dot(n);
+ auto fc = (vc.cross(p-c)).dot(n);
+
+ int ui, vi;
+ if (fa > 0 && fb < 0) {
+ ui = 0; vi = 1;
+ } else if (fb > 0 && fc < 0) {
+ ui = 1; vi = 2;
+ } else if (fc > 0 && fa < 0) {
+ ui = 2; vi = 0;
+ }
+
+ auto get = [&](int i){ return (i == 0) ? a : ((i == 1) ? b : c); };
+ auto u = get(ui);
+ auto v = get(vi);
+
+ if ((v-p).cross(u-p).dot(n) > 0) {
+ // project `p` to the closest edge `uv` if `p` outside triangle
+ // the closest point is either a projected point `q` on edge `uv`
+ // or the vertices `u` or `v`
+ auto n2 = ((v-p).cross(u-p)).cross(u-v);
+ auto q = project(u, n2, p);
+ auto t = (q-u).dot(v-u) / (v-u).dot(v-u);
+
+ if (t >= 0 && t <= 1) {
+ d = sqrt((p-q).squaredNorm() + (x-p).squaredNorm());
+ p = q;
+ } else if (t < 0) {
+ d = sqrt((p-u).squaredNorm() + (x-p).squaredNorm());
+ p = u;
+ } else {
+ d = sqrt((p-v).squaredNorm() + (x-p).squaredNorm());
+ p = v;
+ }
+ } else {
+ // `p` is the closest point on the triangle if `p` inside triangle
+ d = (x-p).norm();
+ }
+
}
diff --git a/src/random_points_on_mesh.cpp b/src/random_points_on_mesh.cpp
index 6e85d75..a22305a 100644
--- a/src/random_points_on_mesh.cpp
+++ b/src/random_points_on_mesh.cpp
@@ -1,13 +1,70 @@
#include "random_points_on_mesh.h"
+#include
+#include
+#include
+#include
+
void random_points_on_mesh(
const int n,
const Eigen::MatrixXd & V,
const Eigen::MatrixXi & F,
Eigen::MatrixXd & X)
{
- // REPLACE WITH YOUR CODE:
- X.resize(n,3);
- for(int i = 0;i(A.rows());
+
+ for (int i = 0; i < C.size(); ++i) {
+ if (i == 0) {
+ C[i] = A(i);
+ } else {
+ C[i] = C[i-1] + A(i);
+ }
+ }
+ for (int i = 0; i < C.size(); ++i) {
+ C[i] = C[i] / total_area;
+ }
+
+ // Uniform random sampling of n triangles
+
+ std::random_device rd;
+ std::mt19937 gen(rd());
+ std::uniform_real_distribution unif(0., 1.);
+
+ int v, T;
+ double alpha, beta;
+ std::vector::iterator upper;
+ Eigen::RowVector3i triangle;
+ Eigen::RowVector3d v1, v2, v3;
+
+ for (int i = 0; i < n; ++i) {
+ upper = std::upper_bound(C.begin(), C.end(), unif(gen));
+ T = upper - C.begin();
+ triangle = F.row(T);
+
+ alpha = unif(gen);
+ beta = unif(gen);
+
+ if (alpha + beta > 1) {
+ alpha = 1. - alpha;
+ beta = 1. - beta;
+ }
+
+ v = rand() % 3;
+ v1 = V.row(triangle(v));
+ v2 = V.row(triangle((v+1) % 3));
+ v3 = V.row(triangle((v+2) % 3));
+
+ X.row(i) = v1.array() + \
+ alpha*(v2.array() - v1.array()) + \
+ beta*(v3.array() - v1.array());
+ }
}