diff --git a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/Skeleton.java b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/Skeleton.java index a6ea10d32..e8cfd9692 100644 --- a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/Skeleton.java +++ b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/Skeleton.java @@ -267,7 +267,7 @@ public class Skeleton { Object[] constrained = constraint.bones.items; int boneCount = constraint.bones.size; - if (constraint.data.local) { + if (constraint.data.localFrom) { for (int i = 0; i < boneCount; i++) { var child = (Bone)constrained[i]; sortBone(child.parent); diff --git a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/SkeletonBinary.java b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/SkeletonBinary.java index 4c5c5c4fe..e82b2794e 100644 --- a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/SkeletonBinary.java +++ b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/SkeletonBinary.java @@ -85,6 +85,20 @@ import com.esotericsoftware.spine.BoneData.Inherit; import com.esotericsoftware.spine.PathConstraintData.PositionMode; import com.esotericsoftware.spine.PathConstraintData.RotateMode; import com.esotericsoftware.spine.PathConstraintData.SpacingMode; +import com.esotericsoftware.spine.TransformConstraintData.FromProperty; +import com.esotericsoftware.spine.TransformConstraintData.FromRotate; +import com.esotericsoftware.spine.TransformConstraintData.FromScaleX; +import com.esotericsoftware.spine.TransformConstraintData.FromScaleY; +import com.esotericsoftware.spine.TransformConstraintData.FromShearY; +import com.esotericsoftware.spine.TransformConstraintData.FromX; +import com.esotericsoftware.spine.TransformConstraintData.FromY; +import com.esotericsoftware.spine.TransformConstraintData.ToProperty; +import com.esotericsoftware.spine.TransformConstraintData.ToRotate; +import com.esotericsoftware.spine.TransformConstraintData.ToScaleX; +import com.esotericsoftware.spine.TransformConstraintData.ToScaleY; +import com.esotericsoftware.spine.TransformConstraintData.ToShearY; +import com.esotericsoftware.spine.TransformConstraintData.ToX; +import com.esotericsoftware.spine.TransformConstraintData.ToY; import com.esotericsoftware.spine.attachments.Attachment; import com.esotericsoftware.spine.attachments.AttachmentLoader; import com.esotericsoftware.spine.attachments.AttachmentType; @@ -269,21 +283,46 @@ public class SkeletonBinary extends SkeletonLoader { data.target = (BoneData)bones[input.readInt(true)]; int flags = input.read(); data.skinRequired = (flags & 1) != 0; - data.local = (flags & 2) != 0; - data.relative = (flags & 4) != 0; - if ((flags & 8) != 0) data.offsetRotation = input.readFloat(); - if ((flags & 16) != 0) data.offsetX = input.readFloat() * scale; - if ((flags & 32) != 0) data.offsetY = input.readFloat() * scale; - if ((flags & 64) != 0) data.offsetScaleX = input.readFloat(); - if ((flags & 128) != 0) data.offsetScaleY = input.readFloat(); + data.localFrom = (flags & 2) != 0; + data.localTo = (flags & 4) != 0; + data.relative = (flags & 8) != 0; + data.clamp = (flags & 16) != 0; + nn = flags >> 5; + for (int ii = 0, tn; ii < nn; ii++) { + FromProperty from = switch (input.readByte()) { + case 0 -> new FromRotate(); + case 1 -> new FromX(); + case 2 -> new FromY(); + case 3 -> new FromScaleX(); + case 4 -> new FromScaleY(); + case 5 -> new FromShearY(); + default -> null; + }; + from.offset = input.readFloat() * scale; + Object[] properties = from.to.setSize(tn = input.readInt(true)); + for (int t = 0; t < tn; t++) { + ToProperty to = switch (input.readByte()) { + case 0 -> new ToRotate(); + case 1 -> new ToX(); + case 2 -> new ToY(); + case 3 -> new ToScaleX(); + case 4 -> new ToScaleY(); + case 5 -> new ToShearY(); + default -> null; + }; + to.offset = input.readFloat() * scale; + to.max = input.readFloat() * scale; + to.scale = input.readFloat(); + properties[i] = to; + } + } flags = input.read(); - if ((flags & 1) != 0) data.offsetShearY = input.readFloat(); - if ((flags & 2) != 0) data.mixRotate = input.readFloat(); - if ((flags & 4) != 0) data.mixX = input.readFloat(); - if ((flags & 8) != 0) data.mixY = input.readFloat(); - if ((flags & 16) != 0) data.mixScaleX = input.readFloat(); - if ((flags & 32) != 0) data.mixScaleY = input.readFloat(); - if ((flags & 64) != 0) data.mixShearY = input.readFloat(); + if ((flags & 1) != 0) data.mixRotate = input.readFloat(); + if ((flags & 2) != 0) data.mixX = input.readFloat(); + if ((flags & 4) != 0) data.mixY = input.readFloat(); + if ((flags & 8) != 0) data.mixScaleX = input.readFloat(); + if ((flags & 16) != 0) data.mixScaleY = input.readFloat(); + if ((flags & 32) != 0) data.mixShearY = input.readFloat(); o[i] = data; } diff --git a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/SkeletonJson.java b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/SkeletonJson.java index bb42f1311..db7952c6b 100644 --- a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/SkeletonJson.java +++ b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/SkeletonJson.java @@ -86,6 +86,20 @@ import com.esotericsoftware.spine.BoneData.Inherit; import com.esotericsoftware.spine.PathConstraintData.PositionMode; import com.esotericsoftware.spine.PathConstraintData.RotateMode; import com.esotericsoftware.spine.PathConstraintData.SpacingMode; +import com.esotericsoftware.spine.TransformConstraintData.FromProperty; +import com.esotericsoftware.spine.TransformConstraintData.FromRotate; +import com.esotericsoftware.spine.TransformConstraintData.FromScaleX; +import com.esotericsoftware.spine.TransformConstraintData.FromScaleY; +import com.esotericsoftware.spine.TransformConstraintData.FromShearY; +import com.esotericsoftware.spine.TransformConstraintData.FromX; +import com.esotericsoftware.spine.TransformConstraintData.FromY; +import com.esotericsoftware.spine.TransformConstraintData.ToProperty; +import com.esotericsoftware.spine.TransformConstraintData.ToRotate; +import com.esotericsoftware.spine.TransformConstraintData.ToScaleX; +import com.esotericsoftware.spine.TransformConstraintData.ToScaleY; +import com.esotericsoftware.spine.TransformConstraintData.ToShearY; +import com.esotericsoftware.spine.TransformConstraintData.ToX; +import com.esotericsoftware.spine.TransformConstraintData.ToY; import com.esotericsoftware.spine.attachments.Attachment; import com.esotericsoftware.spine.attachments.AttachmentLoader; import com.esotericsoftware.spine.attachments.AttachmentType; @@ -242,15 +256,38 @@ public class SkeletonJson extends SkeletonLoader { data.target = skeletonData.findBone(targetName); if (data.target == null) throw new SerializationException("Transform constraint target bone not found: " + targetName); - data.local = constraintMap.getBoolean("local", false); + data.localFrom = constraintMap.getBoolean("localFrom", false); + data.localTo = constraintMap.getBoolean("localTo", false); data.relative = constraintMap.getBoolean("relative", false); + data.clamp = constraintMap.getBoolean("clamp", false); - data.offsetRotation = constraintMap.getFloat("rotation", 0); - data.offsetX = constraintMap.getFloat("x", 0) * scale; - data.offsetY = constraintMap.getFloat("y", 0) * scale; - data.offsetScaleX = constraintMap.getFloat("scaleX", 0); - data.offsetScaleY = constraintMap.getFloat("scaleY", 0); - data.offsetShearY = constraintMap.getFloat("shearY", 0); + for (JsonValue fromEntry = constraintMap.getChild("properties"); fromEntry != null; fromEntry = fromEntry.next) { + FromProperty from = switch (fromEntry.name) { + case "rotate" -> new FromRotate(); + case "x" -> new FromX(); + case "y" -> new FromY(); + case "scaleX" -> new FromScaleX(); + case "scaleY" -> new FromScaleY(); + case "shearY" -> new FromShearY(); + default -> throw new SerializationException("Invalid transform constraint from property: " + fromEntry.name); + }; + from.offset = fromEntry.getFloat("offset", 0) * scale; + for (JsonValue toEntry = constraintMap.getChild("to"); toEntry != null; toEntry = toEntry.next) { + ToProperty to = switch (fromEntry.name) { + case "rotate" -> new ToRotate(); + case "x" -> new ToX(); + case "y" -> new ToY(); + case "scaleX" -> new ToScaleX(); + case "scaleY" -> new ToScaleY(); + case "shearY" -> new ToShearY(); + default -> throw new SerializationException("Invalid transform constraint to property: " + fromEntry.name); + }; + to.offset = toEntry.getFloat("offset", 0) * scale; + to.max = toEntry.getFloat("max", 1) * scale; + to.scale = toEntry.getFloat("scale"); + from.to.add(to); + } + } data.mixRotate = constraintMap.getFloat("mixRotate", 1); data.mixX = constraintMap.getFloat("mixX", 1); diff --git a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/TransformConstraint.java b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/TransformConstraint.java index 5b58f8579..a08b9bd66 100644 --- a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/TransformConstraint.java +++ b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/TransformConstraint.java @@ -29,12 +29,14 @@ package com.esotericsoftware.spine; -import static com.esotericsoftware.spine.utils.SpineUtils.*; +import static com.badlogic.gdx.math.MathUtils.*; import com.badlogic.gdx.math.Vector2; import com.badlogic.gdx.utils.Array; import com.esotericsoftware.spine.Skeleton.Physics; +import com.esotericsoftware.spine.TransformConstraintData.FromProperty; +import com.esotericsoftware.spine.TransformConstraintData.ToProperty; /** Stores the current pose for a transform constraint. A transform constraint adjusts the world transform of the constrained * bones to match that of the target bone. @@ -93,198 +95,31 @@ public class TransformConstraint implements Updatable { /** Applies the constraint to the constrained bones. */ public void update (Physics physics) { if (mixRotate == 0 && mixX == 0 && mixY == 0 && mixScaleX == 0 && mixScaleY == 0 && mixShearY == 0) return; - if (data.local) { - if (data.relative) - applyRelativeLocal(); + + boolean localFrom = data.localFrom, localTo = data.localTo, relative = data.relative, clamp = data.clamp; + Bone target = this.target; + Object[] fromItems = data.properties.items; + int fn = data.properties.size; + Object[] bones = this.bones.items; + for (int i = 0, n = this.bones.size; i < n; i++) { + var bone = (Bone)bones[i]; + for (int f = 0; f < fn; f++) { + var from = (FromProperty)fromItems[f]; + if (from.mix(this) != 0) { + float value = from.value(target, localFrom) - from.offset; + Object[] toItems = from.to.items; + for (int t = 0, tn = from.to.size; t < tn; t++) { + var to = (ToProperty)toItems[t]; + float clamped = to.offset + value * to.scale; + if (clamp) clamped = clamp(clamped, to.offset, to.max); + to.apply(this, bone, clamped, localTo, relative); + } + } + } + if (localTo) + bone.update(null); else - applyAbsoluteLocal(); - } else { - if (data.relative) - applyRelativeWorld(); - else - applyAbsoluteWorld(); - } - } - - private void applyAbsoluteWorld () { - float mixRotate = this.mixRotate, mixX = this.mixX, mixY = this.mixY, mixScaleX = this.mixScaleX, - mixScaleY = this.mixScaleY, mixShearY = this.mixShearY; - boolean translate = mixX != 0 || mixY != 0; - - 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; - - Object[] bones = this.bones.items; - for (int i = 0, n = this.bones.size; i < n; i++) { - Bone bone = (Bone)bones[i]; - - if (mixRotate != 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 *= mixRotate; - 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; - } - - if (translate) { - Vector2 temp = this.temp; - target.localToWorld(temp.set(data.offsetX, data.offsetY)); - bone.worldX += (temp.x - bone.worldX) * mixX; - bone.worldY += (temp.y - bone.worldY) * mixY; - } - - if (mixScaleX != 0) { - float s = (float)Math.sqrt(bone.a * bone.a + bone.c * bone.c); - if (s != 0) s = (s + ((float)Math.sqrt(ta * ta + tc * tc) - s + data.offsetScaleX) * mixScaleX) / s; - bone.a *= s; - bone.c *= s; - } - if (mixScaleY != 0) { - float s = (float)Math.sqrt(bone.b * bone.b + bone.d * bone.d); - if (s != 0) s = (s + ((float)Math.sqrt(tb * tb + td * td) - s + data.offsetScaleY) * mixScaleY) / s; - bone.b *= s; - bone.d *= s; - } - - if (mixShearY > 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) * mixShearY; - float s = (float)Math.sqrt(b * b + d * d); - bone.b = cos(r) * s; - bone.d = sin(r) * s; - } - - bone.updateAppliedTransform(); - } - } - - private void applyRelativeWorld () { - float mixRotate = this.mixRotate, mixX = this.mixX, mixY = this.mixY, mixScaleX = this.mixScaleX, - mixScaleY = this.mixScaleY, mixShearY = this.mixShearY; - boolean translate = mixX != 0 || mixY != 0; - - 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; - - Object[] bones = this.bones.items; - for (int i = 0, n = this.bones.size; i < n; i++) { - Bone bone = (Bone)bones[i]; - - if (mixRotate != 0) { - float a = bone.a, b = bone.b, c = bone.c, d = bone.d; - float r = atan2(tc, ta) + offsetRotation; - if (r > PI) - r -= PI2; - else if (r < -PI) // - r += PI2; - r *= mixRotate; - 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; - } - - if (translate) { - Vector2 temp = this.temp; - target.localToWorld(temp.set(data.offsetX, data.offsetY)); - bone.worldX += temp.x * mixX; - bone.worldY += temp.y * mixY; - } - - if (mixScaleX != 0) { - float s = ((float)Math.sqrt(ta * ta + tc * tc) - 1 + data.offsetScaleX) * mixScaleX + 1; - bone.a *= s; - bone.c *= s; - } - if (mixScaleY != 0) { - float s = ((float)Math.sqrt(tb * tb + td * td) - 1 + data.offsetScaleY) * mixScaleY + 1; - bone.b *= s; - bone.d *= s; - } - - if (mixShearY > 0) { - float r = atan2(td, tb) - atan2(tc, ta); - if (r > PI) - r -= PI2; - else if (r < -PI) // - r += PI2; - float b = bone.b, d = bone.d; - r = atan2(d, b) + (r - PI / 2 + offsetShearY) * mixShearY; - float s = (float)Math.sqrt(b * b + d * d); - bone.b = cos(r) * s; - bone.d = sin(r) * s; - } - - bone.updateAppliedTransform(); - } - } - - private void applyAbsoluteLocal () { - float mixRotate = this.mixRotate, mixX = this.mixX, mixY = this.mixY, mixScaleX = this.mixScaleX, - mixScaleY = this.mixScaleY, mixShearY = this.mixShearY; - - Bone target = this.target; - - Object[] bones = this.bones.items; - for (int i = 0, n = this.bones.size; i < n; i++) { - Bone bone = (Bone)bones[i]; - - float rotation = bone.arotation; - if (mixRotate != 0) rotation += (target.arotation - rotation + data.offsetRotation) * mixRotate; - - float x = bone.ax, y = bone.ay; - x += (target.ax - x + data.offsetX) * mixX; - y += (target.ay - y + data.offsetY) * mixY; - - float scaleX = bone.ascaleX, scaleY = bone.ascaleY; - if (mixScaleX != 0 && scaleX != 0) - scaleX = (scaleX + (target.ascaleX - scaleX + data.offsetScaleX) * mixScaleX) / scaleX; - if (mixScaleY != 0 && scaleY != 0) - scaleY = (scaleY + (target.ascaleY - scaleY + data.offsetScaleY) * mixScaleY) / scaleY; - - float shearY = bone.ashearY; - if (mixShearY != 0) shearY += (target.ashearY - shearY + data.offsetShearY) * mixShearY; - - bone.updateWorldTransform(x, y, rotation, scaleX, scaleY, bone.ashearX, shearY); - } - } - - private void applyRelativeLocal () { - float mixRotate = this.mixRotate, mixX = this.mixX, mixY = this.mixY, mixScaleX = this.mixScaleX, - mixScaleY = this.mixScaleY, mixShearY = this.mixShearY; - - Bone target = this.target; - - Object[] bones = this.bones.items; - for (int i = 0, n = this.bones.size; i < n; i++) { - Bone bone = (Bone)bones[i]; - - float rotation = bone.arotation + (target.arotation + data.offsetRotation) * mixRotate; - float x = bone.ax + (target.ax + data.offsetX) * mixX; - float y = bone.ay + (target.ay + data.offsetY) * mixY; - float scaleX = bone.ascaleX * (((target.ascaleX - 1 + data.offsetScaleX) * mixScaleX) + 1); - float scaleY = bone.ascaleY * (((target.ascaleY - 1 + data.offsetScaleY) * mixScaleY) + 1); - float shearY = bone.ashearY + (target.ashearY + data.offsetShearY) * mixShearY; - - bone.updateWorldTransform(x, y, rotation, scaleX, scaleY, bone.ashearX, shearY); + bone.updateAppliedTransform(); } } diff --git a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/TransformConstraintData.java b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/TransformConstraintData.java index 398000320..8dd2235a0 100644 --- a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/TransformConstraintData.java +++ b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/TransformConstraintData.java @@ -29,6 +29,8 @@ package com.esotericsoftware.spine; +import static com.esotericsoftware.spine.utils.SpineUtils.*; + import com.badlogic.gdx.utils.Array; /** Stores the setup pose for a {@link TransformConstraint}. @@ -38,8 +40,8 @@ public class TransformConstraintData extends ConstraintData { final Array bones = new Array(); BoneData target; float mixRotate, mixX, mixY, mixScaleX, mixScaleY, mixShearY; - float offsetRotation, offsetX, offsetY, offsetScaleX, offsetScaleY, offsetShearY; - boolean relative, local; + boolean localFrom, localTo, relative, clamp; + final Array properties = new Array(); public TransformConstraintData (String name) { super(name); @@ -60,6 +62,11 @@ public class TransformConstraintData extends ConstraintData { this.target = target; } + /** The mapping of transform properties to other transform properties. */ + public Array getProperties () { + return properties; + } + /** A percentage (0-1) that controls the mix between the constrained and unconstrained rotation. */ public float getMixRotate () { return mixRotate; @@ -114,58 +121,20 @@ public class TransformConstraintData extends ConstraintData { this.mixShearY = mixShearY; } - /** An offset added to the constrained bone rotation. */ - public float getOffsetRotation () { - return offsetRotation; + public boolean getLocalFrom () { + return localFrom; } - public void setOffsetRotation (float offsetRotation) { - this.offsetRotation = offsetRotation; + public void setLocalFrom (boolean localFrom) { + this.localFrom = localFrom; } - /** An offset added to the constrained bone X translation. */ - public float getOffsetX () { - return offsetX; + public boolean getLocalTo () { + return localTo; } - public void setOffsetX (float offsetX) { - this.offsetX = offsetX; - } - - /** An offset added to the constrained bone Y translation. */ - public float getOffsetY () { - return offsetY; - } - - public void setOffsetY (float offsetY) { - this.offsetY = offsetY; - } - - /** An offset added to the constrained bone scaleX. */ - public float getOffsetScaleX () { - return offsetScaleX; - } - - public void setOffsetScaleX (float offsetScaleX) { - this.offsetScaleX = offsetScaleX; - } - - /** An offset added to the constrained bone scaleY. */ - public float getOffsetScaleY () { - return offsetScaleY; - } - - public void setOffsetScaleY (float offsetScaleY) { - this.offsetScaleY = offsetScaleY; - } - - /** An offset added to the constrained bone shearY. */ - public float getOffsetShearY () { - return offsetShearY; - } - - public void setOffsetShearY (float offsetShearY) { - this.offsetShearY = offsetShearY; + public void setLocalTo (boolean localTo) { + this.localTo = localTo; } public boolean getRelative () { @@ -176,11 +145,205 @@ public class TransformConstraintData extends ConstraintData { this.relative = relative; } - public boolean getLocal () { - return local; + public boolean getClamp () { + return clamp; } - public void setLocal (boolean local) { - this.local = local; + public void setClamp (boolean clamp) { + this.clamp = clamp; + } + + static abstract public class FromProperty { + public float offset; + public final Array to = new Array(); + + abstract public float value (Bone target, boolean local); + + abstract public float mix (TransformConstraint constraint); + } + + static abstract public class ToProperty { + public float offset, max, scale; + + abstract public void apply (TransformConstraint constraint, Bone bone, float value, boolean local, boolean relative); + } + + static public class FromRotate extends FromProperty { + public float value (Bone target, boolean local) { + return local ? target.arotation : atan2(target.c, target.a) * radDeg; + } + + public float mix (TransformConstraint constraint) { + return constraint.mixRotate; + } + } + + static public class ToRotate extends ToProperty { + public void apply (TransformConstraint constraint, Bone bone, float value, boolean local, boolean relative) { + if (local) { + if (!relative) value -= bone.arotation; + bone.arotation += value * constraint.mixRotate; + } else { + float a = bone.a, b = bone.b, c = bone.c, d = bone.d; + float r = value * degRad; + if (!relative) r -= atan2(c, a); + if (r > PI) + r -= PI2; + else if (r < -PI) // + r += PI2; + r *= constraint.mixRotate; + 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; + } + } + } + + static public class FromX extends FromProperty { + public float value (Bone target, boolean local) { + return local ? target.ax : target.worldX; + } + + public float mix (TransformConstraint constraint) { + return constraint.mixX; + } + } + + static public class ToX extends ToProperty { + public void apply (TransformConstraint constraint, Bone bone, float value, boolean local, boolean relative) { + if (local) { + if (!relative) value -= bone.ax; + bone.ax += value * constraint.mixX; + } else { + if (!relative) value -= bone.worldX; + bone.worldX += value * constraint.mixX; + } + } + } + + static public class FromY extends FromProperty { + public float value (Bone target, boolean local) { + return local ? target.ay : target.worldY; + } + + public float mix (TransformConstraint constraint) { + return constraint.mixY; + } + } + + static public class ToY extends ToProperty { + public void apply (TransformConstraint constraint, Bone bone, float value, boolean local, boolean relative) { + if (local) { + if (!relative) value -= bone.ay; + bone.ay += value * constraint.mixY; + } else { + if (!relative) value -= bone.worldY; + bone.worldY += value * constraint.mixY; + } + } + } + + static public class FromScaleX extends FromProperty { + public float value (Bone target, boolean local) { + return local ? target.arotation : (float)Math.sqrt(target.a * target.a + target.c * target.c); + } + + public float mix (TransformConstraint constraint) { + return constraint.mixScaleX; + } + } + + static public class ToScaleX extends ToProperty { + public void apply (TransformConstraint constraint, Bone bone, float value, boolean local, boolean relative) { + if (local) { + if (relative) + bone.ascaleX *= 1 + ((value - 1) * constraint.mixScaleX); + else if (bone.ascaleX != 0) // + bone.ascaleX = 1 + (value / bone.ascaleX - 1) * constraint.mixScaleX; + } else { + float s; + if (relative) + s = 1 + (value - 1) * constraint.mixScaleX; + else { + s = (float)Math.sqrt(bone.a * bone.a + bone.c * bone.c); + if (s != 0) s = 1 + (value / s - 1) * constraint.mixScaleX; + } + bone.a *= s; + bone.c *= s; + } + } + } + + static public class FromScaleY extends FromProperty { + public float value (Bone target, boolean local) { + return local ? target.arotation : (float)Math.sqrt(target.b * target.b + target.d * target.d); + } + + public float mix (TransformConstraint constraint) { + return constraint.mixScaleY; + } + } + + static public class ToScaleY extends ToProperty { + public void apply (TransformConstraint constraint, Bone bone, float value, boolean local, boolean relative) { + if (local) { + if (relative) + bone.ascaleY *= 1 + ((value - 1) * constraint.mixScaleY); + else if (bone.ascaleY != 0) // + bone.ascaleY = 1 + (value / bone.ascaleY - 1) * constraint.mixScaleY; + } else { + float s; + if (relative) + s = 1 + (value - 1) * constraint.mixScaleY; + else { + s = (float)Math.sqrt(bone.b * bone.b + bone.d * bone.d); + if (s != 0) s = 1 + (value / s - 1) * constraint.mixScaleY; + } + bone.b *= s; + bone.d *= s; + } + } + } + + static public class FromShearY extends FromProperty { + public float value (Bone target, boolean local) { + if (local) return target.ashearY; + float r = atan2(target.d, target.b) - atan2(target.c, target.a); + if (r > PI) + r -= PI2; + else if (r < -PI) // + r += PI2; + return r; + } + + public float mix (TransformConstraint constraint) { + return constraint.mixShearY; + } + } + + static public class ToShearY extends ToProperty { + public void apply (TransformConstraint constraint, Bone bone, float value, boolean local, boolean relative) { + if (local) { + if (!relative) value -= bone.ashearY; + bone.ashearY += value * constraint.mixShearY; + } else { + float b = bone.b, d = bone.d, by = atan2(d, b); + if (relative) + value -= PI / 2; + else { + value -= by - atan2(bone.c, bone.a); + if (value > PI) + value -= PI2; + else if (value < -PI) // + value += PI2; + } + value = by + value * constraint.mixShearY; + float s = (float)Math.sqrt(b * b + d * d); + bone.b = cos(value) * s; + bone.d = sin(value) * s; + } + } } }