コード例 #1
0
ファイル: IKSolver.cs プロジェクト: he110world/WoolWar
        ///***************************************************************************************
        /// CalcIK_2D_CCD
        /// Given a bone chain located at the origin, this function will perform a single cyclic
        /// coordinate descent (CCD) iteration. This finds a solution of bone angles that places
        /// the final bone in the given chain at a target position. The supplied bone angles are
        /// used to prime the CCD iteration. If a valid solution does not exist, the angles will
        /// move as close to the target as possible. The user should resupply the updated angles
        /// until a valid solution is found (or until an iteration limit is met).
        ///
        /// returns: CCDResult.Success when a valid solution was found.
        ///          CCDResult.Processing when still searching for a valid solution.
        ///          CCDResult.Failure when it can get no closer to the target.
        ///***************************************************************************************
        public static CCDResult CalcIK_2D_CCD
        (
            ref List <Bone_2D_CCD> bones, // Bone values to update
            double targetX,               // Target x position for the end effector
            double targetY,               // Target y position for the end effector
            double arrivalDist            // Must get within this range of the target
        )
        {
            // Set an epsilon value to prevent division by small numbers.
            const double epsilon = 0.0001;

            // Set max arc length a bone can move the end effector an be considered no motion
            // so that we can detect a failure state.
            const double trivialArcLength = 0.00001;


            int numBones = bones.Count;
            //Debug.Assert (numBones > 0);

            double arrivalDistSqr = arrivalDist * arrivalDist;

            //===
            // Generate the world space bone data.
            List <Bone_2D_CCD_World> worldBones = new List <Bone_2D_CCD_World> ();

            // Start with the root bone.
            Bone_2D_CCD_World rootWorldBone = new Bone_2D_CCD_World();

            rootWorldBone.x        = bones [0].x;
            rootWorldBone.y        = bones [0].y;
            rootWorldBone.angle    = bones [0].angle;
            rootWorldBone.cosAngle = Math.Cos(rootWorldBone.angle);
            rootWorldBone.sinAngle = Math.Sin(rootWorldBone.angle);
            worldBones.Add(rootWorldBone);

            // Convert child bones to world space.
            for (int boneIdx = 1; boneIdx < numBones; ++boneIdx)
            {
                Bone_2D_CCD_World prevWorldBone = worldBones [boneIdx - 1];
                Bone_2D_CCD       curLocalBone  = bones [boneIdx];

                Bone_2D_CCD_World newWorldBone = new Bone_2D_CCD_World();
                newWorldBone.x = prevWorldBone.x + prevWorldBone.cosAngle * curLocalBone.x
                                 - prevWorldBone.sinAngle * curLocalBone.y;
                newWorldBone.y = prevWorldBone.y + prevWorldBone.sinAngle * curLocalBone.x
                                 + prevWorldBone.cosAngle * curLocalBone.y;
                newWorldBone.angle    = prevWorldBone.angle + curLocalBone.angle;
                newWorldBone.cosAngle = Math.Cos(newWorldBone.angle);
                newWorldBone.sinAngle = Math.Sin(newWorldBone.angle);
                worldBones.Add(newWorldBone);
            }

            //===
            // Track the end effector position (the final bone)
            double endX = worldBones [numBones - 1].x;
            double endY = worldBones [numBones - 1].y;

            //===
            // Perform CCD on the bones by optimizing each bone in a loop
            // from the final bone to the root bone
            bool modifiedBones = false;

            for (int boneIdx = numBones - 2; boneIdx >= 0; --boneIdx)
            {
                // Get the vector from the current bone to the end effector position.
                double curToEndX   = endX - worldBones [boneIdx].x;
                double curToEndY   = endY - worldBones [boneIdx].y;
                double curToEndMag = Math.Sqrt(curToEndX * curToEndX + curToEndY * curToEndY);

                // Get the vector from the current bone to the target position.
                double curToTargetX   = targetX - worldBones [boneIdx].x;
                double curToTargetY   = targetY - worldBones [boneIdx].y;
                double curToTargetMag = Math.Sqrt(curToTargetX * curToTargetX
                                                  + curToTargetY * curToTargetY);

                // Get rotation to place the end effector on the line from the current
                // joint position to the target postion.
                double cosRotAng;
                double sinRotAng;
                double endTargetMag = (curToEndMag * curToTargetMag);
                if (endTargetMag <= epsilon)
                {
                    cosRotAng = 1;
                    sinRotAng = 0;
                }
                else
                {
                    cosRotAng = (curToEndX * curToTargetX + curToEndY * curToTargetY) / endTargetMag;
                    sinRotAng = (curToEndX * curToTargetY - curToEndY * curToTargetX) / endTargetMag;
                }

                // Clamp the cosine into range when computing the angle (might be out of range
                // due to floating point error).
                double rotAng = Math.Acos(Math.Max(-1, Math.Min(1, cosRotAng)));
                if (sinRotAng < 0.0)
                {
                    rotAng = -rotAng;
                }

                // Rotate the end effector position.
                endX = worldBones [boneIdx].x + cosRotAng * curToEndX - sinRotAng * curToEndY;
                endY = worldBones [boneIdx].y + sinRotAng * curToEndX + cosRotAng * curToEndY;

                // Rotate the current bone in local space (this value is output to the user)
                bones [boneIdx].angle = SimplifyAngle(bones [boneIdx].angle + rotAng);

                // Check for termination
                double endToTargetX = (targetX - endX);
                double endToTargetY = (targetY - endY);
                if (endToTargetX * endToTargetX + endToTargetY * endToTargetY <= arrivalDistSqr)
                {
                    // We found a valid solution.
                    return(CCDResult.Success);
                }

                // Track if the arc length that we moved the end effector was
                // a nontrivial distance.
                if (!modifiedBones && Math.Abs(rotAng) * curToEndMag > trivialArcLength)
                {
                    modifiedBones = true;
                }
            }

            // We failed to find a valid solution during this iteration.
            if (modifiedBones)
            {
                return(CCDResult.Processing);
            }
            else
            {
                return(CCDResult.Failure);
            }
        }
