/****************************************************************************
* VCGLib                                                            o o     *
* Visual and Computer Graphics Library                            o     o   *
*                                                                _   O  _   *
* Copyright(C) 2004-2016                                           \/)\/    *
* Visual Computing Lab                                            /\/|      *
* ISTI - Italian National Research Council                           |      *
*                                                                    \      *
* All rights reserved.                                                      *
*                                                                           *
* This program is free software; you can redistribute it and/or modify      *
* it under the terms of the GNU General Public License as published by      *
* the Free Software Foundation; either version 2 of the License, or         *
* (at your option) any later version.                                       *
*                                                                           *
* This program is distributed in the hope that it will be useful,           *
* but WITHOUT ANY WARRANTY; without even the implied warranty of            *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the             *
* GNU General Public License (http://www.gnu.org/licenses/gpl.txt)          *
* for more details.                                                         *
*                                                                           *
****************************************************************************/
/***************************************************************************/

#ifndef __VCG_MARCHING_CUBES
#define __VCG_MARCHING_CUBES

#include "mc_lookup_table.h"
#include <array>

namespace vcg
{
    namespace tri
    {
        // Doxygen documentation
        /** \addtogroup trimesh */
        /*@{*/

        /*
        * Cube description:
        *         3 ________ 2           _____2__
        *         /|       /|         / |       /|
        *       /  |     /  |      11/  3   10/  |
        *   7 /_______ /    |      /__6_|__ /    |1
        *    |     |  |6    |     |     |  |     |
        *    |    0|__|_____|1    |     |__|__0__|
        *    |    /   |    /      7   8/   5    /
        *    |  /     |  /        |  /     |  /9
        *    |/_______|/          |/___4___|/
        *   4          5
        */

        //! This class implements the Marching Cubes algorithm.
        /*!
        *	The implementation is enough generic: this class works only on one volume cell for each
        *	call to <CODE>ProcessCell</CODE>. Using the field value at the cell corners, it adds to the
        *	mesh the triangles set approximating the surface that cross that cell. The ambiguities
        *	are resolved using an enhanced topologically controlled lookup table.
        *	@param TRIMESH_TYPE (Template parameter) the mesh type that will be constructed
        *	@param WALKER_TYPE	(Template parameter) the class that implement the traversal ordering of the volume
        **/
        template<class TRIMESH_TYPE, class WALKER_TYPE>
        class MarchingCubes
        {
        public:
            enum Dimension		 {X, Y, Z};
            typedef typename vcg::tri::Allocator< TRIMESH_TYPE > AllocatorType;
            typedef typename TRIMESH_TYPE::ScalarType			ScalarType;
            typedef typename TRIMESH_TYPE::VertexType			VertexType;
            typedef typename TRIMESH_TYPE::VertexPointer	VertexPointer;
            typedef typename TRIMESH_TYPE::VertexIterator	VertexIterator;
            typedef typename TRIMESH_TYPE::FaceType				FaceType;
            typedef typename TRIMESH_TYPE::FacePointer		FacePointer;
            typedef typename TRIMESH_TYPE::FaceIterator		FaceIterator;
            typedef typename TRIMESH_TYPE::CoordType			CoordType;
            typedef typename TRIMESH_TYPE::CoordType*			CoordPointer;

            /*!
            *	Constructor
            *	\param mesh		the mesh that will be constructed
            *	\param walker	the class implementing the traversal policy
            */
            MarchingCubes(TRIMESH_TYPE &mesh, WALKER_TYPE &walker)
            {
                _mesh		= &mesh;
                _walker	= &walker;
            };

            /*!
            *	Execute the initialiazation.
            *	This method must be executed before the first call to <CODE>ApplyMC</CODE>
            */
            void Initialize()
            {
                _mesh->Clear();
            }; // end of Initialize()

            /*!
            *
            *	This method must be executed after the last call to <CODE>ApplyMC</CODE>
            */
            void Finalize()
            {
                _mesh		= NULL;
                _walker = NULL;
            }; // end of Finalize()

