/** * Adjusts the parent and child bone rotations so the tip of the child is as close to the target * position as possible. The target is specified in the world coordinate system. * * @param child Any descendant bone of the parent. */ public static void apply( Bone parent, Bone child, float targetX, float targetY, int bendDirection, float alpha) { float childRotation = child.rotation, parentRotation = parent.rotation; if (alpha == 0) { child.rotationIK = childRotation; parent.rotationIK = parentRotation; return; } Vector2 position = temp; Bone parentParent = parent.parent; if (parentParent != null) { parentParent.worldToLocal(position.set(targetX, targetY)); targetX = (position.x - parent.x) * parentParent.worldScaleX; targetY = (position.y - parent.y) * parentParent.worldScaleY; } else { targetX -= parent.x; targetY -= parent.y; } if (child.parent == parent) position.set(child.x, child.y); else parent.worldToLocal(child.parent.localToWorld(position.set(child.x, child.y))); float childX = position.x * parent.worldScaleX, childY = position.y * parent.worldScaleY; float offset = (float) Math.atan2(childY, childX); float len1 = (float) Math.sqrt(childX * childX + childY * childY), len2 = child.data.length * child.worldScaleX; // Based on code by Ryan Juckett with permission: Copyright (c) 2008-2009 Ryan Juckett, // http://www.ryanjuckett.com/ float cosDenom = 2 * len1 * len2; if (cosDenom < 0.0001f) { child.rotationIK = childRotation + ((float) Math.atan2(targetY, targetX) * radDeg - parentRotation - childRotation) * alpha; return; } float cos = clamp( (targetX * targetX + targetY * targetY - len1 * len1 - len2 * len2) / cosDenom, -1, 1); float childAngle = (float) Math.acos(cos) * bendDirection; float adjacent = len1 + len2 * cos, opposite = len2 * sin(childAngle); float parentAngle = (float) Math.atan2( targetY * adjacent - targetX * opposite, targetX * adjacent + targetY * opposite); float rotation = (parentAngle - offset) * radDeg - parentRotation; if (rotation > 180) rotation -= 360; else if (rotation < -180) // rotation += 360; parent.rotationIK = parentRotation + rotation * alpha; rotation = (childAngle + offset) * radDeg - childRotation; if (rotation > 180) rotation -= 360; else if (rotation < -180) // rotation += 360; child.rotationIK = childRotation + (rotation + parent.worldRotation - child.parent.worldRotation) * alpha; }
public void update() { float rotateMix = this.rotateMix, translateMix = this.translateMix, scaleMix = this.scaleMix, shearMix = this.shearMix; Bone target = this.target; float ta = target.a, tb = target.b, tc = target.c, td = target.d; float degRadReflect = ta * td - tb * tc > 0 ? degRad : -degRad; float offsetRotation = data.offsetRotation * degRadReflect, offsetShearY = data.offsetShearY * degRadReflect; Array<Bone> bones = this.bones; for (int i = 0, n = bones.size; i < n; i++) { Bone bone = bones.get(i); boolean modified = false; if (rotateMix != 0) { float a = bone.a, b = bone.b, c = bone.c, d = bone.d; float r = atan2(tc, ta) - atan2(c, a) + offsetRotation; if (r > PI) r -= PI2; else if (r < -PI) r += PI2; r *= rotateMix; float cos = cos(r), sin = sin(r); bone.a = cos * a - sin * c; bone.b = cos * b - sin * d; bone.c = sin * a + cos * c; bone.d = sin * b + cos * d; modified = true; } if (translateMix != 0) { Vector2 temp = this.temp; target.localToWorld(temp.set(data.offsetX, data.offsetY)); bone.worldX += (temp.x - bone.worldX) * translateMix; bone.worldY += (temp.y - bone.worldY) * translateMix; modified = true; } if (scaleMix > 0) { float s = (float) Math.sqrt(bone.a * bone.a + bone.c * bone.c); float ts = (float) Math.sqrt(ta * ta + tc * tc); if (s > 0.00001f) s = (s + (ts - s + data.offsetScaleX) * scaleMix) / s; bone.a *= s; bone.c *= s; s = (float) Math.sqrt(bone.b * bone.b + bone.d * bone.d); ts = (float) Math.sqrt(tb * tb + td * td); if (s > 0.00001f) s = (s + (ts - s + data.offsetScaleY) * scaleMix) / s; bone.b *= s; bone.d *= s; modified = true; } if (shearMix > 0) { float b = bone.b, d = bone.d; float by = atan2(d, b); float r = atan2(td, tb) - atan2(tc, ta) - (by - atan2(bone.c, bone.a)); if (r > PI) r -= PI2; else if (r < -PI) r += PI2; r = by + (r + offsetShearY) * shearMix; float s = (float) Math.sqrt(b * b + d * d); bone.b = cos(r) * s; bone.d = sin(r) * s; modified = true; } if (modified) bone.appliedValid = false; } }