From a46174b88b928874b9330347210947137cb5ea32 Mon Sep 17 00:00:00 2001 From: Davide Tantillo Date: Fri, 4 Apr 2025 13:00:00 +0200 Subject: [PATCH] [ts] Transform constraints alignment from reference runtime. --- spine-ts/spine-core/src/AnimationState.ts | 31 +-- spine-ts/spine-core/src/PathConstraint.ts | 30 +-- spine-ts/spine-core/src/PathConstraintData.ts | 10 +- spine-ts/spine-core/src/Skeleton.ts | 13 +- spine-ts/spine-core/src/SkeletonBinary.ts | 25 ++- spine-ts/spine-core/src/SkeletonJson.ts | 73 ++++--- .../spine-core/src/TransformConstraint.ts | 32 +-- .../spine-core/src/TransformConstraintData.ts | 193 ++++++++++-------- 8 files changed, 230 insertions(+), 177 deletions(-) diff --git a/spine-ts/spine-core/src/AnimationState.ts b/spine-ts/spine-core/src/AnimationState.ts index bfcb0c6e4..8b3ef7d40 100644 --- a/spine-ts/spine-core/src/AnimationState.ts +++ b/spine-ts/spine-core/src/AnimationState.ts @@ -564,7 +564,7 @@ export class AnimationState { return this.addAnimationWith(trackIndex, animation, loop, delay); } - /** Adds an animation to be played after the current or last queued animation for a track. If the track is empty, it is + /** Adds an animation to be played after the current or last queued animation for a track. If the track has no entries, this is * equivalent to calling {@link #setAnimationWith()}. * @param delay If > 0, sets {@link TrackEntry#delay}. If <= 0, the delay set is the duration of the previous track entry * minus any mix duration (from the {@link AnimationStateData}) plus the specified `delay` (ie the mix @@ -609,7 +609,10 @@ export class AnimationState { * {@link #addAnimation()} and on the returned track entry, set the * {@link TrackEntry#setMixDuration()}. Mixing from an empty animation causes the new animation to be applied more and * more over the mix duration. Properties keyed in the new animation transition from the value from lower tracks or from the - * setup pose value if no lower tracks key the property to the value keyed in the new animation. */ + * setup pose value if no lower tracks key the property to the value keyed in the new animation. + *

