[Insight-users] Contrib: TriangleCell with centroid and normal

Gavin Baker gavinb+xtk at cs.mu.OZ.AU
Mon Jul 25 05:46:52 EDT 2005


Hello all,

I needed to have the centroid and normal of the TriangleCells in a Mesh
available for a metric.  To support this, and to avoid adding the extra
baggage to the normal TriangleCell (where people might not want to pay for
the extra storage) I've created a new class, imaginatively called
TriangleCellNC.  (Suggestions for a better name welcome!)

All the TriangleCellNC class does is keep track of the extra two attributes,
and allow them to be set and queried.  Then I've written a visitor (that
gets attached to the mesh) and traverses all the faces, calculating the
centroid and normal.  The normal calculation assumes the counter-clockwise
winding rule (as in OpenGL) so the point indices need to be in CCW order for
"outside" to be inferred correctly.

There's also a test program that creates a mesh, does the above
calculations, then prints out the result.

I'd like to contribute this. All feedback welcome!

  :: Gavin

-- 
Gavin Baker                                      Complex Systems Group
http://www.cs.mu.oz.au/~gavinb             The University of Melbourne
-------------- next part --------------
/*=========================================================================

  Program:   Insight Segmentation & Registration Toolkit
  Module:    $RCSfile: $
  Language:  C++
  Date:      $Date: $
  Version:   $Revision: $

  Copyright (c) 2005 Insight Software Consortium. All rights reserved.
  See ITKCopyright.txt or http://www.itk.org/HTML/Copyright.htm for details.

     This software is distributed WITHOUT ANY WARRANTY; without even 
     the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
     PURPOSE.  See the above copyright notices for more information.

=========================================================================*/
#ifndef __itkTriangleCellNC_h
#define __itkTriangleCellNC_h

#include "itkTriangleCell.h"
#include "itkCovariantVector.h"

namespace itk
{

/** \class TriangleCellNC
 * TriangleCell represents a triangle for a Mesh, with a centroid and normal.
 *
 * Template parameters for TriangleCell:
 *
 * TCellInterface =
 *     The cell interface from the mesh
 *
 * This behaves identically to the TriangleCell, except extra storage is
 * added to track the centroid and the normal.  The calculation of the
 * centroid and normal are carried out by
 * TriangleCellCentroidAndNormalVisitor, which needs to be applied to the
 * mesh to update these attributes.
 *
 * \ingroup MeshObjects
 *
 * \author Gavin Baker, Complex Systems Group, The University of Melbourne
 */

template < typename TCellInterface >
class ITK_EXPORT TriangleCellNC: public TriangleCell<TCellInterface>
{
public:
  itkTypeMacro(TriangleCellNC, TriangleCell);

  /** Standard cell class typedefs */
  itkCellCommonTypedefs(TriangleCellNC);
  itkCellInheritedTypedefs(TriangleCell<TCellInterface>);

  /** The triangle surface normal */
  typedef CovariantVector<PixelType> CovariantType;

  TriangleCellNC() {}

  /* */

  void SetCentroid( const PointType& p )
  {
    m_Centroid = p;
  }

  void GetCentroid( PointType* p ) const
  {
    *p = m_Centroid;
  }

  void SetNormal( const CovariantType& n )
  {
    m_Normal = n;
  }
    
  void GetNormal( CovariantType* n ) const
  {
    *n = m_Normal;
  }

protected:

  /** The centroid of the cell, average position in each dimension */
  PointType m_Centroid;

  /** The surface normal */
  CovariantType m_Normal;

private:
  TriangleCellNC(const Self&); //purposely not implemented
  void operator=(const Self&); //purposely not implemented

};


} // end namespace itk

#endif
-------------- next part --------------
/*=========================================================================

  Program:   Insight Segmentation & Registration Toolkit
  Module:    $RCSfile: $
  Language:  C++
  Date:      $Date: $
  Version:   $Revision: $

  Copyright (c) 2005 Insight Software Consortium. All rights reserved.
  See ITKCopyright.txt or http://www.itk.org/HTML/Copyright.htm for details.

     This software is distributed WITHOUT ANY WARRANTY; without even 
     the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
     PURPOSE.  See the above copyright notices for more information.

=========================================================================*/
#ifndef __itkTriangleCellCentroidAndNormalVisitor_h
#define __itkTriangleCellCentroidAndNormalVisitor_h

