Proposals:New Mesh Class

From KitwarePublic
Jump to navigationJump to search

This document derives the motivation and implementation of a new Mesh Class and associated Filters and IO classes for ITK


Overview

itkQuadEdgeMesh (itkQEMesh) is a new data structure for surface meshes. It is more efficient than the current itkMesh for surface processing. However it cannot handle N-dimensional meshes. Hence itkMesh and itkQEMesh will coexist in future releases of ITK. This documents describes how itkQuadEdgeMesh will be integrated into ITK 3.2.

First Implementation Integration issues

Here are the current issues

1: function vs filters

01/23/07
Operations in the QuadEdgeMesh are implemented in Function classes That derive from FunctionBase. We see your point on externalizing the methods from the QuadEdgeMesh, that's certainly compatible with the general ITK design. In your paper you explain that you preferred to use Functions instead of Filters in order to save memory. That is, to make changes in-place, as opposed to creating a copy of the mesh as output to a filter. That's a reasonable argument. I would suggest that we keep the function classes but we also offer them in the form of filters that allow to support const-correctness. Using the data-pipeline is also compatible with the VTK design.

update 01/24/07: eric
Providing those additional filters could be implemented as wrappers around the function classes (with an additional copy of the mesh) which shouldn't be too expensive to maintain. Thus support of const-correctness could thus be cheap.

2: inheritance vs link

01/23/07
QuadEdgeMeshLineCell deriving from the GeometricQuadEdge. This is a bit of a conceptual problem. I would suggest that we remove the inheritance from the QuadEdge, and replace it with the LineCell having a reference to the first GeometricQuadEdge of the local ring. The LineCell may provide a method such as (for example) GetFirstQuadEdge(), that will return a reference to the QuadEdge, and from that QuadEdge we could continue navigating the local topology. From the design point of view (and even the topology) it seems more appropriate to say that :
"The LineCell *has* a reference to a QuadEdge"
than to say that
"The LineCell *is* a QuadEdge"
in particular, considering that the LineCell is the equivalent of the "Physical Edge". The template parameter of the GeometricQuadEdge, still allows you to associate data with the QuadEdge.

update 01/24/07: kitware
Trying to elaborate on the reasons for having the LineCell to derive from the GeometricalQuadEdge, we are wondering if you intended the LineCell to be oriented. Was that part of your motivation ? We are suggesting to remove the inheritance of the LineCell from the GeometricalQuadEdge, and instead have the LineCell keep a pointer to the first of the 4 QuadEdges that will be created by the MakeEdge() method. Similar to how the PolygonCell is doing.

