public static class FABRIKWithConstraints
public static void FABRIK_Pass_2(
float3 P0, float3 P1, float3 P2, float3 E,
float L1, float L2, float L3, float L3_short,
float minAngleP1, float maxAngleP1, // Angle constraints for P1
out float3 P1_out, out float3 P2_out, out float3 E_out)
//-------------------------------
//-------------------------------
// End Effector to P2 direction
var v1 = math.normalize(P2 - E);
// New P2 = End Effector + L3 Length
// Back Extension using L3_short
var P2_Ext = P2 + v1 * L3_short;
// New P2 to old P1 direction
var v2 = math.normalize(P1 - P2);
// PRE-CONSTRAIN PASS New P1 = P2 + L2 Length.
// Building ABC triangle, to find exact position of P1 after constraint
// We use the temp position of P1 and the desired P0 to find the corner C
var C = P1 + math.normalize(P0 - P1) * L3_short;
P1 = new float3(FindMissingScissorPoint(A.xy, B.xy, C.xy), 0);
// Apply angle constraints to P1 in backward pass
P1 = ApplyAngleConstraintBackward(P0, P1, P2, minAngleP1, maxAngleP1);
//-------------------------------
//-------------------------------
v1 = math.normalize(P1 - P0);
var P1B = P0 + v1 * (L1 - L3_short);
v2 = math.normalize(P2 - P1);
var D = new float3(FindMissingScissorPoint(A.xy, B.xy, C.xy), 0);
var v3 = math.normalize(C - D);
var NEXT_JOINT = D + v3 * (L3 + L3_short);
// Apply angle constraints to P1 in forward pass
P1 = ApplyAngleConstraintForward(P0, P1, P2, minAngleP1, maxAngleP1);
private static float2 FindMissingScissorPoint(float2 A, float2 B, float2 C)
// Implement the logic to find the missing point in the scissor mechanism
// This will depend on the specific geometry and constraints of your mechanism
private static float3 ApplyAngleConstraintBackward(float3 P0, float3 P1, float3 P2, float minAngle, float maxAngle)
float3 v1 = math.normalize(P1 - P0);
float3 v2 = math.normalize(P2 - P1);
// Calculate the current angle between the vectors
float dotProduct = math.dot(v1, v2);
float currentAngle = math.degrees(math.acos(dotProduct));
// Constrain the angle within the specified limits
float constrainedAngle = math.clamp(currentAngle, minAngle, maxAngle);
// Calculate the new position for P1 after applying the angle constraint in the backward pass
float angleDifference = constrainedAngle - currentAngle;
float3 axis = math.cross(v1, v2);
quaternion rotation = quaternion.AxisAngle(axis, math.radians(angleDifference));
// Apply the rotation to v1 to get the constrained position of P1
float3 newP1 = P2 - (math.rotate(rotation, v2) * math.distance(P1, P2));
private static float3 ApplyAngleConstraintForward(float3 P0, float3 P1, float3 P2, float minAngle, float maxAngle)
float3 v1 = math.normalize(P1 - P0);
float3 v2 = math.normalize(P2 - P1);
// Calculate the current angle between the vectors
float dotProduct = math.dot(v1, v2);
float currentAngle = math.degrees(math.acos(dotProduct));
// Constrain the angle within the specified limits
float constrainedAngle = math.clamp(currentAngle, minAngle, maxAngle);
// Calculate the new position for P1 after applying the angle constraint in the forward pass
float angleDifference = constrainedAngle - currentAngle;
float3 axis = math.cross(v1, v2);
quaternion rotation = quaternion.AxisAngle(axis, math.radians(angleDifference));
// Apply the rotation to v1 to get the constrained position of P1
float3 newP1 = P0 + (math.rotate(rotation, v1) * math.distance(P0, P1));