// Copyright (C) Karan Singh
//
//  File: extrudecurve.cpp
//
//  Description:
// 		This node makes section curves on a mesh
//

// modified by patrick coleman


#include <string.h>
#include <iostream.h>
#include <math.h>

#include <maya/MPxLocatorNode.h> 
#include <maya/MFnUnitAttribute.h>

#include <maya/MFnNumericAttribute.h>
#include <maya/MFnMatrixAttribute.h>
#include <maya/MFnTypedAttribute.h>
#include <maya/MFnMatrixData.h>  
#include <maya/MFnMeshData.h>
#include <maya/MFnPlugin.h>
#include <maya/MFnNurbsCurveData.h>

#include <maya/MTypeId.h> 
#include <maya/MPlug.h> 
#include <maya/MDataBlock.h>
#include <maya/MDataHandle.h>
#include <maya/MArrayDataHandle.h>
#include <maya/MPoint.h>
#include <maya/MVector.h>
#include <maya/MMatrix.h>
#include <maya/MString.h>
#include <maya/M3dView.h>

#include <maya/MFnMesh.h>
#include <maya/MFnNurbsCurve.h>
#include <maya/MPointArray.h>
#include <maya/MItMeshPolygon.h>
#include <maya/MItMeshEdge.h>
#include <maya/MItMeshVertex.h>
#include <maya/MDoubleArray.h>
#include <maya/MPointArray.h>
#include <maya/MIntArray.h>
#include <maya/MFloatPointArray.h>


#define kPI  3.1415927
#define k2PI 6.2831853


// consts used for drawing circular shape of end effector.

#define kCircleDivisions 20
#define kThetaRes        40
#define kPhiRes          60

#define equiv(x,y) (fabs(x-y)<0.0001)

#define kMaxMeshes 50


static double thStep= (double)kThetaRes/kPI;
static double phStep= (double)kPhiRes/k2PI;
 

static float indx[2*kCircleDivisions];
 


class extrudecurve : public MPxLocatorNode
{
public:
						extrudecurve();
	virtual				~extrudecurve();

	virtual MStatus		compute( const MPlug& plug, MDataBlock& data );
	static  void*		creator();
	static  MStatus		initialize();

	virtual void            draw( M3dView & view, const MDagPath & path, 
								  M3dView::DisplayStyle style,
								  M3dView::DisplayStatus status );


public:


	// local node attributes

	static  MObject     up;    		// there are two ways for a planar curve to 
	                                // wrap around a mesh by default we pick the 
	                                // +ve y direction of the end effector.
	                                // up = false reverses this to the -ve y.

	static  MObject     tolerance; 		// extrudecurve tolerance for intersection testing with mesh	
	static  MObject     curveLength;  		// extrudecurve length
	static  MObject     step;   		// extrudecurve length

	static  MObject     handleMatrix; 	// end effector matrix
	static  MObject     originMatrix; 	// origin locator matrix
	static	MObject		aMeshMatrix;	// mesh world matrix

	static  MObject     sectionCurveObject;        // mesh
	static  MObject     sectionCurveInput; // generated curve
	static  MObject     sectionCurveTrajectory; // generated curve

	static  MTypeId		id;

private:

	bool 		upSpace;	// whether or not to deform in upSpace

	double   	toler,dr,dr2,clength,tt,sd,slp,doop,sstep;

	MMatrix  	handleMat,meshMat[kMaxMeshes],originMat,meshMatinv[kMaxMeshes];
	double   	magnitude;

	MFnNurbsCurve trajCurve,inCurve;
	MFnMesh fmesh[kMaxMeshes];

	MPoint curp,dispp;
	int curpoly,curmesh;
	MVector curnorm;
	double parm;
	MPointArray cpoints;  // this is the list of points that generates the curve.


	MVector planenorm;

	int meshcnt;

};

MTypeId     extrudecurve::id( 0x80094 );

MObject     extrudecurve::up;
MObject     extrudecurve::tolerance;
MObject     extrudecurve::curveLength;
MObject     extrudecurve::step;

MObject		extrudecurve::handleMatrix;
MObject		extrudecurve::originMatrix;
MObject		extrudecurve::aMeshMatrix;
  
MObject     extrudecurve::sectionCurveObject;
MObject     extrudecurve::sectionCurveInput;
MObject     extrudecurve::sectionCurveTrajectory;


extrudecurve::extrudecurve() {
	float i=0.0,inc=(float)(k2PI/(float)kCircleDivisions);
	int j=0;
	while  (j<kCircleDivisions) 
	{
		indx[j]=(float)cos(i); //*100.0;	
			indx[j+kCircleDivisions]=(float)sin(i);//*100.0;
			i+=inc;
			j++;
	}
	meshcnt=0;
}
extrudecurve::~extrudecurve() {}

