#include <string.h>
#include <iostream.h>
#include <stdio.h>
#include <stdlib.h>
#include <math.h>

#include <maya/MPxNode.h> 

#include <maya/MString.h> 
#include <maya/MTypeId.h> 
#include <maya/MPlug.h>

#include <maya/MDataBlock.h>
#include <maya/MDataHandle.h>
#include <maya/MFnPlugin.h>

#include <maya/MAngle.h>
#include <maya/MFnUnitAttribute.h>
#include <maya/MFnNumericAttribute.h>
#include <maya/MFnTypedAttribute.h>

#include <maya/MFloatPoint.h>
#include <maya/MFloatPointArray.h>
#include <maya/MIntArray.h>
#include <maya/MDoubleArray.h>

#include <maya/MFnMesh.h>
#include <maya/MFnMeshData.h>
#include <maya/MItMeshVertex.h>


#define		Rad( x )	( ( x ) * FPI / 180.0F )
#define 	Deg( x )	( ( x ) * 180.0F / FPI )

#define 	FPI 3.14159265358979323846264338327950288419716939937510582F 

#define 	McheckErr( stat,msg )		\
    		if ( MS::kSuccess != stat ) 	\
		{   				\
        		cerr << msg;		\
        		return	MS::kFailure;	\
    		}


class	neoNode : public MPxNode
{
public:
					neoNode();
		virtual 		~neoNode(); 
    		virtual MStatus   	compute( const MPlug& plug, 
							MDataBlock& data );

		static	void *          creator();
		static	MStatus         initialize();

public: 
		static	MTypeId		id;

	//////////////////////////
	// neoNode attributes	//
	//////////////////////////

	// Neo Parameters
	// ---------------
	static	MObject alpha;		// Profile (Helico-spiral) param #1
	static	MObject	beta;		// Profile (Helico-spiral) param #2
	
	static	MObject	omin;		// Spiral start angle
	static	MObject	omax;		// Spiral end angle
	static	MObject	od;		// Spiral angle step
	
	static	MObject	phi;		// Section starting point
	static	MObject	my;		// Section slant
	static	MObject	omega;		// Section angle around Oz
	
	static	MObject	smin;		// Section start angle
	static	MObject	smax;		// Section end angle
	static	MObject	sd;		// Section angle step
	
	static	MObject	A;		// Distance of section from Z-axis
	static	MObject	a;		// Section diameter #1
	static	MObject	b;		// Section diameter #2
	static	MObject	scale;		// Overall scale
	
	// Neo Ribs Parameters
	// --------------------	
	static	MObject	vamp;		// Profile rib amplitude
	static	MObject	vfreq;		// Profile rib frequency
	static	MObject	vrib;		// Profile rib wave percent
	
	static	MObject	uamp;		// Section rib amplitude
	static	MObject	ufreq;		// Section rib frequency
	static	MObject	urib;		// Section rib wave percent

	
	// Neo Primary Nodules
	// --------------------
	static	MObject	P;		// Position on section
	static	MObject	L;		// Amplitude (extrusion) of nodule
	static	MObject	N;		// Nodules frequency on profile
	
	static	MObject	W1;		// Fatness of nodule 1
	static	MObject	W2;		// Fatness of nodule 2
	
	static	MObject	nstart;		// Starting point on spiral

	
	// Neo Secondary Nodules
	// ----------------------
	static	MObject	P2;
	static	MObject	L2;
	static	MObject	N2;
	
	static	MObject	W12;
	static	MObject	W22;
	
	static	MObject	off2;
	
	static	MObject	nstart2;


	// Neo Tertiary Nodules
	// ---------------------
	static	MObject	P3;
	static	MObject	L3;
	static	MObject	N3;
	
	static	MObject	W13;
	static	MObject	W23;
	
	static	MObject	off3;
	
	static	MObject	nstart3;


    	// Output mesh
    	// ------------
    	static  MObject outMesh;


private:

	struct NeoParams 
	{
		// Neo Parameters
		// ---------------
		float	alpha;		// Profile (Helico-spiral) param #1
		float	beta;		// Profile (Helico-spiral) param #2
		