コード例 #2
0
ファイル: IKSolver.cs プロジェクト: he110world/WoolWar
        ///***************************************************************************************
        /// CalcIK_2D_CCD
        /// Given a bone chain located at the origin, this function will perform a single cyclic
        /// coordinate descent (CCD) iteration. This finds a solution of bone angles that places
        /// the final bone in the given chain at a target position. The supplied bone angles are
        /// used to prime the CCD iteration. If a valid solution does not exist, the angles will
        /// move as close to the target as possible. The user should resupply the updated angles 
        /// until a valid solution is found (or until an iteration limit is met).
        ///  
        /// returns: CCDResult.Success when a valid solution was found.
        ///          CCDResult.Processing when still searching for a valid solution.
        ///          CCDResult.Failure when it can get no closer to the target.
        ///***************************************************************************************
        public static CCDResult CalcIK_2D_CCD(
			ref List<Bone_2D_CCD> bones, // Bone values to update
			double targetX, // Target x position for the end effector
			double targetY, // Target y position for the end effector
			double arrivalDist           // Must get within this range of the target
		)
        {
            // Set an epsilon value to prevent division by small numbers.
            const double epsilon = 0.0001;

            // Set max arc length a bone can move the end effector an be considered no motion
            // so that we can detect a failure state.
            const double trivialArcLength = 0.00001;

            int numBones = bones.Count;
            //Debug.Assert (numBones > 0);

            double arrivalDistSqr = arrivalDist * arrivalDist;

            //===
            // Generate the world space bone data.
            List<Bone_2D_CCD_World> worldBones = new List<Bone_2D_CCD_World> ();

            // Start with the root bone.
            Bone_2D_CCD_World rootWorldBone = new Bone_2D_CCD_World ();
            rootWorldBone.x = bones [0].x;
            rootWorldBone.y = bones [0].y;
            rootWorldBone.angle = bones [0].angle;
            rootWorldBone.cosAngle = Math.Cos (rootWorldBone.angle);
            rootWorldBone.sinAngle = Math.Sin (rootWorldBone.angle);
            worldBones.Add (rootWorldBone);

            // Convert child bones to world space.
            for (int boneIdx = 1; boneIdx < numBones; ++boneIdx) {
                Bone_2D_CCD_World prevWorldBone = worldBones [boneIdx - 1];
                Bone_2D_CCD curLocalBone = bones [boneIdx];

                Bone_2D_CCD_World newWorldBone = new Bone_2D_CCD_World ();
                newWorldBone.x = prevWorldBone.x + prevWorldBone.cosAngle * curLocalBone.x
                                             - prevWorldBone.sinAngle * curLocalBone.y;
                newWorldBone.y = prevWorldBone.y + prevWorldBone.sinAngle * curLocalBone.x
                                             + prevWorldBone.cosAngle * curLocalBone.y;
                newWorldBone.angle = prevWorldBone.angle + curLocalBone.angle;
                newWorldBone.cosAngle = Math.Cos (newWorldBone.angle);
                newWorldBone.sinAngle = Math.Sin (newWorldBone.angle);
                worldBones.Add (newWorldBone);
            }

            //===
            // Track the end effector position (the final bone)
            double endX = worldBones [numBones - 1].x;
            double endY = worldBones [numBones - 1].y;

            //===
            // Perform CCD on the bones by optimizing each bone in a loop
            // from the final bone to the root bone
            bool modifiedBones = false;
            for (int boneIdx = numBones-2; boneIdx >= 0; --boneIdx) {
                // Get the vector from the current bone to the end effector position.
                double curToEndX = endX - worldBones [boneIdx].x;
                double curToEndY = endY - worldBones [boneIdx].y;
                double curToEndMag = Math.Sqrt (curToEndX * curToEndX + curToEndY * curToEndY);

                // Get the vector from the current bone to the target position.
                double curToTargetX = targetX - worldBones [boneIdx].x;
                double curToTargetY = targetY - worldBones [boneIdx].y;
                double curToTargetMag = Math.Sqrt (curToTargetX * curToTargetX
                                               + curToTargetY * curToTargetY);

                // Get rotation to place the end effector on the line from the current
                // joint position to the target postion.
                double cosRotAng;
                double sinRotAng;
                double endTargetMag = (curToEndMag * curToTargetMag);
                if (endTargetMag <= epsilon) {
                    cosRotAng = 1;
                    sinRotAng = 0;
                } else {
                    cosRotAng = (curToEndX * curToTargetX + curToEndY * curToTargetY) / endTargetMag;
                    sinRotAng = (curToEndX * curToTargetY - curToEndY * curToTargetX) / endTargetMag;
                }

                // Clamp the cosine into range when computing the angle (might be out of range
                // due to floating point error).
                double rotAng = Math.Acos (Math.Max (-1, Math.Min (1, cosRotAng)));
                if (sinRotAng < 0.0)
                    rotAng = -rotAng;

                // Rotate the end effector position.
                endX = worldBones [boneIdx].x + cosRotAng * curToEndX - sinRotAng * curToEndY;
                endY = worldBones [boneIdx].y + sinRotAng * curToEndX + cosRotAng * curToEndY;

                // Rotate the current bone in local space (this value is output to the user)
                bones [boneIdx].angle = SimplifyAngle (bones [boneIdx].angle + rotAng);

                // Check for termination
                double endToTargetX = (targetX - endX);
                double endToTargetY = (targetY - endY);
                if (endToTargetX * endToTargetX + endToTargetY * endToTargetY <= arrivalDistSqr) {
                    // We found a valid solution.
                    return CCDResult.Success;
                }

                // Track if the arc length that we moved the end effector was
                // a nontrivial distance.
                if (!modifiedBones && Math.Abs (rotAng) * curToEndMag > trivialArcLength) {
                    modifiedBones = true;
                }
            }

            // We failed to find a valid solution during this iteration.
            if (modifiedBones)
                return CCDResult.Processing;
            else
                return CCDResult.Failure;
        }