            /*!
            * Apply the <I>marching cubes</I> algorithm to the volume cell identified by the two points <CODE>min</CODE> and <CODE>max</CODE>.
            *	All the three coordinates of the first point must be smaller than the respectives three coordinatas of the second point.
            *	\param min	the first point
            *	\param max	the second point
            */
            void ProcessCell(const vcg::Point3i &min, const vcg::Point3i &max)
            {
                _case = _subconfig = _config = -1;
                assert(min[0]<max[0] && min[1]<max[1] && min[2]<max[2]);
                _corners[0].X()=min.X();		_corners[0].Y()=min.Y();		_corners[0].Z()=min.Z();
                _corners[1].X()=max.X();		_corners[1].Y()=min.Y();		_corners[1].Z()=min.Z();
                _corners[2].X()=max.X();		_corners[2].Y()=max.Y();		_corners[2].Z()=min.Z();
                _corners[3].X()=min.X();		_corners[3].Y()=max.Y();		_corners[3].Z()=min.Z();
                _corners[4].X()=min.X();		_corners[4].Y()=min.Y();		_corners[4].Z()=max.Z();
                _corners[5].X()=max.X();		_corners[5].Y()=min.Y();		_corners[5].Z()=max.Z();
                _corners[6].X()=max.X();		_corners[6].Y()=max.Y();		_corners[6].Z()=max.Z();
                _corners[7].X()=min.X();		_corners[7].Y()=max.Y();		_corners[7].Z()=max.Z();

                for (int i=0; i<8; i++)
                    _field[i] = _walker->V( _corners[i].X(), _corners[i].Y(), _corners[i].Z() );

                unsigned char cubetype = 0;
                for (int i=0; i<8; i++)
                    if (_field[i]>0) cubetype += 1<<i;

                _case				= MCLookUpTable::Cases(cubetype, 0); //_case				= cases[cubetype][0];
                _config			= MCLookUpTable::Cases(cubetype, 1); //_config			= cases[cubetype][1];
                _subconfig	= 0;

                VertexPointer v12 = NULL;

                switch( _case )
                {
                case  0 : { break ; }
                case  1 : { AddTriangles( MCLookUpTable::Tiling1(_config), 1 ); break; } //case  1 : { AddTriangles( tiling1[_config], 1 ); break; }
                case  2 : { AddTriangles( MCLookUpTable::Tiling2(_config), 2 ); break; } //case  2 : { AddTriangles( tiling2[_config], 2 ); break; }
                case  3 :
                    {
                        //if( TestFace( test3[_config]) ) AddTriangles( tiling3_2[_config], 4 ) ; // 3.2
                        if( TestFace( MCLookUpTable::Test3(_config)) )
                            AddTriangles( MCLookUpTable::Tiling3_2(_config), 4 ) ; // 3.2
                        else
                            AddTriangles( MCLookUpTable::Tiling3_1(_config), 2 ) ; // 3.1
                        break ;
                    }
                case  4 :
                    {
                        //if( TestInterior( test4[_config]) ) AddTriangles( tiling4_1[_config], 2 ) ; // 4.1.1
                        if( TestInterior( MCLookUpTable::Test4(_config) ) )
                            AddTriangles( MCLookUpTable::Tiling4_1(_config), 2 ) ; // 4.1.1
                        else
                            AddTriangles( MCLookUpTable::Tiling4_2(_config), 6 ) ; // 4.1.2
                        break ;
                    }
                case  5 : { AddTriangles( MCLookUpTable::Tiling5(_config), 3 ); break; }
                case  6 :
                    {
                        //if( TestFace( test6[_config][0]) )
                        if( TestFace( MCLookUpTable::Test6(_config, 0)) )
                            AddTriangles( MCLookUpTable::Tiling6_2(_config), 5 ) ; // 6.2
                        else
                        {
                            if( TestInterior( MCLookUpTable::Test6(_config, 1)) )
                                AddTriangles( MCLookUpTable::Tiling6_1_1(_config), 3 ) ; // 6.1.1
                            else
                                AddTriangles( MCLookUpTable::Tiling6_1_2(_config), 7 ) ; // 6.1.2
                        }
                        break ;
                    }
                case  7 :
                    {
                        //if( TestFace( test7[_config][0] ) ) _subconfig +=  1 ;
                        //if( TestFace( test7[_config][1] ) ) _subconfig +=  2 ;
                        //if( TestFace( test7[_config][2] ) ) _subconfig +=  4 ;
                        if( TestFace( MCLookUpTable::Test7(_config, 0) ) ) _subconfig +=  1 ;
                        if( TestFace( MCLookUpTable::Test7(_config, 1) ) ) _subconfig +=  2 ;
                        if( TestFace( MCLookUpTable::Test7(_config, 2) ) ) _subconfig +=  4 ;
                        switch( _subconfig )
                        {
                        case 0 : { AddTriangles( MCLookUpTable::Tiling7_1(_config),		3 ) ; break; }
                        case 1 : { AddTriangles( MCLookUpTable::Tiling7_2(_config,0), 5 ) ; break; }
                        case 2 : { AddTriangles( MCLookUpTable::Tiling7_2(_config,1), 5 ) ; break; }
                        case 3 : { ComputeCVertex(v12); AddTriangles( MCLookUpTable::Tiling7_3(_config,0), 9, v12 ) ; break ; }
                        case 4 : { AddTriangles( MCLookUpTable::Tiling7_2(_config, 2), 5 ) ; break ;}
                        case 5 : { ComputeCVertex(v12); AddTriangles( MCLookUpTable::Tiling7_3(_config,1), 9, v12 ) ; break ; }
                        case 6 : { ComputeCVertex(v12); AddTriangles( MCLookUpTable::Tiling7_3(_config,2), 9, v12 ) ; break ; }
                        case 7 :
                            {
                                if( TestInterior( MCLookUpTable::Test7(_config, 3) ) )
                                    AddTriangles( MCLookUpTable::Tiling7_4_2(_config), 9 ) ;
                                else
                                    AddTriangles( MCLookUpTable::Tiling7_4_1(_config), 5 ) ;
                                break ;
                            }
                        };
                        break ;
                    } // end of case 7
                case  8 : { AddTriangles( MCLookUpTable::Tiling8(_config), 2 ) ; break ;}
                case  9 : { AddTriangles( MCLookUpTable::Tiling9(_config), 4 ) ; break ;}
                case 10 :
                    {
                        if( TestFace( MCLookUpTable::Test10(_config, 0)) ) //if( TestFace( test10[_config][0]) )
                        {
                            if( TestFace( MCLookUpTable::Test10(_config,1) ) )
                                AddTriangles( MCLookUpTable::Tiling10_1_1_(_config), 4 ) ; // 10.1.1
                            else
                            {
                                ComputeCVertex(v12);
                                AddTriangles( MCLookUpTable::Tiling10_2(_config), 8, v12 ) ; // 10.2
                            }
                        }
                        else
                        {
                            if( TestFace( MCLookUpTable::Test10(_config, 1) ) )
                            {
                                ComputeCVertex(v12) ;
                                AddTriangles( MCLookUpTable::Tiling10_2_(_config), 8, v12 ) ; // 10.2
                            }
                            else
                            {
                                if( TestInterior( MCLookUpTable::Test10(_config, 2) ) )
                                    AddTriangles( MCLookUpTable::Tiling10_1_1(_config), 4 ) ; // 10.1.1
                                else
                                    AddTriangles( MCLookUpTable::Tiling10_1_2(_config), 8 ) ; // 10.1.2
                            }
                        }
                        break ;
                    } // end of case 10
                case 11 : { AddTriangles( MCLookUpTable::Tiling11(_config), 4 ) ; break ; }
                case 12 :
                    {
                        if( TestFace( MCLookUpTable::Test12(_config, 0) ) ) //if( TestFace( test12[_config][0]) )
                        {
                            if( TestFace( MCLookUpTable::Test12(_config, 1) ) )
                                AddTriangles( MCLookUpTable::Tiling12_1_1_(_config), 4 ) ; // 12.1.1
                            else
                            {
                                ComputeCVertex(v12) ;
                                AddTriangles( MCLookUpTable::Tiling12_2(_config), 8, v12 ) ; // 12.2
                            }
                        }
                        else
                        {
                            if( TestFace( MCLookUpTable::Test12(_config, 1) ) )
                            {
                                ComputeCVertex(v12) ;
                                AddTriangles( MCLookUpTable::Tiling12_2_(_config), 8, v12 ) ; // 12.2
                            }
                            else
                            {
                                if( TestInterior( MCLookUpTable::Test12(_config, 2) ) )
                                    AddTriangles( MCLookUpTable::Tiling12_1_1(_config), 4 ) ; // 12.1.1
                                else
                                    AddTriangles( MCLookUpTable::Tiling12_1_2(_config), 8 ) ; // 12.1.2
                            }
                        }
                        break ;
                    } // end of case 12
                case 13 :
                    {
                        //if( TestFace( test13[_config][0] ) ) _subconfig +=  1 ;
                        //if( TestFace( test13[_config][1] ) ) _subconfig +=  2 ;
                        //if( TestFace( test13[_config][2] ) ) _subconfig +=  4 ;
                        //if( TestFace( test13[_config][3] ) ) _subconfig +=  8 ;
                        //if( TestFace( test13[_config][4] ) ) _subconfig += 16 ;
                        //if( TestFace( test13[_config][5] ) ) _subconfig += 32 ;
                        if( TestFace( MCLookUpTable::Test13(_config, 0) ) ) _subconfig +=  1 ;
                        if( TestFace( MCLookUpTable::Test13(_config, 1) ) ) _subconfig +=  2 ;
                        if( TestFace( MCLookUpTable::Test13(_config, 2) ) ) _subconfig +=  4 ;
                        if( TestFace( MCLookUpTable::Test13(_config, 3) ) ) _subconfig +=  8 ;
                        if( TestFace( MCLookUpTable::Test13(_config, 4) ) ) _subconfig += 16 ;
                        if( TestFace( MCLookUpTable::Test13(_config, 5) ) ) _subconfig += 32 ;
                        switch( MCLookUpTable::Subconfig13(_subconfig) ) //switch( subconfig13[_subconfig] )
                        {
                        case 0  : { /* 13.1 */ AddTriangles( MCLookUpTable::Tiling13_1(_config)	 , 4 ) ; break ; }
                        case 1  : { /* 13.2 */ AddTriangles( MCLookUpTable::Tiling13_2(_config, 0), 6 ) ; break ; }
                        case 2  : { /* 13.2 */ AddTriangles( MCLookUpTable::Tiling13_2(_config, 1), 6 ) ; break ; }
                        case 3  : { /* 13.2 */ AddTriangles( MCLookUpTable::Tiling13_2(_config, 2), 6 ) ; break ; }
                        case 4  : { /* 13.2 */ AddTriangles( MCLookUpTable::Tiling13_2(_config, 3), 6 ) ; break ; }
                        case 5  : { /* 13.2 */ AddTriangles( MCLookUpTable::Tiling13_2(_config, 4), 6 ) ; break ; }
                        case 6  : { /* 13.2 */ AddTriangles( MCLookUpTable::Tiling13_2(_config, 5), 6 ) ; break ; }
                        case 7  : { /* 13.3 */ ComputeCVertex(v12); AddTriangles( MCLookUpTable::Tiling13_3(_config, 0), 10, v12 ) ; break ; }
                        case 8  : { /* 13.3 */ ComputeCVertex(v12); AddTriangles( MCLookUpTable::Tiling13_3(_config, 1), 10, v12 ) ; break ; }
                        case 9  : { /* 13.3 */ ComputeCVertex(v12); AddTriangles( MCLookUpTable::Tiling13_3(_config, 2), 10, v12 ) ; break ; }
                        case 10 : { /* 13.3 */ ComputeCVertex(v12); AddTriangles( MCLookUpTable::Tiling13_3(_config, 3), 10, v12 ) ; break ; }
                        case 11 : { /* 13.3 */ ComputeCVertex(v12); AddTriangles( MCLookUpTable::Tiling13_3(_config, 4), 10, v12 ) ; break ; }
                        case 12 : { /* 13.3 */ ComputeCVertex(v12); AddTriangles( MCLookUpTable::Tiling13_3(_config, 5), 10, v12 ) ; break ; }
                        case 13 : { /* 13.3 */ ComputeCVertex(v12); AddTriangles( MCLookUpTable::Tiling13_3(_config, 6), 10, v12 ) ; break ; }
                        case 14 : { /* 13.3 */ ComputeCVertex(v12); AddTriangles( MCLookUpTable::Tiling13_3(_config, 7), 10, v12 ) ; break ; }
                        case 15 : { /* 13.3 */ ComputeCVertex(v12); AddTriangles( MCLookUpTable::Tiling13_3(_config, 8), 10, v12 ) ; break ; }
                        case 16 : { /* 13.3 */ ComputeCVertex(v12); AddTriangles( MCLookUpTable::Tiling13_3(_config, 9), 10, v12 ) ; break ; }
                        case 17 : { /* 13.3 */ ComputeCVertex(v12); AddTriangles( MCLookUpTable::Tiling13_3(_config,10), 10, v12 ) ; break ; }
                        case 18 : { /* 13.3 */ ComputeCVertex(v12); AddTriangles( MCLookUpTable::Tiling13_3(_config,11), 10, v12 ) ; break ; }
                        case 19 : { /* 13.4 */ ComputeCVertex(v12); AddTriangles( MCLookUpTable::Tiling13_4(_config, 0), 12, v12 ) ; break ; }
                        case 20 : { /* 13.4 */ ComputeCVertex(v12); AddTriangles( MCLookUpTable::Tiling13_4(_config, 1), 12, v12 ) ; break ; }
                        case 21 : { /* 13.4 */ ComputeCVertex(v12); AddTriangles( MCLookUpTable::Tiling13_4(_config, 2), 12, v12 ) ; break ; }
                        case 22 : { /* 13.4 */ ComputeCVertex(v12); AddTriangles( MCLookUpTable::Tiling13_4(_config, 3), 12, v12 ) ; break ; }
                        case 23 :
                            { /* 13.5 */
                                _subconfig = 0 ;
                                if( TestInterior( MCLookUpTable::Test13(_config, 6) ) )
                                    AddTriangles( MCLookUpTable::Tiling13_5_1(_config, 0), 6 ) ;
                                else
                                    AddTriangles( MCLookUpTable::Tiling13_5_2(_config, 0), 10 ) ;
                                break ;
                            }
                        case 24 :
                            { /* 13.5 */
                                _subconfig = 1 ;
                                if( TestInterior( MCLookUpTable::Test13(_config, 6) ) )
                                    AddTriangles( MCLookUpTable::Tiling13_5_1(_config, 1), 6 ) ;
                                else
                                    AddTriangles( MCLookUpTable::Tiling13_5_2(_config, 1), 10 ) ;
                                break ;
                            }
                        case 25 :
                            {/* 13.5 */
                                _subconfig = 2 ;
                                if( TestInterior( MCLookUpTable::Test13(_config, 6) ) )
                                    AddTriangles( MCLookUpTable::Tiling13_5_1(_config, 2), 6 ) ;
                                else
                                    AddTriangles( MCLookUpTable::Tiling13_5_2(_config, 2), 10 ) ;
                                break ;
                            }
                        case 26 :
                            {/* 13.5 */
                                _subconfig = 3 ;
                                if( TestInterior( MCLookUpTable::Test13(_config, 6) ) )
                                    AddTriangles( MCLookUpTable::Tiling13_5_1(_config, 3), 6 ) ;
                                else
                                    AddTriangles( MCLookUpTable::Tiling13_5_2(_config, 3), 10 ) ;
                                break ;
                            }
                        case 27 : { /* 13.3 */ ComputeCVertex(v12); AddTriangles( MCLookUpTable::Tiling13_3_(_config, 0), 10, v12 ) ; break ; }
                        case 28 : { /* 13.3 */ ComputeCVertex(v12); AddTriangles( MCLookUpTable::Tiling13_3_(_config, 1), 10, v12 ) ; break ; }
                        case 29 : { /* 13.3 */ ComputeCVertex(v12); AddTriangles( MCLookUpTable::Tiling13_3_(_config, 2), 10, v12 ) ; break ; }
                        case 30 : { /* 13.3 */ ComputeCVertex(v12); AddTriangles( MCLookUpTable::Tiling13_3_(_config, 3), 10, v12 ) ; break ; }
                        case 31 : { /* 13.3 */ ComputeCVertex(v12); AddTriangles( MCLookUpTable::Tiling13_3_(_config, 4), 10, v12 ) ; break ; }
                        case 32 : { /* 13.3 */ ComputeCVertex(v12); AddTriangles( MCLookUpTable::Tiling13_3_(_config, 5), 10, v12 ) ; break ; }
                        case 33 : { /* 13.3 */ ComputeCVertex(v12); AddTriangles( MCLookUpTable::Tiling13_3_(_config, 6), 10, v12 ) ; break ; }
                        case 34 : { /* 13.3 */ ComputeCVertex(v12); AddTriangles( MCLookUpTable::Tiling13_3_(_config, 7), 10, v12 ) ; break ; }
                        case 35 : { /* 13.3 */ ComputeCVertex(v12); AddTriangles( MCLookUpTable::Tiling13_3_(_config, 8), 10, v12 ) ; break ; }
                        case 36 : { /* 13.3 */ ComputeCVertex(v12); AddTriangles( MCLookUpTable::Tiling13_3_(_config, 9), 10, v12 ) ; break ; }
                        case 37 : { /* 13.3 */ ComputeCVertex(v12); AddTriangles( MCLookUpTable::Tiling13_3_(_config,10), 10, v12 ) ; break ; }
                        case 38 : { /* 13.3 */ ComputeCVertex(v12); AddTriangles( MCLookUpTable::Tiling13_3_(_config,11), 10, v12 ) ; break ; }
                        case 39 : { /* 13.2 */ AddTriangles( MCLookUpTable::Tiling13_2_(_config,0), 6 ) ; break ; }
                        case 40 : { /* 13.2 */ AddTriangles( MCLookUpTable::Tiling13_2_(_config,1), 6 ) ; break ; }
                        case 41 : { /* 13.2 */ AddTriangles( MCLookUpTable::Tiling13_2_(_config,2), 6 ) ; break ; }
                        case 42 : { /* 13.2 */ AddTriangles( MCLookUpTable::Tiling13_2_(_config,3), 6 ) ; break ; }
                        case 43 : { /* 13.2 */ AddTriangles( MCLookUpTable::Tiling13_2_(_config,4), 6 ) ; break ; }
                        case 44 : { /* 13.2 */ AddTriangles( MCLookUpTable::Tiling13_2_(_config,5), 6 ) ; break ; }
                        case 45 : { /* 13.1 */ AddTriangles( MCLookUpTable::Tiling13_1_(_config)	, 4 ) ; break ; }
                        default : { /*Impossible case 13*/  assert(false); }
                        }
                        break ;
                    } // end of case 13

                case 14 : { AddTriangles( MCLookUpTable::Tiling14(_config), 4 ) ; }
                                    break ;
                } //end of switch (_case)

            }; // end of ApplyMC

