using System.Collections.Generic;
using UnityEngine.Profiling;
using UnityEngine.Scripting.APIUpdating;
namespace UnityEngine.U2D.IK
{
///
/// Component for 2D Forward And Backward Reaching Inverse Kinematics (FABRIK) IK.
///
[MovedFrom("UnityEngine.Experimental.U2D.IK")]
[Solver2DMenu("Chain (FABRIK)")]
public class FabrikSolver2D : Solver2D
{
private const float kMinTolerance = 0.001f;
private const int kMinIterations = 1;
[SerializeField]
private IKChain2D m_Chain = new IKChain2D();
[SerializeField][Range(kMinIterations, 50)]
private int m_Iterations = 10;
[SerializeField][Range(kMinTolerance, 0.1f)]
private float m_Tolerance = 0.01f;
private float[] m_Lengths;
private Vector2[] m_Positions;
private Vector3[] m_WorldPositions;
///
/// Get and Set the solver's itegration count.
///
public int iterations
{
get { return m_Iterations; }
set { m_Iterations = Mathf.Max(value, kMinIterations); }
}
///
/// Get and Set target distance tolerance.
///
public float tolerance
{
get { return m_Tolerance; }
set { m_Tolerance = Mathf.Max(value, kMinTolerance); }
}
///
/// Returns the number of chain in the solver.
///
/// This always returns 1.
protected override int GetChainCount()
{
return 1;
}
///
/// Gets the chain in the solver by index.
///
/// Chain index.
/// Returns IKChain2D at the index.
public override IKChain2D GetChain(int index)
{
return m_Chain;
}
///
/// DoPrepare override from base class.
///
protected override void DoPrepare()
{
if (m_Positions == null || m_Positions.Length != m_Chain.transformCount)
{
m_Positions = new Vector2[m_Chain.transformCount];
m_Lengths = new float[m_Chain.transformCount - 1];
m_WorldPositions = new Vector3[m_Chain.transformCount];
}
for (int i = 0; i < m_Chain.transformCount; ++i)
{
m_Positions[i] = GetPointOnSolverPlane(m_Chain.transforms[i].position);
}
for (int i = 0; i < m_Chain.transformCount - 1; ++i)
{
m_Lengths[i] = (m_Positions[i + 1] - m_Positions[i]).magnitude;
}
}
///
/// DoUpdateIK override from base class.
///
/// Target position for the chain.
protected override void DoUpdateIK(List effectorPositions)
{
Profiler.BeginSample("FABRIKSolver2D.DoUpdateIK");
Vector3 effectorPosition = effectorPositions[0];
effectorPosition = GetPointOnSolverPlane(effectorPosition);
if (FABRIK2D.Solve(effectorPosition, iterations, tolerance, m_Lengths, ref m_Positions))
{
// Convert all plane positions to world positions
for (int i = 0; i < m_Positions.Length; ++i)
{
m_WorldPositions[i] = GetWorldPositionFromSolverPlanePoint(m_Positions[i]);
}
for (int i = 0; i < m_Chain.transformCount - 1; ++i)
{
Vector3 startLocalPosition = m_Chain.transforms[i + 1].localPosition;
Vector3 endLocalPosition = m_Chain.transforms[i].InverseTransformPoint(m_WorldPositions[i + 1]);
m_Chain.transforms[i].localRotation *= Quaternion.FromToRotation(startLocalPosition, endLocalPosition);
}
}
Profiler.EndSample();
}
}
}