Published using Google Docs
naMath.txt
Updated automatically every 5 minutes

//v1.0.0

//Author: Nathaniel Anozie

//ogbonnawork at gmail dot com

//

////description: common math functions

//supports rounding

//

//How to Install: Source:

//  naGeneral.mel   --querying types ex, getting curve type, joints on scene

//  naMath.mel

//

//date created: January 10, 2012

//date last revised: March 21, 2012

//

//Acknowledgements: Duncan Brinsmead for rounding a float to decimal places

//                  Bryan Ewert, xyz2 dot net for rounding a float to decimal places, querying number cvs

//Modify at your own risk

/**return integer number of cvs

@param $userCurve -- curve argument

@result int list where first element is number of cvs

*/

global proc int[]

na_getNumberCurveCV(string $userCurve)

{

    int $result[] = {};

    //check this is a curve    

    int $isCurve = 0;

    $isCurve = na_isCurve($userCurve);

   

    if($isCurve == 1){

        int $degree[]={};

        int $span[]={};

       

        $degree[0] = `getAttr ($userCurve+".degree")`;

        $span[0] = `getAttr ($userCurve+".spans")`;

        //number cvs equals number spans plus degree of curve

        $numcv = $span[0] + $degree[0];

       

        $result[size($result)] = $numcv;

        }

        else{ print("cannot perform operation on this node type\n"); }

        

        return $result;

}

/**

@pre expect clean maya scene

@pre naMath.mel

@pre naSegment.mel

@pre naGeneral.mel

*/

global proc float[]

na_getLength_userTest()

{

    joint -p 0 0 0 ;

    joint -p 7 0 0 ;

    joint -e -zso -oj xyz -sao yup "joint1";

   

    return na_getLength("joint1","joint2");

}

/**return shortest distance between two nodes

*/

global proc float[]

na_getLength(string $startNode, string $endNode)

{

    float $result[];

   

    if(  (`objExists $startNode`) && ( `objExists $endNode` ) )

    {

       

        //make things to help give us length

        //here were going to use the length of curve drawn between end points

        float $startPos[] = {};

        float $endPos[] = {};

        string $tempName = "naTempCurve_na_getLength";

        string $tempNameNode = "naTempNode_na_getLength";

       

        $startPos = getComponentWorldPosition({$startNode});

        $endPos = getComponentWorldPosition({$endNode});

       

        na_drawCurveBySegment(1,$tempName, $startPos, $endPos);

        //note change to drawCurve through all points between these to figure

        //out length for a curving joint hiearchy

       

       

        na_makeCurveInfoNode({$tempName}, {$tempNameNode});

       

       

        if( `objExists $tempNameNode` )

        {  

            $result[size($result)] = `getAttr ($tempNameNode+".arcLength")`;

        }  

       

       

        //clean up created helper things

        if( `objExists $tempName` ){ delete $tempName; }

        if( `objExists $tempNameNode` ){ delete $tempNameNode; }  

    }

   

    return $result;

}

/**excepts float and number of decimal places to round, returns float rounded appropriately

@param float $arg -- number to round

@param int $numberOfPlaces -- number of places to round

@note default returns 0.0

*/

global proc float roundToNearestDecimal(float $arg, int $numberOfPlaces)

{

    //idea adding 0.5 to 0.5 then flooring makes it 1, adding 0.5 to 0.4 makes it 0.9 flooring makes it 0

    //multiply a decimal by 10 raised to number of places puts number in position to add or subtract 0.5

    float $rounded = 0.0;

    if($numberOfPlaces >= 0 ){

       

        int $multiplyFactor = pow(10, $numberOfPlaces);

        if($arg > 0){

            $rounded = trunc( $arg*$multiplyFactor + 0.5 ) / $multiplyFactor ; // since positive adding 0.5 then truncating

        }

        else{

            $rounded = trunc( $arg*$multiplyFactor - 0.5  ) /$multiplyFactor ; //since negative subtracting 0.5 then truncating

        }

    }

    return $rounded;

}

/**return 1 if the integer value is within range specified [min,max)

//ex: indexInRange(0,5),  0 is allowed,  4 is allowed, but 5 is not

@param int $value -- number

@param int $min -- low inclusive

@param int $max -- upper not inclusive

*/

global proc int

indexInRange(int $value, int $min, int $max)

