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..57fc47c 100644
--- a/src/closest_rotation.cpp
+++ b/src/closest_rotation.cpp
@@ -1,9 +1,24 @@
#include "closest_rotation.h"
+#include
+#include
+#include
void closest_rotation(
- const Eigen::Matrix3d & M,
- Eigen::Matrix3d & R)
-{
- // Replace with your code
- R = Eigen::Matrix3d::Identity();
+ const Eigen::Matrix3d &M,
+ Eigen::Matrix3d &R) {
+
+ Eigen::JacobiSVD svd(M, Eigen::ComputeThinV | Eigen::ComputeThinU);
+ Eigen::Matrix3d U = svd.matrixU();
+ Eigen::Matrix3d V = svd.matrixV();
+ Eigen::Matrix3d VT = V.transpose();
+ double d = (U * VT).determinant();
+// std::cout << d << std::endl
+
+ Eigen::Matrix3d O;
+ O << 1, 0, 0,
+ 0, 1, 0,
+ 0, 0, d;
+
+ R = U * O * VT;
+
}
diff --git a/src/hausdorff_lower_bound.cpp b/src/hausdorff_lower_bound.cpp
index 8608964..bf9afeb 100644
--- a/src/hausdorff_lower_bound.cpp
+++ b/src/hausdorff_lower_bound.cpp
@@ -1,12 +1,18 @@
#include "hausdorff_lower_bound.h"
+#include "random_points_on_mesh.h"
+#include "point_mesh_distance.h"
double hausdorff_lower_bound(
- const Eigen::MatrixXd & VX,
- const Eigen::MatrixXi & FX,
- const Eigen::MatrixXd & VY,
- const Eigen::MatrixXi & FY,
- const int n)
-{
- // Replace with your code
- return 0;
+ const Eigen::MatrixXd &VX,
+ const Eigen::MatrixXi &FX,
+ const Eigen::MatrixXd &VY,
+ const Eigen::MatrixXi &FY,
+ const int n) {
+
+ Eigen::VectorXd D;
+ Eigen::MatrixXd X, P, N;
+ random_points_on_mesh(n, VX, FX, X);
+
+ 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..ebcff38 100644
--- a/src/icp_single_iteration.cpp
+++ b/src/icp_single_iteration.cpp
@@ -1,16 +1,42 @@
#include "icp_single_iteration.h"
+#include "random_points_on_mesh.h"
+#include "point_to_plane_rigid_matching.h"
+#include "point_to_point_rigid_matching.h"
+#include "point_mesh_distance.h"
void icp_single_iteration(
- const Eigen::MatrixXd & VX,
- const Eigen::MatrixXi & FX,
- const Eigen::MatrixXd & VY,
- const Eigen::MatrixXi & FY,
- const int num_samples,
- const ICPMethod method,
- Eigen::Matrix3d & R,
- Eigen::RowVector3d & t)
-{
- // Replace with your code
- R = Eigen::Matrix3d::Identity();
- t = Eigen::RowVector3d::Zero();
+ const Eigen::MatrixXd &VX,
+ const Eigen::MatrixXi &FX,
+ const Eigen::MatrixXd &VY,
+ const Eigen::MatrixXi &FY,
+ const int num_samples,
+ const ICPMethod method,
+ Eigen::Matrix3d &R,
+ Eigen::RowVector3d &t) {
+
+ /*
+ * Algorithm:
+ X ← sample source mesh (V_X,F_X)
+ P0 ← project all X onto target mesh (V_Y,F_Y)
+ R,t ← update rigid transform to best match X and P0
+ V_X ← rigidly transform original source mesh by R and t
+ */
+
+ Eigen::MatrixXd X, P, N;
+ Eigen::VectorXd D;
+ random_points_on_mesh(num_samples, VX, FX, X);
+ point_mesh_distance(X, VY, FY, D, P, N);
+
+ switch (method) {
+ case ICPMethod::ICP_METHOD_POINT_TO_PLANE:
+ point_to_plane_rigid_matching(X, P, N, R, t);
+ break;
+ case ICPMethod::ICP_METHOD_POINT_TO_POINT:
+ point_to_point_rigid_matching(X, P, R, t);
+ break;
+ default:
+ assert(true);
+ }
+
+
}
diff --git a/src/point_mesh_distance.cpp b/src/point_mesh_distance.cpp
index 2e6a070..1d96c1e 100644
--- a/src/point_mesh_distance.cpp
+++ b/src/point_mesh_distance.cpp
@@ -1,16 +1,52 @@
#include "point_mesh_distance.h"
+#include "point_triangle_distance.h"
+#include
+#include
void point_mesh_distance(
- const Eigen::MatrixXd & X,
- const Eigen::MatrixXd & VY,
- const Eigen::MatrixXi & FY,
- Eigen::VectorXd & D,
- 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
+
+void createA(const Eigen::MatrixXd &X, Eigen::MatrixXd &A) {
+ Eigen::VectorXd zero = Eigen::VectorXd::Zero(X.rows());
+ Eigen::VectorXd one = Eigen::VectorXd::Ones(X.rows());
+//-
+ Eigen::VectorXd X_1 = X.col(1);
+ Eigen::VectorXd X_2 = X.col(2);
+ Eigen::VectorXd X_0 = X.col(0);
+
+ A << zero, X_2, -X_1, one, zero, zero,
+ -X_2, zero, X_0, zero, one, zero,
+ X_1, -X_0, zero, zero, zero, one;
+}
+
+Eigen::MatrixXd diagN(const Eigen::MatrixXd &N, int idx) {
+ return N.col(idx).asDiagonal();
+}
void point_to_plane_rigid_matching(
- const Eigen::MatrixXd & X,
- const Eigen::MatrixXd & P,
- const Eigen::MatrixXd & N,
- Eigen::Matrix3d & R,
- Eigen::RowVector3d & t)
-{
- // Replace with your code
- R = Eigen::Matrix3d::Identity();
- t = Eigen::RowVector3d::Zero();
+ const Eigen::MatrixXd &X,
+ const Eigen::MatrixXd &P,
+ const Eigen::MatrixXd &N,
+ Eigen::Matrix3d &R,
+ Eigen::RowVector3d &t) {
+
+ Eigen::MatrixXd A(3 * X.rows(), 6);
+ Eigen::MatrixXd d_N(X.rows(), 3 * X.rows());
+
+ //Creating A, diagonal_N and X-P
+ createA(X, A);
+ d_N << diagN(N, 0), diagN(N, 1), diagN(N, 2);
+ Eigen::MatrixXd XP = X - P;
+
+ Eigen::VectorXd B = Eigen::VectorXd::Zero(X.rows() * 3);
+ B << (X - P).col(0), (X - P).col(1), (X - P).col(2);
+
+ //Solving the equation (52) in Readme.
+ A = d_N * A;
+ B = d_N * B;
+
+ //need to be defined dynamically so that JacobiSVD works.
+ Eigen::VectorXd u;
+
+
+ u = (A.transpose() * A).inverse() * (-A.transpose() * B);
+
+ double alpha = u[0];
+ double beta = u[1];
+ double gamma = u[2];
+
+ Eigen::Matrix3d M = Eigen::MatrixXd::Identity(3, 3);
+ Eigen::Matrix3d buff;
+ buff << 0, -gamma, beta,
+ gamma, 0, -alpha,
+ -beta, alpha, 0;
+
+ M = M - buff;
+ closest_rotation(M, R);
+ t << u(3), u(4), u(5);
+
+
}
diff --git a/src/point_to_point_rigid_matching.cpp b/src/point_to_point_rigid_matching.cpp
index 079151f..11825ea 100644
--- a/src/point_to_point_rigid_matching.cpp
+++ b/src/point_to_point_rigid_matching.cpp
@@ -1,14 +1,21 @@
#include "point_to_point_rigid_matching.h"
#include
+#include "closest_rotation.h"
void point_to_point_rigid_matching(
- const Eigen::MatrixXd & X,
- const Eigen::MatrixXd & P,
- Eigen::Matrix3d & R,
- Eigen::RowVector3d & t)
-{
- // Replace with your code
- R = Eigen::Matrix3d::Identity();
- t = Eigen::RowVector3d::Zero();
+ const Eigen::MatrixXd &X,
+ const Eigen::MatrixXd &P,
+ Eigen::Matrix3d &R,
+ Eigen::RowVector3d &t) {
+//
+ //Shift to the origin
+ Eigen::MatrixXd X_ = X.rowwise() - X.colwise().mean();
+ Eigen::MatrixXd P_ = P.rowwise() - P.colwise().mean();
+
+ Eigen::MatrixXd M = (P_.transpose() * X_).transpose();
+ closest_rotation(M, R);
+ //compute t
+ t = P.colwise().mean().transpose() - R * X.colwise().mean().transpose();
+
}
diff --git a/src/point_triangle_distance.cpp b/src/point_triangle_distance.cpp
index 6405100..506eb2c 100644
--- a/src/point_triangle_distance.cpp
+++ b/src/point_triangle_distance.cpp
@@ -1,14 +1,134 @@
#include "point_triangle_distance.h"
+#include "iostream"
+#include
+
+Eigen::RowVector3d PxPy(const Eigen::RowVector3d &x, const Eigen::RowVector3d &y) {
+ return y - x;
+}
+
+Eigen::RowVector3d PxPy_norm(const Eigen::RowVector3d &x, const Eigen::RowVector3d &y) {
+ return PxPy(x, y) / PxPy(x, y).norm();
+}
+
+bool clockwise(double f) {
+ return f < 0;
+}
+
+bool anti_clockwise(double f) {
+ return f > 0;
+}
+
+bool is_outside(const Eigen::RowVector3d &x, const Eigen::RowVector3d &y, const Eigen::RowVector3d &pop,
+ const Eigen::RowVector3d &Np) {
+
+ double val = PxPy(pop, x).cross(PxPy(pop, y)).dot(Np);
+ return val < 0;
+}
+
+void
+getPointDistance(const Eigen::RowVector3d &p1, const Eigen::RowVector3d &p2, const Eigen::RowVector3d &po,
+ const Eigen::RowVector3d &pop,
+ const double popop_l, double &d,
+ Eigen::RowVector3d &p) {
+
+ //P1 and P2 are refereed to equation 7.
+ Eigen::RowVector3d R = PxPy(pop, p2).cross(PxPy(pop, p1)).cross(PxPy(p1, p2));
+ double cos_gamma = PxPy(pop, p1).dot(R) / (PxPy(pop, p1).norm() * R.norm());
+ double poppopp_l = PxPy(pop, p1).norm() * cos_gamma;
+ Eigen::RowVector3d poppopp = poppopp_l * R / R.norm();
+ Eigen::RowVector3d p0pp = pop + poppopp;//eq12
+ Eigen::RowVector3d num = p0pp - p1;
+ Eigen::RowVector3d denom = p2 - p1;
+
+ double t = num.sum() / (denom.sum() + 1e-6);
+
+ if (t >= 0 && t <= 1) {
+ d = sqrt(poppopp_l * poppopp_l + popop_l * popop_l);
+ p = p0pp;
+ } else if (t < 0) {
+ d = PxPy(po, p1).norm();
+ p = p1;
+ } else if (t > 1) {
+ d = PxPy(po, p2).norm();
+ p = p2;
+ }
+}
+
void point_triangle_distance(
- const Eigen::RowVector3d & x,
- const Eigen::RowVector3d & a,
- const Eigen::RowVector3d & b,
- const Eigen::RowVector3d & c,
- double & d,
- Eigen::RowVector3d & p)
-{
- // Replace with your code
- d = 0;
- p = a;
+ const Eigen::RowVector3d &x,
+ const Eigen::RowVector3d &a,
+ const Eigen::RowVector3d &b,
+ const Eigen::RowVector3d &c,
+ double &d,
+ Eigen::RowVector3d &p) {
+
+
+
+ // This implementation follows
+ // http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.104.4264&rep=rep1&type=pdf
+ // 3D Distance from a Point to a Triangle
+
+ //This is a changed of variable to match the equations in the paper.
+ Eigen::RowVector3d p0 = x;
+ Eigen::RowVector3d p1 = a;
+ Eigen::RowVector3d p2 = b;
+ Eigen::RowVector3d p3 = c;
+ ///
+
+
+ // "let's get the perpendicular vector to the triangle plane"
+ Eigen::RowVector3d Np = PxPy(p1, p2).cross(PxPy(p1, p3));
+ double cosalpha = PxPy(p1, p0).dot(Np) / (PxPy(p1, p0).norm() * Np.norm());
+
+
+ // If it were the case that the projection of p0 onto the plane lay within the triangle
+ // this would be the length
+ double p0p0p_l = PxPy(p0, p1).norm() * cosalpha;
+ //--
+ Eigen::RowVector3d p0p0p = (-1.0 * p0p0p_l * Np) / Np.norm();
+ Eigen::RowVector3d pop = p0 + p0p0p; // EQ5
+
+ //--
+ Eigen::RowVector3d v1 = PxPy_norm(p2, p1) + PxPy_norm(p3, p1);
+ Eigen::RowVector3d v2 = PxPy_norm(p3, p2) + PxPy_norm(p1, p2);
+ Eigen::RowVector3d v3 = PxPy_norm(p1, p3) + PxPy_norm(p2, p3);
+
+ //--
+ double f1 = v1.cross(PxPy(p1, pop)).dot(Np);
+ double f2 = v2.cross(PxPy(p2, pop)).dot(Np);
+ double f3 = v3.cross(PxPy(p3, pop)).dot(Np);
+
+ // f1 > 0 if P0 is anticlockwise of V1
+ // f2 > 0 if P0 is anticlockwise of V2
+ // f3 > 0 if P0 is anticlockwise of V3
+
+
+ if (clockwise(f2) && anti_clockwise(f1)) {
+ if (is_outside(p1, p2, pop, Np)) {
+ getPointDistance(p1, p2, p0, pop, p0p0p_l, d, p);
+
+ } else {
+ d = abs(p0p0p_l);
+ p = pop;
+ }
+ } else if (clockwise(f3) && anti_clockwise(f2)) {
+ if (is_outside(p2, p3, pop, Np)) {
+ getPointDistance(p2, p3, p0, pop, p0p0p_l, d, p);
+
+ } else {
+ d = abs(p0p0p_l);
+ p = pop;
+ }
+ } else if (clockwise(f1) && anti_clockwise(f3)) {
+ if (is_outside(p3, p1, pop, Np)) {
+ getPointDistance(p3, p1, p0, pop, p0p0p_l, d, p);
+ } else {
+ d = abs(p0p0p_l);
+ p = pop;
+ }
+ } else {
+ assert(true);
+ }
+// std::cout << "p:" << p << " d: " << d << std::endl;
}
diff --git a/src/random_points_on_mesh.cpp b/src/random_points_on_mesh.cpp
index 6e85d75..d7f3995 100644
--- a/src/random_points_on_mesh.cpp
+++ b/src/random_points_on_mesh.cpp
@@ -1,13 +1,82 @@
#include "random_points_on_mesh.h"
+#include
+#include
+#include
+#include
+#include
+
+int pick_triangle(Eigen::MatrixXd &M, double gamma) {
+ //This can be done based on binary search, or std::upper_bound,
+ //I am leaving like this for now.
+ for (int i = 0; i < M.rows(); i++)
+ if (M(i, 0) > gamma) {
+ return i;
+ }
+ return -1;
+}
+
+Eigen::VectorXd
+pick_inside_triangle(Eigen::VectorXd &v1, Eigen::VectorXd &v2, Eigen::VectorXd &v3, double alpha, double beta) {
+
+ Eigen::VectorXd x;
+
+ if ((alpha + beta) > 1.0) {
+ alpha = 1. - alpha;
+ beta = 1. - beta;
+ }
+
+ x = v1 + alpha * (v2 - v1) + beta * (v3 - v1);
+// std::cout << x << std::endl;
+ return x;
+
+}
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 distribution(0.0, 1.0);
+
+ for (int i = 0; i < X.rows(); i++) {
+ double gamma = distribution(generator);
+ int tid = pick_triangle(C, gamma);
+ assert(tid >= 0);
+
+ Eigen::VectorXd v1 = V.row(F(tid, 0));
+ Eigen::VectorXd v2 = V.row(F(tid, 1));
+ Eigen::VectorXd v3 = V.row(F(tid, 2));
+
+
+ X.row(i) = pick_inside_triangle(v1, v2, v3, distribution(generator), distribution(generator));
+
+// std::cout << X.row(i) << std::endl;
+
+
+ }
}