		float	omin;		// Spiral start angle
		float	omax;		// Spiral end angle
		float	od;		// Spiral angle step
		
		float	phi;		// Section starting point
		float	my;		// Section slant
		float	omega;		// Section angle around Oz
		
		float	smin;		// Section start angle
		float	smax;		// Section end angle
		float	sd;		// Section angle step
		
		float	A;		// Distance of section from Z-axis
		float	a;		// Section diameter #1
		float	b;		// Section diameter #2
		float	scale;		// Overall scale
		
		// Neo Ribs Parameters
		// --------------------
		float	vamp;		// Profile rib amplitude
		float	vfreq;		// Profile rib frequency
		float	vrib;		// Profile rib wave percent
		
		float	uamp;		// Section rib amplitude
		float	ufreq;		// Section rib frequency
		float	urib;		// Section rib wave percent
		

		// Neo Primary Nodules
		// --------------------
		float	P;		// Position on section
		float	L;		// Amplitude (extrusion) of nodule
		float	N;		// Nodules frequency on profile
		
		float	W1;		// Fatness of nodule 1
		float	W2;		// Fatness of nodule 2
		
		float	nstart;		// Starting point on spiral


		// Neo Secondary Nodules
		// ----------------------
		float	P2;
		float	L2;
		float	N2;
		
		float	W12;
		float	W22;
		
		float	off2;
		
		float	nstart2;


		// Neo Tertiary Nodules
		// ---------------------
		float	P3;
		float	L3;
		float	N3;
		
		float	W13;
		float	W23;
		
		float	off3;
		
		float	nstart3;
	};

	NeoParams		neoParams;
	bool        		redoTopology;
	bool        		rebuild;

	// Precomputed neo points
	// -----------------------
	int			ni, nj;
	float			**pnts;


private:
	float 		GetFloatParameter( MObject node, MObject attr );
	
	float 		GetAngleParameter( MObject node, MObject attr );
	
	static void	addFloatParameter( MObject & attr, MString longName, 
					MString briefName, float attrDefault );
	
	static void  	addAngleParameter( MObject & attr, MString longName, 
					MString briefName, float attrDefault );
	void  		UpdateParameters(); 
	void  		RedoTopology();
	void  		Rebuild();
	
	float 		Nodules( float s, float o );
	float 		Ribs( float u, float v );
	
	void  		Eval( float *p, float o, float s );
};



MTypeId neoNode::id( 0x1981a );


neoNode::neoNode() : rebuild( true ), redoTopology( true ), pnts( NULL ),
			ni( 0 ),nj( 0 ){ }
neoNode::~neoNode(){ }



