|
Written by Ryan Juckett
|
|
Monday, 29 December 2008 07:46 |
|
Page 8 of 8
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 )
|