Update 01/24/07:Eric
The problem if you switch from inheritance to aggregation seems that you loose the coupling of the two objects. Consider a filter (or function class) that operates at the QuadEdge level and is written at this level (and we do this all the time). Then in order to the have the LineCell refering to the concerned QuadEdge to be modified in the proper way we would thus need to promote this code at the LineCell level (because a QuadEdge wouldn't "know" of the LineCell). This would make things much harder to write no ? You would need to navigate back and forth between the itk and QuadEdge level in order to keep them synchronized. Wouldn't you ?

Update 01/24/07:leo
When this code was written, Eric's explanation was right. It seemed very hard to make these two data structures (QE and cell-based meshes) compatible. As I just wrote to you, I'm working on solving this problem, but I haven't had the time to finish it.

Update 01/24/07:leo
It seemed simpler to do this to get the QE architecture working under a cell-based interface. Now, knowing a little bit more itk's architecture, I know that we can avoid this double derivation. I started working on this issue, but I haven't had time to finish it. Do you think it would be interesting to continue working on this? I hope to have some time starting next week.

Update 01/25/07:Luis
Yes, we think that it is worth to remove the double derivation. We are heading now to have the LineCell have a pointer to the first QuadEdge of the 4-tuple. It will also have the MakeEdge() method, to be called from the constructor. The MakeEdge() in the GeometricalQuadEdge will be removed.

Update 01/25/07:leo
The idea I've been developing is quite similar to Luis' suggestions. In fact, I've created a new CellInterface class that "keeps track" of QEs (by aggregation). I've tested this new implementation taking "native" itkMesh examples, changing just the template's instantiation and running them. So far, it works fine. Right now, I'm completing this implementation in order to cope with all CellInterface methods. I hope there will not be further suprises. The other idea I'm developing is related to dual meshes. The idea is to keep track, at the same time, of the primal and dual mesh. I hope to use this new representation to simplify simplex mesh-based algorithms, but I'm sure there are more applications to this.

3: Error management

01/23/07
There is not enough error management in the code. For example, there are methods implemented as

             ->GetRot()->GetRot()->GetRot()

that rely on the assumption that none of the intermediate calls return a null pointer. We are moving all these methods from the header file into the .cxx file, and inserting error management in them. The basic principle is that no method should crash. We can do error management by returning null pointers in the intermediate cases, or by throwing exceptions. The unit test should be such that any method could be called in any order, without making the class crash.

Update: 01/24/07: Eric
Fair enough. But we have the structural guarantee that this won't happen, since the edge algebra can NOT be corrupted IF you only manipulate it through Splice(). The only way (I can foresee) for your null pointer scenario to happen would be some exterior corruption of the data structure. Actually QuadEdge.SetRot() and it's SetOnext() counterpart shouldn't be public (are they actually are) but strongly restricted. If you look closer, you should see no occurence of SetRot() except for the constructor and you shouldn't find any occurence of SetOnext() except for the constructor and Splice(). How could you thus break the edge-algebra ?

4: Coding style: Method renaming

01/23/07
We will expand the current abridged names of methods, and for reference we will leave the original name in the Doxygen documentation, so that users can refer to the terms used in the paper.

5: allocation without desallocation

01/23/07
The QuadEdges that are allocated in the MakeEdge() method in the LineCell and in the GeometricalQuadEdge (formerly QEGeom), ...where are they deallocated ? We couldn't find any calls to the "delete"s that should match these "new". We are suggesting to add the deletes to the destructor of the GeometricalQuadEdge

update 01/24/07: eric
Doing so (i.e. removing the LineCell::MakeEdge() method) makes almost all the atomic tests of our test-suite to SegFault ! I know this looks kludgy (since LineCell::MakeEdge() seems to overload the QuadEdge::MakeEdge() while the codes are being almost the same) but is has to do with the fact that we wanted LineCell and QuadEdges to be tigthly coupled. And we couldn't come with a cleaner way of hooking things in order to obtain the proper result in the LineCell constructor (the only time I tried, I failed poorly).

Update 01/24/07: leo
Yep. Coupling QE's and cells forced a reimplementation of MakeEdge() in order to construct edges with the right types (primal and dual).

6: Reimplementation of MakeEdge()

01/23/07
Why is MakeEdge() reimplemented in the LineCell ? if it derives from the GeometricalQuadEdge and the method already exist in that class ? We are suggesting to remove the MakeEdge() method from the LineCell.

Update 01/24/07:eric
LineCell appeared quite late in the coding process (yes, we do such things) and were just introduced for compatibility with itk::Mesh. LineCells are not oriented. QuadEdges are.

Update 01/24/07: leo
They are not oriented. The problem comes from the desire to make QE's and cells working together with the same syntax. As I already wrote, I'm working on solving this.

Second implementation

1: Done

  • LineCell does not inherit from QuadEdgeGeom anymore
  • LineCell has a pointer to a QuadEdgeGeom
  • LineCell constructor creates the 4 QE
  • QuadEdgeGeom keeps memory of the identifier of the LineCell in the mesh container (to be able to delete the edge)
  • MakeEdge() methods removed
  • two macro removed
  • LineCell Destructor deletes the 4 underlying QE
  • all direct creation of QuadEdgeGeom should be prohibited, and replaced in the actual code by LineCell creation
  • Polygon constructor creates the needed LineCells.
  • adress the iteratorMacro

It's working and the test suite passes.

2: To be Done??

  • QuadEdgeGeom constructor is made private and can only be called by LineCell ?
  • QuadEdgeGeom smart initialization to prevent misuse (sym = NULL) ?
  • actually delete the EdgeCell in LightWeightDeleteEdge ?

3: Merging in ITK/review

As of june 27th 2008, the Core is in ITK Review

Changes were made in the code both in the original CVS repository ( cvs -d :pserver:anonymous@cvs.creatis.insa-lyon.fr:/cvs/public/ itkQuadEdgeMesh ) and in the version that was imported in ITK ( /Review of the CVS version of ITK ).

  • in /Review, class and method names may have changed. Macro may have been displaced.
  • in creatis' cvs rep., the second implementation was done.

It matters now to merge everything. the second implementation will be first merged into the ITK/Review, then validated by several individuals, and finally merged back to the Creatis' rep. There will be a migration effort to be made for the existing filters to work (Euler Operators, recoded vtk filters and so), then everything will be ready for inclusion in a further version of ITK.


ITK/Code/Review Names original CVS Names
itkGeometricalQuadEdge.* itkQEQuadEdgeGeom.*
itkQuadEdge.* itkQEQuadEdge.*
itkQuadEdgeMesh.* itkQEMesh.*
itkQuadEdgeMeshBaseIterator.* itkQEBaseIterator.*
itkQuadEdgeMeshBoundaryEdgesMeshFunction.* itkQEBoundaryRepresentativeEdgesMeshFunction.*
itkQuadEdgeMeshFrontIterator.* itkQEFrontIterator.*
itkQuadEdgeMeshLineCell.* itkQELineCell.*
itkQuadEdgeMeshMacro.h itkQEMeshMacro.h
itkQuadEdgeMeshPoint.* itkQEPoint.*
itkQuadEdgeMeshPolygonCell.* itkQEPolygonCell.*
itkQuadEdgeMeshTopologyChecker.* ?
itkQuadEdgeMeshToQuadEdgeMeshFilter.* /BasicFilter/itkQEMeshCopy.*
itkQuadEdgeMeshTraits.h itkQEMeshTraits.h

4: Code Coverage and Memory Leaks

We used the CMake / CTest options to check code coverage and mem leaks (using gcov and valgrind). Here are the results as of June 28 2008

4.1: Code Coverage

ITK/Code/Review Names Coverage # of Lines not covered Analyze - Comments
itkGeometricalQuadEdge.h 69.77% 13 iterators, SetRight( ).
itkGeometricalQuadEdge.txx 75.49% 25 Degenerated cases (Disconnect).
itkQuadEdge.h 07.69% 12 iterators.
itkQuadEdge.cxx 86.15% 36 Degenerated Cases not tested.
itkQuadEdgeMesh.h 58.33% 05 Requestedregion...( ), CopyInformation( ).
itkQuadEdgeMesh.txx 79.07% 72 FindClosestPoint( ), DeleteFace( ), GetEdge( ), GetVector( ), LightWeightDeleteEdge( primal ), FindEdgeCell( ),
itkQuadEdgeMeshBaseIterator.h 68.42% 24 Iterators not used.
itkQuadEdgeMeshBoundaryEdgesMeshFunction.h ------ -- Not Covered - Not Called
itkQuadEdgeMeshBoundaryEdgesMeshFunction.txx ------ -- Not Covered - Not Called
itkQuadEdgeMeshFrontIterator.h 81.82% 04 Const flavor of Constructor, Destructor. To remove?
itkQuadEdgeMeshFrontIterator.txx 81.40% 08 Bad init fall cases, FindDefaultSeed( ).
itkQuadEdgeMeshLineCell.h 66.67% 01 MakeCopy( ).
itkQuadEdgeMeshLineCell.txx 53.85% 30 Accept, BoundaryFeat., PointIds(+1 BUG), Ident, Type, Dimension.
~~itkQuadEdgeMeshMacro.h~~ ------ -- Not Covered - Macros
~~itkQuadEdgeMeshPoint.h~~ 100.0% 00 -----
~~itkQuadEdgeMeshPoint.txx~~ 100.0% 00 -----
itkQuadEdgeMeshPolygonCell.h 77.78% 02 MakeCopy( )
itkQuadEdgeMeshPolygonCell.txx 61.54% 25 Accept( ), BoundaryFeat., PointsIds,
~~itkQuadEdgeMeshTopologyChecker.h~~ 100.0% 00
~~itkQuadEdgeMeshTopologyChecker.txx~~ 100.0% 00
~~itkQuadEdgeMeshToQuadEdgeMeshFilter.h~~ ------ -- Not Covered - Not Used
~~itkQuadEdgeMeshToQuadEdgeMeshFilter.txx~~ ------ -- Not Covered - Not Used
~~itkQuadEdgeMeshTraits.h~~ ------ -- Not Covered - Implicit + only types

4.2: Memory Leak

  • 5 mem leaks
  • 5 potentials - addressed june 29.
  • warnings with borland - addressed july 1.