MStatus extrudecurve::compute( const MPlug& plug, MDataBlock& data )
{
	
	MStatus returnStatus;

	if ( plug == sectionCurveObject)
	{					
		upSpace = data.inputValue(up).asBool();
		toler = data.inputValue(tolerance).asDouble();
		sstep = data.inputValue(step).asDouble();
		clength = data.inputValue(curveLength).asDouble();
		
		MDataHandle handle2 = data.inputValue(sectionCurveInput, &returnStatus );   		
		MObject mobjc=handle2.asNurbsCurve();
		inCurve.setObject(mobjc);

		double st=0,en=0;
		MStatus chk;
		int cvche=inCurve.numCVs(&chk);
		inCurve.getKnotDomain(st,en);
		//inCurve.getPointAtParam(st,stp,MSpace::kWorld);

		long     num_pnts=cvche*4;				    			 //   number of vertices 
		long     num_polys =  (cvche-1)*4+ +2;                  //   number of polygons   
		MFloatPointArray pnts;				     				//   vertex list          
		MIntArray polyCounts;
		MIntArray polyConnects;

		MPointArray ccv;
		inCurve.getCVs(ccv);

		pnts.setLength(num_pnts);
		polyCounts.setLength(num_polys);
		polyConnects.setLength(num_polys*4);


		MVector old,cur;

		if (num_pnts>1)
		{
			old=ccv[1]-ccv[0];
			old.normalize();
		}
		double normx=1,normy=0,onx=1,ony=0;


		for (int j=0;j<cvche;j++)
		{
			if (j==cvche-1)
			{
				normx=cur.x;
				normy=cur.y;
			}
			else 
			{
				cur=ccv[j+1]-ccv[j];
				cur.normalize();
				normx=cur.x+old.x;
				normy=cur.y+old.y;
				old=cur;
			}
			double sze=sqrt(normx*normx+normy*normy);
			if (sze>0.000001)
			{
				normx/=sze;
				normy/=sze;
				onx=normx;
				ony=normy;
			}
			else
			{
				normx=onx;
				normy=ony;
			}
			pnts[4*j]  =MFloatPoint(ccv[j].x+toler*normy,ccv[j].y-toler*normx,0);
			pnts[4*j+1]=MFloatPoint(ccv[j].x-toler*normy,ccv[j].y+toler*normx,0);
			pnts[4*j+2]=MFloatPoint(ccv[j].x-toler*normy,ccv[j].y+toler*normx,sstep);
			pnts[4*j+3]=MFloatPoint(ccv[j].x+toler*normy,ccv[j].y-toler*normx,sstep);
			
			if (j)
			{
				for (int k=0;k<4;k++)
				{
					polyConnects[16*(j-1)+4*k]  =4*j+k;
					polyConnects[16*(j-1)+4*k+1]=4*(j-1)+k;
					polyConnects[16*(j-1)+4*k+2]=4*(j-1)+ (k+1)%4;
					polyConnects[16*(j-1)+4*k+3]=4*j+ (k+1)%4;;
				}
			}
		}

		for (j=0;j<num_polys;j++)
		{
			polyCounts[j]=4;			
		}


		if (num_pnts>4)
		{
			polyConnects[(num_polys-2)*4 ]   =3;
			polyConnects[(num_polys-2)*4 +1] =2;
			polyConnects[(num_polys-2)*4 +2] =1;
			polyConnects[(num_polys-2)*4 +3] =0;

			polyConnects[(num_polys-1)*4 ]   =num_pnts-4;
			polyConnects[(num_polys-1)*4 +1] =num_pnts-3;
			polyConnects[(num_polys-1)*4 +2] =num_pnts-2;
			polyConnects[(num_polys-1)*4 +3] =num_pnts-1;


			//for (int m=0;m<num_pnts;m++)
			//{
			//	printf("pt %d %f %f %f\n",m,pnts[m].x,pnts[m].y,pnts[m].z);
			//}
			//for (m=0;m<num_polys*4;m++)
			//{
			//	printf("%d ",polyConnects[m]);
			//}
			//printf("\n");

			MFnMesh mesh;				
			MDataHandle outHandle = data.outputValue(sectionCurveObject);
			MObject mobj = outHandle.asMesh();
			if ( mobj.isNull() ) {
				MFnMeshData dataFn;
				mobj = dataFn.create();
			}
			mesh.create(num_pnts,num_polys,pnts,polyCounts,
											polyConnects,mobj);
			outHandle.set( mobj );
			
			outHandle.setClean();
			//printf("just set it\n");
		}
	}  
	return MS::kSuccess;
} 

void* extrudecurve::creator()
{
	return new extrudecurve();
}

