I'm building a 2D inverse kinematics system and can't figure out how to find all the joint positions. I have the math to make a typical IK set up with a single bend (e.g. arm, leg), but I get stuck when adding another bend.
How do I find the position of all joints for an inverse kinematic chain that is longer than 2 segments?
Here's an example of the planar solution I'm looking for: https://youtu.be/3sqd_CZNq7s?t=6s
I'm building this in Adobe After Effects so using an existing IK solver isn't going to work.
Here's my visual setup for reference:
What I'm aiming for is that when the blue box is moved (target) joints 2 and 3 bend appropriately to maintain their length. Joint 1 is static and doesn't move.
To make this work, I need x,y coordinates to be calculated in real time. No need to render anything on screen—just the position values for each joint.
Here's my javascript so far:
var angle1, angle2, angle3, theta1, theta2, theta3, targetSqrDist;
// joints
var joint1 = thisComp.layer("joint 1");
var joint2 = thisComp.layer("joint 2");
var joint3 = thisComp.layer("joint 3");
var joint4 = thisComp.layer("joint 4");
// segment lengths
var segment1Len = length(joint2.transform.position, joint1.transform.position);
var segment2Len = length(joint3.transform.position, joint2.transform.position);
var segment3Len = length(joint4.transform.position, joint3.transform.position);
// target position
var target = thisComp.layer("target");
var ix = target.transform.position[0] - joint1.transform.position[0];
var iy = target.transform.position[1] - joint1.transform.position[1];
// target square distance
targetSqrDist = ix * ix + iy * iy;
// Segment 1
angle1 = Math.max(-1, Math.min( 1,
(targetSqrDist + (segment1Len*segment1Len) - (segment2Len*segment2Len)) / (2 * segment1Len * Math.sqrt(targetSqrDist))
));
theta1 = Math.atan2(iy, ix) - Math.acos(angle1);
var s1X = joint1.transform.position[0] + segment1Len * Math.cos(theta1);
var s1Y = joint1.transform.position[1] + segment1Len * Math.sin(theta1);
// Segment 2
angle2 = Math.max(-1, Math.min( 1,
(targetSqrDist - (segment1Len*segment1Len) - (segment2Len*segment2Len)) / (2 * segment1Len * segment2Len)
));
theta2 = Math.acos(angle2);
var s2X = s1X + segment2Len * Math.cos(theta2 + theta1);
var s2Y = s1Y + segment2Len * Math.sin(theta2 + theta1);
// Segment 3
angle3 = Math.max(-1, Math.min( 1,
(targetSqrDist - (segment2Len*segment2Len) - (segment3Len*segment3Len)) / (2 * segment2Len * segment3Len)
));
theta3 = Math.acos(angle3);
var s3X = s2X + segment3Len * Math.cos(theta3 + theta2);
var s3Y = s2Y + segment3Len * Math.sin(theta3 + theta2);
This code would be applied to each joint with a unique position value assigned to it, depending on the joint's location.