#include <math.h>
#include <maya/MIOStream.h>
#include <maya/MFnPlugin.h>
#include <maya/MObject.h>
#include <maya/MDagPath.h>
#include <maya/MString.h>
#include <maya/MPoint.h>
#include <maya/MVector.h>
#include <maya/MMatrix.h>
#include <maya/MQuaternion.h>
#include <AwPoint.h>
#include <AwVector.h>
#include <AwMatrix.h>
#include <AwQuaternion.h>
#include <maya/MPxIkSolverNode.h>
#include <maya/MIkHandleGroup.h>
#include <maya/MFnIkHandle.h>
#include <maya/MFnIkEffector.h>
#include <maya/MFnIkJoint.h>
#include <maya/MArgList.h>
#include <maya/MPxCommand.h>
#include <maya/MGlobal.h>
#include <maya/MSceneMessage.h>
#include <maya/MSelectionList.h>
#define kSolverType "ik2Bsolver"
#define kPi 3.14159265358979323846264338327950
#define kEpsilon 1.0e-5
#define absoluteValue(x) ((x) < 0 ? (-(x)) : (x))
public:
                    ik2Bsolver      ();
                    ~ik2Bsolver     ()       override;
    static  void*   creator();
private:
    AwVector poleVectorFromHandle(
const MDagPath &handlePath);
 
    double  twistFromHandle(
const MDagPath &handlePath);
 
};
void solveIK(const AwPoint &startJointPos,
             const AwPoint &midJointPos,
             const AwPoint &effectorPos,
             const AwPoint &handlePos,
             const AwVector &poleVector,
             double twistValue,
             AwQuaternion &qStart,
             AwQuaternion &qMid);
MTypeId ik2Bsolver::id(0x58000030);
 
ik2Bsolver::ik2Bsolver()
{
}
ik2Bsolver::~ik2Bsolver() {}
void ik2Bsolver::postConstructor()
{
    setRotatePlane(true);
}
void* ik2Bsolver::creator()
{
    return new ik2Bsolver;
}
{ 
    return MS::kSuccess;
}
MString ik2Bsolver::solverTypeName() const
 
{
}
{
    
    
    if (NULL == handle_group) {
        return MS::kFailure;
    }
    
    
    
    
    
    
    
    
    
    handleFn.getEffector(effectorPath);
    
    
    
    
    handleFn.getStartJoint(startJointPath);
    
    
    double startJointPrefAngle[3];
    double midJointPrefAngle[3];
    startJointFn.getPreferedAngle(startJointPrefAngle);
    midJointFn.getPreferedAngle(midJointPrefAngle);
    
    
    startJointFn.setRotation(startJointPrefAngle, 
                             startJointFn.rotationOrder());
    midJointFn.setRotation(midJointPrefAngle, 
                           midJointFn.rotationOrder());
    AwVector poleVector = poleVectorFromHandle(handlePath);
    double twistValue = twistFromHandle(handlePath);
    
    AwQuaternion qStart, qMid;
    solveIK(startJointPos,
            midJointPos,
            effectorPos,
            handlePos,
            poleVector,
            twistValue,
            qStart,
            qMid);
    return MS::kSuccess;
}
void solveIK(const AwPoint &startJointPos,
             const AwPoint &midJointPos,
             const AwPoint &effectorPos,
             const AwPoint &handlePos,
             const AwVector &poleVector,
             double twistValue,
             AwQuaternion &qStart,
             AwQuaternion &qMid)
{
    
    AwVector vector1 = midJointPos - startJointPos;
    
    AwVector vector2 = effectorPos - midJointPos;
    
    AwVector vectorH = handlePos - startJointPos;
    
    AwVector vectorE = effectorPos - startJointPos;
    
    double length1 = vector1.length();
    double length2 = vector2.length();
    double lengthH = vectorH.length();
    
    AwVector vectorO =
        vector1 - vectorE*((vector1*vectorE)/(vectorE*vectorE));
    
    
    double vectorAngle12 = vector1.angle(vector2);
    
    AwVector vectorCross12 = vector1^vector2;
    double lengthHsquared = lengthH*lengthH;
    
    double cos_theta = 
        (lengthHsquared - length1*length1 - length2*length2)
        /(2*length1*length2);
    if (cos_theta > 1) 
        cos_theta = 1;
    else if (cos_theta < -1) 
        cos_theta = -1;
    double theta = acos(cos_theta);
    
    AwQuaternion q12(theta - vectorAngle12, vectorCross12);
    
    
    
    vector2 = vector2.rotateBy(q12);
    
    vectorE = vector1 + vector2;
    
    AwQuaternion qEH(vectorE, vectorH);
    
    
    vector1 = vector1.rotateBy(qEH);
    if (vector1.isParallel(vectorH))
        
        vector1 = vectorO.rotateBy(qEH);
    
    AwQuaternion qNP;
    if (!poleVector.isParallel(vectorH) && (lengthHsquared != 0)) {
        
        AwVector vectorN = 
            vector1 - vectorH*((vector1*vectorH)/lengthHsquared);
        
        AwVector vectorP = 
            poleVector - vectorH*((poleVector*vectorH)/lengthHsquared);
        double dotNP = (vectorN*vectorP)/(vectorN.length()*vectorP.length());
        if (absoluteValue(dotNP + 1.0) < kEpsilon) {
            
            AwQuaternion qNP1(kPi, vectorH);
            qNP = qNP1;
        }
        else {
            AwQuaternion qNP2(vectorN, vectorP);
            qNP = qNP2;
        }
    }
    
    AwQuaternion qTwist(twistValue, vectorH);
    
    qMid = q12; 
    
    qStart = qEH*qNP*qTwist;
}
AwVector ik2Bsolver::poleVectorFromHandle(
const MDagPath &handlePath)
 