MStatus neoNode::compute( const MPlug& plug, MDataBlock& data )
{    
	MStatus 	returnStatus;
    	int 		i, j;
    
    	// Read updated input parameters
    	// ------------------------------
			UpdateParameters();
    	bool 		createNewMesh = redoTopology;
    			RedoTopology();
    			Rebuild();
    
    	if ( !pnts || ni < 2 || nj < 2 ) 
		return 	MS::kSuccess;

    	if ( plug == outMesh ) 
	{  
        	if ( !pnts || ni < 2 || nj < 2 ) 
			return	MS::kSuccess;
        
		MDataHandle outputHandle = data.outputValue( outMesh, 
								&returnStatus );
		McheckErr( returnStatus, 
				"ERROR getting polygon data handle\n" );
        
		MObject mesh = outputHandle.asMesh();
               
        	if ( createNewMesh || mesh.isNull() ) 
		{
            		MFnMeshData dataCreator;
            		
			MObject newOutputData = 
					dataCreator.create( &returnStatus );
            		
			McheckErr( returnStatus, 
					"ERROR creating outputData" );
            
		    	MFloatPointArray vertices;

    	    		// Build vertices array
    	    		// ---------------------
    	    		for( j = 0 ; j < nj ; ++j ) 
			{
		    		for( i = 0 ; i < ni ; ++i ) 
				{
	        			const float *p = pnts[j] + 3 * i;
	        			vertices.append( 
						MFloatPoint( p[0],p[1],p[2] ) );
		    		}
    	    		}

    	    		// Build poly vertex count array
    	    		// ------------------------------
    	    		MIntArray pcounts;
    	    		i = ( nj - 1 ) * ( ni - 1 );
    	    		
			while( i-- ) 
			{
                		pcounts.append( 4 );
            		}

    	    		// Build poly connectivity array
    	    		// ------------------------------
    	    		MIntArray pconnect;
    	    		
			for( j = 0 ; j < nj - 1 ; ++j ) 
			{
		    		for( i = 0 ; i < ni - 1 ; ++i ) 
				{
	        			int 	corner = i + j * ni;
	        			
					pconnect.append( corner );
	        			pconnect.append( corner + 1 );
	        			pconnect.append( corner + 1 + ni );
	        			pconnect.append( corner + ni );
		    		}
			}

			// Create some stuff needed by the mesh
   			// -------------------------------------
    			MIntArray	fec;
    			MDoubleArray	sp;
    			MDoubleArray	tp;

    			// Build maya poly object
    			// -----------------------
    			MStatus 	stat;
    			MFnMesh 	meshFn;
    			MObject 	mesh = meshFn.create(
		    			nj * ni,	
						// number of vertices
		    			( nj - 1 ) * ( ni - 1 ),
						// number of polygons
		    			vertices,
						// The points
		    			pcounts,	
						// # of vertex for each poly
		    			pconnect,	
						// Vertices index for each poly
		    			newOutputData, 	
						// Dependency graph data object
		    			&returnStatus
    	    				);

    			// Update surface 
    			// ---------------
        		outputHandle.set( newOutputData );
        	}
	
		else 
		{
         		// The topology hasn't changed, 
			//	so we can just set the points 
			//	in the existing mesh
            		// -----------------------------------
            		MItMeshVertex 	vertIt( mesh, &returnStatus);
			McheckErr( returnStatus, 
				"ERROR creating iterator\n" );
            
            		for( j = 0 ; j < nj ; ++j ) 
			{
		    		for( i = 0 ; i < ni ; ++i ) 
				{
                    			if ( vertIt.isDone() ) 
						break;
	        		
					const float 	*p = pnts[j] + 3 * i;
	        			vertIt.setPosition( 
						MPoint( p[0],p[1],p[2] ) );
                    			vertIt.next();
		    		}
                
				if ( vertIt.isDone() ) 
					break;
    	    		}
        	}
        
		data.setClean( plug );
    	}   

	return	MS::kSuccess;
}



void*	neoNode::creator()
{
	return	new neoNode();
}



//////////////////////
// Neo Algorithms   //
//////////////////////

void	neoNode::RedoTopology()
//
// Description:
//	Adjust our data storage to reflect new parameters
// -------------------------------------------------------
{
    	if ( !redoTopology ) 
		return;

	redoTopology = false;

    	int	oldnj = pnts ? nj : 0;

    	ni, nj = 0;

  /* Section start angle, Section end angle, Section angle step */
  /* ----------------------------------------------------------- */  
	for( float s = neoParams.smin ; s < neoParams.smax ; s += neoParams.sd )
	{
		ni++;	// Lazy
	}

  /* Spiral start angle, Spiral end angle, Spiral angle step */
  /* -------------------------------------------------------- */
    	for( float o = neoParams.omin ; o < neoParams.omax ; o += neoParams.od )
	{
		nj++;	// Lazy
	}

    	if ( nj < oldnj ) 
	{
		for( int i = nj ; i < oldnj ; ++i ) 
		{
			if ( pnts[i] ) 
				free( pnts[i] );
		}
	}

    	if ( nj != oldnj ) 
	{
		pnts = ( float** ) 
			realloc( pnts, nj * sizeof( float* ) );
	}

    	if ( nj > oldnj ) 
	{
		memset( pnts + oldnj, 0, ( nj - oldnj ) * sizeof( float* ) );
	}
	
    	for( int j = 0 ; j < nj ; ++j ) 
	{
		pnts[j] = ( float* ) 
			realloc( pnts[j], 3 * ni * sizeof( float ) );
	}
}


