Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fixing PointVector implicit conversions #1345

Merged
merged 52 commits into from Mar 17, 2019

Conversation

rolanddenis
Copy link
Member

@rolanddenis rolanddenis commented Sep 10, 2018

PR Description

Fixing conversions between PointVector with different Euclidean Ring type and storage (see #1341).
Automatic Euclidean Ring return type for compatible operations.

Note that all external operators (+, -, <, ...) and functions (dotProduct, crossProduct, ...) that are related to PointVector are declared as friend of this class. It is not necessary since those operators and functions never access to protected/private members but it allows to have them documented on the same page as PointVector in a "Friends" section.

Checklist

  • Unit-test of your feature with Catch.
  • Doxygen documentation of the code completed (classes, methods, types, members...)
  • Documentation module page added or updated.
  • New entry in the ChangeLog.md added.
  • No warning raised in Debug cmake mode (otherwise, Travis C.I. will fail).
  • All continuous integration tests pass (Travis & appveyor)
  • Clean commit history

PointVector checklist

  • Assignment operator
  • Unary & binary functor conversion constructors
  • Arithmetic binary operators
  • Arithmetic unary operators with assignment (+=, -=,...)
  • Comparison operators (using lexicographical order)
  • Other binary functions on PointVector (dot, crossProduct, inf,...) . Also external versions.
  • Adding comparison tests
  • partialCopy and partialEqual
  • New functors to ease the choice of rounding mode without specifying type (like with std::round)
  • crossProduct of 2D vectors returns a scalar. This is a critical change that may lead to compilation errors (e.g. https://github.com/rolanddenis/DGtal/blob/fix_pointvector/src/DGtal/shapes/Mesh.ih#L464)

Conversion fixes

Q&A

Why is there a conversion issue ?

When a PointVector<dim,T> is converted into another PointVector<dim,U> in a implicit way (e.g. when passing the first type to a function accepting the second type) and if converting from T to U is ambiguous (e.g. from double to int), then this conversion will fail.

Which conversions are valid ?

The conversion follows the implicit arithmetic conversion rules (see https://en.cppreference.com/w/cpp/language/operator_arithmetic#Conversions), that is T can be converted to U if T + U has type U. This type deduction is explained in the link above and is quite natural except for the signed/unsigned conversion: if T and U are both integers with same precision but one is signed and the other unsigned, then T + U is unsigned. Thus, conversion from signed int to unsigned int is valid while the contrary, unsigned int to signed int, is invalid.

How to fix a conversion issue ?

It depends on what the initial code was supposed to do:

  • if a simple cast was meant (beware of numerical errors !), then you can use static_cast or explicitly call the constructor: instead of
PointVector<2, int> p1;
PointVector<2, double> p2; 
...
p1 = p2; // Implicit conversion from double to int requested => compiler error !

write

PointVector<2, int> p1;
PointVector<2, double> p2; 
...
p1 = PointVector<2, int>(p2); // Explicit call to the appropriate constructor. 
  • if the value must be modified before the cast, like using a rounding function, you can explicitly call the constructor with the appropriate functor as second argument (take a look at the new functors introduced in src/DGtal/base/BasicFunctors.h):
    instead of
PointVector<2, int> p1;
PointVector<2, double> p2; 
...
p1 = p2; // Implicit conversion from double to int requested => compiler error !

write

PointVector<2, int> p1;
PointVector<2, double> p2; 
...
// Explicit call to the constructor with conversion functor.
p1 = PointVector<2, int>(p2, functors::Round<>());

So basically, in most cases, we just need to know if the conversion should be fixed using static_cast, trunc, ceil, floor, round or something more exotic.

Why are some developers pinned to specific issues ?

I've just use git blame on erroneous lines 😉
I need to know if the proposed fix is valid or, if the issue is not already fixed (unchecked box), I need to know how to fix it (basically, what conversion function should be used).

Remaining issues

In DGtal

In DGtalTools

  • estimators/2dLocalEstimators.cpp
  • estimators/volSurfaceRegularization.cpp
  • converters/mesh2heightfield.cpp
  • converters/img2freeman.cpp
  • converters/vol2heightfield.cpp
  • visualisation/3dCurvatureViewerNoise.cpp
  • visualisation/3dCurvatureViewer.cpp
  • visualisation/3dVolViewer.cpp

In DGtalTools-contrib

  • geometry3d/basicEditMesh.cpp

@troussil
Copy link
Member

For ParametricShapeArcLengthFunctor.h, you can use RealPoint instead of Vector.

@rolanddenis
Copy link
Member Author

rolanddenis commented Nov 21, 2018

For ParametricShapeArcLengthFunctor.h, you can use RealPoint instead of Vector.

Ok, thanks ! By the way, what is the purpose of the int32 cast followed by a int64 cast and a double cast in the next line?

double n = (double) NumberTraits<Integer>::castToInt64_t( (const DGtal::int32_t)v.norm(Vector::L_infty) );
unsigned int nbSamples = (unsigned int) ceil( n*100 );

@troussil
Copy link
Member

I have no idea... If you use RealPoint, then
double n = v.norm(RealPoint::L_infty)
is enough.

@rolanddenis
Copy link
Member Author

Ok, thanks 😉

@troussil
Copy link
Member

To solve issues involved in exampleConvexHull2D.cpp and exampleAlphaShape.cpp (and maybe other similar issues), I suggest to add a constructor to HyperRectDomain that takes input bounds of type Space::RealPoint. Something like (using C++11 syntax, but I didn't test the code):

DGtal::HyperRectDomain< TSpace >::HyperRectDomain (
  const typename TSpace::RealPoint& aPointA,
  const typename TSpace::RealPoint& aPointB ) : 
    DGtal::HyperRectDomain< TSpace >::HyperRectDomain (
      Point(aPointA, functors::Floor<>()), 
      Point(aPointB, functors::Ceil<>()) )
{}

@rolanddenis
Copy link
Member Author

Ok, good idea ! However, it is not clear for me if, for example, [3.4, 3.6] should lead to an empty 1D domain (since no integer point is within the given interval) or to the [3, 4] domain ?

@troussil
Copy link
Member

troussil commented Nov 22, 2018

In my view, it is better to have a digital bounding box larger than the real one (for digitization purpose for instance). That's why I suggest to do "floor" on the lower bound and "ceil" on the upper one.

So [3.4, 3.6] -> [3,4] is fine for me.

It creates the smallest domain with integer coordinates that contains
the given bounds.

It fixes several conversion issues when creating an HyperRectDomain from
a shape bounds.
@rolanddenis
Copy link
Member Author

Ok, thanks !

The call is now ambiguous due to the additional HyperRectDomain
constructor that take RealPoint bounds.
@dcoeurjo
Copy link
Member

hi @rolanddenis and @kerautret, can we push forward a bit this PR ?

what remains to be done ?

@kerautret
Copy link
Member

hi David yes for me all fine, just merge the branch of src remote of @rolanddenis
For the tools it just contain some prog to fix I will make point

@rolanddenis
Copy link
Member Author

Sorry, I was on vacation for few days, I will reviews your PRs later today or tomorrow !

rolanddenis and others added 8 commits March 14, 2019 14:27
A point with signed integer coordinates was multiplied by an unsigned
integer resulting in a point with unsigned integer coordinates. The
assignment to a point with signed integer was then resulting in a
conversion error.

The fix is simply to cast the factor (myNbRepeat) to the coordinate type
before multiplying.
The fix of Mesh::changeScale argument type from double to the Component
type of the handled points implies that a Component typedef is needed
for all Point class used with Mesh.
@rolanddenis
Copy link
Member Author

I've added the "Ready to review" tag to this PR since the proposed modifications to the PointVector class are in a final state.

Since the last accepted PR, the main Travis jobs (that includes only the tests) run now successfully, thanks everyone 🎉 🎊

It remains testMonge (not tested since it requires CGAL) and testBasicPointFunctors (temporary but invalid fix) to be fixed, and also several examples files and tools.

We are almost at the end of this PR!

@dcoeurjo
Copy link
Member

What is the problem with testMonge ? The numerical values ?

@dcoeurjo
Copy link
Member

you haven't use my testMonge edit ?

volDTgranulo (and more... wait for it)
@rolanddenis
Copy link
Member Author

Indeed, just merged your PR, thanks!

@rolanddenis
Copy link
Member Author

About the issues in DGtal, it seems that last commits solve all remaining examples 😉

It just remains testBasicPointFunctors...

@dcoeurjo
Copy link
Member

Thx! Let’s wait for Travis and I’ll merge. Huge thank for this painful pandora box ;)

I’ve PR some edits in Bertrand’s DGtaltools fixes. Tools should be ok.

@dcoeurjo
Copy link
Member

For the remaining failing test.. can you please open up a new issue ?

@dcoeurjo
Copy link
Member

Tools failing but it’s normal ;)
Thx merging

@dcoeurjo dcoeurjo changed the title [WIP] Fixing PointVector implicit conversions Fixing PointVector implicit conversions Mar 17, 2019
@dcoeurjo dcoeurjo merged commit 6570103 into DGtal-team:master Mar 17, 2019
@rolanddenis
Copy link
Member Author

Yep, already done in #1348 😉

@rolanddenis
Copy link
Member Author

Oops, it seems that I just forget to finish the documentation of PointVector (the FIXME), I will look at that next week 😅

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

5 participants