#include "itkTriangleCellNC.h"

#include <vnl/vnl_vector.h>
#include <vnl/vnl_cross.h>

namespace itk
{

/** \class TriangleCellCentroidAndNormalVisitor
 * Visits a mesh, calculating the centroid and surface normal for each face.
 *
 * Specifically designed to work with the TriangleCellNC, since it has the
 * added support for keeping track of its own centroid and normal.
 *
 * NB: Assumes the counter-clockwise (CCW) winding rule for determining the
 * inside and outside of a face (the default in OpenGL).
 *
 * \ingroup MeshObjects
 *
 * \author Gavin Baker, Complex Systems Group, The University of Melbourne
 */

template<typename TMeshType>
class ITK_EXPORT TriangleCellCentroidAndNormalVisitor
{
public:

  typedef TMeshType                         MeshType;
  typedef typename MeshType::CellType       CellType;
  typedef typename MeshType::PointType      PointType;
  typedef itk::TriangleCellNC<CellType>     TriType;
  typedef typename TriType::CovariantType   CovariantType;
  typedef vnl_vector<float>                 vectorf;

  /**
   * The mesh must be supplied so we can retrieve the points.
   */
  void SetMesh( MeshType* mesh )
  {
    m_Mesh = mesh;
  }

  /**
   * Visit each TriangleCellNC, retrieve its points from the mesh, and
   * calculate its centroid (by averaging in each dimension) and its normal
   * (by taking the cross-product of vectors constructed according to the
   * CCW winding rule).
   */
  void Visit( unsigned long cellid, TriType* tri )
  {
    if ( ! m_Mesh )
      {
      throw "a hissy fit";
      }

    typename TriType::PointIdIterator pointId = tri->PointIdsBegin();

    // Centroid

    PointType p0;
    PointType p1;
    PointType p2;

    m_Mesh->GetPoint( *pointId++, &p0 );
    m_Mesh->GetPoint( *pointId++, &p1 );
    m_Mesh->GetPoint( *pointId++, &p2 );

    PointType centroid;

    centroid[0] = (p0[0]+p1[0]+p2[0])/3.0;
    centroid[1] = (p0[1]+p1[1]+p2[1])/3.0;
    centroid[2] = (p0[2]+p1[2]+p2[2])/3.0;

    tri->SetCentroid( centroid );

    // Face Normal

    vectorf v0 = p1.GetVnlVector() - p0.GetVnlVector();
    vectorf v1 = p2.GetVnlVector() - p0.GetVnlVector();

    vectorf normal = vnl_cross_3d( v0, v1 );

    CovariantType cov_normal;
    cov_normal.Set_vnl_vector( normal );
    cov_normal.Normalize();

    tri->SetNormal( cov_normal );
  }

protected:

  typename MeshType::Pointer m_Mesh;
};


} /* namespace itk */

#endif
-------------- next part --------------
/*=========================================================================

  Program:   Insight Segmentation & Registration Toolkit
  Module:    $RCSfile: $
  Language:  C++
  Date:      $Date: $
  Version:   $Revision: $

  Copyright (c) 2005 Insight Software Consortium. All rights reserved.
  See ITKCopyright.txt or http://www.itk.org/HTML/Copyright.htm for details.

     This software is distributed WITHOUT ANY WARRANTY; without even 
     the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
     PURPOSE.  See the above copyright notices for more information.

=========================================================================*/

#include "itkMesh.h"
#include "itkTriangleCell.h"
#include "itkTriangleCellCentroidAndNormalVisitor.h"

typedef float                             PixelType;
typedef itk::Mesh< PixelType, 3 >         MeshType;

typedef MeshType::CellType                CellType;

typedef itk::TriangleCellNC< CellType >   TriangleType;

// Simple printing visitor

