#include <stdio.h>
#include <maya/MIOStream.h>
#include <math.h>
#include <maya/MString.h>
#include <maya/MArgList.h>
#include <maya/MEvent.h>
#include <maya/MGlobal.h>
#include <maya/M3dView.h>
#include <maya/MPoint.h>
#include <maya/MPointArray.h>
#include <maya/MDoubleArray.h>
#include <maya/MDagPath.h>
#include <maya/MPxContext.h>
#include <maya/MPxContextCommand.h>
#include <maya/MPxToolCommand.h>
#include <maya/MToolsInfo.h>
#include <maya/MFnPlugin.h>
#include <maya/MFnNurbsCurve.h>
#include <maya/MSyntax.h>
#include <maya/MArgParser.h>
#include <maya/MArgDatabase.h>
#include <maya/MCursor.h>
#if defined(OSMac_MachO_)
#include <OpenGL/gl.h>
#include <OpenGL/glu.h>
#else
#include <GL/gl.h>
#include <GL/glu.h>
#endif
#define kPitchFlag "-p"
#define kPitchFlagLong "-pitch"
#define kRadiusFlag "-r"
#define kRadiusFlagLong "-radius"
#define kNumberCVsFlag "-ncv"
#define kNumberCVsFlagLong "-numCVs"
#define kUpsideDownFlag "-ud"
#define kUpsideDownFlagLong "-upsideDown"
#define NUMBER_OF_CVS 20
class helixTool : public MPxToolCommand
{
public:
helixTool();
virtual ~helixTool();
static void* creator();
MStatus doIt(const MArgList& args);
MStatus parseArgs(const MArgList& args);
MStatus redoIt();
MStatus undoIt();
bool isUndoable() const;
MStatus finalize();
static MSyntax newSyntax();
void setRadius(double newRadius);
void setPitch(double newPitch);
void setNumCVs(unsigned newNumCVs);
void setUpsideDown(bool newUpsideDown);
private:
double radius;
double pitch;
unsigned numCV;
bool upDown;
MDagPath path;
};
void* helixTool::creator()
{
return new helixTool;
}
helixTool::~helixTool() {}
helixTool::helixTool()
{
numCV = 20;
upDown = false;
setCommandString("helixToolCmd");
}
MSyntax helixTool::newSyntax()
{
MSyntax syntax;
syntax.addFlag(kPitchFlag, kPitchFlagLong, MSyntax::kDouble);
syntax.addFlag(kRadiusFlag, kRadiusFlagLong, MSyntax::kDouble);
syntax.addFlag(kNumberCVsFlag, kNumberCVsFlagLong, MSyntax::kUnsigned);
syntax.addFlag(kUpsideDownFlag, kUpsideDownFlagLong, MSyntax::kBoolean);
return syntax;
}
MStatus helixTool::doIt(const MArgList &args)
{
MStatus status;
status = parseArgs(args);
if (MS::kSuccess != status)
return status;
return redoIt();
}
MStatus helixTool::parseArgs(const MArgList &args)
{
MStatus status;
MArgDatabase argData(syntax(), args);
if (argData.isFlagSet(kPitchFlag)) {
double tmp;
status = argData.getFlagArgument(kPitchFlag, 0, tmp);
if (!status) {
status.perror("pitch flag parsing failed");
return status;
}
pitch = tmp;
}
if (argData.isFlagSet(kRadiusFlag)) {
double tmp;
status = argData.getFlagArgument(kRadiusFlag, 0, tmp);
if (!status) {
status.perror("radius flag parsing failed");
return status;
}
radius = tmp;
}
if (argData.isFlagSet(kNumberCVsFlag)) {
unsigned tmp;
status = argData.getFlagArgument(kNumberCVsFlag, 0, tmp);
if (!status) {
status.perror("numCVs flag parsing failed");
return status;
}
numCV = tmp;
}
if (argData.isFlagSet(kUpsideDownFlag)) {
bool tmp;
status = argData.getFlagArgument(kUpsideDownFlag, 0, tmp);
if (!status) {
status.perror("upside down flag parsing failed");
return status;
}
upDown = tmp;
}
return MS::kSuccess;
}
MStatus helixTool::redoIt()
{
MStatus stat;
const unsigned deg = 3;
const unsigned ncvs = numCV;
const unsigned spans = ncvs - deg;
const unsigned nknots = spans+2*deg-1;
unsigned i;
MPointArray controlVertices;
MDoubleArray knotSequences;
int upFactor;
if (upDown) upFactor = -1;
else upFactor = 1;
for (i = 0; i < ncvs; i++)
controlVertices.append(MPoint(radius * cos((double) i),
upFactor * pitch * (double) i,
radius * sin((double) i)));
for (i = 0; i < nknots; i++)
knotSequences.append((double) i);
MFnNurbsCurve curveFn;
curveFn.create(controlVertices, knotSequences, deg,
MFnNurbsCurve::kOpen, false, false,
MObject::kNullObj, &stat);
if (!stat) {
stat.perror("Error creating curve");
return stat;
}
stat = curveFn.getPath( path );
return stat;
}
MStatus helixTool::undoIt()
{
MStatus stat;
MObject transform = path.transform();
stat = MGlobal::removeFromModel( transform );
return stat;
}
bool helixTool::isUndoable() const
{
return true;
}
MStatus helixTool::finalize()
{
MArgList command;
command.addArg(commandString());
command.addArg(MString(kRadiusFlag));
command.addArg(radius);
command.addArg(MString(kPitchFlag));
command.addArg(pitch);
command.addArg(MString(kNumberCVsFlag));
command.addArg((int) numCV);
command.addArg(MString(kUpsideDownFlag));
command.addArg(upDown);
return MPxToolCommand::doFinalize( command );
}
void helixTool::setRadius(double newRadius)
{
radius = newRadius;
}
void helixTool::setPitch(double newPitch)
{
pitch = newPitch;
}
void helixTool::setNumCVs(unsigned newNumCVs)
{
numCV = newNumCVs;
}
void helixTool::setUpsideDown(bool newUpsideDown)
{
upDown = newUpsideDown;
}
const char helpString[] = "Click and drag to draw helix";
class helixContext : public MPxContext
{
public:
helixContext();
virtual void toolOnSetup(MEvent &event);
virtual MStatus doPress(MEvent &event);
virtual MStatus doDrag(MEvent &event);
virtual MStatus doRelease(MEvent &event);
virtual MStatus doEnterRegion(MEvent &event);
virtual void getClassName(MString & name) const;
void setNumCVs(unsigned newNumCVs);
void setUpsideDown(bool newUpsideDown);
unsigned numCVs();
bool upsideDown();
private:
short startPos_x, startPos_y;
short endPos_x, endPos_y;
unsigned numCV;
bool upDown;
M3dView view;
GLdouble height,radius;
};
helixContext::helixContext()
{
numCV = 20;
upDown = false;
setTitleString("Helix Tool");
setCursor( MCursor::defaultCursor );
setImage("helixTool.xpm", MPxContext::kImage1 );
}
void helixContext::toolOnSetup(MEvent &)
{
setHelpString(helpString);
}
MStatus helixContext::doPress(MEvent &event)
{
event.getPosition(startPos_x, startPos_y);
view = M3dView::active3dView();
view.beginGL();
view.beginOverlayDrawing();
return MS::kSuccess;
}
MStatus helixContext::doDrag(MEvent & event)
{
event.getPosition(endPos_x, endPos_y);
view.clearOverlayPlane();
glIndexi(2);
int upFactor;
if (upDown) upFactor = 1;
else upFactor = -1;
glMatrixMode(GL_MODELVIEW);
glPushMatrix();
glRotatef(upFactor*90.0f, 1.0f, 0.0f, 0.0f);
GLUquadricObj *qobj = gluNewQuadric();
gluQuadricDrawStyle(qobj, GLU_LINE);
GLdouble factor = (GLdouble)numCV;
radius = double(abs(endPos_x - startPos_x))/factor + 0.1;
height = double(abs(endPos_y - startPos_y))/factor + 0.1;
gluCylinder( qobj, radius, radius, height, 8, 1 );
glPopMatrix();
#ifdef _WIN32
SwapBuffers( view.deviceContext() );
#elif defined (OSMac_)
# if !defined(__x86_64__)
::aglSwapBuffers( view.display());
# endif
#else
glXSwapBuffers( view.display(), view.window() );
#endif // _WIN32
return MS::kSuccess;
}
MStatus helixContext::doRelease( MEvent & )
{
view.clearOverlayPlane();
view.endOverlayDrawing();
view.endGL();
helixTool * cmd = (helixTool*)newToolCommand();
cmd->setPitch( height/numCV );
cmd->setRadius( radius );
cmd->setNumCVs( numCV );
cmd->setUpsideDown( upDown );
cmd->redoIt();
cmd->finalize();
return MS::kSuccess;
}
MStatus helixContext::doEnterRegion( MEvent & )
{
return setHelpString( helpString );
}
void helixContext::getClassName( MString & name ) const
{
name.set("helix");
}
void helixContext::setNumCVs( unsigned newNumCVs )
{
numCV = newNumCVs;
MToolsInfo::setDirtyFlag(*this);
}
void helixContext::setUpsideDown( bool newUpsideDown )
{
upDown = newUpsideDown;
MToolsInfo::setDirtyFlag(*this);
}
unsigned helixContext::numCVs()
{
return numCV;
}
bool helixContext::upsideDown()
{
return upDown;
}
class helixContextCmd : public MPxContextCommand
{
public:
helixContextCmd();
virtual MStatus doEditFlags();
virtual MStatus doQueryFlags();
virtual MPxContext* makeObj();
virtual MStatus appendSyntax();
static void* creator();
protected:
helixContext* fHelixContext;
};
helixContextCmd::helixContextCmd() {}
MPxContext* helixContextCmd::makeObj()
{
fHelixContext = new helixContext();
return fHelixContext;
}
void* helixContextCmd::creator()
{
return new helixContextCmd;
}
MStatus helixContextCmd::doEditFlags()
{
MStatus status = MS::kSuccess;
MArgParser argData = parser();
if (argData.isFlagSet(kNumberCVsFlag)) {
unsigned numCVs;
status = argData.getFlagArgument(kNumberCVsFlag, 0, numCVs);
if (!status) {
status.perror("numCVs flag parsing failed.");
return status;
}
fHelixContext->setNumCVs(numCVs);
}
if (argData.isFlagSet(kUpsideDownFlag)) {
bool upsideDown;
status = argData.getFlagArgument(kUpsideDownFlag, 0, upsideDown);
if (!status) {
status.perror("upsideDown flag parsing failed.");
return status;
}
fHelixContext->setUpsideDown(upsideDown);
}
return MS::kSuccess;
}
MStatus helixContextCmd::doQueryFlags()
{
MArgParser argData = parser();
if (argData.isFlagSet(kNumberCVsFlag)) {
setResult((int) fHelixContext->numCVs());
}
if (argData.isFlagSet(kUpsideDownFlag)) {
setResult(fHelixContext->upsideDown());
}
return MS::kSuccess;
}
MStatus helixContextCmd::appendSyntax()
{
MSyntax mySyntax = syntax();
if (MS::kSuccess != mySyntax.addFlag(kNumberCVsFlag, kNumberCVsFlagLong,
MSyntax::kUnsigned)) {
return MS::kFailure;
}
if (MS::kSuccess !=
mySyntax.addFlag(kUpsideDownFlag, kUpsideDownFlagLong,
MSyntax::kBoolean)) {
return MS::kFailure;
}
return MS::kSuccess;
}
MStatus initializePlugin( MObject obj )
{
MStatus status;
MFnPlugin plugin(obj, PLUGIN_COMPANY, "3.0", "Any");
status = plugin.registerContextCommand("helixToolContext",
helixContextCmd::creator,
"helixToolCmd",
helixTool::creator,
helixTool::newSyntax);
if (!status) {
status.perror("registerContextCommand");
return status;
}
return status;
}
MStatus uninitializePlugin( MObject obj)
{
MStatus status;
MFnPlugin plugin( obj );
status = plugin.deregisterContextCommand( "helixToolContext",
"helixToolCmd" );
if (!status) {
status.perror("deregisterContextCommand");
return status;
}
return status;
}