mirror of
https://github.com/EsotericSoftware/spine-runtimes.git
synced 2026-02-04 22:34:53 +08:00
[ts] Port of Transform constraint property mapping.
This commit is contained in:
parent
457be16385
commit
4eeb2b62cb
@ -299,7 +299,7 @@ export class Skeleton {
|
||||
|
||||
let constrained = constraint.bones;
|
||||
let boneCount = constrained.length;
|
||||
if (constraint.data.local) {
|
||||
if (constraint.data.localFrom) {
|
||||
for (let i = 0; i < boneCount; i++) {
|
||||
let child = constrained[i];
|
||||
this.sortBone(child.parent!);
|
||||
|
||||
@ -42,7 +42,7 @@ import { PhysicsConstraintData } from "./PhysicsConstraintData.js";
|
||||
import { SkeletonData } from "./SkeletonData.js";
|
||||
import { Skin } from "./Skin.js";
|
||||
import { SlotData } from "./SlotData.js";
|
||||
import { TransformConstraintData } from "./TransformConstraintData.js";
|
||||
import { FromProperty, FromRotate, FromScaleX, FromScaleY, FromShearY, FromX, FromY, ToProperty, ToRotate, ToScaleX, ToScaleY, ToShearY, ToX, ToY, TransformConstraintData } from "./TransformConstraintData.js";
|
||||
import { Color, Utils } from "./Utils.js";
|
||||
|
||||
/** Loads skeleton data in the Spine binary format.
|
||||
@ -174,23 +174,57 @@ export class SkeletonBinary {
|
||||
for (let ii = 0; ii < nn; ii++)
|
||||
data.bones.push(skeletonData.bones[input.readInt(true)]);
|
||||
data.target = skeletonData.bones[input.readInt(true)];
|
||||
let flags = input.readByte();
|
||||
let flags = input.readUnsignedByte();
|
||||
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 (let ii = 0, tn; ii < nn; ii++) {
|
||||
let from: FromProperty | null;
|
||||
let type = input.readByte();
|
||||
switch (type) {
|
||||
case 0: from = new FromRotate(); break;
|
||||
case 1: from = new FromX(); break;
|
||||
case 2: from = new FromY(); break;
|
||||
case 3: from = new FromScaleX(); break;
|
||||
case 4: from = new FromScaleY(); break;
|
||||
case 5: from = new FromShearY(); break;
|
||||
default: from = null;
|
||||
}
|
||||
if (!from) continue;
|
||||
from.offset = input.readFloat() * scale;
|
||||
tn = input.readByte();
|
||||
for (let t = 0; t < tn; t++) {
|
||||
let to: ToProperty | null;
|
||||
type = input.readByte();
|
||||
switch (type) {
|
||||
case 0: to = new ToRotate(); break;
|
||||
case 1: to = new ToX(); break;
|
||||
case 2: to = new ToY(); break;
|
||||
case 3: to = new ToScaleX(); break;
|
||||
case 4: to = new ToScaleY(); break;
|
||||
case 5: to = new ToShearY(); break;
|
||||
default: to = null;
|
||||
}
|
||||
if (!to) continue;
|
||||
to.offset = input.readFloat() * scale;
|
||||
to.max = input.readFloat() * scale;
|
||||
to.scale = input.readFloat();
|
||||
from.to[t] = to;
|
||||
}
|
||||
data.properties[ii] = from;
|
||||
}
|
||||
|
||||
flags = input.readByte();
|
||||
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();
|
||||
skeletonData.transformConstraints.push(data);
|
||||
}
|
||||
|
||||
@ -972,6 +1006,8 @@ export class SkeletonBinary {
|
||||
break;
|
||||
case PHYSICS_MIX:
|
||||
timelines.push(readTimeline1(input, new PhysicsConstraintMixTimeline(frameCount, bezierCount, index), 1));
|
||||
default:
|
||||
throw new Error("Unknown physics timeline type.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -39,7 +39,7 @@ import { PathConstraintData, PositionMode, SpacingMode, RotateMode } from "./Pat
|
||||
import { SkeletonData } from "./SkeletonData.js";
|
||||
import { Skin } from "./Skin.js";
|
||||
import { SlotData, BlendMode } from "./SlotData.js";
|
||||
import { TransformConstraintData } from "./TransformConstraintData.js";
|
||||
import { FromProperty, FromRotate, FromScaleX, FromScaleY, FromShearY, FromX, FromY, ToProperty, ToRotate, ToScaleX, ToScaleY, ToShearY, ToX, ToY, TransformConstraintData } from "./TransformConstraintData.js";
|
||||
import { Utils, Color, NumberArrayLike } from "./Utils.js";
|
||||
import { Sequence, SequenceMode } from "./attachments/Sequence.js";
|
||||
import { SequenceTimeline } from "./Animation.js";
|
||||
@ -184,14 +184,47 @@ export class SkeletonJson {
|
||||
if (!target) throw new Error(`Couldn't find target bone ${targetName} for transform constraint ${constraintMap.name}.`);
|
||||
data.target = target;
|
||||
|
||||
data.local = getValue(constraintMap, "local", false);
|
||||
data.localFrom = getValue(constraintMap, "localFrom", false);
|
||||
data.localFrom = getValue(constraintMap, "localTo", false);
|
||||
data.relative = getValue(constraintMap, "relative", false);
|
||||
data.offsetRotation = getValue(constraintMap, "rotation", 0);
|
||||
data.offsetX = getValue(constraintMap, "x", 0) * scale;
|
||||
data.offsetY = getValue(constraintMap, "y", 0) * scale;
|
||||
data.offsetScaleX = getValue(constraintMap, "scaleX", 0);
|
||||
data.offsetScaleY = getValue(constraintMap, "scaleY", 0);
|
||||
data.offsetShearY = getValue(constraintMap, "shearY", 0);
|
||||
data.clamp = getValue(constraintMap, "clamp", false);
|
||||
|
||||
const propertiesEntries = Object.entries(getValue(constraintMap, "properties", {})) as [string, any][];
|
||||
for (let ii = 0; ii < propertiesEntries.length; ii++) {
|
||||
let name = propertiesEntries[ii][0];
|
||||
let from: FromProperty;
|
||||
switch (name) {
|
||||
case "rotate": from = new FromRotate(); break;
|
||||
case "x": from = new FromX(); break;
|
||||
case "y": from = new FromY(); break;
|
||||
case "scaleX": from = new FromScaleX(); break;
|
||||
case "scaleY": from = new FromScaleY(); break;
|
||||
case "shearY": from = new FromShearY(); break;
|
||||
default: throw new Error("Invalid transform constraint from property: " + name);
|
||||
}
|
||||
const fromEntry = propertiesEntries[ii][1];
|
||||
from.offset = getValue(fromEntry, "offset", 0) * scale;
|
||||
const toEntries = Object.entries(getValue(fromEntry, "to", {})) as [string, any][];
|
||||
for (let t = 0; t < toEntries.length; t++) {
|
||||
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;
|
||||
default: throw new Error("Invalid transform constraint to property: " + name);
|
||||
}
|
||||
let toEntry = toEntries[t][1];
|
||||
to.offset = getValue(toEntry, "offset", 0) * scale;
|
||||
to.max = getValue(toEntry, "max", 1) * scale;
|
||||
to.scale = getValue(toEntry, "scale", 1);
|
||||
from.to.push(to);
|
||||
}
|
||||
if (from.to.length > 0) data.properties.push(from);
|
||||
}
|
||||
|
||||
data.mixRotate = getValue(constraintMap, "mixRotate", 1);
|
||||
data.mixX = getValue(constraintMap, "mixX", 1);
|
||||
@ -687,7 +720,10 @@ export class SkeletonJson {
|
||||
}
|
||||
|
||||
timelines.push(timeline);
|
||||
} else {
|
||||
throw new Error("Invalid timeline type for a slot: " + timelineMap.name + " (" + slotMap.name + ")");
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -740,6 +776,8 @@ export class SkeletonJson {
|
||||
timeline.setFrame(frame, getValue(aFrame, "time", 0), Utils.enumValue(Inherit, getValue(aFrame, "inherit", "Normal")));
|
||||
}
|
||||
timelines.push(timeline);
|
||||
} else {
|
||||
throw new Error("Invalid timeline type for a bone: " + timelineMap.name + " (" + boneMap.name + ")");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -94,199 +94,36 @@ 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;
|
||||
|
||||
if (this.data.local) {
|
||||
if (this.data.relative)
|
||||
this.applyRelativeLocal();
|
||||
const data = this.data, localFrom = data.localFrom, localTo = data.localTo, relative = data.relative, clamp = data.clamp;
|
||||
const target = this.target;
|
||||
const fromItems = data.properties;
|
||||
const fn = data.properties.length;
|
||||
const bones = this.bones;
|
||||
for (let i = 0, n = this.bones.length; i < n; i++) {
|
||||
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];
|
||||
let clamped = to.offset + value * to.scale;
|
||||
if (clamp) {
|
||||
if (to.offset < to.max)
|
||||
clamped = MathUtils.clamp(clamped, to.offset, to.max);
|
||||
else
|
||||
clamped = MathUtils.clamp(clamped, to.max, to.offset);
|
||||
}
|
||||
to.apply(bone, clamped, localTo, relative, mix);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (localTo)
|
||||
bone.update(null);
|
||||
else
|
||||
this.applyAbsoluteLocal();
|
||||
} else {
|
||||
if (this.data.relative)
|
||||
this.applyRelativeWorld();
|
||||
else
|
||||
this.applyAbsoluteWorld();
|
||||
}
|
||||
}
|
||||
|
||||
applyAbsoluteWorld () {
|
||||
let mixRotate = this.mixRotate, mixX = this.mixX, mixY = this.mixY, mixScaleX = this.mixScaleX,
|
||||
mixScaleY = this.mixScaleY, mixShearY = this.mixShearY;
|
||||
let translate = mixX != 0 || mixY != 0;
|
||||
|
||||
let target = this.target;
|
||||
let ta = target.a, tb = target.b, tc = target.c, td = target.d;
|
||||
let degRadReflect = ta * td - tb * tc > 0 ? MathUtils.degRad : -MathUtils.degRad;
|
||||
let offsetRotation = this.data.offsetRotation * degRadReflect;
|
||||
let offsetShearY = this.data.offsetShearY * degRadReflect;
|
||||
|
||||
let bones = this.bones;
|
||||
for (let i = 0, n = bones.length; i < n; i++) {
|
||||
let bone = bones[i];
|
||||
|
||||
if (mixRotate != 0) {
|
||||
let a = bone.a, b = bone.b, c = bone.c, d = bone.d;
|
||||
let r = Math.atan2(tc, ta) - Math.atan2(c, a) + offsetRotation;
|
||||
if (r > MathUtils.PI)
|
||||
r -= MathUtils.PI2;
|
||||
else if (r < -MathUtils.PI) //
|
||||
r += MathUtils.PI2;
|
||||
r *= mixRotate;
|
||||
let cos = Math.cos(r), sin = Math.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) {
|
||||
let temp = this.temp;
|
||||
target.localToWorld(temp.set(this.data.offsetX, this.data.offsetY));
|
||||
bone.worldX += (temp.x - bone.worldX) * mixX;
|
||||
bone.worldY += (temp.y - bone.worldY) * mixY;
|
||||
}
|
||||
|
||||
if (mixScaleX != 0) {
|
||||
let s = Math.sqrt(bone.a * bone.a + bone.c * bone.c);
|
||||
if (s != 0) s = (s + (Math.sqrt(ta * ta + tc * tc) - s + this.data.offsetScaleX) * mixScaleX) / s;
|
||||
bone.a *= s;
|
||||
bone.c *= s;
|
||||
}
|
||||
if (mixScaleY != 0) {
|
||||
let s = Math.sqrt(bone.b * bone.b + bone.d * bone.d);
|
||||
if (s != 0) s = (s + (Math.sqrt(tb * tb + td * td) - s + this.data.offsetScaleY) * mixScaleY) / s;
|
||||
bone.b *= s;
|
||||
bone.d *= s;
|
||||
}
|
||||
|
||||
if (mixShearY > 0) {
|
||||
let b = bone.b, d = bone.d;
|
||||
let by = Math.atan2(d, b);
|
||||
let r = Math.atan2(td, tb) - Math.atan2(tc, ta) - (by - Math.atan2(bone.c, bone.a));
|
||||
if (r > MathUtils.PI)
|
||||
r -= MathUtils.PI2;
|
||||
else if (r < -MathUtils.PI) //
|
||||
r += MathUtils.PI2;
|
||||
r = by + (r + offsetShearY) * mixShearY;
|
||||
let s = Math.sqrt(b * b + d * d);
|
||||
bone.b = Math.cos(r) * s;
|
||||
bone.d = Math.sin(r) * s;
|
||||
}
|
||||
|
||||
bone.updateAppliedTransform();
|
||||
}
|
||||
}
|
||||
|
||||
applyRelativeWorld () {
|
||||
let mixRotate = this.mixRotate, mixX = this.mixX, mixY = this.mixY, mixScaleX = this.mixScaleX,
|
||||
mixScaleY = this.mixScaleY, mixShearY = this.mixShearY;
|
||||
let translate = mixX != 0 || mixY != 0;
|
||||
|
||||
let target = this.target;
|
||||
let ta = target.a, tb = target.b, tc = target.c, td = target.d;
|
||||
let degRadReflect = ta * td - tb * tc > 0 ? MathUtils.degRad : -MathUtils.degRad;
|
||||
let offsetRotation = this.data.offsetRotation * degRadReflect, offsetShearY = this.data.offsetShearY * degRadReflect;
|
||||
|
||||
let bones = this.bones;
|
||||
for (let i = 0, n = bones.length; i < n; i++) {
|
||||
let bone = bones[i];
|
||||
|
||||
if (mixRotate != 0) {
|
||||
let a = bone.a, b = bone.b, c = bone.c, d = bone.d;
|
||||
let r = Math.atan2(tc, ta) + offsetRotation;
|
||||
if (r > MathUtils.PI)
|
||||
r -= MathUtils.PI2;
|
||||
else if (r < -MathUtils.PI) //
|
||||
r += MathUtils.PI2;
|
||||
r *= mixRotate;
|
||||
let cos = Math.cos(r), sin = Math.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) {
|
||||
let temp = this.temp;
|
||||
target.localToWorld(temp.set(this.data.offsetX, this.data.offsetY));
|
||||
bone.worldX += temp.x * mixX;
|
||||
bone.worldY += temp.y * mixY;
|
||||
}
|
||||
|
||||
if (mixScaleX != 0) {
|
||||
let s = (Math.sqrt(ta * ta + tc * tc) - 1 + this.data.offsetScaleX) * mixScaleX + 1;
|
||||
bone.a *= s;
|
||||
bone.c *= s;
|
||||
}
|
||||
if (mixScaleY != 0) {
|
||||
let s = (Math.sqrt(tb * tb + td * td) - 1 + this.data.offsetScaleY) * mixScaleY + 1;
|
||||
bone.b *= s;
|
||||
bone.d *= s;
|
||||
}
|
||||
|
||||
if (mixShearY > 0) {
|
||||
let r = Math.atan2(td, tb) - Math.atan2(tc, ta);
|
||||
if (r > MathUtils.PI)
|
||||
r -= MathUtils.PI2;
|
||||
else if (r < -MathUtils.PI) //
|
||||
r += MathUtils.PI2;
|
||||
let b = bone.b, d = bone.d;
|
||||
r = Math.atan2(d, b) + (r - MathUtils.PI / 2 + offsetShearY) * mixShearY;
|
||||
let s = Math.sqrt(b * b + d * d);
|
||||
bone.b = Math.cos(r) * s;
|
||||
bone.d = Math.sin(r) * s;
|
||||
}
|
||||
|
||||
bone.updateAppliedTransform();
|
||||
}
|
||||
}
|
||||
|
||||
applyAbsoluteLocal () {
|
||||
let mixRotate = this.mixRotate, mixX = this.mixX, mixY = this.mixY, mixScaleX = this.mixScaleX,
|
||||
mixScaleY = this.mixScaleY, mixShearY = this.mixShearY;
|
||||
|
||||
let target = this.target;
|
||||
|
||||
let bones = this.bones;
|
||||
for (let i = 0, n = bones.length; i < n; i++) {
|
||||
let bone = bones[i];
|
||||
|
||||
let rotation = bone.arotation;
|
||||
if (mixRotate != 0) rotation += (target.arotation - rotation + this.data.offsetRotation) * mixRotate;
|
||||
|
||||
let x = bone.ax, y = bone.ay;
|
||||
x += (target.ax - x + this.data.offsetX) * mixX;
|
||||
y += (target.ay - y + this.data.offsetY) * mixY;
|
||||
|
||||
let scaleX = bone.ascaleX, scaleY = bone.ascaleY;
|
||||
if (mixScaleX != 0 && scaleX != 0)
|
||||
scaleX = (scaleX + (target.ascaleX - scaleX + this.data.offsetScaleX) * mixScaleX) / scaleX;
|
||||
if (mixScaleY != 0 && scaleY != 0)
|
||||
scaleY = (scaleY + (target.ascaleY - scaleY + this.data.offsetScaleY) * mixScaleY) / scaleY;
|
||||
|
||||
let shearY = bone.ashearY;
|
||||
if (mixShearY != 0) shearY += (target.ashearY - shearY + this.data.offsetShearY) * mixShearY;
|
||||
|
||||
bone.updateWorldTransformWith(x, y, rotation, scaleX, scaleY, bone.ashearX, shearY);
|
||||
}
|
||||
}
|
||||
|
||||
applyRelativeLocal () {
|
||||
let mixRotate = this.mixRotate, mixX = this.mixX, mixY = this.mixY, mixScaleX = this.mixScaleX,
|
||||
mixScaleY = this.mixScaleY, mixShearY = this.mixShearY;
|
||||
|
||||
let target = this.target;
|
||||
|
||||
let bones = this.bones;
|
||||
for (let i = 0, n = bones.length; i < n; i++) {
|
||||
let bone = bones[i];
|
||||
|
||||
let rotation = bone.arotation + (target.arotation + this.data.offsetRotation) * mixRotate;
|
||||
let x = bone.ax + (target.ax + this.data.offsetX) * mixX;
|
||||
let y = bone.ay + (target.ay + this.data.offsetY) * mixY;
|
||||
let scaleX = bone.ascaleX * (((target.ascaleX - 1 + this.data.offsetScaleX) * mixScaleX) + 1);
|
||||
let scaleY = bone.ascaleY * (((target.ascaleY - 1 + this.data.offsetScaleY) * mixScaleY) + 1);
|
||||
let shearY = bone.ashearY + (target.ashearY + this.data.offsetShearY) * mixShearY;
|
||||
|
||||
bone.updateWorldTransformWith(x, y, rotation, scaleX, scaleY, bone.ashearX, shearY);
|
||||
bone.updateAppliedTransform();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -29,6 +29,9 @@
|
||||
|
||||
import { ConstraintData } from "./ConstraintData.js";
|
||||
import { BoneData } from "./BoneData.js";
|
||||
import { Bone } from "./Bone.js";
|
||||
import { TransformConstraint } from "./TransformConstraint.js";
|
||||
import { MathUtils } from "./Utils.js";
|
||||
|
||||
/** Stores the setup pose for a {@link TransformConstraint}.
|
||||
*
|
||||
@ -53,28 +56,227 @@ export class TransformConstraintData extends ConstraintData {
|
||||
mixScaleY = 0;
|
||||
mixShearY = 0;
|
||||
|
||||
/** An offset added to the constrained bone rotation. */
|
||||
offsetRotation = 0;
|
||||
/** Reads the target bone's local transform instead of its world transform. */
|
||||
localFrom = false;
|
||||
|
||||
/** An offset added to the constrained bone X translation. */
|
||||
offsetX = 0;
|
||||
|
||||
/** An offset added to the constrained bone Y translation. */
|
||||
offsetY = 0;
|
||||
|
||||
/** An offset added to the constrained bone scaleX. */
|
||||
offsetScaleX = 0;
|
||||
|
||||
/** An offset added to the constrained bone scaleY. */
|
||||
offsetScaleY = 0;
|
||||
|
||||
/** An offset added to the constrained bone shearY. */
|
||||
offsetShearY = 0;
|
||||
/** Sets the constrained bones' local transforms instead of their world transforms. */
|
||||
localTo = false;
|
||||
|
||||
/** Adds the target bone transform to the constrained bones instead of setting it absolutely. */
|
||||
relative = false;
|
||||
local = false;
|
||||
|
||||
/** Prevents constrained bones from exceeding the ranged defined by {@link ToProperty#offset} and {@link ToProperty#max}. */
|
||||
clamp = false;
|
||||
|
||||
/** The mapping of transform properties to other transform properties. */
|
||||
readonly properties: Array<FromProperty> = [];
|
||||
|
||||
constructor (name: string) {
|
||||
super(name, 0, false);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/** Source property for a {@link TransformConstraint}. */
|
||||
export abstract class FromProperty {
|
||||
/** The value of this property that corresponds to {@link ToProperty#offset}. */
|
||||
offset = 0;
|
||||
|
||||
/** Constrained properties. */
|
||||
readonly to: Array<ToProperty> = [];
|
||||
|
||||
/** 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;
|
||||
}
|
||||
|
||||
/** Constrained property for a {@link TransformConstraint}. */
|
||||
export abstract class ToProperty {
|
||||
/** The value of this property that corresponds to {@link FromProperty#offset}. */
|
||||
offset = 0;
|
||||
|
||||
/** The maximum value of this property when {@link TransformConstraintData#clamp clamped}. */
|
||||
max = 0;
|
||||
|
||||
/** The scale of the {@link FromProperty} value in relation to this property. */
|
||||
scale = 0;
|
||||
|
||||
/** Applies the value to this property. */
|
||||
abstract apply (bone: Bone, value: number, local: boolean, relative: boolean, mix: number): 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;
|
||||
}
|
||||
}
|
||||
|
||||
export class ToRotate extends ToProperty {
|
||||
apply (bone: Bone, value: number, local: boolean, relative: boolean, mix: number): void {
|
||||
if (local) {
|
||||
if (!relative) value -= bone.arotation;
|
||||
bone.arotation += value * mix;
|
||||
} 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 (value > MathUtils.PI)
|
||||
value -= MathUtils.PI2;
|
||||
else if (value < -MathUtils.PI) //
|
||||
value += MathUtils.PI2;
|
||||
value *= mix;
|
||||
const cos = Math.cos(value), sin = Math.sin(value);
|
||||
bone.a = cos * a - sin * c;
|
||||
bone.b = cos * b - sin * d;
|
||||
bone.c = sin * a + cos * c;
|
||||
bone.d = sin * b + cos * d;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export class FromX extends FromProperty {
|
||||
value (target: Bone, local: boolean): number {
|
||||
return local ? target.ax : target.worldX;
|
||||
}
|
||||
|
||||
mix (constraint: TransformConstraint): number {
|
||||
return constraint.mixX;
|
||||
}
|
||||
}
|
||||
|
||||
export class ToX extends ToProperty {
|
||||
apply (bone: Bone, value: number, local: boolean, relative: boolean, mix: number): void {
|
||||
if (local) {
|
||||
if (!relative) value -= bone.ax;
|
||||
bone.ax += value * mix;
|
||||
} else {
|
||||
if (!relative) value -= bone.worldX;
|
||||
bone.worldX += value * mix;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export class FromY extends FromProperty {
|
||||
value (target: Bone, local: boolean): number {
|
||||
return local ? target.ay : target.worldY;
|
||||
}
|
||||
|
||||
mix (constraint: TransformConstraint): number {
|
||||
return constraint.mixY;
|
||||
}
|
||||
}
|
||||
|
||||
export class ToY extends ToProperty {
|
||||
apply (bone: Bone, value: number, local: boolean, relative: boolean, mix: number): void {
|
||||
if (local) {
|
||||
if (!relative) value -= bone.ay;
|
||||
bone.ay += value * mix;
|
||||
} else {
|
||||
if (!relative) value -= bone.worldY;
|
||||
bone.worldY += value * mix;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
export class ToScaleX extends ToProperty {
|
||||
apply (bone: Bone, value: number, local: boolean, relative: boolean, mix: number): void {
|
||||
if (local) {
|
||||
if (relative)
|
||||
bone.ascaleX *= 1 + ((value - 1) * mix);
|
||||
else if (bone.ascaleX != 0) //
|
||||
bone.ascaleX = 1 + (value / bone.ascaleX - 1) * mix;
|
||||
} else {
|
||||
let s: number;
|
||||
if (relative)
|
||||
s = 1 + (value - 1) * mix;
|
||||
else {
|
||||
s = Math.sqrt(bone.a * bone.a + bone.c * bone.c);
|
||||
if (s != 0) s = 1 + (value / s - 1) * mix;
|
||||
}
|
||||
bone.a *= s;
|
||||
bone.c *= s;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
export class ToScaleY extends ToProperty {
|
||||
apply (bone: Bone, value: number, local: boolean, relative: boolean, mix: number): void {
|
||||
if (local) {
|
||||
if (relative)
|
||||
bone.ascaleY *= 1 + ((value - 1) * mix);
|
||||
else if (bone.ascaleY != 0) //
|
||||
bone.ascaleY = 1 + (value / bone.ascaleY - 1) * mix;
|
||||
} else {
|
||||
let s: number;
|
||||
if (relative)
|
||||
s = 1 + (value - 1) * mix;
|
||||
else {
|
||||
s = Math.sqrt(bone.b * bone.b + bone.d * bone.d);
|
||||
if (s != 0) s = 1 + (value / s - 1) * mix;
|
||||
}
|
||||
bone.b *= s;
|
||||
bone.d *= s;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
export class ToShearY extends ToProperty {
|
||||
apply (bone: Bone, value: number, local: boolean, relative: boolean, mix: number): void {
|
||||
if (local) {
|
||||
if (!relative) value -= bone.ashearY;
|
||||
bone.ashearY += value * mix;
|
||||
} else {
|
||||
const b = bone.b, d = bone.d, by = Math.atan2(d, b);
|
||||
value = (value + 90) * MathUtils.degRad;
|
||||
if (relative)
|
||||
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) //
|
||||
value += MathUtils.PI2;
|
||||
}
|
||||
value = by + value * mix;
|
||||
const s = Math.sqrt(b * b + d * d);
|
||||
bone.b = Math.cos(value) * s;
|
||||
bone.d = Math.sin(value) * s;
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user