-
-
Notifications
You must be signed in to change notification settings - Fork 668
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
Add SetPointsByCoordinates
to PointSet, add warning to SetPoints(PointsVectorContainer *)
#4850
Add SetPointsByCoordinates
to PointSet, add warning to SetPoints(PointsVectorContainer *)
#4850
Conversation
Aims to mitigate issue InsightSoftwareConsortium#4848 "`PointSet::SetPoints(PointsVectorContainer *)` overload leads to undefined behavior"
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If I remember correctly, the main point of the old method was convenient usage from Python. @PranjalSahu is that correct?
@N-Dekker how would use of the proposed method look like from Python? Perhaps write a Python test to demonstrate that?
I'm sorry I'm not very good in Python tests. 🤷 But it looks like SWIG supports passing a Python tuple or list as argument, when the parameter type is |
Yes, another library supported such functionality of directly passing a 1-D vector of points in any dimension, so we wanted to have a similar feature here. It would be better to find an alternative to reinterpret_cast instead of adding a new method. If there are no alternatives then yes we can add a new method. Also did @N-Dekker did you come across any platform where this method is breaking? |
Honestly, I did not. But it's really quite a coincidence that the In general, undefined behavior should be avoided because the compiler (especially the optimizer) may mess up things as it is allowed to assume that there is no undefined behavior. |
My idea would be to deprecate Why would you not want to have |
ok it will be great if we could create a scenario where it fails! My understanding is that it is converting the pointer type of the continuous memory block where the 1D vector of 1D points of length 3N is stored, to a vector of length N of 3D Points. Essentially the same as Numpy reshape operator (1 x 3N -> 3xN). This is needed because only 1D vectors are supported while converting the Numpy vector to VectorContainer and vice-versa. |
|
||
for (const auto & point : points) | ||
{ | ||
if (!std::equal(point.cbegin(), point.cend(), coordinateIterator)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Interesting errors at https://open.cdash.org/viewBuildError.php?buildid=9909255, please have a look:
/.../s/Modules/Core/Common/include/itkPointSet.hxx: In instantiation of 'void itk::PointSet<TPixelType, VDimension, TMeshTraits>::SetPointsByCoordinates(const std::vector<typename TMeshTraits::CoordRepType>&) [with TPixelType = double; unsigned int VDimension = 2; TMeshTraits = itk::QuadEdgeMeshTraits<double, 2, bool, bool, float, float>; typename TMeshTraits::CoordRepType = float]':
Wrapping/Modules/ITKQuadEdgeMesh/itkQuadEdgeMeshBasePython.cpp:11546:97: required from here
Modules/Core/Common/include/itkPointSet.hxx:109:31: error: 'const struct std::pair<const long unsigned int, itk::QuadEdgeMeshPoint<float, 2, itk::GeometricalQuadEdge<long unsigned int, long unsigned int, bool, bool, true> > >' has no member named 'cbegin'
It appears that PointsContainer
may be an itk::MapContainer
, rather than just an itk::VectorContainer
!
using PointsContainer = MapContainer<PointIdentifier, PointType>; |
Obviously reinterpret_cast<PointsContainer *>(points)
won't work when PointsContainer
is an instance of itk::MapContainer
. But my pull request should also still be adjusted to support itk::MapContainer
, apparently 🤷
3e16be3
to
37e1b83
Compare
@PranjalSahu Please try the following: #include "itkPointSet.h"
#include "../../QuadEdgeMesh/include/itkQuadEdgeMeshTraits.h"
#include <gtest/gtest.h>
TEST(PointSet, SetPoints)
{
static constexpr auto PointDimension = 2;
using MeshTraits = itk::QuadEdgeMeshTraits<double, PointDimension, bool, bool>;
using PointSetType = itk::PointSet<double, PointDimension, MeshTraits>;
using CoordRepType = PointSetType::CoordRepType;
auto pointSet = PointSetType::New();
auto pointsVectorContainer = PointSetType::PointsVectorContainer::New();
auto numberOfPoints = 42;
pointsVectorContainer->CastToSTLContainer() = std::vector<CoordRepType>(numberOfPoints * PointDimension);
pointSet->SetPoints(pointsVectorContainer);
auto points = pointSet->GetPoints();
EXPECT_EQ(points->size(), numberOfPoints);
} It appears that after calling your |
Ok yes, it was not designed to be used with such complex point types. |
/azp run ITK.Linux.Python |
By the way, I proposed a different name for the added member function, "SetPointsByCoordinates", because it is fundamentally different from the two existing SetPoints overloads. Those overloads both "share" the ownership of the object passed as argument (using ITK's reference counting mechanism). Whereas "SetPointsByCoordinates" copies the coordinates from the specified argument. Personally I think the name "SetPointsByCoordinates" is fine, but it could also be named, for example, "SetPointsFromCoordinates" or "CopyCoordinatesToPoints". A matter of taste, I guess 🤷 |
Yes, that was also one of the requirements. Because the number of points could be large, we wanted to avoid copying the data. |
How large could the number of points possibly be, in your application? Can you please give the Undefined behavior is just not an option, for any professional application or any serious open source project. |
We will have to add tests for Python wrapping also. |
OK, thanks for reminding me, @PranjalSahu, but Python tests can also be added in a follow-up pull request. The given PR does not break any existing use case. While it does add a valid C++ use case, already. (I like to keep pull requests small, so that they can be reviewed easily, and merged smoothly.) PS My first impression is that passing an |
37e1b83
to
9cb3f7c
Compare
Aims to provide a safe alternative to `PointSet::SetPoints(PointsVectorContainer *)`.
9cb3f7c
to
60d183c
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This looks like a good and clean addition.
It only adds comments about an unsafe methods. These additional request for testing and compatibility ( python api?) could be done if the unsafe methods is marked for deprecation or removed in future work.
Before removing this it would be great if the Python wrapping could be tested and merged. |
Is there a Python wheel for Windows available, based on the latest commit, including this pull request? I would like to try out the new |
A Python wheel is not built automatically. You need a local build with wrapping enabled. Then, copy |
Specifically tested `SetPointsByCoordinates` with a NumPy array as input argument, as was suggested by Pranjal Sahu. - Follow-up to pull request InsightSoftwareConsortium#4850 commit f43b1b8 ENH: Add `PointSet::SetPointsByCoordinates(coordinates)`
PointSet::SetPoints(PointsVectorContainer *)
, mitigating issuePointSet::SetPoints(PointsVectorContainer *)
overload leads to undefined behavior #4848PointSet::SetPointsByCoordinates
as a safe alternative.