void	neoNode::Rebuild()
//
// Description:
//	Rebuild the mesh geometry given the new inputs
// ----------------------------------------------------
{
    	if ( !rebuild ) 
		return;
    	
	rebuild = 0;

    	int		i;
    	int		j;
    	float		s;
    	float		o;
    	float		*p;

    	o = neoParams.omin;	// Spiral start angle
    	
	for( j = 0 ; j < nj ; ++j )
    	{
		s = neoParams.smin;	// Section start angle
		p = pnts[j];
		
		for( i = 0 ; i < ni ; ++i )
		{
			Eval( p, o, s );
			s += neoParams.sd;	// Section angle step
			p += 3;
		}

		o += neoParams.od;	// Spiral angle step
    	}
}


inline	float SafeCot( float x )
{
    	float 	s = sinf( x );
    	
	return	s ? cosf( x ) / s : 0.0F;
}


inline	float G( float a, float n )
{
    	if ( !n ) 
		return	n;
    	
	float	z = 2.0F * FPI;
    		a *= n / z;
    	
	return 	z / n * ( a - floorf( 0.5F + a ) );
}


float	neoNode::Ribs( float u, float v )
{
	NeoParams& 	np = neoParams;

    	float 		zu = 0.0F;

	
	if ( np.uamp )		/* Section rib amplitude */
    	{
	  /* Section rib amplitude, Section rib frequency */
	  /* ---------------------- */
		zu = np.uamp * cosf( 2.0F * FPI * np.ufreq * u );
	
	  /* Section rib wave percent */
	  /* ------------------------- */
		if ( zu < 0 ) 
			zu *= ( 1.0F - 2.0F * np.urib );
    	}

   	float 		zv = 0.0F;
	
	if ( np.vamp )		/* Profile rib amplitude */
    	{
	  /* Profile rib amplitude, Profile rib frequency */
	  /* ---------------------- */
		zv = np.vamp * cosf( 2.0F * FPI * np.vfreq * v );

	  /* Profile rib wave percent */
	  /* ------------------------- */
		if ( zv < 0 ) 
			zv *= ( 1.0F - 2.0F * np.vrib );
    	}

    	return	zu + zv;
}


float	neoNode::Nodules( float s, float o )
{
	NeoParams& 	np = neoParams;

    	float 		p1;
    	float 		p2;
    	float 		k = 0.0F;
    	float 		g = 0.0F;


  /* Amplitude of nodule, Nodules frequency on profile, */
  /* Starting point on spiral */
  /* --------------------------------------------------- */
    	if ( np.L && np.N && o >= np.nstart )
    	{
		g = G( o,np.N );
		
		p1 = g / np.W2;		// Fatness of nodule 2

		/* Position on section, Fatness of nodule 1 */
		/* ----------------------------------------- */
		p2 = ( s - np.P ) / np.W1;
		
		k = np.L * expf( -4.0F * ( p1 * p1 + p2 * p2 ) );
    	}

    	if ( np.L2 && np.N2 && o >= np.nstart2 )
    	{
		g = G( o + np.off2, np.N2 ); 
		
		p1 = g / np.W22 ;
		p2 = ( s - np.P2 ) / np.W12;
		
		k += np.L2 * expf( -4.0F * ( p1 * p1 + p2 * p2 ) );
    	}

    	if ( np.L3 && np.N3 && o >= np.nstart3 )
    	{
		g = G( o + np.off3, np.N3 );
		
		p1 = g / np.W23;
		p2 = ( s - np.P3 ) / np.W13;
		
		k += np.L3 * expf( -4.0F * ( p1 * p1 + p2 * p2 ) );
    	}

    	return	k;
}