{

    int $result = 0;

    if( ($value >= 0) && ($value < $max ) ){

        $result = 1;

    }

    return $result;

}

/**give me the world positions for these components

@param string list $component  components

@note supports: vertices, joints, edges, faces

@note returned world positions  {x y z, x y z, ... x y z} a 1-D array to get a vtx use [i*3+0]..[i*3+2]

*/

global proc float[]

getComponentWorldPosition( string $component[] )

{

    float $result[];

   

    //loop over component

    for($i=0; $i<size($component); $i++){

       

        if(  `objExists $component[$i]` == 0 ){error("cannot find: "+$component[$i]);}

        float $selPos[] = `xform -q -t -ws $component[$i]`;

        $result[size($result)] = $selPos[0];

        $result[size($result)] = $selPos[1];

        $result[size($result)] = $selPos[2];

       

    }

   

    return $result;

}

/**get difference in position of two joints

@param string $jointStart

@param string $jointEnd

@result 3 element float array

*/

global proc float[]

na_getJointWorldPositionDiff(string $jointStart, string $jointEnd)

{

    float $result[] = {};

    string $start = "";

    string $end = "";

    int $directionFound = 0; //when we find a direction don't need to check others

       

   

    //verify they are joints

    int $isStartJoint = na_isType($jointStart,"joint") ;

    int $isEndJoint = na_isType($jointEnd,"joint") ;

   

    if( $isStartJoint == 0  || $isEndJoint == 0 )

    {error("expecting joints in direction calculation");}

   

    $start = $jointStart;

    $end = $jointEnd;

   

    //we have world positions of start and end

    float $pos[] = getComponentWorldPosition({$start,$end});

    float $start_tx = $pos[0*3+0];

    float $start_ty = $pos[0*3+1];

    float $start_tz = $pos[0*3+2];

    float $end_tx = $pos[1*3+0];

    float $end_ty = $pos[1*3+1];

    float $end_tz = $pos[1*3+2];

    //then we get difference

    float $tx = $end_tx - $start_tx;

    float $ty = $end_ty - $start_ty;

    float $tz = $end_tz - $start_tz;

   

    $result[size($result)] = $tx;

    $result[size($result)] = $ty;

    $result[size($result)] = $tz;

   

    return $result;

}

/**expects empty scene

*/

global proc

na_getStraightJointDirectionOfPair_unitTest_1()

{

    select -cl;

    joint -p 0 0 0 ;

    joint -p 7 0 0 ;

   

    string $result1[] = na_getStraightJointDirectionOfPair("joint1","joint2");

   

   

    select -cl;

   

    joint -p 0 0 0 ;

    joint -p 0 4 0 ;

   

    string $result2[] = na_getStraightJointDirectionOfPair("joint3","joint4");

   

   

    print $result1;

    print "\n";

    print $result2;

}

global proc

na_getStraightJointDirectionOfPair_unitTest_2()

{

    joint -p 0 -2 0 ;

    joint -p 0 4 0 ;

    joint -e -zso -oj xyz -sao yup joint1;

    string $result1[] = na_getStraightJointDirectionOfPair("joint1","joint2");

   

    print $result1;

}

global proc

na_getStraightJointSignOfPair_unitTest_1()

{

    select -cl;

    joint -p 0 0 0 ;

    joint -p 7 0 0 ;

   

    int $result1[] = na_getStraightJointSignOfPair("joint1","joint2");

   

   

    select -cl;

   

    joint -p 0 0 0 ;

    joint -p -7 0 0 ;

   

    int $result2[] = na_getStraightJointSignOfPair("joint3","joint4");

   

   

    print $result1;

    print "\n";

    print $result2;

}

/**get direction for a straight joint in world space returns empty if joints not straight

@param string $jointStart -- start joint

@param string $jointEnd -- end joint

@result direction going from one joint to the next joint ex: "X" or "Y" or "Z"

@note pair joints don't need to be in same hierarchy

@note returns empty result if input not on same plane that is if joint end has more than one nonzero translate axis

@pre there needs to be some sort of length of chain to find direction

*/

global proc string[]

na_getStraightJointDirectionOfPair(string $jointStart, string $jointEnd)

