bool CameraLidarTransformer::transformServiceCallback(
    navigator_msgs::CameraToLidarTransform::Request &req,
    navigator_msgs::CameraToLidarTransform::Response &res) {
  visualization_msgs::MarkerArray markers;
  sensor_msgs::PointCloud2ConstPtr scloud =
      lidarCache.getElemAfterTime(req.header.stamp);
  if (!scloud) {
    res.success = false;
    res.error =  navigator_msgs::CameraToLidarTransform::Response::CLOUD_NOT_FOUND;
    return true;
  }
  geometry_msgs::TransformStamped transform =
      tfBuffer.lookupTransform(req.header.frame_id, "velodyne", req.header.stamp);
  sensor_msgs::PointCloud2 cloud_transformed;
  tf2::doTransform(*scloud, cloud_transformed, transform);
#ifdef DO_ROS_DEBUG
  cv::Mat debug_image(camera_info.height, camera_info.width, CV_8UC3,
                      cv::Scalar(0));
#endif
  Eigen::Matrix3d matrixFindPlaneA;
  matrixFindPlaneA << 0, 0, 0, 0, 0, 0, 0, 0, 0;
  Eigen::Vector3d matrixFindPlaneB(0, 0, 0);

  cv::circle(debug_image, cv::Point(req.point.x, req.point.y), 8,
             cv::Scalar(255, 0, 0), -1);
  //Tracks the closest lidar point to the requested camera point
  double minDistance = std::numeric_limits<double>::max();
  for (auto ii = 0, jj = 0; ii < cloud_transformed.width;
       ++ii, jj += cloud_transformed.point_step) {
    floatConverter x, y, z, i;
    for (int kk = 0; kk < 4; ++kk) {
      x.data[kk] = cloud_transformed.data[jj + kk];
      y.data[kk] = cloud_transformed.data[jj + 4 + kk];
      z.data[kk] = cloud_transformed.data[jj + 8 + kk];
      i.data[kk] = cloud_transformed.data[jj + 16 + kk];
    }
    cam_model.fromCameraInfo(camera_info);
    if (int(z.f) < 0 || int(z.f) > 30) continue;
    cv::Point2d point = cam_model.project3dToPixel(cv::Point3d(x.f, y.f, z.f));

    if (int(point.x) > 0 && int(point.x) < camera_info.width &&
        int(point.y) > 0 && int(point.y) < camera_info.height) {
#ifdef DO_ROS_DEBUG
      visualization_msgs::Marker marker_point;
      marker_point.header = req.header;
      marker_point.header.seq = 0;
      marker_point.header.frame_id = req.header.frame_id;
      marker_point.id = (int)ii;
      marker_point.type = visualization_msgs::Marker::CUBE;
      marker_point.pose.position.x = x.f;
      marker_point.pose.position.y = y.f;
      marker_point.pose.position.z = z.f;
      marker_point.scale.x = 1;
      marker_point.scale.y = 0.1;
      marker_point.scale.z = 0.1;
      marker_point.color.a = 1.0;
      marker_point.color.r = 0.0;
      marker_point.color.g = 1.0;
      marker_point.color.b = 0.0;
      //marker_point.lifetime = ros::Duration(0.5);
      markers.markers.push_back(marker_point);

      // Draw
      if (int(point.x - 20) > 0 && int(point.x + 20) < camera_info.width &&
          int(point.y - 20) > 0 && int(point.y + 20) < camera_info.height) {
        cv::circle(debug_image, point, 3,
                   cv::Scalar(0, 0, 255 * std::min(1.0, z.f / 20.0)), -1);
      }
#endif
      double distance = sqrt(pow(point.x - req.point.x ,2) + pow(point.y - req.point.y,2) ); //Distance (2D) from request point to projected lidar point
      if (distance < req.tolerance) {
#ifdef DO_ROS_DEBUG
        if (int(point.x - 20) > 0 && int(point.x + 20) < camera_info.width &&
            int(point.y - 20) > 0 && int(point.y + 20) < camera_info.height) {
          cv::circle(debug_image, point, 3, cv::Scalar(0, 255, 0), -1);
        }
#endif
        matrixFindPlaneA(0, 0) += x.f * x.f;
        matrixFindPlaneA(1, 0) += y.f * x.f;
        matrixFindPlaneA(2, 0) += x.f;
        matrixFindPlaneA(0, 1) += x.f * y.f;
        matrixFindPlaneA(1, 1) += y.f * y.f;
        matrixFindPlaneA(2, 1) += y.f;
        matrixFindPlaneA(0, 2) += x.f;
        matrixFindPlaneA(1, 2) += y.f;
        matrixFindPlaneA(2, 2) += 1;
        matrixFindPlaneB(0, 0) -= x.f * z.f;
        matrixFindPlaneB(1, 0) -= y.f * z.f;
        matrixFindPlaneB(2, 0) -= z.f;
        
        geometry_msgs::Point geo_point;
        geo_point.x = x.f;
        geo_point.y = y.f;
        geo_point.z = z.f;
        if (distance < minDistance)
        {
          res.closest = geo_point;
          minDistance = distance;
        }
        res.transformed.push_back(geo_point);
      }
    }
  }
  if (res.transformed.size() < 1) {
    res.success = false;
    res.error = navigator_msgs::CameraToLidarTransform::Response::NO_POINTS_FOUND;
    return true;
  }
  
  //Filter out lidar points behind the plane (I don't think this will work, no good way to do this)
  // ~double min_z = (*std::min_element(res.transformed.begin(),res.transformed.end(), [=] (const geometry_msgs::Point& a,const geometry_msgs::Point& b) {
    // ~return a.z < b.z && a.z > MIN_Z;
  // ~})).z;
  // ~res.transformed.erase(std::remove_if(res.transformed.begin(),res.transformed.end(),[min_z] (const geometry_msgs::Point& p) {
    // ~return (p.z - min_z) < MAX_Z_ERROR; 
  // ~}),res.transformed.end());

  res.distance = res.closest.z;
  Eigen::Vector3d normalvec = matrixFindPlaneA.colPivHouseholderQr()
                                  .solve(matrixFindPlaneB)
                                  .normalized();
  geometry_msgs::Vector3 normalvec_ros;
  normalvec_ros.x = normalvec(0, 0);
  normalvec_ros.y = normalvec(1, 0);
  normalvec_ros.z = normalvec(2, 0);
  res.normal = normalvec_ros;
  std::cout << "Plane solution: " << normalvec << std::endl;

#ifdef DO_ROS_DEBUG
  //Create marker for normal
  visualization_msgs::Marker marker_normal;
  marker_normal.header = req.header;
  marker_normal.header.seq = 0;
  marker_normal.header.frame_id =  req.header.frame_id;
  marker_normal.id = 3000;
  marker_normal.type = visualization_msgs::Marker::ARROW;

  geometry_msgs::Point sdp_normalvec_ros;
  sdp_normalvec_ros.x = normalvec_ros.x + res.transformed[0].x;
  sdp_normalvec_ros.y = normalvec_ros.y + res.transformed[0].y;
  sdp_normalvec_ros.z = normalvec_ros.z + res.transformed[0].z;
  marker_normal.points.push_back(res.closest);
  marker_normal.points.push_back(sdp_normalvec_ros);

  marker_normal.scale.x = 0.1;
  marker_normal.scale.y = 0.5;
  marker_normal.scale.z = 0.5;
  marker_normal.color.a = 1.0;
  marker_normal.color.r = 1.0;
  marker_normal.color.g = 0.0;
  marker_normal.color.b = 0.0;
  markers.markers.push_back(marker_normal);
  
  //Publish 3D debug market
  pubMarkers.publish(markers);
  
  //Publish debug image
  cv_bridge::CvImage ros_debug_image;
  ros_debug_image.encoding = "bgr8";
  ros_debug_image.image = debug_image.clone();
  points_debug_publisher.publish(ros_debug_image.toImageMsg());
#endif
  res.success = true;
  return true;
}
void mesh_core::Plane::leastSquaresFast(
      const EigenSTL::vector_Vector3d& points,
      Eigen::Vector3d* average)
{
  Eigen::Matrix3d m;
  Eigen::Vector3d b;
  Eigen::Vector3d c;

  m.setZero();
  b.setZero();
  c.setZero();

  EigenSTL::vector_Vector3d::const_iterator p = points.begin();
  EigenSTL::vector_Vector3d::const_iterator end = points.end();
  for ( ; p != end ; ++p)
  {
    m(0,0) += p->x() * p->x();
    m(1,0) += p->x() * p->y();
    m(2,0) += p->x();
    m(1,1) += p->y() * p->y();
    m(2,1) += p->y();
    b(0) += p->x() * p->z();
    b(1) += p->y() * p->z();
    b(2) += p->z();
    c += *p;
  }
  m(0,1) = m(1,0);
  m(0,2) = m(2,0);
  m(1,2) = m(2,1);
  m(2,2) = double(points.size());
  c *= 1.0/double(points.size());

  normal_ = m.colPivHouseholderQr().solve(b);
  if (normal_.squaredNorm() > std::numeric_limits<double>::epsilon())
    normal_.normalize();

  d_ = -c.dot(normal_);

  if (average)
    *average = c;
}
std::vector<double>
jerkMinimizingTrajectory(const motion& state0, const motion& state1, double dt) {
  double dt2 = dt*dt;
  double dt3 = dt2*dt;
  double dt4 = dt3*dt;
  double dt5 = dt4*dt;

  Eigen::Matrix3d a;
  a <<  dt3,    dt4,    dt5,
    3*dt2,  4*dt3,  5*dt4,
    6*dt, 12*dt2, 20*dt3;

  Eigen::Vector3d b;
  b << state1[0] - (state0[0] + dt*state0[1] + 0.5*dt2*state0[2]),
    state1[1] - (state0[1] + dt*state0[2]),
    state1[2] -  state0[2];

  Eigen::VectorXd p = a.colPivHouseholderQr().solve(b);

  return {state0[0], state0[1], state0[2]/2.0, p[0], p[1], p[2]};
}
Types::Transform PointOnPlaneCalibration::estimateTransform(const std::vector<PointPlanePair> & pair_vector)
{
  const int size = pair_vector.size();

  // Point-Plane Constraints

  // Initial system (Eq. 10)

  Eigen::MatrixXd system(size, 13);
  for (int i = 0; i < size; ++i)
  {
    const double d = pair_vector[i].plane_.offset();
    const Eigen::Vector3d n = pair_vector[i].plane_.normal();

    const Types::Point3 & x = pair_vector[i].point_;

    system.row(i) <<  d + n[0] * x[0] + n[1] * x[1] + n[2] * x[2],  // q_0^2
                      2 * n[2] * x[1] - 2 * n[1] * x[2],            // q_0 * q_1
                      2 * n[0] * x[2] - 2 * n[2] * x[0],            // q_0 * q_2
                      2 * n[1] * x[0] - 2 * n[0] * x[1],            // q_0 * q_3
                      d + n[0] * x[0] - n[1] * x[1] - n[2] * x[2],  // q_1^2
                      2 * n[0] * x[1] + 2 * n[1] * x[0],            // q_1 * q_2
                      2 * n[0] * x[2] + 2 * n[2] * x[0],            // q_1 * q_3
                      d - n[0] * x[0] + n[1] * x[1] - n[2] * x[2],  // q_2^2
                      2 * n[1] * x[2] + 2 * n[2] * x[1],            // q_2 * q_3
                      d - n[0] * x[0] - n[1] * x[1] + n[2] * x[2],  // q_3^2
                      n[0], n[1], n[2]; // q'*q*t

  }

  // Gaussian reduction
  for (int k = 0; k < 3; ++k)
    for (int i = k + 1; i < size; ++i)
      system.row(i) = system.row(i) - system.row(k) * system.row(i)[10 + k] / system.row(k)[10 + k];

  // Quaternion q
  Eigen::Vector4d q;

  // Transform to inhomogeneous (Eq. 13)
  bool P_is_ok(false);
  while (not P_is_ok)
  {
    Eigen::Matrix4d P(Eigen::Matrix4d::Random());
    while (P.determinant() < 1e-5)
      P = Eigen::Matrix4d::Random();

    Eigen::MatrixXd reduced_system(size - 3, 10);
    for (int i = 3; i < size; ++i)
    {
      const Eigen::VectorXd & row = system.row(i);
      Eigen::Matrix4d Mi_tilde;

      Mi_tilde << row[0]    , row[1] / 2, row[2] / 2, row[3] / 2,
                  row[1] / 2, row[4]    , row[5] / 2, row[6] / 2,
                  row[2] / 2, row[5] / 2, row[7]    , row[8] / 2,
                  row[3] / 2, row[6] / 2, row[8] / 2, row[9]    ;

      Eigen::Matrix4d Mi_bar(P.transpose() * Mi_tilde * P);

      reduced_system.row(i - 3) << Mi_bar(0, 0),
                                   Mi_bar(0, 1) + Mi_bar(1, 0),
                                   Mi_bar(0, 2) + Mi_bar(2, 0),
                                   Mi_bar(0, 3) + Mi_bar(3, 0),
                                   Mi_bar(1, 1),
                                   Mi_bar(1, 2) + Mi_bar(2, 1),
                                   Mi_bar(1, 3) + Mi_bar(3, 1),
                                   Mi_bar(2, 2),
                                   Mi_bar(2, 3) + Mi_bar(3, 2),
                                   Mi_bar(3, 3);
    }

    // Solve  A m* = b
    Eigen::MatrixXd A = reduced_system.rightCols<9>();
    Eigen::VectorXd b = - reduced_system.leftCols<1>();
    Eigen::VectorXd m_star = A.jacobiSvd(Eigen::ComputeFullU | Eigen::ComputeFullV).solve(b);

    Eigen::Vector4d q_bar(1, m_star[0], m_star[1], m_star[2]);

    Eigen::VectorXd err(6);
    err << q_bar[1] * q_bar[1],
           q_bar[1] * q_bar[2],
           q_bar[1] * q_bar[3],
           q_bar[2] * q_bar[2],
           q_bar[2] * q_bar[3],
           q_bar[3] * q_bar[3];
    err -= m_star.tail<6>();

    //if (err.norm() < 0.1) // P is not ok?
      P_is_ok = true;

    //std::cout << m_star.transpose() << std::endl;
    //std::cout << q_bar[1] * q_bar[1] << " " << q_bar[1] * q_bar[2] << " " << q_bar[1] * q_bar[3] << " "
    //<< q_bar[2] * q_bar[2] << " " << q_bar[2] * q_bar[3] << " " << q_bar[3] * q_bar[3] << std::endl;

    q = P * q_bar;
  }

  // We want q.w > 0 (Why?)
  //if (q[0] < 0)
  //  q = -q;

  Eigen::Vector4d tmp_q(q.normalized());
  Eigen::Quaterniond rotation(tmp_q[0], tmp_q[1], tmp_q[2], tmp_q[3]);

  Eigen::VectorXd m(10);
  m << q[0] * q[0],
       q[0] * q[1],
       q[0] * q[2],
       q[0] * q[3],
       q[1] * q[1],
       q[1] * q[2],
       q[1] * q[3],
       q[2] * q[2],
       q[2] * q[3],
       q[3] * q[3];

  // Solve A (q^T q t) = b

  Eigen::Matrix3d A = system.topRightCorner<3, 3>();
  Eigen::Vector3d b = - system.topLeftCorner<3, 10>() * m;
  Eigen::Translation3d translation(A.colPivHouseholderQr().solve(b) / q.squaredNorm());

//  Eigen::Quaterniond tmp(pose.linear());
//  Eigen::Translation3d tmp2(pose.translation());
//  std::cout << "Prev: " << tmp.normalized().coeffs().transpose() << " " << tmp2.vector().transpose() << std::endl;
  //std::cout << "PoP : " << rotation.coeffs().transpose() << " " << translation.vector().transpose() << std::endl;

  return translation * rotation;
}