RyanJuckett.com

Analytic Two-Bone IK in 2D - Writing the code Print E-mail
  
Monday, 29 December 2008 07:46
Article Index
Analytic Two-Bone IK in 2D
Defining the problem
The solvable domain
Math review
Describing the problem with math
Solving for angle 2
Solving for angle 1
Writing the code
All Pages

 

Writing the code

We can now write our algorithm in code. The function will calculate a valid IK solution if possible. If there is no valid solution, it will calculate one that gets the end of bone2 as close to the target as possible. Check out the RJ_Demo_IK application for a functional implementation of this code.

 

These code samples are released under the following license.

License
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
/******************************************************************************
  Copyright (c) 2008-2009 Ryan Juckett
  http://www.ryanjuckett.com/
 
  This software is provided 'as-is', without any express or implied
  warranty. In no event will the authors be held liable for any damages
  arising from the use of this software.
 
  Permission is granted to anyone to use this software for any purpose,
  including commercial applications, and to alter it and redistribute it
  freely, subject to the following restrictions:
 
  1. The origin of this software must not be misrepresented; you must not
     claim that you wrote the original software. If you use this software
     in a product, an acknowledgment in the product documentation would be
     appreciated but is not required.
 
  2. Altered source versions must be plainly marked as such, and must not be
     misrepresented as being the original software.
 
  3. This notice may not be removed or altered from any source
     distribution.
******************************************************************************/

 

C# Code
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
///***************************************************************************************
/// CalcIK_2D_TwoBoneAnalytic
/// Given a two bone chain located at the origin (bone1 is the parent of bone2), this
/// function will compute the bone angles needed for the end of the chain to line up
/// with a target position. If there is no valid solution, the angles will be set to
/// get as close to the target as possible.
///  
/// returns: True when a valid solution was found.
///***************************************************************************************
public static bool CalcIK_2D_TwoBoneAnalytic
(
    out double angle1,   // Angle of bone 1
    out double angle2,   // Angle of bone 2
    bool solvePosAngle2, // Solve for positive angle 2 instead of negative angle 2
    double length1,      // Length of bone 1. Assumed to be >= zero
    double length2,      // Length of bone 2. Assumed to be >= zero
    double targetX,      // Target x position for the bones to reach
    double targetY       // Target y position for the bones to reach
)
{
    Debug.Assert(length1 >= 0);
    Debug.Assert(length2 >= 0);
 
    const double epsilon = 0.0001; // used to prevent division by small numbers
 
    bool foundValidSolution = true;
 
    double targetDistSqr = (targetX*targetX + targetY*targetY);
 
    //===
    // Compute a new value for angle2 along with its cosine
    double sinAngle2;
    double cosAngle2;
 
    double cosAngle2_denom = 2*length1*length2;
    if( cosAngle2_denom > epsilon )
    {
        cosAngle2 =   (targetDistSqr - length1*length1 - length2*length2)
                    / (cosAngle2_denom);
 
        // if our result is not in the legal cosine range, we can not find a
        // legal solution for the target
        if( (cosAngle2 < -1.0) || (cosAngle2 > 1.0) )
            foundValidSolution = false;
 
        // clamp our value into range so we can calculate the best
        // solution when there are no valid ones
        cosAngle2 = Math.Max(-1, Math.Min( 1, cosAngle2 ) );
 
        // compute a new value for angle2
        angle2 = Math.Acos( cosAngle2 );
 
        // adjust for the desired bend direction
        if( !solvePosAngle2 )
            angle2 = -angle2;
 
        // compute the sine of our angle
        sinAngle2 = Math.Sin( angle2 );
    }
    else
    {
        // At leaset one of the bones had a zero length. This means our
        // solvable domain is a circle around the origin with a radius
        // equal to the sum of our bone lengths.
        double totalLenSqr = (length1 + length2) * (length1 + length2);
        if(    targetDistSqr < (totalLenSqr-epsilon)
            || targetDistSqr > (totalLenSqr+epsilon) )
        {
            foundValidSolution = false;
        }
 
        // Only the value of angle1 matters at this point. We can just
        // set angle2 to zero. 
        angle2    = 0.0;
        cosAngle2 = 1.0;
        sinAngle2 = 0.0;
    }
 
    //===
    // Compute the value of angle1 based on the sine and cosine of angle2
    double triAdjacent = length1 + length2*cosAngle2;
    double triOpposite = length2*sinAngle2;
 
    double tanY = targetY*triAdjacent - targetX*triOpposite;
    double tanX = targetX*triAdjacent + targetY*triOpposite;
 
    // Note that it is safe to call Atan2(0,0) which will happen if targetX and
    // targetY are zero
    angle1 = Math.Atan2( tanY, tanX );
 
    return foundValidSolution;
}

 



Last Updated ( Sunday, 02 May 2010 06:32 )
 

Creative Commons License
RyanJuckett.com site content by Ryan Juckett is licensed under a Creative Commons Attribution 3.0 United States License.