{

    string $result[] = {};

    int $directionFound = 0; //when we find a direction don't need to check others

    float $diff[] = {};

    $diff = na_getJointWorldPositionDiff($jointStart,$jointEnd);

    if( size($diff) != 3 ){ error("skipping -- cannot compute direction"); }

   

    //then we get difference

    float $tx = $diff[0];

    float $ty = $diff[1];

    float $tz = $diff[2];

   

    //because channel editor showed zero there was still some numeric value really small

    //anything below this i assume as zero

    float $constZero = (1.0 * pow(10,-14) );

   

    //non zero translate z but all others zero says

    //direction is z

    if($directionFound == 0){

        if(  ((abs($ty) + abs($tx)) < $constZero) && (abs($tz) > 0) )

        {

            $result[size($result)] = "Z";

            $directionFound = 1;

        }

    }

   

    //non zero translate y but all others zero says

    //direction is y

    if($directionFound == 0){

        if(  ((abs($tx) + abs($tz)) < $constZero) && (abs($ty) > 0) )

        {

            $result[size($result)] = "Y";

            $directionFound = 1;

        }

    }

   

    //

    if($directionFound == 0){

        if(  ((abs($ty) + abs($tz)) < $constZero) && (abs($tx) > 0) )

        {

            $result[size($result)] = "X";

            $directionFound = 1;

        }

    }

    return $result;

}

/**what sign is straight joint chain in world space

@param start joint

@param end joint

*/

global proc int[]

na_getStraightJointSignOfPair(string $jointStart, string $jointEnd)

{

    int $result[] = {};

    string $direction[] = {};

    float $val[] = {};

    $direction = na_getStraightJointDirectionOfPair($jointStart, $jointEnd);

   

    if(size($direction) == 1 ){

       

        //weve got the direction the chain is in world

        //now we use it to find the sign of the direction

        //ex: is it positive or negative.

        float $diff[] = {};

        $diff = na_getJointWorldPositionDiff($jointStart,$jointEnd);

        if( size($diff) != 3 ){ error("skipping -- cannot compute sign direction down chain"); }

        int $index[] = {};

        if( `strcmp $direction[0] "X"` == 0 ){ $index[0] = 0;}

        else if( `strcmp $direction[0] "Y"` == 0 ){ $index[0] = 1;}

        else if( `strcmp $direction[0] "Z"` == 0 ){ $index[0] = 2;}

        else{error("error computing sing -- unknown direction input");}  

        $val[size($val)] = $diff[$index[0]];//index 0 for X, 1 for Y, 2 for Z

       

        if($val[0] > 0)

            { $result[size($result)] = -1; }

        else if($val[0] < 0)

            { $result[size($result)] = 1; }

        else{ print("please verify chain has a positive length\n"); }

    }

    else{ print("skipping currently needs straightness of chain to figure out direction of chain \n") ;}

   

    return $result;

}

/**get me the nearest joints to specified world positions

@param float array     world positions  {x y z, x y z, ... x y z} a 1-D array to get a vertex use [i*3+0]..[i*3+2]

@param float           how close do we need to be

*/

global proc string[]

getClosestJointToWorldPositionArray(float $pointArray[], float $epsilon)

{

    string $result[] = {};  

   

    if( isDivisibleByThis(size($pointArray),3) == 0){

        error("check for 3 worldpositions per component");

    }

   

    //this could be extended to getting nearest locators, clusters by

    //moving this to be a parameter

    string $argJoint[] = getListAllJoints();

    $result = getClosestToComponentArray($argJoint,$pointArray,$epsilon);

         

    return $result;

}

/**get me the vertices that are closest to the worldpositions i give you in the mesh i give you

it really is expecting this to be done on meshes where the positions we give are exactly at a vertex

@param string          poly

@param float array     world positions  {x y z, x y z, ... x y z} a 1-D array to get a vertex use [i*3+0]..[i*3+2]

*/

//assumes nearestPointOnMesh plugin loaded

global proc string[]

getClosestVertexToWorldPosition(string $polyName, float $wolrdPos[], float $epsilon )