+ * See Empty animations in the Spine + * Runtimes Guide. */ setEmptyAnimation (trackIndex: number, mixDuration: number = 0) { let entry = this.setAnimationWith(trackIndex, AnimationState.emptyAnimation(), false); entry.mixDuration = mixDuration; @@ -618,16 +621,18 @@ export class AnimationState { } /** Adds an empty animation to be played after the current or last queued animation for a track, and sets the track entry's - * {@link TrackEntry#mixDuration}. If the track is empty, it is equivalent to calling - * {@link #setEmptyAnimation()}. - * - * See {@link #setEmptyAnimation()}. - * @param delay If > 0, sets {@link TrackEntry#delay}. If <= 0, the delay set is the duration of the previous track entry - * minus any mix duration plus the specified `delay` (ie the mix ends at (`delay` = 0) or - * before (`delay` < 0) the previous track entry duration). If the previous entry is looping, its next + * {@link TrackEntry#getMixDuration()}. If the track has no entries, it is equivalent to calling + * {@link #setEmptyAnimation(int, float)}. + *

+ * See {@link #setEmptyAnimation(int, float)} and + * Empty animations in the Spine + * Runtimes Guide. + * @param delay If > 0, sets {@link TrackEntry#getDelay()}. If <= 0, the delay set is the duration of the previous track entry + * minus any mix duration plus the specified delay (ie the mix ends at (delay = 0) or + * before (delay < 0) the previous track entry duration). If the previous entry is looping, its next * loop completion is used instead of its duration. * @return A track entry to allow further customization of animation playback. References to the track entry must not be kept - * after the {@link AnimationStateListener#dispose()} event occurs. */ + * after the {@link AnimationStateListener#dispose(TrackEntry)} event occurs. */ addEmptyAnimation (trackIndex: number, mixDuration: number = 0, delay: number = 0) { let entry = this.addAnimationWith(trackIndex, AnimationState.emptyAnimation(), false, delay); if (delay <= 0) entry.delay += entry.mixDuration - mixDuration; @@ -636,8 +641,10 @@ export class AnimationState { return entry; } - /** Sets an empty animation for every track, discarding any queued animations, and mixes to it over the specified mix - * duration. */ + /** Sets an empty animation for every track, discarding any queued animations, and mixes to it over the specified mix duration. + *

+ * See Empty animations in the Spine + * Runtimes Guide. */ setEmptyAnimations (mixDuration: number = 0) { let oldDrainDisabled = this.queue.drainDisabled; this.queue.drainDisabled = true; diff --git a/spine-ts/spine-core/src/PathConstraint.ts b/spine-ts/spine-core/src/PathConstraint.ts index 9bdd57d0e..4127691c3 100644 --- a/spine-ts/spine-core/src/PathConstraint.ts +++ b/spine-ts/spine-core/src/PathConstraint.ts @@ -51,7 +51,7 @@ export class PathConstraint implements Updatable { bones: Array; /** The slot whose path attachment will be used to constrained the bones. */ - target: Slot; + slot: Slot; /** The position along the path. */ position = 0; @@ -82,9 +82,9 @@ export class PathConstraint implements Updatable { if (!bone) throw new Error(`Couldn't find bone ${data.bones[i].name}.`); this.bones.push(bone); } - let target = skeleton.findSlot(data.target.name); - if (!target) throw new Error(`Couldn't find target bone ${data.target.name}`); - this.target = target; + let target = skeleton.findSlot(data.slot.name); + if (!target) throw new Error(`Couldn't find target bone ${data.slot.name}`); + this.slot = target; this.position = data.position; this.spacing = data.spacing; @@ -107,7 +107,7 @@ export class PathConstraint implements Updatable { } update (physics: Physics) { - let attachment = this.target.getAttachment(); + let attachment = this.slot.getAttachment(); if (!(attachment instanceof PathAttachment)) return; let mixRotate = this.mixRotate, mixX = this.mixX, mixY = this.mixY; @@ -179,7 +179,7 @@ export class PathConstraint implements Updatable { tip = data.rotateMode == RotateMode.Chain; else { tip = false; - let p = this.target.bone; + let p = this.slot.bone; offsetRotation *= p.a * p.d - p.b * p.c > 0 ? MathUtils.degRad : -MathUtils.degRad; } for (let i = 0, p = 3; i < boneCount; i++, p += 3) { @@ -232,7 +232,7 @@ export class PathConstraint implements Updatable { } computeWorldPositions (path: PathAttachment, spacesCount: number, tangents: boolean) { - let target = this.target; + let slot = this.slot; let position = this.position; let spaces = this.spaces, out = Utils.setArraySize(this.positions, spacesCount * 3 + 2), world: Array = this.world; let closed = path.closed; @@ -268,14 +268,14 @@ export class PathConstraint implements Updatable { } else if (p < 0) { if (prevCurve != PathConstraint.BEFORE) { prevCurve = PathConstraint.BEFORE; - path.computeWorldVertices(target, 2, 4, world, 0, 2); + path.computeWorldVertices(slot, 2, 4, world, 0, 2); } this.addBeforePosition(p, world, 0, out, o); continue; } else if (p > pathLength) { if (prevCurve != PathConstraint.AFTER) { prevCurve = PathConstraint.AFTER; - path.computeWorldVertices(target, verticesLength - 6, 4, world, 0, 2); + path.computeWorldVertices(slot, verticesLength - 6, 4, world, 0, 2); } this.addAfterPosition(p - pathLength, world, 0, out, o); continue; @@ -296,10 +296,10 @@ export class PathConstraint implements Updatable { if (curve != prevCurve) { prevCurve = curve; if (closed && curve == curveCount) { - path.computeWorldVertices(target, verticesLength - 4, 4, world, 0, 2); - path.computeWorldVertices(target, 0, 4, world, 4, 2); + path.computeWorldVertices(slot, verticesLength - 4, 4, world, 0, 2); + path.computeWorldVertices(slot, 0, 4, world, 4, 2); } else - path.computeWorldVertices(target, curve * 6 + 2, 8, world, 0, 2); + path.computeWorldVertices(slot, curve * 6 + 2, 8, world, 0, 2); } this.addCurvePosition(p, world[0], world[1], world[2], world[3], world[4], world[5], world[6], world[7], out, o, tangents || (i > 0 && space == 0)); @@ -311,15 +311,15 @@ export class PathConstraint implements Updatable { if (closed) { verticesLength += 2; world = Utils.setArraySize(this.world, verticesLength); - path.computeWorldVertices(target, 2, verticesLength - 4, world, 0, 2); - path.computeWorldVertices(target, 0, 2, world, verticesLength - 4, 2); + path.computeWorldVertices(slot, 2, verticesLength - 4, world, 0, 2); + path.computeWorldVertices(slot, 0, 2, world, verticesLength - 4, 2); world[verticesLength - 2] = world[0]; world[verticesLength - 1] = world[1]; } else { curveCount--; verticesLength -= 4; world = Utils.setArraySize(this.world, verticesLength); - path.computeWorldVertices(target, 2, verticesLength, world, 0, 2); + path.computeWorldVertices(slot, 2, verticesLength, world, 0, 2); } // Curve lengths. diff --git a/spine-ts/spine-core/src/PathConstraintData.ts b/spine-ts/spine-core/src/PathConstraintData.ts index ffa65c697..f4eaa5a6e 100644 --- a/spine-ts/spine-core/src/PathConstraintData.ts +++ b/spine-ts/spine-core/src/PathConstraintData.ts @@ -41,11 +41,11 @@ export class PathConstraintData extends ConstraintData { bones = new Array(); /** The slot whose path attachment will be used to constrained the bones. */ - private _target: SlotData | null = null; - public set target (slotData: SlotData) { this._target = slotData; } - public get target () { - if (!this._target) throw new Error("SlotData not set.") - else return this._target; + private _slot: SlotData | null = null; + public set slot (slotData: SlotData) { this._slot = slotData; } + public get slot () { + if (!this._slot) throw new Error("SlotData not set.") + else return this._slot; } /** The mode for positioning the first bone on the path. */ diff --git a/spine-ts/spine-core/src/Skeleton.ts b/spine-ts/spine-core/src/Skeleton.ts index 6322d2688..141a794ab 100644 --- a/spine-ts/spine-core/src/Skeleton.ts +++ b/spine-ts/spine-core/src/Skeleton.ts @@ -241,8 +241,7 @@ export class Skeleton { constraint.active = constraint.target.isActive() && (!constraint.data.skinRequired || (this.skin && Utils.contains(this.skin.constraints, constraint.data, true)))!; if (!constraint.active) return; - let target = constraint.target; - this.sortBone(target); + this.sortBone(constraint.target); let constrained = constraint.bones; let parent = constrained[0]; @@ -263,11 +262,11 @@ export class Skeleton { } sortPathConstraint (constraint: PathConstraint) { - constraint.active = constraint.target.bone.isActive() + constraint.active = constraint.slot.bone.isActive() && (!constraint.data.skinRequired || (this.skin && Utils.contains(this.skin.constraints, constraint.data, true)))!; if (!constraint.active) return; - let slot = constraint.target; + let slot = constraint.slot; let slotIndex = slot.data.index; let slotBone = slot.bone; if (this.skin) this.sortPathConstraintAttachment(this.skin, slotIndex, slotBone); @@ -292,14 +291,14 @@ export class Skeleton { } sortTransformConstraint (constraint: TransformConstraint) { - constraint.active = constraint.target.isActive() && (!constraint.data.skinRequired || (this.skin && Utils.contains(this.skin.constraints, constraint.data, true)))!; + constraint.active = constraint.source.isActive() && (!constraint.data.skinRequired || (this.skin && Utils.contains(this.skin.constraints, constraint.data, true)))!; if (!constraint.active) return; - this.sortBone(constraint.target); + this.sortBone(constraint.source); let constrained = constraint.bones; let boneCount = constrained.length; - if (constraint.data.localFrom) { + if (constraint.data.localSource) { for (let i = 0; i < boneCount; i++) { let child = constrained[i]; this.sortBone(child.parent!); diff --git a/spine-ts/spine-core/src/SkeletonBinary.ts b/spine-ts/spine-core/src/SkeletonBinary.ts index 555db38db..dfb2cd9d9 100644 --- a/spine-ts/spine-core/src/SkeletonBinary.ts +++ b/spine-ts/spine-core/src/SkeletonBinary.ts @@ -173,12 +173,12 @@ export class SkeletonBinary { nn = input.readInt(true); for (let ii = 0; ii < nn; ii++) data.bones.push(skeletonData.bones[input.readInt(true)]); - data.target = skeletonData.bones[input.readInt(true)]; + data.source = skeletonData.bones[input.readInt(true)]; let flags = input.readUnsignedByte(); data.skinRequired = (flags & 1) != 0; - data.localFrom = (flags & 2) != 0; - data.localTo = (flags & 4) != 0; - data.relative = (flags & 8) != 0; + data.localSource = (flags & 2) != 0; + data.localTarget = (flags & 4) != 0; + data.additive = (flags & 8) != 0; data.clamp = (flags & 16) != 0; nn = flags >> 5; @@ -219,12 +219,15 @@ export class SkeletonBinary { } flags = input.readByte(); - 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(); + if ((flags & 1) != 0) data.offsetX = input.readFloat(); + if ((flags & 2) != 0) data.offsetY = input.readFloat(); + if ((flags & 4) != 0) data.mixRotate = input.readFloat(); + if ((flags & 8) != 0) data.mixX = input.readFloat(); + if ((flags & 16) != 0) data.mixY = input.readFloat(); + if ((flags & 32) != 0) data.mixScaleX = input.readFloat(); + if ((flags & 64) != 0) data.mixScaleY = input.readFloat(); + if ((flags & 128) != 0) data.mixShearY = input.readFloat(); + skeletonData.transformConstraints.push(data); } @@ -239,7 +242,7 @@ export class SkeletonBinary { nn = input.readInt(true); for (let ii = 0; ii < nn; ii++) data.bones.push(skeletonData.bones[input.readInt(true)]); - data.target = skeletonData.slots[input.readInt(true)]; + data.slot = skeletonData.slots[input.readInt(true)]; const flags = input.readByte(); data.positionMode = flags & 1; data.spacingMode = (flags >> 1) & 3; diff --git a/spine-ts/spine-core/src/SkeletonJson.ts b/spine-ts/spine-core/src/SkeletonJson.ts index 6a65deffc..72e7257d6 100644 --- a/spine-ts/spine-core/src/SkeletonJson.ts +++ b/spine-ts/spine-core/src/SkeletonJson.ts @@ -179,16 +179,17 @@ export class SkeletonJson { data.bones.push(bone); } - let targetName: string = constraintMap.target; - let target = skeletonData.findBone(targetName); - if (!target) throw new Error(`Couldn't find target bone ${targetName} for transform constraint ${constraintMap.name}.`); - data.target = target; + let sourceName: string = constraintMap.source; + let source = skeletonData.findBone(sourceName); + if (!source) throw new Error(`Couldn't find source bone ${sourceName} for transform constraint ${constraintMap.name}.`); + data.source = source; - data.localFrom = getValue(constraintMap, "localFrom", false); - data.localFrom = getValue(constraintMap, "localTo", false); - data.relative = getValue(constraintMap, "relative", false); + data.localSource = getValue(constraintMap, "localSource", false); + data.localTarget = getValue(constraintMap, "localTarget", false); + data.additive = getValue(constraintMap, "additive", false); data.clamp = getValue(constraintMap, "clamp", false); + let rotate = false, x = false, y = false, scaleX = false, scaleY = false, shearY = false; const propertiesEntries = Object.entries(getValue(constraintMap, "properties", {})) as [string, any][]; for (let ii = 0; ii < propertiesEntries.length; ii++) { let name = propertiesEntries[ii][0]; @@ -209,12 +210,36 @@ export class SkeletonJson { let name = toEntries[t][0]; let to: ToProperty; switch (name) { - case "rotate": to = new ToRotate(); break; - case "x": to = new ToX(); break; - case "y": to = new ToY(); break; - case "scaleX": to = new ToScaleX(); break; - case "scaleY": to = new ToScaleY(); break; - case "shearY": to = new ToShearY(); break; + case "rotate": { + rotate = true + to = new ToRotate(); + break; + } + case "x": { + x = true + to = new ToX(); + break; + } + case "y": { + y = true + to = new ToY(); + break; + } + case "scaleX": { + scaleX = true + to = new ToScaleX(); + break; + } + case "scaleY": { + scaleY = true + to = new ToScaleY(); + break; + } + case "shearY": { + shearY = true + to = new ToShearY(); + break; + } default: throw new Error("Invalid transform constraint to property: " + name); } let toEntry = toEntries[t][1]; @@ -226,12 +251,14 @@ export class SkeletonJson { if (from.to.length > 0) data.properties.push(from); } - data.mixRotate = getValue(constraintMap, "mixRotate", 1); - data.mixX = getValue(constraintMap, "mixX", 1); - data.mixY = getValue(constraintMap, "mixY", data.mixX); - data.mixScaleX = getValue(constraintMap, "mixScaleX", 1); - data.mixScaleY = getValue(constraintMap, "mixScaleY", data.mixScaleX); - data.mixShearY = getValue(constraintMap, "mixShearY", 1); + data.offsetX = getValue(constraintMap, "x", 0); + data.offsetY = getValue(constraintMap, "y", 0); + if (rotate) data.mixRotate = getValue(constraintMap, "mixRotate", 1); + if (x) data.mixX = getValue(constraintMap, "mixX", 1); + if (y) data.mixY = getValue(constraintMap, "mixY", data.mixX); + if (scaleX) data.mixScaleX = getValue(constraintMap, "mixScaleX", 1); + if (scaleY) data.mixScaleY = getValue(constraintMap, "mixScaleY", data.mixScaleX); + if (shearY) data.mixShearY = getValue(constraintMap, "mixShearY", 1); skeletonData.transformConstraints.push(data); } @@ -252,10 +279,10 @@ export class SkeletonJson { data.bones.push(bone); } - let targetName: string = constraintMap.target; - let target = skeletonData.findSlot(targetName); - if (!target) throw new Error(`Couldn't find target slot ${targetName} for path constraint ${constraintMap.name}.`); - data.target = target; + let slotName: string = constraintMap.slot; + let slot = skeletonData.findSlot(slotName); + if (!slot) throw new Error(`Couldn't find slot ${slotName} for path constraint ${constraintMap.name}.`); + data.slot = slot; data.positionMode = Utils.enumValue(PositionMode, getValue(constraintMap, "positionMode", "Percent")); data.spacingMode = Utils.enumValue(SpacingMode, getValue(constraintMap, "spacingMode", "Length")); diff --git a/spine-ts/spine-core/src/TransformConstraint.ts b/spine-ts/spine-core/src/TransformConstraint.ts index 667370957..798656bf9 100644 --- a/spine-ts/spine-core/src/TransformConstraint.ts +++ b/spine-ts/spine-core/src/TransformConstraint.ts @@ -35,7 +35,7 @@ import { Vector2, MathUtils } from "./Utils.js"; /** 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. + * bones to match that of the source bone. * * See [Transform constraints](http://esotericsoftware.com/spine-transform-constraints) in the Spine User Guide. */ export class TransformConstraint implements Updatable { @@ -46,8 +46,8 @@ export class TransformConstraint implements Updatable { /** The bones that will be modified by this transform constraint. */ bones: Array; - /** The target bone whose world transform will be copied to the constrained bones. */ - target: Bone; + /** The bone whose world transform will be copied to the constrained bones. */ + source: Bone; mixRotate = 0; mixX = 0; mixY = 0; mixScaleX = 0; mixScaleY = 0; mixShearY = 0; @@ -65,9 +65,9 @@ export class TransformConstraint implements Updatable { if (!bone) throw new Error(`Couldn't find bone ${data.bones[i].name}.`); this.bones.push(bone); } - let target = skeleton.findBone(data.target.name); - if (!target) throw new Error(`Couldn't find target bone ${data.target.name}.`); - this.target = target; + let target = skeleton.findBone(data.source.name); + if (!target) throw new Error(`Couldn't find target bone ${data.source.name}.`); + this.source = target; this.mixRotate = data.mixRotate; this.mixX = data.mixX; @@ -94,8 +94,9 @@ export class TransformConstraint implements Updatable { update (physics: Physics) { if (this.mixRotate == 0 && this.mixX == 0 && this.mixY == 0 && this.mixScaleX == 0 && this.mixScaleY == 0 && this.mixShearY == 0) return; - const data = this.data, localFrom = data.localFrom, localTo = data.localTo, relative = data.relative, clamp = data.clamp; - const target = this.target; + const data = this.data; + const localFrom = data.localSource, localTarget = data.localTarget, additive = data.additive, clamp = data.clamp; + const source = this.source; const fromItems = data.properties; const fn = data.properties.length; const bones = this.bones; @@ -103,12 +104,11 @@ export class TransformConstraint implements Updatable { const bone = bones[i]; for (let f = 0; f < fn; f++) { const from = fromItems[f]; - const mix = from.mix(this); - if (mix != 0) { - const value = from.value(target, localFrom) - from.offset; - const toItems = from.to; - for (let t = 0, tn = from.to.length; t < tn; t++) { - var to = toItems[t]; + const value = from.value(data, source, localFrom) - from.offset; + const toItems = from.to; + for (let t = 0, tn = from.to.length; t < tn; t++) { + var to = toItems[t]; + if (to.mix(this) != 0) { let clamped = to.offset + value * to.scale; if (clamp) { if (to.offset < to.max) @@ -116,11 +116,11 @@ export class TransformConstraint implements Updatable { else clamped = MathUtils.clamp(clamped, to.max, to.offset); } - to.apply(bone, clamped, localTo, relative, mix); + to.apply(this, bone, clamped, localTarget, additive); } } } - if (localTo) + if (localTarget) bone.update(null); else bone.updateAppliedTransform(); diff --git a/spine-ts/spine-core/src/TransformConstraintData.ts b/spine-ts/spine-core/src/TransformConstraintData.ts index 89f162d3e..a2d3c5376 100644 --- a/spine-ts/spine-core/src/TransformConstraintData.ts +++ b/spine-ts/spine-core/src/TransformConstraintData.ts @@ -41,29 +41,46 @@ export class TransformConstraintData extends ConstraintData { /** The bones that will be modified by this transform constraint. */ bones = new Array(); - /** The target bone whose world transform will be copied to the constrained bones. */ - private _target: BoneData | null = null; - public set target (boneData: BoneData) { this._target = boneData; } - public get target () { - if (!this._target) throw new Error("BoneData not set.") - else return this._target; + /** The bone whose world transform will be copied to the constrained bones. */ + private _source: BoneData | null = null; + public set source (source: BoneData) { this._source = source; } + public get source () { + if (!this._source) throw new Error("BoneData not set.") + else return this._source; } + /** An offset added to the constrained bone X translation. */ + offsetX = 0; + + /** An offset added to the constrained bone Y translation. */ + offsetY = 0; + + /** A percentage (0-1) that controls the mix between the constrained and unconstrained rotation. */ mixRotate = 0; + + /** A percentage (0-1) that controls the mix between the constrained and unconstrained translation X. */ mixX = 0; + + /** A percentage (0-1) that controls the mix between the constrained and unconstrained translation Y. */ mixY = 0; + + /** A percentage (0-1) that controls the mix between the constrained and unconstrained scale X. */ mixScaleX = 0; + + /** A percentage (0-1) that controls the mix between the constrained and unconstrained scale Y. */ mixScaleY = 0; + + /** A percentage (0-1) that controls the mix between the constrained and unconstrained shear Y. */ mixShearY = 0; - /** Reads the target bone's local transform instead of its world transform. */ - localFrom = false; + /** Reads the source bone's local transform instead of its world transform. */ + localSource = false; /** Sets the constrained bones' local transforms instead of their world transforms. */ - localTo = false; + localTarget = false; - /** Adds the target bone transform to the constrained bones instead of setting it absolutely. */ - relative = false; + /** Adds the source bone transform to the constrained bones instead of setting it absolutely. */ + additive = false; /** Prevents constrained bones from exceeding the ranged defined by {@link ToProperty#offset} and {@link ToProperty#max}. */ clamp = false; @@ -86,10 +103,7 @@ export abstract class FromProperty { readonly to: Array = []; /** Reads this property from the specified bone. */ - abstract value (target: Bone, local: boolean): number; - - /** Reads the mix for this property from the specified constraint. */ - abstract mix (constraint: TransformConstraint): number; + abstract value (data: TransformConstraintData, source: Bone, local: boolean): number; } /** Constrained property for a {@link TransformConstraint}. */ @@ -103,34 +117,37 @@ export abstract class ToProperty { /** The scale of the {@link FromProperty} value in relation to this property. */ scale = 0; + /** Reads the mix for this property from the specified constraint. */ + abstract mix (constraint: TransformConstraint): number; + /** Applies the value to this property. */ - abstract apply (bone: Bone, value: number, local: boolean, relative: boolean, mix: number): void; + abstract apply (constraint: TransformConstraint, bone: Bone, value: number, local: boolean, additive: boolean): void; } export class FromRotate extends FromProperty { - value (target: Bone, local: boolean): number { - return local ? target.arotation : Math.atan2(target.c, target.a) * MathUtils.radDeg; - } - - mix (constraint: TransformConstraint): number { - return constraint.mixRotate; + value (data: TransformConstraintData, source: Bone, local: boolean): number { + return local ? source.arotation : Math.atan2(source.c, source.a) * MathUtils.radDeg; } } export class ToRotate extends ToProperty { - apply (bone: Bone, value: number, local: boolean, relative: boolean, mix: number): void { + mix (constraint: TransformConstraint): number { + return constraint.mixRotate; + } + + apply (constraint: TransformConstraint, bone: Bone, value: number, local: boolean, additive: boolean): void { if (local) { - if (!relative) value -= bone.arotation; - bone.arotation += value * mix; + if (!additive) value -= bone.arotation; + bone.arotation += value * constraint.mixRotate; } else { const a = bone.a, b = bone.b, c = bone.c, d = bone.d; value *= MathUtils.degRad; - if (!relative) value -= Math.atan2(c, a); + if (!additive) value -= Math.atan2(c, a); if (value > MathUtils.PI) value -= MathUtils.PI2; else if (value < -MathUtils.PI) // value += MathUtils.PI2; - value *= mix; + value *= constraint.mixRotate; const cos = Math.cos(value), sin = Math.sin(value); bone.a = cos * a - sin * c; bone.b = cos * b - sin * d; @@ -141,73 +158,73 @@ export class ToRotate extends ToProperty { } export class FromX extends FromProperty { - value (target: Bone, local: boolean): number { - return local ? target.ax : target.worldX; - } - - mix (constraint: TransformConstraint): number { - return constraint.mixX; + value (data: TransformConstraintData, source: Bone, local: boolean): number { + return local ? source.ax + data.offsetX : data.offsetX * source.a + data.offsetY * source.b + source.worldX; } } export class ToX extends ToProperty { - apply (bone: Bone, value: number, local: boolean, relative: boolean, mix: number): void { + mix (constraint: TransformConstraint): number { + return constraint.mixX; + } + + apply (constraint: TransformConstraint, bone: Bone, value: number, local: boolean, additive: boolean): void { if (local) { - if (!relative) value -= bone.ax; - bone.ax += value * mix; + if (!additive) value -= bone.ax; + bone.ax += value * constraint.mixX; } else { - if (!relative) value -= bone.worldX; - bone.worldX += value * mix; + if (!additive) value -= bone.worldX; + bone.worldX += value * constraint.mixX; } } } export class FromY extends FromProperty { - value (target: Bone, local: boolean): number { - return local ? target.ay : target.worldY; - } - - mix (constraint: TransformConstraint): number { - return constraint.mixY; + value (data: TransformConstraintData, source: Bone, local: boolean): number { + return local ? source.ay + data.offsetY : data.offsetX * source.c + data.offsetY * source.d + source.worldY; } } export class ToY extends ToProperty { - apply (bone: Bone, value: number, local: boolean, relative: boolean, mix: number): void { + mix (constraint: TransformConstraint): number { + return constraint.mixY; + } + + apply (constraint: TransformConstraint, bone: Bone, value: number, local: boolean, additive: boolean): void { if (local) { - if (!relative) value -= bone.ay; - bone.ay += value * mix; + if (!additive) value -= bone.ay; + bone.ay += value * constraint.mixY; } else { - if (!relative) value -= bone.worldY; - bone.worldY += value * mix; + if (!additive) value -= bone.worldY; + bone.worldY += value * constraint.mixY; } } } export class FromScaleX extends FromProperty { - value (target: Bone, local: boolean): number { - return local ? target.ascaleX : Math.sqrt(target.a * target.a + target.c * target.c); - } - - mix (constraint: TransformConstraint): number { - return constraint.mixScaleX; + value (data: TransformConstraintData, source: Bone, local: boolean): number { + return local ? source.ascaleX : Math.sqrt(source.a * source.a + source.c * source.c); } } export class ToScaleX extends ToProperty { - apply (bone: Bone, value: number, local: boolean, relative: boolean, mix: number): void { + mix (constraint: TransformConstraint): number { + return constraint.mixScaleX; + } + + apply (constraint: TransformConstraint, bone: Bone, value: number, local: boolean, additive: boolean): void { if (local) { - if (relative) - bone.ascaleX *= 1 + ((value - 1) * mix); - else if (bone.ascaleX != 0) // - bone.ascaleX = 1 + (value / bone.ascaleX - 1) * mix; + if (additive) + bone.ascaleX *= 1 + ((value - 1) * constraint.mixScaleX); + else if (bone.ascaleX != 0) + bone.ascaleX = 1 + (value / bone.ascaleX - 1) * constraint.mixScaleX; } else { let s: number; - if (relative) - s = 1 + (value - 1) * mix; + if (additive) + s = 1 + (value - 1) * constraint.mixScaleX; else { s = Math.sqrt(bone.a * bone.a + bone.c * bone.c); - if (s != 0) s = 1 + (value / s - 1) * mix; + if (s != 0) s = 1 + (value / s - 1) * constraint.mixScaleX; } bone.a *= s; bone.c *= s; @@ -216,29 +233,29 @@ export class ToScaleX extends ToProperty { } export class FromScaleY extends FromProperty { - value (target: Bone, local: boolean): number { - return local ? target.ascaleY : Math.sqrt(target.b * target.b + target.d * target.d); - } - - mix (constraint: TransformConstraint): number { - return constraint.mixScaleY; + value (data: TransformConstraintData, source: Bone, local: boolean): number { + return local ? source.ascaleY : Math.sqrt(source.b * source.b + source.d * source.d); } } export class ToScaleY extends ToProperty { - apply (bone: Bone, value: number, local: boolean, relative: boolean, mix: number): void { + mix (constraint: TransformConstraint): number { + return constraint.mixScaleY; + } + + apply (constraint: TransformConstraint, bone: Bone, value: number, local: boolean, additive: boolean): void { if (local) { - if (relative) - bone.ascaleY *= 1 + ((value - 1) * mix); + if (additive) + bone.ascaleY *= 1 + ((value - 1) * constraint.mixScaleY); else if (bone.ascaleY != 0) // - bone.ascaleY = 1 + (value / bone.ascaleY - 1) * mix; + bone.ascaleY = 1 + (value / bone.ascaleY - 1) * constraint.mixScaleY; } else { let s: number; - if (relative) - s = 1 + (value - 1) * mix; + if (additive) + s = 1 + (value - 1) * constraint.mixScaleY; else { s = Math.sqrt(bone.b * bone.b + bone.d * bone.d); - if (s != 0) s = 1 + (value / s - 1) * mix; + if (s != 0) s = 1 + (value / s - 1) * constraint.mixScaleY; } bone.b *= s; bone.d *= s; @@ -247,33 +264,33 @@ export class ToScaleY extends ToProperty { } export class FromShearY extends FromProperty { - value (target: Bone, local: boolean): number { - return local ? target.ashearY : (Math.atan2(target.d, target.b) - Math.atan2(target.c, target.a)) * MathUtils.radDeg - 90; - } - - mix (constraint: TransformConstraint): number { - return constraint.mixShearY; + value (data: TransformConstraintData, source: Bone, local: boolean): number { + return local ? source.ashearY : (Math.atan2(source.d, source.b) - Math.atan2(source.c, source.a)) * MathUtils.radDeg - 90; } } export class ToShearY extends ToProperty { - apply (bone: Bone, value: number, local: boolean, relative: boolean, mix: number): void { + mix (constraint: TransformConstraint): number { + return constraint.mixShearY; + } + + apply (constraint: TransformConstraint, bone: Bone, value: number, local: boolean, additive: boolean): void { if (local) { - if (!relative) value -= bone.ashearY; - bone.ashearY += value * mix; + if (!additive) value -= bone.ashearY; + bone.ashearY += value * constraint.mixShearY; } else { const b = bone.b, d = bone.d, by = Math.atan2(d, b); value = (value + 90) * MathUtils.degRad; - if (relative) + if (additive) value -= MathUtils.PI / 2; else { value -= by - Math.atan2(bone.c, bone.a); if (value > MathUtils.PI) value -= MathUtils.PI2; - else if (value < -MathUtils.PI) // + else if (value < -MathUtils.PI) value += MathUtils.PI2; } - value = by + value * mix; + value = by + value * constraint.mixShearY; const s = Math.sqrt(b * b + d * d); bone.b = Math.cos(value) * s; bone.d = Math.sin(value) * s;