        private:
            /*!
            */
            WALKER_TYPE		*_walker;
            /*!
            */
            TRIMESH_TYPE	*_mesh;

            /*!
            *	The field value at the cell corners
            */
            ScalarType _field[8];

            /*!
            *	Array of the 8 corners of the volume cell being processed
            */
            vcg::Point3i _corners[8];

            /*!
            *	Case of the volume cell being processed
            */
            unsigned char _case;

            /*!
            *	 Configuration of the volume cell being processed
            */
            unsigned char _config;

            /*!
            *	Subconfiguration of the volume cell being processed
            */
            unsigned char _subconfig;

            /*!
            *	Tests if the components of the tesselation of the cube should be connected
            *	by the interior of an ambiguous face
            */
            inline bool TestFace(signed char face)
            {
                ScalarType A = 0, B = 0, C = 0, D = 0 ;

                switch( face )
                {
                case -1 : case 1 :  A = _field[0] ;  B = _field[4] ;  C = _field[5] ;  D = _field[1] ;  break ;
                case -2 : case 2 :  A = _field[1] ;  B = _field[5] ;  C = _field[6] ;  D = _field[2] ;  break ;
                case -3 : case 3 :  A = _field[2] ;  B = _field[6] ;  C = _field[7] ;  D = _field[3] ;  break ;
                case -4 : case 4 :  A = _field[3] ;  B = _field[7] ;  C = _field[4] ;  D = _field[0] ;  break ;
                case -5 : case 5 :  A = _field[0] ;  B = _field[3] ;  C = _field[2] ;  D = _field[1] ;  break ;
                case -6 : case 6 :  A = _field[4] ;  B = _field[7] ;  C = _field[6] ;  D = _field[5] ;  break ;
                default : assert(false); // Invalid face code
                };

                return face * A * ( A*C - B*D ) >= 0  ;  // face and A invert signs
            } // end of TestFace