{

    string $result[];

    print(`date -time`+"\n");

    string $sel[] = `ls -sl`;  

   

    if( isDivisibleByThis(size($wolrdPos),3) == 0){

        error("check for 3 worldpositions per vertex");

    }

    int $numberVertices = (size($wolrdPos)/3);

   

    if(  `objExists $polyName`  ){

       

        string $nearestFace[];  //nearest face to each vtx

       

        //get shape for mesh that has these verts

        string $shapes[] = `listRelatives - shapes $polyName`;

        string $shape = $shapes[0];

       

       

        //this will be able to tell given a point what is nearest face to it

        string $near = "nearNode";

        addNearNodeToShape($shape,$near);

   

        //loop over verts

        for($i=0; $i<$numberVertices; $i++){

           

            float $selPos[] = { ($wolrdPos[$i*3+0]), ($wolrdPos[$i*3+1]), ($wolrdPos[$i*3+2])  };

           

            //get position for each slected vtx

            float $vPosX = $selPos[0];

            float $vPosY = $selPos[1];

            float $vPosZ = $selPos[2];

           

            //we give it the point

            tellNodeWhereToLook($near, {$vPosX,$vPosY,$vPosZ});

           

            //we get the face  

            $nearestFace[$i] = getClosestFace( $polyName, $near  );

           

            //we get the vertex

            string $closeVtx[] = getClosestVertToPosOfFace($nearestFace[$i],{$vPosX,$vPosY,$vPosZ});

            $result[$i] = $closeVtx[0];

           

        }

        //end loop

       

       

        //delete created nodes

        delete($near);

    }

    else{ print("skipping -- poly not found\n"); }

    print(`date -time`+"\n");

   

    select -r $sel;

   

    return $result;

}

/**give near node point were interested in

@param    string          near node name

@param float array     world position  {x y z}

@note kindof lowlevel cause it needs a node to operate

*/

global proc tellNodeWhereToLook(string $nearNodeName, float $pos[])

{

    if(  `objExists $nearNodeName` ){

        setAttr ($nearNodeName+".inPositionX") ($pos[0]);

        setAttr ($nearNodeName+".inPositionY") ($pos[1]);

        setAttr ($nearNodeName+".inPositionZ") ($pos[2]);

    }

    else{

        error("cannot find node: "+$nearNodeName);

    }  

}

/**this will be able to tell given a point what is nearest face to it

@param string shape

@param string name for near node

*/

global proc

addNearNodeToShape(string $shape, string $nearNodeName)

{

    //no checks for if shape exists

   

    //don't make if node exists

    if(  `objExists $nearNodeName` == 0  ){

        createNode nearestPointOnMesh -n $nearNodeName;

        connectAttr -f ($shape+".outMesh") ($nearNodeName+".inMesh");

    }

    else{

        print("skipping, node exists\n");

    }

}

/**given face and a world position get the closest vertex of face to that position

@param string name of face ex: "pCube1.f[2]"

@param float 3 element array ex: {0.5,0.5,0.5}

@note I think this is more useful on onetime operations than something like attaching nulls to world position of geometry

*/

global proc string[]

getClosestVertToPosOfFace(string $_face, float $wpos[] )

{

    string $sel[] = `ls -sl`;

   

    string $result[];  

    string $face = $_face;

   

   

    string $_vertsOfFace[] = `polyListComponentConversion -ff -tv $face`;//has [2:3] possibly

   

    //replace with filter expand

    select -cl;

    select -r $_vertsOfFace;

    string $vertsOfFace[] = `filterExpand -sm 31 -expand true`;

   

    if(size($wpos)==3){

        string $minVertex = $vertsOfFace[0];

        float $minWorldPos[] = `xform -q -t  -ws $minVertex`;

        float $minValue[] = euclidDistance( $minWorldPos, $wpos );

       

        //loop verts of face

        int $i=0;

        string $_minVertex="";

        float $_minWorldPos[];

        float $_minValue[];

       

        for($i=0; $i<size($vertsOfFace); $i++)

        {

            $_minVertex = $vertsOfFace[$i];

            $_minWorldPos = `xform -q -t  -ws $_minVertex`;

            $_minValue = euclidDistance( $_minWorldPos, $wpos );    

           

            //if found a closer point to input position save it

            if( $_minValue[0] < $minValue[0] )

            {

                $minVertex = $_minVertex;

                $minWorldPos = $_minWorldPos;

                $minValue[0] = $_minValue[0];

            }

            //end if

            clear($_minWorldPos);

            clear($_minValue);

            $_minVertex="";

        }

        //end loop

        $result[0] = $minVertex;  

    }

   

   

    select -r $sel;

   

    return $result;

}