void	neoNode::Eval( float *p, float o, float s )
{
	NeoParams& 	np = neoParams;

    	float 		ss = sinf( s );
    	float 		cs = cosf( s );


  /* Section diameter #1, Section diameter #2 */
  /* ----------------------------------------- */    	
	float 		re = 1.0F / sqrtf( cs * cs / ( np.a * np.a ) 
				+ ss * ss / ( np.b * np.b ) );

  /* Overall scale, Profile (Helico-spiral) param #1 */
  /* ------------------------------------------------ */
    	float 		sc = np.scale * expf( o * SafeCot( np.alpha ) );
    
  /* Section starting point */
  /* ----------------------- */
	float 		csphi = cosf( s + np.phi );
    	float		ssphi = sinf( s + np.phi );

  /* Profile (Helico-spiral) param #2 */
  /* --------------------------------- */    	
	float 		sbeta = sinf( np.beta );

  /* Section slant */
  /* -------------- */
    	float 		smy = sinf( np.my );


  /* COMPLETE DATA */
  /* -------------- */     	
	float r = re + Nodules( s, o ) + Ribs( s, o );

	/* Section angle around Oz */
	/* ------------------------ */
    	float x = np.A * sbeta * cosf( o ) + r * csphi * cosf( o + np.omega )	
              		- r * smy * ssphi * sinf( o );

    	float y = - np.A * sbeta * sinf( o ) - r * csphi * sinf( o + np.omega )	
              		- r * smy * ssphi * cosf( o );

    	float z = - np.A * cosf( np.beta ) + r * ssphi * cosf( np.my );

    	p[0] =  x * sc;
    	p[1] = -z * sc;
    	p[2] =  y * sc;
}



/////////////////////////////////////
// Attribute Setup and Maintenance //
/////////////////////////////////////

// Neo parameters
// ---------------
	MObject neoNode::alpha;		// Profile (Helico-spiral) param #1
	MObject	neoNode::beta;		// Profile (Helico-spiral) param #2
	
	MObject	neoNode::omin;		// Spiral start angle
	MObject	neoNode::omax;		// Spiral end angle
	MObject	neoNode::od;		// Spiral angle step
	
	MObject	neoNode::phi;		// Section starting point
	MObject	neoNode::my;		// Section slant
	MObject	neoNode::omega;		// Section angle around Z
	
	MObject	neoNode::smin;		// Section start angle
	MObject	neoNode::smax;		// Section end angle
	MObject	neoNode::sd;		// Section angle step
	
	MObject	neoNode::A;		// Distance of section from Z-axis
	MObject	neoNode::a;		// Section diameter #1
	MObject	neoNode::b;		// Section diameter #2
	MObject	neoNode::scale;		// Overall scale
	
	// Neo Ribs Parameters
	// --------------------
	MObject	neoNode::vamp;		// Profile rib amplitude
	MObject	neoNode::vfreq;		// Profile rib frequency
	MObject	neoNode::vrib;		// Profile rib/wave percent
	
	MObject	neoNode::uamp;		// Section rib amplitude
	MObject	neoNode::ufreq;		// Section rib frequency
	MObject	neoNode::urib;		// Section rib/wave percent

	
// Neo Primary Nodules 
// --------------------
	MObject	neoNode::P;		// Position on section
	MObject	neoNode::L;		// Amplitude (extrusion) of nodule
	MObject	neoNode::N;		// Nodules frequency on profile
	
	MObject	neoNode::W1;		// Fatness of nodule 1
	MObject	neoNode::W2;		// Fatness of nodule 2
	
	MObject	neoNode::nstart;	// Starting point on spiral

	
// Neo Secondary Nodules
// ----------------------
	MObject	neoNode::P2;
	MObject	neoNode::L2;
	MObject	neoNode::N2;
	
	MObject	neoNode::W12;
	MObject	neoNode::W22;
	
	MObject	neoNode::off2;
	
	MObject	neoNode::nstart2;


// Neo Tertiary Nodules
// ---------------------
	MObject	neoNode::P3;
	MObject	neoNode::L3;
	MObject	neoNode::N3;
	
	MObject	neoNode::W13;
	MObject	neoNode::W23;
	
	MObject	neoNode::off3;
	
	MObject	neoNode::nstart3;


