Skip to content

Commit

Permalink
[CORPS] Added a 'modulo' operation for float and double in vpMath + […
Browse files Browse the repository at this point in the history
…FIX] Corrected visibility check for single axis crossing
  • Loading branch information
rlagneau committed Oct 30, 2023
1 parent 3d3cc30 commit 084d1b8
Show file tree
Hide file tree
Showing 3 changed files with 153 additions and 43 deletions.
48 changes: 39 additions & 9 deletions modules/core/include/visp3/core/vpMath.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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$.
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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))
Expand Down
47 changes: 13 additions & 34 deletions modules/core/src/image/vpImageCircle.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<float>::epsilon()) {
delta_theta = 0.f;

Check warning on line 81 in modules/core/src/image/vpImageCircle.cpp

View check run for this annotation

Codecov / codecov/patch

modules/core/src/image/vpImageCircle.cpp#L81

Added line #L81 was not covered by tests
}
}

/*!
Expand All @@ -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<float>::epsilon()) {
delta_theta = 0.f;

Check warning on line 106 in modules/core/src/image/vpImageCircle.cpp

View check run for this annotation

Codecov / codecov/patch

modules/core/src/image/vpImageCircle.cpp#L106

Added line #L106 was not covered by tests
}
}

/*!
Expand Down Expand Up @@ -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<float>::epsilon()) {
delta_theta = 0.f;
}
}

/*!
Expand Down Expand Up @@ -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<float>::epsilon()) {
delta_theta = 0.f;
}
}

/*!
Expand Down Expand Up @@ -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;
}
}

/*!
Expand Down Expand Up @@ -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;
Expand Down
101 changes: 101 additions & 0 deletions modules/core/test/tools/geometry/testImageCircle.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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;
}

0 comments on commit 084d1b8

Please sign in to comment.