/**give me the nearest face

@param    string poly

@param   string  name for near node

*/

global proc string

getClosestFace( string $polyName, string $nearNodeName  )

{

    string $result;

   

    //does poly exist

    if(  `objExists $polyName` == 0 ){

        error("cannot find poly: "+$polyName);

    }

   

    //get face using node if node is there

    if(  `objExists $nearNodeName` ){

    string $nearestFaceId = `getAttr $nearNodeName (".nearestFaceIndex")`;

    $result = $polyName+".f"+"["+$nearestFaceId+"]";

    }

    else{

        error("cannot find node: "+$nearNodeName);

    }

    return $result;

}

/**get me the nearest component to specified world position

@param string array       components we will search in for nearest point

@param float array        1-d array of 3d points to find closest component to

@param float           how close do we need to be

*/

global proc string[]

getClosestToComponentArray(string $argComponent[], float $pointArray[], float $epsilon)

{

    string $result[] = {};  

   

    if( isDivisibleByThis(size($pointArray),3) == 0){

        error("check for 3 worldpositions per component");

    }

    int $numberMatchingComponentsWeNeed = (size($pointArray)/3);

   

    //loop points

    string $nearestComponent[]={};//temporary hold nearest component

    for($j=0; $j < $numberMatchingComponentsWeNeed; $j++){

        //first get a single point x y z

        float $point[] ={};

        $point[size($point)] = $pointArray[$j*3+0];

        $point[size($point)] = $pointArray[$j*3+1];

        $point[size($point)] = $pointArray[$j*3+2];

       

        //saving nearest component if possible

        clear($nearestComponent);

        $nearestComponent = getClosestComponentToWorldPosition($argComponent, $point, $epsilon);

        if(size($nearestComponent)>0)

        {

            $result[size($result)] = $nearestComponent[0];

        }

        else

            { error("could not find a match for point (xyz)"+$point[0]+","+$point[1]+","+$point[2]+"\n");}

    }

   

    return $result;

}

/**get me the nearest joint to specified world position

@param float array     the point we should compare every candidate too        

@param float           how close do we need to be

@note note it returns a single element as soon as it finds a joint close enough  to the threshold

*/

global proc string[]

getClosestComponentToWorldPosition(string $argComponent[], float $point[], float $epsilon)

{

    string $result[] = {}

    ;  

    if( isDivisibleByThis(size($point),3) == 0){

        error("check for 3 worldpositions per component");

    }

   

    //loop candidate components comparing to this point

    for($i=0; $i < size($argComponent); $i++){

       

        //which of all components is closest to this point

        string $component = $argComponent[$i];

        float $posComponent[] = `xform -q -t  -ws $component`;

        float $distanceArray[] = euclidDistance( $point, $posComponent );

        if(size($distanceArray) > 0){

            $distance = $distanceArray[0];

           

            //exit when found a nearest component to point

            if( $distance < $epsilon ){

                //print("found nearest component: "+$component+"\n");

                $result[size($result)] = $component;

                break;//exit loop

            }

        }

    }

    return $result;

}

/**get 1 if argument is divisibile by second integer

@param    int     size of argument array

@param   int     what we need it to be divisible by

@note ex: isDivisibleByThis( 5,2 ) get 0, but isDivisibleByThis( 6,2 ) get 1

*/

global proc int

isDivisibleByThis( int $arg, int $divisibleBy )

{

    int $result = 0;

   

    //checking divisibility by 3 for world position

    //a way of doing that check if ceil and floor of result are equal

    float $numberArg = ($arg/$divisibleBy );

    if( ceil($numberArg) == floor($numberArg) ){

        $result = 1;

    }

   

    return $result;

}

/**given two 3-D vectors return euclidean distance

@param float $a[] -- first vector of 3 elements

@param float $b[] -- second vector of 3 elements

@note this is used to find closeness on a mesh

suppose you wanted to know of the vertices of a face

which one is closest this other point, user can

use this to find out

*/

global proc float[]

euclidDistance(float $a[], float $b[] )

{

    float $result[];

   

    if( (size($a)==3) && (size($b)==3) )

    {

        $result[0] = sqrt( pow(($a[0]-$b[0]),2)+

        pow(($a[1]-$b[1]),2)+

        pow(($a[2]-$b[2]),2)

        );

    }

   

    return $result;

}