{
    MPlug pvxPlug = handleFn.findPlug(
"pvx", 
true);
 
    MPlug pvyPlug = handleFn.findPlug(
"pvy", 
true);
 
    MPlug pvzPlug = handleFn.findPlug(
"pvz", 
true);
 
    double pvxValue, pvyValue, pvzValue;
    AwVector poleVector(pvxValue, pvyValue, pvzValue);
    return poleVector;
}
double ik2Bsolver::twistFromHandle(
const MDagPath &handlePath)
 
{
    MPlug twistPlug = handleFn.findPlug(
"twist", 
true);
 
    double twistValue;
    return twistValue;
}
class addIK2BsolverCallbacks : 
public MPxCommand {
 
public:                                                         
                    addIK2BsolverCallbacks() {};
    static void*    creator();
    
    
    static MCallbackId afterNewId;
    static MCallbackId afterOpenId;
};
MCallbackId addIK2BsolverCallbacks::afterNewId;
MCallbackId addIK2BsolverCallbacks::afterOpenId;                                                            
void *addIK2BsolverCallbacks::creator()
{
    return new addIK2BsolverCallbacks;
}
void createIK2BsolverAfterNew(void *clientData)
{
}
void createIK2BsolverAfterOpen(void *clientData)
{
    }
}
{
    
    
                               createIK2BsolverAfterNew);
                               createIK2BsolverAfterOpen);
    return MS::kSuccess;
}
{
    MFnPlugin   plugin(obj, PLUGIN_COMPANY, 
"2.5", 
"Any");
 
    status = plugin.registerNode("ik2Bsolver", 
                                 ik2Bsolver::id,
                                 &ik2Bsolver::creator,
                                 &ik2Bsolver::initialize,
    if (!status) {
        status.
perror(
"registerNode");
 
        return status;
    }
    status = plugin.registerCommand("addIK2BsolverCallbacks",
                                    addIK2BsolverCallbacks::creator);
    if (!status) {
        status.
perror(
"registerCommand");
 
        return status;
    }
    
    
    
    
    
    
    
    return status;
}
{
    status = plugin.deregisterNode(ik2Bsolver::id);
    if (!status) {
        status.
perror(
"deregisterNode");
 
        return status;
    }
    status = plugin.deregisterCommand("addIK2BsolverCallbacks");
    if (!status) {
        status.
perror(
"deregisterCommand");
 
        return status;
    }
    
    
    return status;
}