            /*!
            *	Tests if the components of the tesselation of the cube should be connected
            *	through the interior of the cube
            */
            inline bool TestInterior(signed char s)
            {
                ScalarType t, At=0, Bt=0, Ct=0, Dt=0, a, b ;
                char  test =  0 ;
                char  edge = -1 ; // reference edge of the triangulation

                switch( _case )
                {
                case  4 :
                case 10 :
                    {
                        a = (_field[4]-_field[0])*(_field[6]-_field[2]) - (_field[7]-_field[3])*(_field[5]-_field[1]);
                        b =  _field[2]*(_field[4]-_field[0])+_field[0]*(_field[6]-_field[2])-_field[1]*(_field[7]-_field[3])-_field[3]*(_field[5]-_field[1]);
                        t = - b / (2*a) ;
                        if( t<0 || t>1 )
                            return s>0 ;

                        At = _field[0] + ( _field[4] - _field[0] ) * t ;
                        Bt = _field[3] + ( _field[7] - _field[3] ) * t ;
                        Ct = _field[2] + ( _field[6] - _field[2] ) * t ;
                        Dt = _field[1] + ( _field[5] - _field[1] ) * t ;
                        break ;
                    }
                case  6 :
                case  7 :
                case 12 :
                case 13 :
                    switch( _case )
                    {
                    case  6 : edge = MCLookUpTable::Test6 (_config, 2) ; break ;
                    case  7 : edge = MCLookUpTable::Test7 (_config, 4) ; break ;
                    case 12 : edge = MCLookUpTable::Test12(_config, 3) ; break ;
                    case 13 : edge = MCLookUpTable::Tiling13_5_1(_config, _subconfig)[0] ; break ;
                    }
                    switch( edge )
                    {
                    case  0 :
                        t  = _field[0] / ( _field[0] - _field[1] ) ;
                        At = 0 ;
                        Bt = _field[3] + ( _field[2] - _field[3] ) * t ;
                        Ct = _field[7] + ( _field[6] - _field[7] ) * t ;
                        Dt = _field[4] + ( _field[5] - _field[4] ) * t ;
                        break ;
                    case  1 :
                        t  = _field[1] / ( _field[1] - _field[2] ) ;
                        At = 0 ;
                        Bt = _field[0] + ( _field[3] - _field[0] ) * t ;
                        Ct = _field[4] + ( _field[7] - _field[4] ) * t ;
                        Dt = _field[5] + ( _field[6] - _field[5] ) * t ;
                        break ;
                    case  2 :
                        t  = _field[2] / ( _field[2] - _field[3] ) ;
                        At = 0 ;
                        Bt = _field[1] + ( _field[0] - _field[1] ) * t ;
                        Ct = _field[5] + ( _field[4] - _field[5] ) * t ;
                        Dt = _field[6] + ( _field[7] - _field[6] ) * t ;
                        break ;
                    case  3 :
                        t  = _field[3] / ( _field[3] - _field[0] ) ;
                        At = 0 ;
                        Bt = _field[2] + ( _field[1] - _field[2] ) * t ;
                        Ct = _field[6] + ( _field[5] - _field[6] ) * t ;
                        Dt = _field[7] + ( _field[4] - _field[7] ) * t ;
                        break ;
                    case  4 :
                        t  = _field[4] / ( _field[4] - _field[5] ) ;
                        At = 0 ;
                        Bt = _field[7] + ( _field[6] - _field[7] ) * t ;
                        Ct = _field[3] + ( _field[2] - _field[3] ) * t ;
                        Dt = _field[0] + ( _field[1] - _field[0] ) * t ;
                        break ;
                    case  5 :
                        t  = _field[5] / ( _field[5] - _field[6] ) ;
                        At = 0 ;
                        Bt = _field[4] + ( _field[7] - _field[4] ) * t ;
                        Ct = _field[0] + ( _field[3] - _field[0] ) * t ;
                        Dt = _field[1] + ( _field[2] - _field[1] ) * t ;
                        break ;
                    case  6 :
                        t  = _field[6] / ( _field[6] - _field[7] ) ;
                        At = 0 ;
                        Bt = _field[5] + ( _field[4] - _field[5] ) * t ;
                        Ct = _field[1] + ( _field[0] - _field[1] ) * t ;
                        Dt = _field[2] + ( _field[3] - _field[2] ) * t ;
                        break ;
                    case  7 :
                        t  = _field[7] / ( _field[7] - _field[4] ) ;
                        At = 0 ;
                        Bt = _field[6] + ( _field[5] - _field[6] ) * t ;
                        Ct = _field[2] + ( _field[1] - _field[2] ) * t ;
                        Dt = _field[3] + ( _field[0] - _field[3] ) * t ;
                        break ;
                    case  8 :
                        t  = _field[0] / ( _field[0] - _field[4] ) ;
                        At = 0 ;
                        Bt = _field[3] + ( _field[7] - _field[3] ) * t ;
                        Ct = _field[2] + ( _field[6] - _field[2] ) * t ;
                        Dt = _field[1] + ( _field[5] - _field[1] ) * t ;
                        break ;
                    case  9 :
                        t  = _field[1] / ( _field[1] - _field[5] ) ;
                        At = 0 ;
                        Bt = _field[0] + ( _field[4] - _field[0] ) * t ;
                        Ct = _field[3] + ( _field[7] - _field[3] ) * t ;
                        Dt = _field[2] + ( _field[6] - _field[2] ) * t ;
                        break ;
                    case 10 :
                        t  = _field[2] / ( _field[2] - _field[6] ) ;
                        At = 0 ;
                        Bt = _field[1] + ( _field[5] - _field[1] ) * t ;
                        Ct = _field[0] + ( _field[4] - _field[0] ) * t ;
                        Dt = _field[3] + ( _field[7] - _field[3] ) * t ;
                        break ;
                    case 11 :
                        t  = _field[3] / ( _field[3] - _field[7] ) ;
                        At = 0 ;
                        Bt = _field[2] + ( _field[6] - _field[2] ) * t ;
                        Ct = _field[1] + ( _field[5] - _field[1] ) * t ;
                        Dt = _field[0] + ( _field[4] - _field[0] ) * t ;
                        break ;
                    default: { assert(false); /* Invalid edge */ break ; }
                    }
                    break ;

                default : assert(false); /* Invalid ambiguous case */  break;
                }

                if( At >= 0 ) test ++ ;
                if( Bt >= 0 ) test += 2 ;
                if( Ct >= 0 ) test += 4 ;
                if( Dt >= 0 ) test += 8 ;
                switch( test )
                {
                case  0 : return s>0 ;
                case  1 : return s>0 ;
                case  2 : return s>0 ;
                case  3 : return s>0 ;
                case  4 : return s>0 ;
                case  5 : if( At * Ct <  Bt * Dt ) return s>0 ; break ;
                case  6 : return s>0 ;
                case  7 : return s<0 ;
                case  8 : return s>0 ;
                case  9 : return s>0 ;
                case 10 : if( At * Ct >= Bt * Dt ) return s>0 ; break ;
                case 11 : return s<0 ;
                case 12 : return s>0 ;
                case 13 : return s<0 ;
                case 14 : return s<0 ;
                case 15 : return s<0 ;
                }
                return s<0 ;
            } //end of TestInterior