template <typename TMesh>
class PrintingVisitor
  {
      typedef TMesh MeshType;
      typedef typename TriangleType::PointType PointType;
      typedef typename TriangleType::CovariantType CovariantType;

  public:

      void SetMesh( MeshType* mesh )
          {
              m_Mesh = mesh;
          }
      
      void Visit(unsigned long cellId, TriangleType * tri )
          {
              std::cout << "Cell " << cellId << std::endl;

              PointType p;
              tri->GetCentroid( &p );
              std::cout << "    Centroid: " << p << std::endl;

              CovariantType n;
              tri->GetNormal( &n );
              std::cout << "    Normal: " << n << std::endl;
          }

  protected:

      typename MeshType::Pointer m_Mesh;
};

int main(int, char *[])
{
  MeshType::Pointer  mesh = MeshType::New();

  // Creating the points and inserting them in the mesh
  //
  MeshType::PointType   point0;
  MeshType::PointType   point1;
  MeshType::PointType   point2;
  MeshType::PointType   point3;

  point0[0] = -1; point0[1] = -1; point0[2] = -1; 
  point1[0] =  1; point1[1] =  1; point1[2] = -1; 
  point2[0] =  1; point2[1] = -1; point2[2] =  1; 
  point3[0] = -1; point3[1] =  1; point3[2] =  1; 

  mesh->SetPoint( 0, point0 );
  mesh->SetPoint( 1, point1 );
  mesh->SetPoint( 2, point2 );
  mesh->SetPoint( 3, point3 );


  // Creating and associating the Tetrahedron
  //
  CellType::CellAutoPointer cellpointer;

  // Creating and associating the Triangles
  //
  cellpointer.TakeOwnership( new TriangleType );
  cellpointer->SetPointId( 0, 0 );
  cellpointer->SetPointId( 1, 1 );
  cellpointer->SetPointId( 2, 2 );
  mesh->SetCell( 1, cellpointer );

  cellpointer.TakeOwnership( new TriangleType );
  cellpointer->SetPointId( 0, 0 );
  cellpointer->SetPointId( 1, 2 );
  cellpointer->SetPointId( 2, 3 );
  mesh->SetCell( 2, cellpointer );

  cellpointer.TakeOwnership( new TriangleType );
  cellpointer->SetPointId( 0, 0 );
  cellpointer->SetPointId( 1, 3 );
  cellpointer->SetPointId( 2, 1 );
  mesh->SetCell( 3, cellpointer );

  cellpointer.TakeOwnership( new TriangleType );
  cellpointer->SetPointId( 0, 3 );
  cellpointer->SetPointId( 1, 2 );
  cellpointer->SetPointId( 2, 1 );
  mesh->SetCell( 4, cellpointer );

  // Simple verification of the number of points and cells inserted
  //
  std::cout << "# Points= " << mesh->GetNumberOfPoints() << std::endl;
  std::cout << "# Cell  = " << mesh->GetNumberOfCells() << std::endl;

  // Visitor - have one to calculate and the other to print

  typedef itk::TriangleCellCentroidAndNormalVisitor<MeshType> NCVisitorType;

  typedef PrintingVisitor<MeshType> PrintingVisitorType;

  typedef itk::CellInterfaceVisitorImplementation<
      PixelType, MeshType::CellTraits, TriangleType,
      NCVisitorType> NCTriangleVisitorType;

  typedef itk::CellInterfaceVisitorImplementation<
      PixelType, MeshType::CellTraits, TriangleType,
      PrintingVisitorType> PrintingTriangleVisitorType;

  NCTriangleVisitorType::Pointer triangleVisitor =
      NCTriangleVisitorType::New();

  PrintingTriangleVisitorType::Pointer printingVisitor =
      PrintingTriangleVisitorType::New();

  triangleVisitor->SetMesh( mesh ); 
  printingVisitor->SetMesh( mesh ); 

  // Multi-Visitor

  typedef CellType::MultiVisitor CellMultiVisitorType;
  CellMultiVisitorType::Pointer multiVisitor = CellMultiVisitorType::New();  

  // Process the mesh...
  multiVisitor->AddVisitor( triangleVisitor );
  mesh->Accept( multiVisitor );

  // Now print it (We can reuse the same multiVisitor because only one
  // visitor for each topology is kept at a time.  It should probably use a
  // hash map or something, so multiple visitors can actually be used.)
  multiVisitor->AddVisitor( printingVisitor );
  mesh->Accept( multiVisitor );

  return 0;
}



More information about the Insight-users mailing list