// Output mesh
// ------------
	MObject	neoNode::outMesh;

    
void	neoNode::addFloatParameter( MObject & attr, MString longName, 
					MString briefName, float attrDefault )
//
// Description:
//	Add a float input parameter to the node
// ---------------------------------------------
{
	MStatus 		stat;
	MFnNumericAttribute 	nAttr;
	
	attr = nAttr.create( longName, briefName, 
					MFnNumericData::kFloat, 0.0, &stat );
	if ( stat != MS::kSuccess ) 
		throw stat;

	stat = nAttr.setDefault  ( attrDefault );
	if ( stat != MS::kSuccess ) 
		throw stat;

	stat = nAttr.setKeyable  ( true );
	if ( stat != MS::kSuccess ) 
		throw stat;

	stat = nAttr.setCached   ( true );
	if ( stat != MS::kSuccess ) 
		throw stat;

	stat = nAttr.setStorable ( true );
	if ( stat != MS::kSuccess ) 
		throw stat;

	stat = addAttribute( attr );
	if ( stat != MS::kSuccess ) 
		throw stat;
    
	stat = attributeAffects( attr, outMesh );
	if ( stat != MS::kSuccess ) 
		throw stat;
}


void	neoNode::addAngleParameter( MObject & attr, MString longName, 
					MString briefName, float attrDefault )
//
// Description:
//	Add an angle input parameter to the node
// ----------------------------------------------
{
	MStatus 		stat;
	MFnUnitAttribute 	uAttr;

	MAngle	defaultAngle( ( double )attrDefault, MAngle::kDegrees );
    
	attr = uAttr.create( longName, briefName, defaultAngle, &stat );
	if ( stat != MS::kSuccess ) 
		throw stat;

	stat = uAttr.setKeyable  ( true );
	if ( stat != MS::kSuccess ) 
		throw stat;

	stat = uAttr.setCached   ( true );
	if ( stat != MS::kSuccess ) 
		throw stat;

	stat = uAttr.setStorable ( true );
	if ( stat != MS::kSuccess ) 
		throw stat;

	stat = addAttribute( attr );
	if ( stat != MS::kSuccess ) 
		throw stat;
    
	stat = attributeAffects( attr, outMesh );
	if ( stat != MS::kSuccess ) 
		throw stat;
}