MStatus extrudecurve::initialize()
{

	MFnNumericAttribute nAttr;

	up=nAttr.create( "upDirection", "up", MFnNumericData::kBoolean);
	    nAttr.setDefault(true);
	    nAttr.setKeyable(true);

	tolerance=nAttr.create( "tolerance", "tol", MFnNumericData::kDouble );
	    nAttr.setDefault(0.5);
	    nAttr.setKeyable(true);

	step=nAttr.create( "step", "stp", MFnNumericData::kDouble );
	    nAttr.setDefault(1.0);
	    nAttr.setKeyable(true);

	MFnUnitAttribute uAttr;

	curveLength=uAttr.create( "length", "len", MFnUnitAttribute::kDistance );
	    uAttr.setDefault(5.0);
	    uAttr.setKeyable(true);
	
	
	// handle world mat	

	MFnMatrixAttribute  mAttr;
	handleMatrix=mAttr.create( "handleMatrix", "hm");
	mAttr.setStorable(false);
	mAttr.setConnectable(true);


	// origin world mat

	originMatrix=mAttr.create( "originMatrix", "om");
	mAttr.setStorable(false);


	// mesh mat

	aMeshMatrix = mAttr.create("meshMatrix", "mm");
	mAttr.setStorable(false);
	mAttr.setConnectable(true);
	mAttr.setArray(true);


	// mesh geom

	MStatus statt;
	MFnTypedAttribute  tAttr;
	sectionCurveObject=tAttr.create( "sectionCurveObject", "mo",MFnData::kMesh,&statt);
	tAttr.setStorable(false);
	tAttr.setConnectable(true);

	
	// curve geom

	sectionCurveTrajectory=tAttr.create( "sectionCurveTrajectory", "mt",
								  MFnData::kNurbsCurve,&statt);
	tAttr.setStorable(false);
	tAttr.setConnectable(true);
	

	sectionCurveInput=tAttr.create( "sectionCurveInput", "mi",
								  MFnData::kNurbsCurve,&statt);
	tAttr.setStorable(false);
	tAttr.setConnectable(true);


	addAttribute( up);
	addAttribute( tolerance);
	addAttribute( step);
	addAttribute( curveLength);
	addAttribute( handleMatrix);
	addAttribute( originMatrix);
	addAttribute( aMeshMatrix);

	addAttribute( sectionCurveObject);
	addAttribute( sectionCurveTrajectory);
	addAttribute( sectionCurveInput);

	attributeAffects( extrudecurve::up, extrudecurve::sectionCurveObject );
    attributeAffects( extrudecurve::tolerance, extrudecurve::sectionCurveObject );
    attributeAffects( extrudecurve::step, extrudecurve::sectionCurveObject );
    attributeAffects( extrudecurve::curveLength, extrudecurve::sectionCurveObject );
    attributeAffects( extrudecurve::sectionCurveInput, extrudecurve::sectionCurveObject );

    attributeAffects( extrudecurve::originMatrix, extrudecurve::sectionCurveObject );
    attributeAffects( extrudecurve::handleMatrix, extrudecurve::sectionCurveObject );	
	attributeAffects( extrudecurve::aMeshMatrix,  extrudecurve::sectionCurveObject);

	return MS::kSuccess;
}




void extrudecurve::draw( M3dView & view, const MDagPath & path, 
					  M3dView::DisplayStyle style,
					  M3dView::DisplayStatus status )
{ 
        view.beginGL();
		
		//glBegin(GL_LINE_LOOP);
		//for (int j=0;j<kCircleDivisions;++j) 
		//{
		//	glVertex3f(indx[j],indx[j+kCircleDivisions],0.0);
		//}
		//glEnd();
		
		glBegin(GL_LINES);
			glVertex3f(0.0,0.0,0.0);
			glVertex3f(0.0,1.0,0.0);
		glEnd();

		if (cpoints.length())
		{
			MPoint rs=cpoints[0];//*handleMat.inverse();
			glBegin(GL_LINES);
				glVertex3f(rs[0],rs[1],rs[2]);
				glVertex3f(0,0,0);
			glEnd();
		}

		//glBegin(GL_LINES);
		//	glVertex3f(realsource[0],realsource[1],realsource[2]);
		//	glVertex3f(realsource[0]+planenorm[0]*3,realsource[1]+planenorm[1]*3,realsource[2]+planenorm[2]*3);
		//glEnd();

		glBegin(GL_LINE_LOOP);
		for (int j=0;j<kCircleDivisions;++j) 
		{
			glVertex3f(indx[j],0.0,indx[j+kCircleDivisions]);
		}
		glEnd();

		/*glBegin(GL_LINE_LOOP);
		for ( j=0;j<kCircleDivisions;++j) 
		{
			glVertex3f(0.0,indx[j],indx[j+kCircleDivisions]);
		}
		glEnd();	
		*/
        view.endGL();
}



// standard initialazation procedures
//

MStatus initializePlugin( MObject obj )
{ 
	MFnPlugin plugin( obj, "Alias|Wavefront", "4.0", "Any");
	plugin.registerNode( "extrudecurve", extrudecurve::id, extrudecurve::creator, 
						 extrudecurve::initialize, MPxNode::kLocatorNode );

	return MS::kSuccess;
}

MStatus uninitializePlugin( MObject obj)
{
	MFnPlugin plugin( obj );
	plugin.deregisterNode( extrudecurve::id );

	return MS::kSuccess;
}

 