            /*!
            *	Adds a vertex inside the current cube
            *	\param v	The pointer to the new vertex along the edge
            */
            inline void ComputeCVertex(VertexPointer &v12)
            {
                v12 = &*AllocatorType::AddVertices(*_mesh, 1);
                v12->P() = CoordType(0.0, 0.0, 0.0);

                unsigned int count = 0;
                VertexPointer v = NULL;
                if (_walker->Exist(_corners[0], _corners[1], v) )
                {
                    count++;
                    v12->P() += v->P();
                }
                if (_walker->Exist(_corners[1], _corners[2], v) )
                {
                    count++;
                    v12->P() += v->P();
                }
                if (_walker->Exist(_corners[3], _corners[2], v) )
                {
                    count++;
                    v12->P() += v->P();
                }
                if (_walker->Exist(_corners[0], _corners[3], v) )
                {
                    count++;
                    v12->P() += v->P();
                }
                if (_walker->Exist(_corners[4], _corners[5], v) )
                {
                    count++;
                    v12->P() += v->P();
                }
                if (_walker->Exist(_corners[5], _corners[6], v) )
                {
                    count++;
                    v12->P() += v->P();
                }
                if (_walker->Exist(_corners[7], _corners[6], v) )
                {
                    count++;
                    v12->P() += v->P();
                }
                if (_walker->Exist(_corners[4], _corners[7], v) )
                {
                    count++;
                    v12->P() += v->P();
                }
                if (_walker->Exist(_corners[0], _corners[4], v) )
                {
                    count++;
                    v12->P() += v->P();
                }
                if (_walker->Exist(_corners[1], _corners[5], v) )
                {
                    count++;
                    v12->P() += v->P();
                }
                if (_walker->Exist(_corners[2], _corners[6], v) )
                {
                    count++;
                    v12->P() += v->P();
                }
                if (_walker->Exist(_corners[3], _corners[7], v) )
                {
                    count++;
                    v12->P() += v->P();
                }
                v12->P() /= (float) count;
            } // end of AddCVertex
            /*!
            *	Adds new triangles to the mesh
            *	\param vertices_list	The list of vertex indices
            *	\param n							The number of triangles that will be added to the mesh
            *	\param v12						The pointer to the vertex inside the current cell
            */
            inline void AddTriangles(const char *vertices_list, char n, VertexPointer v12=NULL)
            {
                VertexPointer vp		= NULL;
                size_t face_idx			= _mesh->face.size();
                size_t v12_idx			= -1;
				std::array<size_t, 3> vertices_idx;
                if (v12 != NULL) v12_idx = v12 - &_mesh->vert[0];
                AllocatorType::AddFaces(*_mesh, (int) n);

                for (int trig=0; trig<3*n; face_idx++ )
                {
                    vp = NULL;
					vertices_idx.fill(-1);
                    for (int vert=0; vert<3; vert++, trig++) //ok
                    {

                        switch ( vertices_list[trig] )
                        {
                        case  0: { _walker->GetXIntercept(_corners[0], _corners[1], vp); vertices_idx[vert] = tri::Index(*_mesh,vp); break; }
                        case  1: { _walker->GetYIntercept(_corners[1], _corners[2], vp); vertices_idx[vert] = tri::Index(*_mesh,vp); break; }
                        case  2: { _walker->GetXIntercept(_corners[3], _corners[2], vp); vertices_idx[vert] = tri::Index(*_mesh,vp); break; }
                        case  3: { _walker->GetYIntercept(_corners[0], _corners[3], vp); vertices_idx[vert] = tri::Index(*_mesh,vp); break; }
                        case  4: { _walker->GetXIntercept(_corners[4], _corners[5], vp); vertices_idx[vert] = tri::Index(*_mesh,vp); break; }
                        case  5: { _walker->GetYIntercept(_corners[5], _corners[6], vp); vertices_idx[vert] = tri::Index(*_mesh,vp); break; }
                        case  6: { _walker->GetXIntercept(_corners[7], _corners[6], vp); vertices_idx[vert] = tri::Index(*_mesh,vp); break; }
                        case  7: { _walker->GetYIntercept(_corners[4], _corners[7], vp); vertices_idx[vert] = tri::Index(*_mesh,vp); break; }
                        case  8: { _walker->GetZIntercept(_corners[0], _corners[4], vp); vertices_idx[vert] = tri::Index(*_mesh,vp); break; }
                        case  9: { _walker->GetZIntercept(_corners[1], _corners[5], vp); vertices_idx[vert] = tri::Index(*_mesh,vp); break; }
                        case 10: { _walker->GetZIntercept(_corners[2], _corners[6], vp); vertices_idx[vert] = tri::Index(*_mesh,vp); break; }
                        case 11: { _walker->GetZIntercept(_corners[3], _corners[7], vp); vertices_idx[vert] = tri::Index(*_mesh,vp); break; }
                        case 12: { assert(v12 != NULL); vertices_idx[vert] = v12_idx; break; }
                        default: { assert(false); /* Invalid edge identifier */ }
                        } // end of switch

                        // Note that vp can be zero if we are in case 12 and that vertices_idx is surely >0 so the following assert has to be corrected as below.
                        // assert((vp - &_mesh->vert[0])>=0 && vertices_idx[vert]<_mesh->vert.size());
                        assert(vertices_idx[vert]<_mesh->vert.size());
                    } // end for (int vert=0 ...)

                    _mesh->face[face_idx].V(0) = &_mesh->vert[vertices_idx[0]];
                    _mesh->face[face_idx].V(1) = &_mesh->vert[vertices_idx[1]];
                    _mesh->face[face_idx].V(2) = &_mesh->vert[vertices_idx[2]];
                } // end for (int trig=0...)
            } // end of AddTriangles


        }; // end of class MarchingCubes

        /*! @} */
        //end of Doxygen documentation

    } // end of namespace tri
} // end of namespace vcg

#endif //__VCG_MARCHING_CUBES