MStatus neoNode::initialize()
//
// Description:
//	Set up node attributes
// ----------------------------
{
	MFnTypedAttribute   	typedFn;
	MStatus			stat;

    	outMesh = typedFn.create( "outMesh", "o", MFnData::kMesh, &stat ); 
    	
	if ( MS::kSuccess != stat ) 
	{
    		cerr << "ERROR creating animCube output attribute\n";
        	return	stat;
    	}
    
	typedFn.setStorable( false );
    	typedFn.setWritable( false );
    	
	stat = addAttribute( outMesh );
    	
	McheckErr( stat, "ERROR adding attribute" );	

   try {
	// Neo Parameters  
	// ---------------
	addAngleParameter ( alpha, "profileParam1", "pp1", 80.0F );
	addAngleParameter ( beta,  "profileParam2", "pp2", 90.0F );
	
	addAngleParameter ( omin,  "spiralStartAngle",	"sps",	0.0F );
	addAngleParameter ( omax,  "spiralEndAngle",	"spe", 	1200.0F );
	addAngleParameter ( od,    "spiralAngleStep",	"spa", 	4.0F );
	
	addAngleParameter ( phi,   "sectionStartingPoint", "ssp", 1.0F );
	addAngleParameter ( my,    "sectionSlant", "ss", 1.0F );
	addAngleParameter ( omega, "sectionAngleZ", "saz", 1.0F );
	
	addAngleParameter ( smin,  "sectionStartAngle", "ssa",	-190.0F );
	addAngleParameter ( smax,  "sectionEndAngle", 	"sea",	190.0F );
	addAngleParameter ( sd,    "sectionAngleStep",	"sas",	17.0F );

	addFloatParameter ( A,     "distanceFromZ",	"dfz",	1.9F );
	addFloatParameter ( a,     "sectionDiameter1",	"sd1",	1.0F );
	addFloatParameter ( b,     "sectionDiameter2",	"sd2",	0.9F );
	addFloatParameter ( scale, "scale",		"s",	0.03F );
	
	// Neo Ribs Parameters
	// --------------------
	addFloatParameter ( vamp,  "profileRibAmplitude", "pra", 0.0F );
	addFloatParameter ( vfreq, "profileRibFrequency", "prf", 0.0F );
	addFloatParameter ( vrib,  "profileRibWavePercent", "prw", 0.0F );
	
	addFloatParameter ( uamp,  "sectionRibAmplitude", "sra", 0.0F );
	addFloatParameter ( ufreq, "sectionRibFrequency", "srf", 0.0F );
	addFloatParameter ( urib,  "sectionRibWavePercent", "srw", 0.0F );


	// Neo Primary Nodules
	// --------------------
	addAngleParameter ( P,	   "positionOnSection1",      "ps1",   10.0F );
	addFloatParameter ( L,     "noduleAmplitude1",        "na1",    1.0F );
	addFloatParameter ( N,     "noduleProfileFrequency1", "nf1",   15.0F );
	
	addAngleParameter ( W1,    "noduleFatness1-1",         "f11",  100.0F );
	addAngleParameter ( W2,    "noduleFatness2-1",         "f21",   20.0F );
	
	addAngleParameter ( nstart,"spiralStartingPoint1",    "sp1",    0.0F );


	// Neo Secondary Nodules
	// ----------------------
	addAngleParameter ( P2,     "positionOnSection2",      "ps2",    0.0F );
	addFloatParameter ( L2,     "noduleAmplitude2",        "na2",    0.0F );
	addFloatParameter ( N2,     "noduleProfileFrequency2", "nf2",    0.0F );
	
	addAngleParameter ( W12,    "noduleFatness1-2",        "f12",   30.0F );
	addAngleParameter ( W22,    "noduleFatness2-2",        "f22",   30.0F );
	
	addAngleParameter ( off2,   "noduleOffset2",           "no2",    0.0F );
	
	addAngleParameter ( nstart2,"spiralStartingPoint2",    "sp2",    0.0F );


	// Neo Tertiary Nodules
	// ---------------------
	addAngleParameter ( P3,     "positionOnSection3",      "ps3",    0.0F );
	addFloatParameter ( L3,     "noduleAmplitude3",        "na3",    0.0F );
	addFloatParameter ( N3,     "noduleProfileFrequency3", "nf3",    0.0F );
	
	addAngleParameter ( W13,    "noduleFatness1-3",        "f13",   30.0F );
	addAngleParameter ( W23,    "noduleFatness2-3",        "f23",   30.0F );
	
	addAngleParameter ( off3,   "noduleOffset3",           "no3",    0.0F );
	
	addAngleParameter ( nstart3,"spiralStartingPoint3",    "sp3",    0.0F );
       } 
	
	catch ( MStatus stat ) 
	{
		fprintf( stderr,"Attribute Initialize Failed\n" );
		return	stat;
	}

	return	MS::kSuccess;
}


inline	float neoNode::GetFloatParameter( MObject node, MObject attr )
{
	MPlug	plug( node, attr );
	float	value;
	
	plug.getValue( value );
	
	return	value;
}

inline	float neoNode::GetAngleParameter( MObject node, MObject attr )
{
	MPlug	plug( node, attr );
	MAngle	angle;
	
	plug.getValue( angle );
	
	return	( float )angle.asRadians();
}


