diff --git a/modules/core/include/visp3/core/vpMath.h b/modules/core/include/visp3/core/vpMath.h index db8bf2e9c7..17bbbe6235 100644 --- a/modules/core/include/visp3/core/vpMath.h +++ b/modules/core/include/visp3/core/vpMath.h @@ -140,7 +140,7 @@ class VISP_EXPORT vpMath if (theta1 > M_PIf) { theta1 -= 2.0f * M_PIf; } - else if (theta1 < -M_PIf) { + else if (theta1 <= -M_PIf) { theta1 += 2.0f * M_PIf; } return theta1; @@ -164,6 +164,36 @@ class VISP_EXPORT vpMath return theta1; } + /** + * \brief Gives the rest of \b value divided by \b modulo when + * the quotient can only be an integer. + * + * \param[in] value The value we want to know the rest in the "modulo" operation. + * \param[in] modulo The divider. + * \return float The rest as in a modulo operation. + */ + static float moduloFloat(const float &value, const float &modulo) + { + float quotient = std::floor(value / modulo); + float rest = value - quotient * modulo; + return rest; + } + + /** + * \brief Gives the rest of \b value divided by \b modulo when + * the quotient can only be an integer. + * + * \param[in] value The value we want to know the rest in the "modulo" operation. + * \param[in] modulo The divider. + * \return double The rest as in a modulo operation. + */ + static double moduloDouble(const double &value, const double &modulo) + { + double quotient = std::floor(value / modulo); + double rest = value - quotient * modulo; + return rest; + } + /*! Compute x square value. \return Square value \f$ x^2 \f$. @@ -193,9 +223,9 @@ class VISP_EXPORT vpMath } return (v < lower) ? lower : (upper < v) ? upper : v; #endif - } + } - // round x to the nearest integer + // round x to the nearest integer static inline int round(double x); // return the sign of x (+-1) @@ -330,14 +360,14 @@ class VISP_EXPORT vpMath private: static const double ang_min_sinc; static const double ang_min_mc; - }; +}; - // Begining of the inline functions definition +// Begining of the inline functions definition - /*! - Computes and returns x! - \param x : parameter of factorial function. - */ +/*! + Computes and returns x! + \param x : parameter of factorial function. +*/ double vpMath::fact(unsigned int x) { if ((x == 1) || (x == 0)) diff --git a/modules/core/src/image/vpImageCircle.cpp b/modules/core/src/image/vpImageCircle.cpp index 4a7c4c6bcc..5f3dc1e059 100644 --- a/modules/core/src/image/vpImageCircle.cpp +++ b/modules/core/src/image/vpImageCircle.cpp @@ -77,6 +77,9 @@ void computeIntersectionsLeftBorderOnly(const float &u_c, const float &umin_roi, float theta_min = std::min(theta1, theta2); float theta_max = std::max(theta1, theta2); delta_theta = theta_max - theta_min; + if (u_c < umin_roi && std::abs(delta_theta - 2 * M_PIf) < 2.f * std::numeric_limits::epsilon()) { + delta_theta = 0.f; + } } /*! @@ -99,6 +102,9 @@ void computeIntersectionsRightBorderOnly(const float &u_c, const float &umax_roi float theta_min = std::min(theta1, theta2); float theta_max = std::max(theta1, theta2); delta_theta = 2.f * M_PIf - (theta_max - theta_min); + if (u_c > umax_roi && std::abs(delta_theta - 2 * M_PIf) < 2.f * std::numeric_limits::epsilon()) { + delta_theta = 0.f; + } } /*! @@ -138,6 +144,9 @@ void computeIntersectionsTopBorderOnly(const float &v_c, const float &vmin_roi, else { delta_theta = theta_max - theta_min; } + if (v_c < vmin_roi && std::abs(delta_theta - 2 * M_PIf) < 2.f * std::numeric_limits::epsilon()) { + delta_theta = 0.f; + } } /*! @@ -177,6 +186,9 @@ void computeIntersectionsBottomBorderOnly(const float &v_c, const float &vmax_ro else { delta_theta = 2.f * M_PIf - (theta_max - theta_min); } + if (v_c > vmax_roi && std::abs(delta_theta - 2 * M_PIf) < 2.f * std::numeric_limits::epsilon()) { + delta_theta = 0.f; + } } /*! @@ -527,38 +539,6 @@ void computeIntersectionsTopLeftBottom(const float &u_c, const float &v_c, const computeIntersectionsTopLeft(u_c, v_c, umin_roi, vmin_roi, radius, delta_theta); cas = 4; } - - if (delta_theta < 0.f) { - std::cout << "--- computeIntersectionsTopLeftBottom with negative result ---" << std::endl; - std::cout << "\tu_umin_top = " << u_umin_top << "\tu_umax_top = " << u_umax_top << std::endl; - std::cout << "\tu_umin_bot = " << u_umin_bottom << "\tu_umax_bot = " << u_umax_bottom << std::endl; - std::cout << "\tv_vmin = " << v_vmin << "\tv_vmax = " << v_vmax << std::endl; - std::cout << "\ttheta_u_min_top = " << theta_u_min_top << "\ttheta_u_max_top = " << theta_u_max_top << std::endl; - std::cout << "\ttheta_u_min_bot = " << theta_u_min_bottom << "\ttheta_u_max_bot = " << theta_u_max_bottom << std::endl; - std::cout << "\ttheta_v_min = " << theta_v_min << "\ttheta_v_max = " << theta_v_max << std::endl; - std::cout << "\tcas = "; - std::string nameCase; - switch (cas) { - case 0: - nameCase = "top + left + bottom twice"; - break; - case 1: - nameCase = "top and bottom"; - break; - case 2: - nameCase = "left only"; - break; - case 3: - nameCase = "bottom/left corner"; - break; - case 4: - nameCase = "top/left corner"; - break; - default: - throw (vpException(vpException::fatalError, "Uncorrect case")); - } - std::cout << nameCase << std::endl; - } } /*! @@ -1029,8 +1009,7 @@ float vpImageCircle::computeAngularCoverageInRoI(const vpRect &roi, const float } if (delta_theta < 0 || delta_theta > 2.f * M_PIf) { // Needed since M_PIf is used - float quotient = std::floor(delta_theta / (2.f * M_PIf)); - float rest = delta_theta - quotient * 2.f * M_PIf; + float rest = vpMath::moduloFloat(delta_theta, 2.f * M_PIf); if (rest < roundingTolerance && (delta_theta < -M_PIf || delta_theta > M_PIf)) { // If the angle is a negative multiple of 2.f * M_PIf we consider it to be 2.f * M_PIf delta_theta = 2.f * M_PIf; diff --git a/modules/core/test/tools/geometry/testImageCircle.cpp b/modules/core/test/tools/geometry/testImageCircle.cpp index bc4cd432b1..8b78ae4ae3 100644 --- a/modules/core/test/tools/geometry/testImageCircle.cpp +++ b/modules/core/test/tools/geometry/testImageCircle.cpp @@ -173,6 +173,31 @@ int main() hasSucceeded &= isValueOK; } + // Test with circle touching the left border, all the circle is hidden + { + // Formula: uc = OFFSET - RADIUS * cos(theta) + // theta := PI + float uc = OFFSET - RADIUS; + float vc = OFFSET - 100.f; + vpImageCircle circle(vpImagePoint(vc, uc), RADIUS); + float arcLengthCircle = circle.computeArcLengthInRoI(roi); + float theoreticalValue = 0.f; + bool isValueOK = equal(arcLengthCircle, theoreticalValue); + std::string statusTest; + if (isValueOK) { + statusTest = "SUCCESS"; + } + else { + statusTest = "FAILED"; + } + std::cout << "Test with circle touching the left border, all the circle is hidden." << std::endl; + std::cout << "\tarc length =" << arcLengthCircle << std::endl; + std::cout << "\ttheoretical length =" << theoreticalValue << std::endl; + std::cout << "\ttest status = " << statusTest << std::endl; + + hasSucceeded &= isValueOK; + } + // Test with intersections with the right border, more than half a circle visible { // Formula: uc = OFFSET + WIDTH - RADIUS * cos(theta) @@ -248,6 +273,31 @@ int main() hasSucceeded &= isValueOK; } + // Test with circle touching the right border, all the circle is hidden + { + // Formula: uc = OFFSET + WIDTH - RADIUS * cos(theta) + // theta := 0 + float uc = OFFSET + WIDTH + RADIUS; + float vc = OFFSET + 100.f; + vpImageCircle circle(vpImagePoint(vc, uc), RADIUS); + float arcLengthCircle = circle.computeArcLengthInRoI(roi); + float theoreticalValue = 0.f; + bool isValueOK = equal(arcLengthCircle, theoreticalValue); + std::string statusTest; + if (isValueOK) { + statusTest = "SUCCESS"; + } + else { + statusTest = "FAILED"; + } + std::cout << "Test with circle touching the right border, all the circle is hidden." << std::endl; + std::cout << "\tarc length =" << arcLengthCircle << std::endl; + std::cout << "\ttheoretical length =" << theoreticalValue << std::endl; + std::cout << "\ttest status = " << statusTest << std::endl; + + hasSucceeded &= isValueOK; + } + // Test with intersections with the top border, more than half a circle visible { // v = vc - r sin(theta) @@ -326,6 +376,32 @@ int main() hasSucceeded &= isValueOK; } + // Test with circle touching the top border, all the circle is hidden + { + // v = vc - r sin(theta) + // Formula: vc = OFFSET + RADIUS * sin(theta) + float theta = -M_PI_2f; + float uc = OFFSET + 100.f; + float vc = OFFSET + RADIUS * sin(theta); + vpImageCircle circle(vpImagePoint(vc, uc), RADIUS); + float arcLengthCircle = circle.computeArcLengthInRoI(roi); + float theoreticalValue = 0.f; + bool isValueOK = equal(arcLengthCircle, theoreticalValue); + std::string statusTest; + if (isValueOK) { + statusTest = "SUCCESS"; + } + else { + statusTest = "FAILED"; + } + std::cout << "Test with circle touching the top border, all the circle is hidden." << std::endl; + std::cout << "\tarc length =" << arcLengthCircle << std::endl; + std::cout << "\ttheoretical length =" << theoreticalValue << std::endl; + std::cout << "\ttest status = " << statusTest << std::endl; + + hasSucceeded &= isValueOK; + } + // Test with intersections with the bottom border, more than half a circle visible { // v = vc - r sin(theta) @@ -402,6 +478,30 @@ int main() hasSucceeded &= isValueOK; } + // Test with circle touching the bottom border, all the circle is hidden + { + // Formula: vc = OFFSET + HEIGHT + RADIUS * sin(theta) + float uc = OFFSET + 100.f; + float vc = OFFSET + HEIGHT + RADIUS; + vpImageCircle circle(vpImagePoint(vc, uc), RADIUS); + float arcLengthCircle = circle.computeArcLengthInRoI(roi); + float theoreticalValue = 0.f; + bool isValueOK = equal(arcLengthCircle, theoreticalValue); + std::string statusTest; + if (isValueOK) { + statusTest = "SUCCESS"; + } + else { + statusTest = "FAILED"; + } + std::cout << "Test with circle touching the bottom border, all the circle is hidden." << std::endl; + std::cout << "\tarc length =" << arcLengthCircle << std::endl; + std::cout << "\ttheoretical length =" << theoreticalValue << std::endl; + std::cout << "\ttest status = " << statusTest << std::endl; + + hasSucceeded &= isValueOK; + } + // Test with intersections with the top and the left border, crossing each axis once in the RoI { // Formula: u_cross_top_max = uc + r cos (theta_u_top_max) >= umin ; vmin = vc - r sin(theta_u_top_max) @@ -2000,6 +2100,7 @@ int main() std::cout << "testImageCircle overall result: SUCCESS" << std::endl; return EXIT_SUCCESS; } + std::cout << "testImageCircle overall result: FAILED" << std::endl; return EXIT_FAILURE; }