#define	UpdateFloatAttr( ATTR,TOPOLOGY )	\
	oldValue = neoParams. ## ATTR;		\	
    	neoParams. ## ATTR = GetFloatParameter( thisObj, ATTR );	\
	if ( neoParams. ## ATTR != oldValue )	\
	{					\
		rebuild = true;			\
		redoTopology = TOPOLOGY ? true : redoTopology;		\
	}

#define UpdateAngleAttr( ATTR,TOPOLOGY ) 	\
	oldValue = neoParams. ## ATTR;		\
    	neoParams. ## ATTR = GetAngleParameter( thisObj, ATTR );	\
	if ( neoParams. ## ATTR != oldValue ) 	\
	{					\
	    	rebuild = true;			\
		redoTopology = TOPOLOGY ? true : redoTopology;		\
	}


void neoNode::UpdateParameters() 
//
// Description: 
//	Read all of the neo parameters and determine what has changed
// -------------------------------------------------------------------
{
	MObject 	thisObj = thisMObject();
	float 		oldValue;

	// Neo Parameters
	// ---------------
	UpdateAngleAttr ( alpha, false );
	UpdateAngleAttr ( beta, false );
	
		// These settings change the topology of the geometry
		// ---------------------------------------------------
	UpdateAngleAttr ( omin, true );
	UpdateAngleAttr ( omax, true );
	UpdateAngleAttr ( od, true );
	
	UpdateAngleAttr ( phi, false );
	UpdateAngleAttr ( my, false );
	UpdateAngleAttr ( omega, false );
	
		// These settings change the topology of the geometry
		// ---------------------------------------------------
	UpdateAngleAttr ( smin, true );
	UpdateAngleAttr ( smax, true );
	UpdateAngleAttr ( sd, true );
	
	UpdateFloatAttr ( A, false );
	UpdateFloatAttr ( a, false );
	UpdateFloatAttr ( b, false );
	UpdateFloatAttr ( scale, false );
	
	// Neo Ribs Parameters
	// --------------------
	UpdateFloatAttr ( vamp, false );
	UpdateFloatAttr ( vfreq, false );
	UpdateFloatAttr ( vrib, false );
	
	UpdateFloatAttr ( uamp, false );
	UpdateFloatAttr ( ufreq, false );
	UpdateFloatAttr ( urib, false );


	// Neo Primary Nodules
	// --------------------
	UpdateAngleAttr ( P, false );
	UpdateFloatAttr ( L, false );
	UpdateFloatAttr ( N, false );
	
	UpdateAngleAttr ( W1, false );
	UpdateAngleAttr ( W2, false );
	
	UpdateAngleAttr ( nstart, false );


	// Neo Secondary Nodules
	// ----------------------
	UpdateAngleAttr ( P2, false );
	UpdateFloatAttr ( L2, false );
	UpdateFloatAttr ( N2, false );
	
	UpdateAngleAttr ( W12, false );
	UpdateAngleAttr ( W22, false );
	
	UpdateAngleAttr ( off2, false );
	
	UpdateAngleAttr ( nstart2, false );


	// Neo Tertiary Nodules
	// ---------------------	
	UpdateAngleAttr ( P3, false );
	UpdateFloatAttr ( L3, false );
	UpdateFloatAttr ( N3, false );
	
	UpdateAngleAttr ( W13, false );
	UpdateAngleAttr ( W23, false );
	
	UpdateAngleAttr ( off3, false );
	
	UpdateAngleAttr ( nstart3, false );
}


////////////////////////////
// Plug-in Initialization //
////////////////////////////

MStatus initializePlugin( MObject obj )
{ 
	MStatus		status;
	MFnPlugin	plugin( obj, "Yuuichi Kawamoto", "2.5 - 4.0", "Any" );

	status = plugin.registerNode( "neo", neoNode::id, 
				&neoNode::creator, &neoNode::initialize,
				MPxNode::kDependNode );
	if ( !status ) 
	{
		status.perror(" registerNode" );
		return	status;
	}

	return	status;
}


MStatus uninitializePlugin( MObject obj )
{
	MStatus		status;
	MFnPlugin 	plugin( obj );

	status = plugin.deregisterNode( neoNode::id );
	if ( !status ) 
	{
		status.perror( "deregisterNode" );
		return	status;
	}

	return	status;
}


// end of script

