Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

pixi-spine

Package Overview
Dependencies
Maintainers
2
Versions
110
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

pixi-spine - npm Package Compare versions

Comparing version 1.4.2 to 1.5.1

src/core/attachments/ClippingAttachment.ts

305

bin/pixi-spine.d.ts

@@ -8,3 +8,3 @@ /// <reference types="pixi.js" />

constructor(name: string, timelines: Array<Timeline>, duration: number);
apply(skeleton: Skeleton, lastTime: number, time: number, loop: boolean, events: Array<Event>, alpha: number, setupPose: boolean, mixingOut: boolean): void;
apply(skeleton: Skeleton, lastTime: number, time: number, loop: boolean, events: Array<Event>, alpha: number, pose: MixPose, direction: MixDirection): void;
static binarySearch(values: ArrayLike<number>, target: number, step?: number): number;

@@ -14,5 +14,14 @@ static linearSearch(values: ArrayLike<number>, target: number, step: number): number;

interface Timeline {
apply(skeleton: Skeleton, lastTime: number, time: number, events: Array<Event>, alpha: number, setupPose: boolean, mixingOut: boolean): void;
apply(skeleton: Skeleton, lastTime: number, time: number, events: Array<Event>, alpha: number, pose: MixPose, direction: MixDirection): void;
getPropertyId(): number;
}
enum MixPose {
setup = 0,
current = 1,
currentLayered = 2,
}
enum MixDirection {
in = 0,
out = 1,
}
enum TimelineType {

@@ -33,2 +42,3 @@ rotate = 0,

pathConstraintMix = 13,
twoColor = 14,
}

@@ -49,3 +59,3 @@ abstract class CurveTimeline implements Timeline {

getCurvePercent(frameIndex: number, percent: number): number;
abstract apply(skeleton: Skeleton, lastTime: number, time: number, events: Array<Event>, alpha: number, setupPose: boolean, mixingOut: boolean): void;
abstract apply(skeleton: Skeleton, lastTime: number, time: number, events: Array<Event>, alpha: number, pose: MixPose, direction: MixDirection): void;
}

@@ -62,3 +72,3 @@ class RotateTimeline extends CurveTimeline {

setFrame(frameIndex: number, time: number, degrees: number): void;
apply(skeleton: Skeleton, lastTime: number, time: number, events: Array<Event>, alpha: number, setupPose: boolean, mixingOut: boolean): void;
apply(skeleton: Skeleton, lastTime: number, time: number, events: Array<Event>, alpha: number, pose: MixPose, direction: MixDirection): void;
}

@@ -77,3 +87,3 @@ class TranslateTimeline extends CurveTimeline {

setFrame(frameIndex: number, time: number, x: number, y: number): void;
apply(skeleton: Skeleton, lastTime: number, time: number, events: Array<Event>, alpha: number, setupPose: boolean, mixingOut: boolean): void;
apply(skeleton: Skeleton, lastTime: number, time: number, events: Array<Event>, alpha: number, pose: MixPose, direction: MixDirection): void;
}

@@ -83,3 +93,3 @@ class ScaleTimeline extends TranslateTimeline {

getPropertyId(): number;
apply(skeleton: Skeleton, lastTime: number, time: number, events: Array<Event>, alpha: number, setupPose: boolean, mixingOut: boolean): void;
apply(skeleton: Skeleton, lastTime: number, time: number, events: Array<Event>, alpha: number, pose: MixPose, direction: MixDirection): void;
}

@@ -89,3 +99,3 @@ class ShearTimeline extends TranslateTimeline {

getPropertyId(): number;
apply(skeleton: Skeleton, lastTime: number, time: number, events: Array<Event>, alpha: number, setupPose: boolean, mixingOut: boolean): void;
apply(skeleton: Skeleton, lastTime: number, time: number, events: Array<Event>, alpha: number, pose: MixPose, direction: MixDirection): void;
}

@@ -108,4 +118,28 @@ class ColorTimeline extends CurveTimeline {

setFrame(frameIndex: number, time: number, r: number, g: number, b: number, a: number): void;
apply(skeleton: Skeleton, lastTime: number, time: number, events: Array<Event>, alpha: number, setupPose: boolean, mixingOut: boolean): void;
apply(skeleton: Skeleton, lastTime: number, time: number, events: Array<Event>, alpha: number, pose: MixPose, direction: MixDirection): void;
}
class TwoColorTimeline extends CurveTimeline {
static ENTRIES: number;
static PREV_TIME: number;
static PREV_R: number;
static PREV_G: number;
static PREV_B: number;
static PREV_A: number;
static PREV_R2: number;
static PREV_G2: number;
static PREV_B2: number;
static R: number;
static G: number;
static B: number;
static A: number;
static R2: number;
static G2: number;
static B2: number;
slotIndex: number;
frames: ArrayLike<number>;
constructor(frameCount: number);
getPropertyId(): number;
setFrame(frameIndex: number, time: number, r: number, g: number, b: number, a: number, r2: number, g2: number, b2: number): void;
apply(skeleton: Skeleton, lastTime: number, time: number, events: Array<Event>, alpha: number, pose: MixPose, direction: MixDirection): void;
}
class AttachmentTimeline implements Timeline {

@@ -119,3 +153,3 @@ slotIndex: number;

setFrame(frameIndex: number, time: number, attachmentName: string): void;
apply(skeleton: Skeleton, lastTime: number, time: number, events: Array<Event>, alpha: number, setupPose: boolean, mixingOut: boolean): void;
apply(skeleton: Skeleton, lastTime: number, time: number, events: Array<Event>, alpha: number, pose: MixPose, direction: MixDirection): void;
}

@@ -130,3 +164,3 @@ class DeformTimeline extends CurveTimeline {

setFrame(frameIndex: number, time: number, vertices: ArrayLike<number>): void;
apply(skeleton: Skeleton, lastTime: number, time: number, firedEvents: Array<Event>, alpha: number, setupPose: boolean, mixingOut: boolean): void;
apply(skeleton: Skeleton, lastTime: number, time: number, firedEvents: Array<Event>, alpha: number, pose: MixPose, direction: MixDirection): void;
}

@@ -140,3 +174,3 @@ class EventTimeline implements Timeline {

setFrame(frameIndex: number, event: Event): void;
apply(skeleton: Skeleton, lastTime: number, time: number, firedEvents: Array<Event>, alpha: number, setupPose: boolean, mixingOut: boolean): void;
apply(skeleton: Skeleton, lastTime: number, time: number, firedEvents: Array<Event>, alpha: number, pose: MixPose, direction: MixDirection): void;
}

@@ -150,3 +184,3 @@ class DrawOrderTimeline implements Timeline {

setFrame(frameIndex: number, time: number, drawOrder: Array<number>): void;
apply(skeleton: Skeleton, lastTime: number, time: number, firedEvents: Array<Event>, alpha: number, setupPose: boolean, mixingOut: boolean): void;
apply(skeleton: Skeleton, lastTime: number, time: number, firedEvents: Array<Event>, alpha: number, pose: MixPose, direction: MixDirection): void;
}

@@ -165,3 +199,3 @@ class IkConstraintTimeline extends CurveTimeline {

setFrame(frameIndex: number, time: number, mix: number, bendDirection: number): void;
apply(skeleton: Skeleton, lastTime: number, time: number, firedEvents: Array<Event>, alpha: number, setupPose: boolean, mixingOut: boolean): void;
apply(skeleton: Skeleton, lastTime: number, time: number, firedEvents: Array<Event>, alpha: number, pose: MixPose, direction: MixDirection): void;
}

@@ -184,3 +218,3 @@ class TransformConstraintTimeline extends CurveTimeline {

setFrame(frameIndex: number, time: number, rotateMix: number, translateMix: number, scaleMix: number, shearMix: number): void;
apply(skeleton: Skeleton, lastTime: number, time: number, firedEvents: Array<Event>, alpha: number, setupPose: boolean, mixingOut: boolean): void;
apply(skeleton: Skeleton, lastTime: number, time: number, firedEvents: Array<Event>, alpha: number, pose: MixPose, direction: MixDirection): void;
}

@@ -197,3 +231,3 @@ class PathConstraintPositionTimeline extends CurveTimeline {

setFrame(frameIndex: number, time: number, value: number): void;
apply(skeleton: Skeleton, lastTime: number, time: number, firedEvents: Array<Event>, alpha: number, setupPose: boolean, mixingOut: boolean): void;
apply(skeleton: Skeleton, lastTime: number, time: number, firedEvents: Array<Event>, alpha: number, pose: MixPose, direction: MixDirection): void;
}

@@ -203,3 +237,3 @@ class PathConstraintSpacingTimeline extends PathConstraintPositionTimeline {

getPropertyId(): number;
apply(skeleton: Skeleton, lastTime: number, time: number, firedEvents: Array<Event>, alpha: number, setupPose: boolean, mixingOut: boolean): void;
apply(skeleton: Skeleton, lastTime: number, time: number, firedEvents: Array<Event>, alpha: number, pose: MixPose, direction: MixDirection): void;
}

@@ -218,3 +252,3 @@ class PathConstraintMixTimeline extends CurveTimeline {

setFrame(frameIndex: number, time: number, rotateMix: number, translateMix: number): void;
apply(skeleton: Skeleton, lastTime: number, time: number, firedEvents: Array<Event>, alpha: number, setupPose: boolean, mixingOut: boolean): void;
apply(skeleton: Skeleton, lastTime: number, time: number, firedEvents: Array<Event>, alpha: number, pose: MixPose, direction: MixDirection): void;
}

@@ -225,2 +259,6 @@ }

static emptyAnimation: Animation;
static SUBSEQUENT: number;
static FIRST: number;
static DIP: number;
static DIP_MIX: number;
data: AnimationStateData;

@@ -232,2 +270,3 @@ tracks: TrackEntry[];

propertyIDs: IntSet;
mixingTo: TrackEntry[];
animationsChanged: boolean;

@@ -238,6 +277,6 @@ timeScale: number;

update(delta: number): void;
updateMixingFrom(entry: TrackEntry, delta: number): void;
apply(skeleton: Skeleton): void;
applyMixingFrom(entry: TrackEntry, skeleton: Skeleton): number;
applyRotateTimeline(timeline: Timeline, skeleton: Skeleton, time: number, alpha: number, setupPose: boolean, timelinesRotation: Array<number>, i: number, firstFrame: boolean): void;
updateMixingFrom(to: TrackEntry, delta: number): boolean;
apply(skeleton: Skeleton): boolean;
applyMixingFrom(to: TrackEntry, skeleton: Skeleton, currentPose: MixPose): number;
applyRotateTimeline(timeline: Timeline, skeleton: Skeleton, time: number, alpha: number, pose: MixPose, timelinesRotation: Array<number>, i: number, firstFrame: boolean): void;
queueEvents(entry: TrackEntry, animationTime: number): void;

@@ -258,5 +297,2 @@ clearTracks(): void;

_animationsChanged(): void;
setTimelinesFirst(entry: TrackEntry): void;
checkTimelinesFirst(entry: TrackEntry): void;
checkTimelinesUsage(entry: TrackEntry, usageArray: Array<boolean>): void;
getCurrent(trackIndex: number): TrackEntry;

@@ -302,6 +338,10 @@ addListener(listener: AnimationStateListener2): void;

mixDuration: number;
mixAlpha: number;
timelinesFirst: boolean[];
interruptAlpha: number;
totalAlpha: number;
timelineData: number[];
timelineDipMix: TrackEntry[];
timelinesRotation: number[];
reset(): void;
setTimelineData(to: TrackEntry, mixingToArray: Array<TrackEntry>, propertyIDs: IntSet): TrackEntry;
hasTimeline(id: number): boolean;
getAnimationTime(): number;

@@ -383,2 +423,4 @@ setAnimationLast(animationLast: number): void;

newPathAttachment(skin: Skin, name: string): PathAttachment;
newPointAttachment(skin: Skin, name: string): PointAttachment;
newClippingAttachment(skin: Skin, name: string): ClippingAttachment;
}

@@ -392,2 +434,4 @@ }

abstract class VertexAttachment extends Attachment {
private static nextID;
id: number;
bones: Array<number>;

@@ -397,4 +441,4 @@ vertices: ArrayLike<number>;

constructor(name: string);
computeWorldVertices(slot: Slot, worldVertices: ArrayLike<number>): void;
computeWorldVerticesWith(slot: Slot, start: number, count: number, worldVertices: ArrayLike<number>, offset: number): void;
computeWorldVerticesOld(slot: Slot, worldVertices: ArrayLike<number>): void;
computeWorldVertices(slot: Slot, start: number, count: number, worldVertices: ArrayLike<number>, offset: number, stride: number): void;
applyDeform(sourceAttachment: VertexAttachment): boolean;

@@ -409,2 +453,4 @@ }

newPathAttachment(skin: Skin, name: string): PathAttachment;
newPointAttachment(skin: Skin, name: string): PointAttachment;
newClippingAttachment(skin: Skin, name: string): ClippingAttachment;
}

@@ -419,2 +465,3 @@ }

Path = 4,
Point = 5,
}

@@ -429,2 +476,9 @@ }

declare module PIXI.spine.core {
class ClippingAttachment extends VertexAttachment {
endSlot: SlotData;
color: Color;
constructor(name: string);
}
}
declare module PIXI.spine.core {
class MeshAttachment extends VertexAttachment {

@@ -434,2 +488,3 @@ region: TextureRegion;

regionUVs: ArrayLike<number>;
uvs: ArrayLike<number>;
triangles: Array<number>;

@@ -442,3 +497,2 @@ color: Color;

constructor(name: string);
updateWorldVertices(slot: Slot, premultipliedAlpha: boolean): ArrayLike<number>;
updateUVs(region: TextureRegion, uvs: ArrayLike<number>): ArrayLike<number>;

@@ -460,3 +514,54 @@ applyDeform(sourceAttachment: VertexAttachment): boolean;

declare module PIXI.spine.core {
class PointAttachment extends VertexAttachment {
x: number;
y: number;
rotation: number;
color: Color;
constructor(name: string);
computeWorldPosition(bone: Bone, point: Vector2): Vector2;
computeWorldRotation(bone: Bone): number;
}
}
declare module PIXI.spine.core {
class RegionAttachment extends Attachment {
static OX1: number;
static OY1: number;
static OX2: number;
static OY2: number;
static OX3: number;
static OY3: number;
static OX4: number;
static OY4: number;
static X1: number;
static Y1: number;
static C1R: number;
static C1G: number;
static C1B: number;
static C1A: number;
static U1: number;
static V1: number;
static X2: number;
static Y2: number;
static C2R: number;
static C2G: number;
static C2B: number;
static C2A: number;
static U2: number;
static V2: number;
static X3: number;
static Y3: number;
static C3R: number;
static C3G: number;
static C3B: number;
static C3A: number;
static U3: number;
static V3: number;
static X4: number;
static Y4: number;
static C4R: number;
static C4G: number;
static C4B: number;
static C4A: number;
static U4: number;
static V4: number;
x: number;

@@ -471,5 +576,11 @@ y: number;

path: string;
rendererObject: any;
region: TextureRegion;
offset: ArrayLike<number>;
uvs: ArrayLike<number>;
tempColor: Color;
constructor(name: string);
updateWorldVertices(slot: Slot, premultipliedAlpha: boolean): ArrayLike<number>;
updateOffset(): void;
setRegion(region: TextureRegion): void;
computeWorldVertices(bone: Bone, worldVertices: ArrayLike<number>, offset: number, stride: number): void;
}

@@ -520,8 +631,8 @@ }

getWorldScaleY(): number;
worldToLocalRotationX(): number;
worldToLocalRotationY(): number;
rotateWorld(degrees: number): void;
updateAppliedTransform(): void;
worldToLocal(world: Vector2): Vector2;
localToWorld(local: Vector2): Vector2;
worldToLocalRotation(worldRotation: number): number;
localToWorldRotation(localRotation: number): number;
rotateWorld(degrees: number): void;
}

@@ -584,3 +695,2 @@ }

bendDirection: number;
level: number;
constructor(data: IkConstraintData, skeleton: Skeleton);

@@ -708,3 +818,3 @@ getOrder(): number;

findPathConstraint(constraintName: string): PathConstraint;
getBounds(offset: Vector2, size: Vector2): void;
getBounds(offset: Vector2, size: Vector2, temp: Array<number>): void;
update(delta: number): void;

@@ -737,2 +847,21 @@ }

declare module PIXI.spine.core {
class SkeletonClipping {
private triangulator;
private clippingPolygon;
private clipOutput;
clippedVertices: number[];
clippedTriangles: number[];
private scratch;
private clipAttachment;
private clippingPolygons;
clipStart(slot: Slot, clip: ClippingAttachment): number;
clipEndWithSlot(slot: Slot): void;
clipEnd(): void;
isClipping(): boolean;
clipTriangles(vertices: ArrayLike<number>, verticesLength: number, triangles: ArrayLike<number>, trianglesLength: number, uvs: ArrayLike<number>, light: Color, dark: Color, twoColor: boolean): void;
clip(x1: number, y1: number, x2: number, y2: number, x3: number, y3: number, clippingArea: Array<number>, output: Array<number>): boolean;
static makeClockwise(polygon: ArrayLike<number>): void;
}
}
declare module PIXI.spine.core {
class SkeletonData {

@@ -775,3 +904,3 @@ name: string;

readSkeletonData(json: string | any): SkeletonData;
readAttachment(map: any, skin: Skin, slotIndex: number, name: string): Attachment;
readAttachment(map: any, skin: Skin, slotIndex: number, name: string, skeletonData: SkeletonData): Attachment;
readVertices(map: any, attachment: VertexAttachment, verticesLength: number): void;

@@ -781,3 +910,3 @@ readAnimation(map: any, name: string, skeletonData: SkeletonData): void;

getValue(map: any, prop: string, defaultValue: any): any;
static blendModeFromString(str: string): number;
static blendModeFromString(str: string): BlendMode;
static positionModeFromString(str: string): PositionMode;

@@ -787,3 +916,2 @@ static spacingModeFromString(str: string): SpacingMode;

static transformModeFromString(str: string): TransformMode;
static transformModeLegacy(inheritRotation: boolean, inheritScale: boolean): TransformMode.Normal | TransformMode.OnlyTranslation | TransformMode.NoRotationOrReflection | TransformMode.NoScaleOrReflection;
}

@@ -805,2 +933,4 @@ }

currentSprite: any;
currentGraphics: any;
clippingContainer: any;
meshes: any;

@@ -816,2 +946,3 @@ currentMeshName: string;

color: Color;
darkColor: Color;
attachment: Attachment;

@@ -834,4 +965,5 @@ private attachmentTime;

color: Color;
darkColor: Color;
attachmentName: string;
blendMode: number;
blendMode: BlendMode;
constructor(index: number, name: string, boneData: BoneData);

@@ -927,2 +1059,6 @@ }

update(): void;
applyAbsoluteWorld(): void;
applyRelativeWorld(): void;
applyAbsoluteLocal(): void;
applyRelativeLocal(): void;
getOrder(): number;

@@ -947,2 +1083,4 @@ }

offsetShearY: number;
relative: boolean;
local: boolean;
constructor(name: string);

@@ -952,2 +1090,18 @@ }

declare module PIXI.spine.core {
class Triangulator {
private convexPolygons;
private convexPolygonsIndices;
private indicesArray;
private isConcaveArray;
private triangles;
private polygonPool;
private polygonIndicesPool;
triangulate(verticesArray: ArrayLike<number>): Array<number>;
decompose(verticesArray: Array<number>, triangles: Array<number>): Array<Array<number>>;
private static isConcave(index, vertexCount, vertices, indices);
private static positiveArea(p1x, p1y, p2x, p2y, p3x, p3y);
private static winding(p1x, p1y, p2x, p2y, p3x, p3y);
}
}
declare module PIXI.spine.core {
interface Updatable {

@@ -971,2 +1125,5 @@ update(): void;

}
interface Restorable {
restore(): void;
}
class Color {

@@ -1002,3 +1159,18 @@ r: number;

static cbrt(x: number): number;
static randomTriangular(min: number, max: number): number;
static randomTriangularWith(min: number, max: number, mode: number): number;
}
abstract class Interpolation {
protected abstract applyInternal(a: number): number;
apply(start: number, end: number, a: number): number;
}
class Pow extends Interpolation {
protected power: number;
constructor(power: number);
applyInternal(a: number): number;
}
class PowOut extends Pow {
constructor(power: number);
applyInternal(a: number): number;
}
class Utils {

@@ -1011,3 +1183,5 @@ static SUPPORTS_TYPED_ARRAYS: boolean;

static newFloatArray(size: number): ArrayLike<number>;
static newShortArray(size: number): ArrayLike<number>;
static toFloatArray(array: Array<number>): number[] | Float32Array;
static toSinglePrecision(value: number): number;
}

@@ -1048,3 +1222,46 @@ class DebugUtils {

}
class WindowedMean {
values: Array<number>;
addedValues: number;
lastValue: number;
mean: number;
dirty: boolean;
constructor(windowSize?: number);
hasEnoughData(): boolean;
addValue(value: number): void;
getMean(): number;
}
}
declare module PIXI.spine.core {
interface VertexEffect {
begin(skeleton: Skeleton): void;
transform(position: Vector2, uv: Vector2, light: Color, dark: Color): void;
end(): void;
}
}
declare module pixi_spine.core {
class JitterEffect implements VertexEffect {
jitterX: number;
jitterY: number;
constructor(jitterX: number, jitterY: number);
begin(skeleton: Skeleton): void;
transform(position: Vector2, uv: Vector2, light: Color, dark: Color): void;
end(): void;
}
}
declare module pixi_spine.core {
class SwirlEffect implements VertexEffect {
static interpolation: PowOut;
centerX: number;
centerY: number;
radius: number;
angle: number;
private worldX;
private worldY;
constructor(radius: number);
begin(skeleton: Skeleton): void;
transform(position: Vector2, uv: Vector2, light: Color, dark: Color): void;
end(): void;
}
}
declare module PIXI.spine {

@@ -1054,3 +1271,9 @@ function atlasParser(): (resource: PIXI.loaders.Resource, next: () => any) => any;

function syncImageLoaderAdapter(baseUrl: any, crossOrigin: any): (line: any, callback: any) => void;
function staticImageLoader(pages: {
[key: string]: (PIXI.BaseTexture | PIXI.Texture);
}): (line: any, callback: any) => void;
}
interface Math {
fround(n: number): number;
}
declare module PIXI.spine {

@@ -1073,2 +1296,3 @@ class SpineSprite extends PIXI.Sprite {

slotContainers: Array<PIXI.Container>;
tempClipContainers: Array<PIXI.Container>;
constructor(spineData: core.SkeletonData);

@@ -1084,2 +1308,5 @@ autoUpdate: boolean;

createMesh(slot: core.Slot, attachment: core.MeshAttachment): SpineMesh;
static clippingPolygon: Array<number>;
createGraphics(slot: core.Slot, clip: core.ClippingAttachment): PIXI.Graphics;
updateGraphics(slot: core.Slot, clip: core.ClippingAttachment): void;
hackTextureBySlotIndex(slotIndex: number, texture?: PIXI.Texture, size?: PIXI.Rectangle): boolean;

@@ -1086,0 +1313,0 @@ hackTextureBySlotName: (slotName: string, texture?: PIXI.Texture, size?: PIXI.Rectangle) => any;

2

package.json
{
"name": "pixi-spine",
"version": "1.4.2",
"version": "1.5.1",
"description": "Spine implementation for pixi v^3 and v^4",

@@ -5,0 +5,0 @@ "author": "Mat Groves",

@@ -32,1021 +32,1216 @@ /******************************************************************************

namespace pixi_spine.core {
export class Animation {
name: string;
timelines: Array<Timeline>;
duration: number;
export class Animation {
name: string;
timelines: Array<Timeline>;
duration: number;
constructor (name: string, timelines: Array<Timeline>, duration: number) {
if (name == null) throw new Error("name cannot be null.");
if (timelines == null) throw new Error("timelines cannot be null.");
this.name = name;
this.timelines = timelines;
this.duration = duration;
}
constructor (name: string, timelines: Array<Timeline>, duration: number) {
if (name == null) throw new Error("name cannot be null.");
if (timelines == null) throw new Error("timelines cannot be null.");
this.name = name;
this.timelines = timelines;
this.duration = duration;
}
apply (skeleton: Skeleton, lastTime: number, time: number, loop: boolean, events: Array<Event>, alpha: number, setupPose: boolean, mixingOut: boolean) {
if (skeleton == null) throw new Error("skeleton cannot be null.");
apply (skeleton: Skeleton, lastTime: number, time: number, loop: boolean, events: Array<Event>, alpha: number, pose: MixPose, direction: MixDirection) {
if (skeleton == null) throw new Error("skeleton cannot be null.");
if (loop && this.duration != 0) {
time %= this.duration;
if (lastTime > 0) lastTime %= this.duration;
}
if (loop && this.duration != 0) {
time %= this.duration;
if (lastTime > 0) lastTime %= this.duration;
}
let timelines = this.timelines;
for (let i = 0, n = timelines.length; i < n; i++)
timelines[i].apply(skeleton, lastTime, time, events, alpha, setupPose, mixingOut);
}
let timelines = this.timelines;
for (let i = 0, n = timelines.length; i < n; i++)
timelines[i].apply(skeleton, lastTime, time, events, alpha, pose, direction);
}
static binarySearch (values: ArrayLike<number>, target: number, step: number = 1) {
let low = 0;
let high = values.length / step - 2;
if (high == 0) return step;
let current = high >>> 1;
while (true) {
if (values[(current + 1) * step] <= target)
low = current + 1;
else
high = current;
if (low == high) return (low + 1) * step;
current = (low + high) >>> 1;
}
}
static binarySearch (values: ArrayLike<number>, target: number, step: number = 1) {
let low = 0;
let high = values.length / step - 2;
if (high == 0) return step;
let current = high >>> 1;
while (true) {
if (values[(current + 1) * step] <= target)
low = current + 1;
else
high = current;
if (low == high) return (low + 1) * step;
current = (low + high) >>> 1;
}
}
static linearSearch (values: ArrayLike<number>, target: number, step: number) {
for (let i = 0, last = values.length - step; i <= last; i += step)
if (values[i] > target) return i;
return -1;
}
}
static linearSearch (values: ArrayLike<number>, target: number, step: number) {
for (let i = 0, last = values.length - step; i <= last; i += step)
if (values[i] > target) return i;
return -1;
}
}
export interface Timeline {
apply (skeleton: Skeleton, lastTime: number, time: number, events: Array<Event>, alpha: number, setupPose: boolean, mixingOut: boolean): void;
getPropertyId (): number;
}
export interface Timeline {
apply (skeleton: Skeleton, lastTime: number, time: number, events: Array<Event>, alpha: number, pose: MixPose, direction: MixDirection): void;
getPropertyId (): number;
}
export enum TimelineType {
rotate, translate, scale, shear,
attachment, color, deform,
event, drawOrder,
ikConstraint, transformConstraint,
pathConstraintPosition, pathConstraintSpacing, pathConstraintMix
}
export enum MixPose {
setup,
current,
currentLayered
}
export abstract class CurveTimeline implements Timeline {
static LINEAR = 0; static STEPPED = 1; static BEZIER = 2;
static BEZIER_SIZE = 10 * 2 - 1;
export enum MixDirection {
in, out
}
private curves: ArrayLike<number>; // type, x, y, ...
export enum TimelineType {
rotate, translate, scale, shear,
attachment, color, deform,
event, drawOrder,
ikConstraint, transformConstraint,
pathConstraintPosition, pathConstraintSpacing, pathConstraintMix,
twoColor
}
abstract getPropertyId(): number;
export abstract class CurveTimeline implements Timeline {
static LINEAR = 0; static STEPPED = 1; static BEZIER = 2;
static BEZIER_SIZE = 10 * 2 - 1;
constructor (frameCount: number) {
if (frameCount <= 0) throw new Error("frameCount must be > 0: " + frameCount);
this.curves = Utils.newFloatArray((frameCount - 1) * CurveTimeline.BEZIER_SIZE);
}
private curves: ArrayLike<number>; // type, x, y, ...
getFrameCount () {
return this.curves.length / CurveTimeline.BEZIER_SIZE + 1;
}
abstract getPropertyId(): number;
setLinear (frameIndex: number) {
this.curves[frameIndex * CurveTimeline.BEZIER_SIZE] = CurveTimeline.LINEAR;
}
constructor (frameCount: number) {
if (frameCount <= 0) throw new Error("frameCount must be > 0: " + frameCount);
this.curves = Utils.newFloatArray((frameCount - 1) * CurveTimeline.BEZIER_SIZE);
}
setStepped (frameIndex: number) {
this.curves[frameIndex * CurveTimeline.BEZIER_SIZE] = CurveTimeline.STEPPED;
}
getFrameCount () {
return this.curves.length / CurveTimeline.BEZIER_SIZE + 1;
}
getCurveType (frameIndex: number): number {
let index = frameIndex * CurveTimeline.BEZIER_SIZE;
if (index == this.curves.length) return CurveTimeline.LINEAR;
let type = this.curves[index];
if (type == CurveTimeline.LINEAR) return CurveTimeline.LINEAR;
if (type == CurveTimeline.STEPPED) return CurveTimeline.STEPPED;
return CurveTimeline.BEZIER;
}
setLinear (frameIndex: number) {
this.curves[frameIndex * CurveTimeline.BEZIER_SIZE] = CurveTimeline.LINEAR;
}
/** Sets the control handle positions for an interpolation bezier curve used to transition from this keyframe to the next.
* cx1 and cx2 are from 0 to 1, representing the percent of time between the two keyframes. cy1 and cy2 are the percent of
* the difference between the keyframe's values. */
setCurve (frameIndex: number, cx1: number, cy1: number, cx2: number, cy2: number) {
let tmpx = (-cx1 * 2 + cx2) * 0.03, tmpy = (-cy1 * 2 + cy2) * 0.03;
let dddfx = ((cx1 - cx2) * 3 + 1) * 0.006, dddfy = ((cy1 - cy2) * 3 + 1) * 0.006;
let ddfx = tmpx * 2 + dddfx, ddfy = tmpy * 2 + dddfy;
let dfx = cx1 * 0.3 + tmpx + dddfx * 0.16666667, dfy = cy1 * 0.3 + tmpy + dddfy * 0.16666667;
setStepped (frameIndex: number) {
this.curves[frameIndex * CurveTimeline.BEZIER_SIZE] = CurveTimeline.STEPPED;
}
let i = frameIndex * CurveTimeline.BEZIER_SIZE;
let curves = this.curves;
curves[i++] = CurveTimeline.BEZIER;
getCurveType (frameIndex: number): number {
let index = frameIndex * CurveTimeline.BEZIER_SIZE;
if (index == this.curves.length) return CurveTimeline.LINEAR;
let type = this.curves[index];
if (type == CurveTimeline.LINEAR) return CurveTimeline.LINEAR;
if (type == CurveTimeline.STEPPED) return CurveTimeline.STEPPED;
return CurveTimeline.BEZIER;
}
let x = dfx, y = dfy;
for (let n = i + CurveTimeline.BEZIER_SIZE - 1; i < n; i += 2) {
curves[i] = x;
curves[i + 1] = y;
dfx += ddfx;
dfy += ddfy;
ddfx += dddfx;
ddfy += dddfy;
x += dfx;
y += dfy;
}
}
/** Sets the control handle positions for an interpolation bezier curve used to transition from this keyframe to the next.
* cx1 and cx2 are from 0 to 1, representing the percent of time between the two keyframes. cy1 and cy2 are the percent of
* the difference between the keyframe's values. */
setCurve (frameIndex: number, cx1: number, cy1: number, cx2: number, cy2: number) {
let tmpx = (-cx1 * 2 + cx2) * 0.03, tmpy = (-cy1 * 2 + cy2) * 0.03;
let dddfx = ((cx1 - cx2) * 3 + 1) * 0.006, dddfy = ((cy1 - cy2) * 3 + 1) * 0.006;
let ddfx = tmpx * 2 + dddfx, ddfy = tmpy * 2 + dddfy;
let dfx = cx1 * 0.3 + tmpx + dddfx * 0.16666667, dfy = cy1 * 0.3 + tmpy + dddfy * 0.16666667;
getCurvePercent (frameIndex: number, percent: number) {
percent = MathUtils.clamp(percent, 0, 1);
let curves = this.curves;
let i = frameIndex * CurveTimeline.BEZIER_SIZE;
let type = curves[i];
if (type == CurveTimeline.LINEAR) return percent;
if (type == CurveTimeline.STEPPED) return 0;
i++;
let x = 0;
for (let start = i, n = i + CurveTimeline.BEZIER_SIZE - 1; i < n; i += 2) {
x = curves[i];
if (x >= percent) {
let prevX: number, prevY: number;
if (i == start) {
prevX = 0;
prevY = 0;
} else {
prevX = curves[i - 2];
prevY = curves[i - 1];
}
return prevY + (curves[i + 1] - prevY) * (percent - prevX) / (x - prevX);
}
}
let y = curves[i - 1];
return y + (1 - y) * (percent - x) / (1 - x); // Last point is 1,1.
}
let i = frameIndex * CurveTimeline.BEZIER_SIZE;
let curves = this.curves;
curves[i++] = CurveTimeline.BEZIER;
abstract apply (skeleton: Skeleton, lastTime: number, time: number, events: Array<Event>, alpha: number, setupPose: boolean, mixingOut: boolean): void;
}
let x = dfx, y = dfy;
for (let n = i + CurveTimeline.BEZIER_SIZE - 1; i < n; i += 2) {
curves[i] = x;
curves[i + 1] = y;
dfx += ddfx;
dfy += ddfy;
ddfx += dddfx;
ddfy += dddfy;
x += dfx;
y += dfy;
}
}
export class RotateTimeline extends CurveTimeline {
static ENTRIES = 2;
static PREV_TIME = -2; static PREV_ROTATION = -1;
static ROTATION = 1;
getCurvePercent (frameIndex: number, percent: number) {
percent = MathUtils.clamp(percent, 0, 1);
let curves = this.curves;
let i = frameIndex * CurveTimeline.BEZIER_SIZE;
let type = curves[i];
if (type == CurveTimeline.LINEAR) return percent;
if (type == CurveTimeline.STEPPED) return 0;
i++;
let x = 0;
for (let start = i, n = i + CurveTimeline.BEZIER_SIZE - 1; i < n; i += 2) {
x = curves[i];
if (x >= percent) {
let prevX: number, prevY: number;
if (i == start) {
prevX = 0;
prevY = 0;
} else {
prevX = curves[i - 2];
prevY = curves[i - 1];
}
return prevY + (curves[i + 1] - prevY) * (percent - prevX) / (x - prevX);
}
}
let y = curves[i - 1];
return y + (1 - y) * (percent - x) / (1 - x); // Last point is 1,1.
}
boneIndex: number;
frames: ArrayLike<number>; // time, degrees, ...
abstract apply (skeleton: Skeleton, lastTime: number, time: number, events: Array<Event>, alpha: number, pose: MixPose, direction: MixDirection): void;
}
constructor (frameCount: number) {
super(frameCount);
this.frames = Utils.newFloatArray(frameCount << 1);
}
export class RotateTimeline extends CurveTimeline {
static ENTRIES = 2;
static PREV_TIME = -2; static PREV_ROTATION = -1;
static ROTATION = 1;
getPropertyId () {
return (TimelineType.rotate << 24) + this.boneIndex;
}
boneIndex: number;
frames: ArrayLike<number>; // time, degrees, ...
/** Sets the time and angle of the specified keyframe. */
setFrame (frameIndex: number, time: number, degrees: number) {
frameIndex <<= 1;
this.frames[frameIndex] = time;
this.frames[frameIndex + RotateTimeline.ROTATION] = degrees;
}
constructor (frameCount: number) {
super(frameCount);
this.frames = Utils.newFloatArray(frameCount << 1);
}
apply (skeleton: Skeleton, lastTime: number, time: number, events: Array<Event>, alpha: number, setupPose: boolean, mixingOut: boolean) {
let frames = this.frames;
getPropertyId () {
return (TimelineType.rotate << 24) + this.boneIndex;
}
let bone = skeleton.bones[this.boneIndex];
if (time < frames[0]) {
if (setupPose) bone.rotation = bone.data.rotation;
return;
}
/** Sets the time and angle of the specified keyframe. */
setFrame (frameIndex: number, time: number, degrees: number) {
frameIndex <<= 1;
this.frames[frameIndex] = time;
this.frames[frameIndex + RotateTimeline.ROTATION] = degrees;
}
if (time >= frames[frames.length - RotateTimeline.ENTRIES]) { // Time is after last frame.
if (setupPose)
bone.rotation = bone.data.rotation + frames[frames.length + RotateTimeline.PREV_ROTATION] * alpha;
else {
let r = bone.data.rotation + frames[frames.length + RotateTimeline.PREV_ROTATION] - bone.rotation;
r -= (16384 - ((16384.499999999996 - r / 360) | 0)) * 360; // Wrap within -180 and 180.
bone.rotation += r * alpha;
}
return;
}
apply (skeleton: Skeleton, lastTime: number, time: number, events: Array<Event>, alpha: number, pose: MixPose, direction: MixDirection) {
let frames = this.frames;
// Interpolate between the previous frame and the current frame.
let frame = Animation.binarySearch(frames, time, RotateTimeline.ENTRIES);
let prevRotation = frames[frame + RotateTimeline.PREV_ROTATION];
let frameTime = frames[frame];
let percent = this.getCurvePercent((frame >> 1) - 1,
1 - (time - frameTime) / (frames[frame + RotateTimeline.PREV_TIME] - frameTime));
let bone = skeleton.bones[this.boneIndex];
if (time < frames[0]) {
switch (pose) {
case MixPose.setup:
bone.rotation = bone.data.rotation;
return;
case MixPose.current:
let r = bone.data.rotation - bone.rotation;
r -= (16384 - ((16384.499999999996 - r / 360) | 0)) * 360;
bone.rotation += r * alpha;
}
return;
}
let r = frames[frame + RotateTimeline.ROTATION] - prevRotation;
r -= (16384 - ((16384.499999999996 - r / 360) | 0)) * 360;
r = prevRotation + r * percent;
if (setupPose) {
r -= (16384 - ((16384.499999999996 - r / 360) | 0)) * 360;
bone.rotation = bone.data.rotation + r * alpha;
} else {
r = bone.data.rotation + r - bone.rotation;
r -= (16384 - ((16384.499999999996 - r / 360) |0)) * 360;
bone.rotation += r * alpha;
}
}
}
if (time >= frames[frames.length - RotateTimeline.ENTRIES]) { // Time is after last frame.
if (pose == MixPose.setup)
bone.rotation = bone.data.rotation + frames[frames.length + RotateTimeline.PREV_ROTATION] * alpha;
else {
let r = bone.data.rotation + frames[frames.length + RotateTimeline.PREV_ROTATION] - bone.rotation;
r -= (16384 - ((16384.499999999996 - r / 360) | 0)) * 360; // Wrap within -180 and 180.
bone.rotation += r * alpha;
}
return;
}
export class TranslateTimeline extends CurveTimeline {
static ENTRIES = 3;
static PREV_TIME = -3; static PREV_X = -2; static PREV_Y = -1;
static X = 1; static Y = 2;
// Interpolate between the previous frame and the current frame.
let frame = Animation.binarySearch(frames, time, RotateTimeline.ENTRIES);
let prevRotation = frames[frame + RotateTimeline.PREV_ROTATION];
let frameTime = frames[frame];
let percent = this.getCurvePercent((frame >> 1) - 1,
1 - (time - frameTime) / (frames[frame + RotateTimeline.PREV_TIME] - frameTime));
boneIndex: number;
frames: ArrayLike<number>; // time, x, y, ...
let r = frames[frame + RotateTimeline.ROTATION] - prevRotation;
r -= (16384 - ((16384.499999999996 - r / 360) | 0)) * 360;
r = prevRotation + r * percent;
if (pose == MixPose.setup) {
r -= (16384 - ((16384.499999999996 - r / 360) | 0)) * 360;
bone.rotation = bone.data.rotation + r * alpha;
} else {
r = bone.data.rotation + r - bone.rotation;
r -= (16384 - ((16384.499999999996 - r / 360) |0)) * 360;
bone.rotation += r * alpha;
}
}
}
constructor (frameCount: number) {
super(frameCount);
this.frames = Utils.newFloatArray(frameCount * TranslateTimeline.ENTRIES);
}
export class TranslateTimeline extends CurveTimeline {
static ENTRIES = 3;
static PREV_TIME = -3; static PREV_X = -2; static PREV_Y = -1;
static X = 1; static Y = 2;
getPropertyId () {
return (TimelineType.translate << 24) + this.boneIndex;
}
boneIndex: number;
frames: ArrayLike<number>; // time, x, y, ...
/** Sets the time and value of the specified keyframe. */
setFrame (frameIndex: number, time: number, x: number, y: number) {
frameIndex *= TranslateTimeline.ENTRIES;
this.frames[frameIndex] = time;
this.frames[frameIndex + TranslateTimeline.X] = x;
this.frames[frameIndex + TranslateTimeline.Y] = y;
}
constructor (frameCount: number) {
super(frameCount);
this.frames = Utils.newFloatArray(frameCount * TranslateTimeline.ENTRIES);
}
apply (skeleton: Skeleton, lastTime: number, time: number, events: Array<Event>, alpha: number, setupPose: boolean, mixingOut: boolean) {
let frames = this.frames;
getPropertyId () {
return (TimelineType.translate << 24) + this.boneIndex;
}
let bone = skeleton.bones[this.boneIndex];
if (time < frames[0]) {
if (setupPose) {
bone.x = bone.data.x;
bone.y = bone.data.y;
}
return;
}
/** Sets the time and value of the specified keyframe. */
setFrame (frameIndex: number, time: number, x: number, y: number) {
frameIndex *= TranslateTimeline.ENTRIES;
this.frames[frameIndex] = time;
this.frames[frameIndex + TranslateTimeline.X] = x;
this.frames[frameIndex + TranslateTimeline.Y] = y;
}
let x = 0, y = 0;
if (time >= frames[frames.length - TranslateTimeline.ENTRIES]) { // Time is after last frame.
x = frames[frames.length + TranslateTimeline.PREV_X];
y = frames[frames.length + TranslateTimeline.PREV_Y];
} else {
// Interpolate between the previous frame and the current frame.
let frame = Animation.binarySearch(frames, time, TranslateTimeline.ENTRIES);
x = frames[frame + TranslateTimeline.PREV_X];
y = frames[frame + TranslateTimeline.PREV_Y];
let frameTime = frames[frame];
let percent = this.getCurvePercent(frame / TranslateTimeline.ENTRIES - 1,
1 - (time - frameTime) / (frames[frame + TranslateTimeline.PREV_TIME] - frameTime));
apply (skeleton: Skeleton, lastTime: number, time: number, events: Array<Event>, alpha: number, pose: MixPose, direction: MixDirection) {
let frames = this.frames;
x += (frames[frame + TranslateTimeline.X] - x) * percent;
y += (frames[frame + TranslateTimeline.Y] - y) * percent;
}
if (setupPose) {
bone.x = bone.data.x + x * alpha;
bone.y = bone.data.y + y * alpha;
} else {
bone.x += (bone.data.x + x - bone.x) * alpha;
bone.y += (bone.data.y + y - bone.y) * alpha;
}
}
}
let bone = skeleton.bones[this.boneIndex];
if (time < frames[0]) {
switch (pose) {
case MixPose.setup:
bone.x = bone.data.x;
bone.y = bone.data.y;
return;
case MixPose.current:
bone.x += (bone.data.x - bone.x) * alpha;
bone.y += (bone.data.y - bone.y) * alpha;
}
return;
}
export class ScaleTimeline extends TranslateTimeline {
constructor (frameCount: number) {
super(frameCount);
}
let x = 0, y = 0;
if (time >= frames[frames.length - TranslateTimeline.ENTRIES]) { // Time is after last frame.
x = frames[frames.length + TranslateTimeline.PREV_X];
y = frames[frames.length + TranslateTimeline.PREV_Y];
} else {
// Interpolate between the previous frame and the current frame.
let frame = Animation.binarySearch(frames, time, TranslateTimeline.ENTRIES);
x = frames[frame + TranslateTimeline.PREV_X];
y = frames[frame + TranslateTimeline.PREV_Y];
let frameTime = frames[frame];
let percent = this.getCurvePercent(frame / TranslateTimeline.ENTRIES - 1,
1 - (time - frameTime) / (frames[frame + TranslateTimeline.PREV_TIME] - frameTime));
getPropertyId () {
return (TimelineType.scale << 24) + this.boneIndex;
}
x += (frames[frame + TranslateTimeline.X] - x) * percent;
y += (frames[frame + TranslateTimeline.Y] - y) * percent;
}
if (pose == MixPose.setup) {
bone.x = bone.data.x + x * alpha;
bone.y = bone.data.y + y * alpha;
} else {
bone.x += (bone.data.x + x - bone.x) * alpha;
bone.y += (bone.data.y + y - bone.y) * alpha;
}
}
}
apply (skeleton: Skeleton, lastTime: number, time: number, events: Array<Event>, alpha: number, setupPose: boolean, mixingOut: boolean) {
let frames = this.frames;
export class ScaleTimeline extends TranslateTimeline {
constructor (frameCount: number) {
super(frameCount);
}
let bone = skeleton.bones[this.boneIndex];
if (time < frames[0]) {
if (setupPose) {
bone.scaleX = bone.data.scaleX;
bone.scaleY = bone.data.scaleY;
}
return;
}
getPropertyId () {
return (TimelineType.scale << 24) + this.boneIndex;
}
let x = 0, y = 0;
if (time >= frames[frames.length - ScaleTimeline.ENTRIES]) { // Time is after last frame.
x = frames[frames.length + ScaleTimeline.PREV_X] * bone.data.scaleX;
y = frames[frames.length + ScaleTimeline.PREV_Y] * bone.data.scaleY;
} else {
// Interpolate between the previous frame and the current frame.
let frame = Animation.binarySearch(frames, time, ScaleTimeline.ENTRIES);
x = frames[frame + ScaleTimeline.PREV_X];
y = frames[frame + ScaleTimeline.PREV_Y];
let frameTime = frames[frame];
let percent = this.getCurvePercent(frame / ScaleTimeline.ENTRIES - 1,
1 - (time - frameTime) / (frames[frame + ScaleTimeline.PREV_TIME] - frameTime));
apply (skeleton: Skeleton, lastTime: number, time: number, events: Array<Event>, alpha: number, pose: MixPose, direction: MixDirection) {
let frames = this.frames;
x = (x + (frames[frame + ScaleTimeline.X] - x) * percent) * bone.data.scaleX;
y = (y + (frames[frame + ScaleTimeline.Y] - y) * percent) * bone.data.scaleY;
}
if (alpha == 1) {
bone.scaleX = x;
bone.scaleY = y;
} else {
let bx = 0, by = 0;
if (setupPose) {
bx = bone.data.scaleX;
by = bone.data.scaleY;
} else {
bx = bone.scaleX;
by = bone.scaleY;
}
// Mixing out uses sign of setup or current pose, else use sign of key.
if (mixingOut) {
x = Math.abs(x) * MathUtils.signum(bx);
y = Math.abs(y) * MathUtils.signum(by);
} else {
bx = Math.abs(bx) * MathUtils.signum(x);
by = Math.abs(by) * MathUtils.signum(y);
}
bone.scaleX = bx + (x - bx) * alpha;
bone.scaleY = by + (y - by) * alpha;
}
}
}
let bone = skeleton.bones[this.boneIndex];
if (time < frames[0]) {
switch (pose) {
case MixPose.setup:
bone.scaleX = bone.data.scaleX;
bone.scaleY = bone.data.scaleY;
return;
case MixPose.current:
bone.scaleX += (bone.data.scaleX - bone.scaleX) * alpha;
bone.scaleY += (bone.data.scaleY - bone.scaleY) * alpha;
}
return;
}
export class ShearTimeline extends TranslateTimeline {
constructor (frameCount: number) {
super(frameCount);
}
let x = 0, y = 0;
if (time >= frames[frames.length - ScaleTimeline.ENTRIES]) { // Time is after last frame.
x = frames[frames.length + ScaleTimeline.PREV_X] * bone.data.scaleX;
y = frames[frames.length + ScaleTimeline.PREV_Y] * bone.data.scaleY;
} else {
// Interpolate between the previous frame and the current frame.
let frame = Animation.binarySearch(frames, time, ScaleTimeline.ENTRIES);
x = frames[frame + ScaleTimeline.PREV_X];
y = frames[frame + ScaleTimeline.PREV_Y];
let frameTime = frames[frame];
let percent = this.getCurvePercent(frame / ScaleTimeline.ENTRIES - 1,
1 - (time - frameTime) / (frames[frame + ScaleTimeline.PREV_TIME] - frameTime));
getPropertyId () {
return (TimelineType.shear << 24) + this.boneIndex;
}
x = (x + (frames[frame + ScaleTimeline.X] - x) * percent) * bone.data.scaleX;
y = (y + (frames[frame + ScaleTimeline.Y] - y) * percent) * bone.data.scaleY;
}
if (alpha == 1) {
bone.scaleX = x;
bone.scaleY = y;
} else {
let bx = 0, by = 0;
if (pose == MixPose.setup) {
bx = bone.data.scaleX;
by = bone.data.scaleY;
} else {
bx = bone.scaleX;
by = bone.scaleY;
}
// Mixing out uses sign of setup or current pose, else use sign of key.
if (direction == MixDirection.out) {
x = Math.abs(x) * MathUtils.signum(bx);
y = Math.abs(y) * MathUtils.signum(by);
} else {
bx = Math.abs(bx) * MathUtils.signum(x);
by = Math.abs(by) * MathUtils.signum(y);
}
bone.scaleX = bx + (x - bx) * alpha;
bone.scaleY = by + (y - by) * alpha;
}
}
}
apply (skeleton: Skeleton, lastTime: number, time: number, events: Array<Event>, alpha: number, setupPose: boolean, mixingOut: boolean) {
let frames = this.frames;
export class ShearTimeline extends TranslateTimeline {
constructor (frameCount: number) {
super(frameCount);
}
let bone = skeleton.bones[this.boneIndex];
if (time < frames[0]) {
if (setupPose) {
bone.shearX = bone.data.shearX;
bone.shearY = bone.data.shearY;
}
return;
}
getPropertyId () {
return (TimelineType.shear << 24) + this.boneIndex;
}
let x = 0, y = 0;
if (time >= frames[frames.length - ShearTimeline.ENTRIES]) { // Time is after last frame.
x = frames[frames.length + ShearTimeline.PREV_X];
y = frames[frames.length + ShearTimeline.PREV_Y];
} else {
// Interpolate between the previous frame and the current frame.
let frame = Animation.binarySearch(frames, time, ShearTimeline.ENTRIES);
x = frames[frame + ShearTimeline.PREV_X];
y = frames[frame + ShearTimeline.PREV_Y];
let frameTime = frames[frame];
let percent = this.getCurvePercent(frame / ShearTimeline.ENTRIES - 1,
1 - (time - frameTime) / (frames[frame + ShearTimeline.PREV_TIME] - frameTime));
apply (skeleton: Skeleton, lastTime: number, time: number, events: Array<Event>, alpha: number, pose: MixPose, direction: MixDirection) {
let frames = this.frames;
x = x + (frames[frame + ShearTimeline.X] - x) * percent;
y = y + (frames[frame + ShearTimeline.Y] - y) * percent;
}
if (setupPose) {
bone.shearX = bone.data.shearX + x * alpha;
bone.shearY = bone.data.shearY + y * alpha;
} else {
bone.shearX += (bone.data.shearX + x - bone.shearX) * alpha;
bone.shearY += (bone.data.shearY + y - bone.shearY) * alpha;
}
}
}
let bone = skeleton.bones[this.boneIndex];
if (time < frames[0]) {
switch (pose) {
case MixPose.setup:
bone.shearX = bone.data.shearX;
bone.shearY = bone.data.shearY;
return;
case MixPose.current:
bone.shearX += (bone.data.shearX - bone.shearX) * alpha;
bone.shearY += (bone.data.shearY - bone.shearY) * alpha;
}
return;
}
export class ColorTimeline extends CurveTimeline {
static ENTRIES = 5;
static PREV_TIME = -5; static PREV_R = -4; static PREV_G = -3; static PREV_B = -2; static PREV_A = -1;
static R = 1; static G = 2; static B = 3; static A = 4;
let x = 0, y = 0;
if (time >= frames[frames.length - ShearTimeline.ENTRIES]) { // Time is after last frame.
x = frames[frames.length + ShearTimeline.PREV_X];
y = frames[frames.length + ShearTimeline.PREV_Y];
} else {
// Interpolate between the previous frame and the current frame.
let frame = Animation.binarySearch(frames, time, ShearTimeline.ENTRIES);
x = frames[frame + ShearTimeline.PREV_X];
y = frames[frame + ShearTimeline.PREV_Y];
let frameTime = frames[frame];
let percent = this.getCurvePercent(frame / ShearTimeline.ENTRIES - 1,
1 - (time - frameTime) / (frames[frame + ShearTimeline.PREV_TIME] - frameTime));
slotIndex: number;
frames: ArrayLike<number>; // time, r, g, b, a, ...
x = x + (frames[frame + ShearTimeline.X] - x) * percent;
y = y + (frames[frame + ShearTimeline.Y] - y) * percent;
}
if (pose == MixPose.setup) {
bone.shearX = bone.data.shearX + x * alpha;
bone.shearY = bone.data.shearY + y * alpha;
} else {
bone.shearX += (bone.data.shearX + x - bone.shearX) * alpha;
bone.shearY += (bone.data.shearY + y - bone.shearY) * alpha;
}
}
}
constructor (frameCount: number) {
super(frameCount);
this.frames = Utils.newFloatArray(frameCount * ColorTimeline.ENTRIES);
}
export class ColorTimeline extends CurveTimeline {
static ENTRIES = 5;
static PREV_TIME = -5; static PREV_R = -4; static PREV_G = -3; static PREV_B = -2; static PREV_A = -1;
static R = 1; static G = 2; static B = 3; static A = 4;
getPropertyId () {
return (TimelineType.color << 24) + this.slotIndex;
}
slotIndex: number;
frames: ArrayLike<number>; // time, r, g, b, a, ...
/** Sets the time and value of the specified keyframe. */
setFrame (frameIndex: number, time: number, r: number, g: number, b: number, a: number) {
frameIndex *= ColorTimeline.ENTRIES;
this.frames[frameIndex] = time;
this.frames[frameIndex + ColorTimeline.R] = r;
this.frames[frameIndex + ColorTimeline.G] = g;
this.frames[frameIndex + ColorTimeline.B] = b;
this.frames[frameIndex + ColorTimeline.A] = a;
}
constructor (frameCount: number) {
super(frameCount);
this.frames = Utils.newFloatArray(frameCount * ColorTimeline.ENTRIES);
}
apply (skeleton: Skeleton, lastTime: number, time: number, events: Array<Event>, alpha: number, setupPose: boolean, mixingOut: boolean) {
let slot = skeleton.slots[this.slotIndex];
let frames = this.frames;
if (time < frames[0]) {
if (setupPose) slot.color.setFromColor(slot.data.color);
return;
}
getPropertyId () {
return (TimelineType.color << 24) + this.slotIndex;
}
let r = 0, g = 0, b = 0, a = 0;
if (time >= frames[frames.length - ColorTimeline.ENTRIES]) { // Time is after last frame.
let i = frames.length;
r = frames[i + ColorTimeline.PREV_R];
g = frames[i + ColorTimeline.PREV_G];
b = frames[i + ColorTimeline.PREV_B];
a = frames[i + ColorTimeline.PREV_A];
} else {
// Interpolate between the previous frame and the current frame.
let frame = Animation.binarySearch(frames, time, ColorTimeline.ENTRIES);
r = frames[frame + ColorTimeline.PREV_R];
g = frames[frame + ColorTimeline.PREV_G];
b = frames[frame + ColorTimeline.PREV_B];
a = frames[frame + ColorTimeline.PREV_A];
let frameTime = frames[frame];
let percent = this.getCurvePercent(frame / ColorTimeline.ENTRIES - 1,
1 - (time - frameTime) / (frames[frame + ColorTimeline.PREV_TIME] - frameTime));
/** Sets the time and value of the specified keyframe. */
setFrame (frameIndex: number, time: number, r: number, g: number, b: number, a: number) {
frameIndex *= ColorTimeline.ENTRIES;
this.frames[frameIndex] = time;
this.frames[frameIndex + ColorTimeline.R] = r;
this.frames[frameIndex + ColorTimeline.G] = g;
this.frames[frameIndex + ColorTimeline.B] = b;
this.frames[frameIndex + ColorTimeline.A] = a;
}
r += (frames[frame + ColorTimeline.R] - r) * percent;
g += (frames[frame + ColorTimeline.G] - g) * percent;
b += (frames[frame + ColorTimeline.B] - b) * percent;
a += (frames[frame + ColorTimeline.A] - a) * percent;
}
if (alpha == 1)
slot.color.set(r, g, b, a);
else {
let color = slot.color;
if (setupPose) color.setFromColor(slot.data.color);
color.add((r - color.r) * alpha, (g - color.g) * alpha, (b - color.b) * alpha, (a - color.a) * alpha);
}
}
}
apply (skeleton: Skeleton, lastTime: number, time: number, events: Array<Event>, alpha: number, pose: MixPose, direction: MixDirection) {
let slot = skeleton.slots[this.slotIndex];
let frames = this.frames;
if (time < frames[0]) {
switch (pose) {
case MixPose.setup:
slot.color.setFromColor(slot.data.color);
return;
case MixPose.current:
let color = slot.color, setup = slot.data.color;
color.add((setup.r - color.r) * alpha, (setup.g - color.g) * alpha, (setup.b - color.b) * alpha,
(setup.a - color.a) * alpha);
}
return;
}
export class AttachmentTimeline implements Timeline {
slotIndex: number;
frames: ArrayLike<number> // time, ...
attachmentNames: Array<string>;
let r = 0, g = 0, b = 0, a = 0;
if (time >= frames[frames.length - ColorTimeline.ENTRIES]) { // Time is after last frame.
let i = frames.length;
r = frames[i + ColorTimeline.PREV_R];
g = frames[i + ColorTimeline.PREV_G];
b = frames[i + ColorTimeline.PREV_B];
a = frames[i + ColorTimeline.PREV_A];
} else {
// Interpolate between the previous frame and the current frame.
let frame = Animation.binarySearch(frames, time, ColorTimeline.ENTRIES);
r = frames[frame + ColorTimeline.PREV_R];
g = frames[frame + ColorTimeline.PREV_G];
b = frames[frame + ColorTimeline.PREV_B];
a = frames[frame + ColorTimeline.PREV_A];
let frameTime = frames[frame];
let percent = this.getCurvePercent(frame / ColorTimeline.ENTRIES - 1,
1 - (time - frameTime) / (frames[frame + ColorTimeline.PREV_TIME] - frameTime));
constructor (frameCount: number) {
this.frames = Utils.newFloatArray(frameCount);
this.attachmentNames = new Array<string>(frameCount);
}
r += (frames[frame + ColorTimeline.R] - r) * percent;
g += (frames[frame + ColorTimeline.G] - g) * percent;
b += (frames[frame + ColorTimeline.B] - b) * percent;
a += (frames[frame + ColorTimeline.A] - a) * percent;
}
if (alpha == 1)
slot.color.set(r, g, b, a);
else {
let color = slot.color;
if (pose == MixPose.setup) color.setFromColor(slot.data.color);
color.add((r - color.r) * alpha, (g - color.g) * alpha, (b - color.b) * alpha, (a - color.a) * alpha);
}
}
}
getPropertyId () {
return (TimelineType.attachment << 24) + this.slotIndex;
}
export class TwoColorTimeline extends CurveTimeline {
static ENTRIES = 8;
static PREV_TIME = -8; static PREV_R = -7; static PREV_G = -6; static PREV_B = -5; static PREV_A = -4;
static PREV_R2 = -3; static PREV_G2 = -2; static PREV_B2 = -1;
static R = 1; static G = 2; static B = 3; static A = 4; static R2 = 5; static G2 = 6; static B2 = 7;
getFrameCount () {
return this.frames.length;
}
slotIndex: number;
frames: ArrayLike<number>; // time, r, g, b, a, r2, g2, b2, ...
/** Sets the time and value of the specified keyframe. */
setFrame (frameIndex: number, time: number, attachmentName: string) {
this.frames[frameIndex] = time;
this.attachmentNames[frameIndex] = attachmentName;
}
constructor (frameCount: number) {
super(frameCount);
this.frames = Utils.newFloatArray(frameCount * TwoColorTimeline.ENTRIES);
}
apply (skeleton: Skeleton, lastTime: number, time: number, events: Array<Event>, alpha: number, setupPose: boolean, mixingOut: boolean) {
let slot = skeleton.slots[this.slotIndex];
if (mixingOut && setupPose) {
let attachmentName = slot.data.attachmentName;
slot.setAttachment(attachmentName == null ? null : skeleton.getAttachment(this.slotIndex, attachmentName));
return;
}
getPropertyId () {
return (TimelineType.twoColor << 24) + this.slotIndex;
}
let frames = this.frames;
if (time < frames[0]) {
if (setupPose) {
let attachmentName = slot.data.attachmentName;
slot.setAttachment(attachmentName == null ? null : skeleton.getAttachment(this.slotIndex, attachmentName));
}
return;
}
/** Sets the time and value of the specified keyframe. */
setFrame (frameIndex: number, time: number, r: number, g: number, b: number, a: number, r2: number, g2: number, b2: number) {
frameIndex *= TwoColorTimeline.ENTRIES;
this.frames[frameIndex] = time;
this.frames[frameIndex + TwoColorTimeline.R] = r;
this.frames[frameIndex + TwoColorTimeline.G] = g;
this.frames[frameIndex + TwoColorTimeline.B] = b;
this.frames[frameIndex + TwoColorTimeline.A] = a;
this.frames[frameIndex + TwoColorTimeline.R2] = r2;
this.frames[frameIndex + TwoColorTimeline.G2] = g2;
this.frames[frameIndex + TwoColorTimeline.B2] = b2;
}
let frameIndex = 0;
if (time >= frames[frames.length - 1]) // Time is after last frame.
frameIndex = frames.length - 1;
else
frameIndex = Animation.binarySearch(frames, time, 1) - 1;
apply (skeleton: Skeleton, lastTime: number, time: number, events: Array<Event>, alpha: number, pose: MixPose, direction: MixDirection) {
let slot = skeleton.slots[this.slotIndex];
let frames = this.frames;
if (time < frames[0]) {
switch (pose) {
case MixPose.setup:
slot.color.setFromColor(slot.data.color);
slot.darkColor.setFromColor(slot.data.darkColor);
return;
case MixPose.current:
let light = slot.color, dark = slot.darkColor, setupLight = slot.data.color, setupDark = slot.data.darkColor;
light.add((setupLight.r - light.r) * alpha, (setupLight.g - light.g) * alpha, (setupLight.b - light.b) * alpha,
(setupLight.a - light.a) * alpha);
dark.add((setupDark.r - dark.r) * alpha, (setupDark.g - dark.g) * alpha, (setupDark.b - dark.b) * alpha, 0);
}
return;
}
let attachmentName = this.attachmentNames[frameIndex];
skeleton.slots[this.slotIndex]
.setAttachment(attachmentName == null ? null : skeleton.getAttachment(this.slotIndex, attachmentName));
}
}
let r = 0, g = 0, b = 0, a = 0, r2 = 0, g2 = 0, b2 = 0;
if (time >= frames[frames.length - TwoColorTimeline.ENTRIES]) { // Time is after last frame.
let i = frames.length;
r = frames[i + TwoColorTimeline.PREV_R];
g = frames[i + TwoColorTimeline.PREV_G];
b = frames[i + TwoColorTimeline.PREV_B];
a = frames[i + TwoColorTimeline.PREV_A];
r2 = frames[i + TwoColorTimeline.PREV_R2];
g2 = frames[i + TwoColorTimeline.PREV_G2];
b2 = frames[i + TwoColorTimeline.PREV_B2];
} else {
// Interpolate between the previous frame and the current frame.
let frame = Animation.binarySearch(frames, time, TwoColorTimeline.ENTRIES);
r = frames[frame + TwoColorTimeline.PREV_R];
g = frames[frame + TwoColorTimeline.PREV_G];
b = frames[frame + TwoColorTimeline.PREV_B];
a = frames[frame + TwoColorTimeline.PREV_A];
r2 = frames[frame + TwoColorTimeline.PREV_R2];
g2 = frames[frame + TwoColorTimeline.PREV_G2];
b2 = frames[frame + TwoColorTimeline.PREV_B2];
let frameTime = frames[frame];
let percent = this.getCurvePercent(frame / TwoColorTimeline.ENTRIES - 1,
1 - (time - frameTime) / (frames[frame + TwoColorTimeline.PREV_TIME] - frameTime));
export class DeformTimeline extends CurveTimeline {
slotIndex: number;
attachment: VertexAttachment;
frames: ArrayLike<number>; // time, ...
frameVertices: Array<ArrayLike<number>>;
r += (frames[frame + TwoColorTimeline.R] - r) * percent;
g += (frames[frame + TwoColorTimeline.G] - g) * percent;
b += (frames[frame + TwoColorTimeline.B] - b) * percent;
a += (frames[frame + TwoColorTimeline.A] - a) * percent;
r2 += (frames[frame + TwoColorTimeline.R2] - r2) * percent;
g2 += (frames[frame + TwoColorTimeline.G2] - g2) * percent;
b2 += (frames[frame + TwoColorTimeline.B2] - b2) * percent;
}
if (alpha == 1) {
slot.color.set(r, g, b, a);
slot.darkColor.set(r2, g2, b2, 1);
} else {
let light = slot.color, dark = slot.darkColor;
if (pose == MixPose.setup) {
light.setFromColor(slot.data.color);
dark.setFromColor(slot.data.darkColor);
}
light.add((r - light.r) * alpha, (g - light.g) * alpha, (b - light.b) * alpha, (a - light.a) * alpha);
dark.add((r2 - dark.r) * alpha, (g2 - dark.g) * alpha, (b2 - dark.b) * alpha, 0);
}
}
}
constructor (frameCount: number) {
super(frameCount);
this.frames = Utils.newFloatArray(frameCount);
this.frameVertices = new Array<ArrayLike<number>>(frameCount);
}
export class AttachmentTimeline implements Timeline {
slotIndex: number;
frames: ArrayLike<number> // time, ...
attachmentNames: Array<string>;
getPropertyId () {
return (TimelineType.deform << 24) + this.slotIndex;
}
constructor (frameCount: number) {
this.frames = Utils.newFloatArray(frameCount);
this.attachmentNames = new Array<string>(frameCount);
}
/** Sets the time of the specified keyframe. */
setFrame (frameIndex: number, time: number, vertices: ArrayLike<number>) {
this.frames[frameIndex] = time;
this.frameVertices[frameIndex] = vertices;
}
getPropertyId () {
return (TimelineType.attachment << 24) + this.slotIndex;
}
apply (skeleton: Skeleton, lastTime: number, time: number, firedEvents: Array<Event>, alpha: number, setupPose: boolean, mixingOut: boolean) {
let slot: Slot = skeleton.slots[this.slotIndex];
let slotAttachment: Attachment = slot.getAttachment();
if (!(slotAttachment instanceof VertexAttachment) || !(<VertexAttachment>slotAttachment).applyDeform(this.attachment)) return;
getFrameCount () {
return this.frames.length;
}
let frames = this.frames;
let verticesArray: Array<number> = slot.attachmentVertices;
if (time < frames[0]) {
if (setupPose) Utils.setArraySize(verticesArray, 0);
return;
}
/** Sets the time and value of the specified keyframe. */
setFrame (frameIndex: number, time: number, attachmentName: string) {
this.frames[frameIndex] = time;
this.attachmentNames[frameIndex] = attachmentName;
}
let frameVertices = this.frameVertices;
let vertexCount = frameVertices[0].length;
apply (skeleton: Skeleton, lastTime: number, time: number, events: Array<Event>, alpha: number, pose: MixPose, direction: MixDirection) {
let slot = skeleton.slots[this.slotIndex];
if (direction == MixDirection.out && pose == MixPose.setup) {
let attachmentName = slot.data.attachmentName;
slot.setAttachment(attachmentName == null ? null : skeleton.getAttachment(this.slotIndex, attachmentName));
return;
}
if (verticesArray.length != vertexCount) alpha = 1; // Don't mix from uninitialized slot vertices.
let vertices: Array<number> = Utils.setArraySize(verticesArray, vertexCount);
let frames = this.frames;
if (time < frames[0]) {
if (pose == MixPose.setup) {
let attachmentName = slot.data.attachmentName;
slot.setAttachment(attachmentName == null ? null : skeleton.getAttachment(this.slotIndex, attachmentName));
}
return;
}
if (time >= frames[frames.length - 1]) { // Time is after last frame.
let lastVertices = frameVertices[frames.length - 1];
if (alpha == 1) {
Utils.arrayCopy(lastVertices, 0, vertices, 0, vertexCount);
} else if (setupPose) {
let vertexAttachment = slotAttachment as VertexAttachment;
if (vertexAttachment.bones == null) {
// Unweighted vertex positions, with alpha.
let setupVertices = vertexAttachment.vertices;
for (let i = 0; i < vertexCount; i++) {
let setup = setupVertices[i];
vertices[i] = setup + (lastVertices[i] - setup) * alpha;
}
} else {
// Weighted deform offsets, with alpha.
for (let i = 0; i < vertexCount; i++)
vertices[i] = lastVertices[i] * alpha;
}
} else {
for (let i = 0; i < vertexCount; i++)
vertices[i] += (lastVertices[i] - vertices[i]) * alpha;
}
return;
}
let frameIndex = 0;
if (time >= frames[frames.length - 1]) // Time is after last frame.
frameIndex = frames.length - 1;
else
frameIndex = Animation.binarySearch(frames, time, 1) - 1;
// Interpolate between the previous frame and the current frame.
let frame = Animation.binarySearch(frames, time);
let prevVertices = frameVertices[frame - 1];
let nextVertices = frameVertices[frame];
let frameTime = frames[frame];
let percent = this.getCurvePercent(frame - 1, 1 - (time - frameTime) / (frames[frame - 1] - frameTime));
let attachmentName = this.attachmentNames[frameIndex];
skeleton.slots[this.slotIndex]
.setAttachment(attachmentName == null ? null : skeleton.getAttachment(this.slotIndex, attachmentName));
}
}
if (alpha == 1) {
for (let i = 0; i < vertexCount; i++) {
let prev = prevVertices[i];
vertices[i] = prev + (nextVertices[i] - prev) * percent;
}
} else if (setupPose) {
let vertexAttachment = slotAttachment as VertexAttachment;
if (vertexAttachment.bones == null) {
// Unweighted vertex positions, with alpha.
let setupVertices = vertexAttachment.vertices;
for (let i = 0; i < vertexCount; i++) {
let prev = prevVertices[i], setup = setupVertices[i];
vertices[i] = setup + (prev + (nextVertices[i] - prev) * percent - setup) * alpha;
}
} else {
// Weighted deform offsets, with alpha.
for (let i = 0; i < vertexCount; i++) {
let prev = prevVertices[i];
vertices[i] = (prev + (nextVertices[i] - prev) * percent) * alpha;
}
}
} else {
// Vertex positions or deform offsets, with alpha.
for (let i = 0; i < vertexCount; i++) {
let prev = prevVertices[i];
vertices[i] += (prev + (nextVertices[i] - prev) * percent - vertices[i]) * alpha;
}
}
}
}
let zeros : ArrayLike<number> = null;
export class EventTimeline implements Timeline {
frames: ArrayLike<number>; // time, ...
events: Array<Event>;
export class DeformTimeline extends CurveTimeline {
slotIndex: number;
attachment: VertexAttachment;
frames: ArrayLike<number>; // time, ...
frameVertices: Array<ArrayLike<number>>;
constructor (frameCount: number) {
this.frames = Utils.newFloatArray(frameCount);
this.events = new Array<Event>(frameCount);
}
constructor (frameCount: number) {
super(frameCount);
this.frames = Utils.newFloatArray(frameCount);
this.frameVertices = new Array<ArrayLike<number>>(frameCount);
if (zeros == null) zeros = Utils.newFloatArray(64);
}
getPropertyId () {
return TimelineType.event << 24;
}
getPropertyId () {
return (TimelineType.deform << 27) + + this.attachment.id + this.slotIndex;
}
getFrameCount () {
return this.frames.length;
}
/** Sets the time of the specified keyframe. */
setFrame (frameIndex: number, time: number, vertices: ArrayLike<number>) {
this.frames[frameIndex] = time;
this.frameVertices[frameIndex] = vertices;
}
/** Sets the time of the specified keyframe. */
setFrame (frameIndex: number, event: Event) {
this.frames[frameIndex] = event.time;
this.events[frameIndex] = event;
}
apply (skeleton: Skeleton, lastTime: number, time: number, firedEvents: Array<Event>, alpha: number, pose: MixPose, direction: MixDirection) {
let slot: Slot = skeleton.slots[this.slotIndex];
let slotAttachment: Attachment = slot.getAttachment();
if (!(slotAttachment instanceof VertexAttachment) || !(<VertexAttachment>slotAttachment).applyDeform(this.attachment)) return;
/** Fires events for frames > lastTime and <= time. */
apply (skeleton: Skeleton, lastTime: number, time: number, firedEvents: Array<Event>, alpha: number, setupPose: boolean, mixingOut: boolean) {
if (firedEvents == null) return;
let frames = this.frames;
let frameCount = this.frames.length;
let verticesArray: Array<number> = slot.attachmentVertices;
let frameVertices = this.frameVertices;
let vertexCount = frameVertices[0].length;
let vertices: Array<number> = Utils.setArraySize(verticesArray, vertexCount);
if (lastTime > time) { // Fire events after last time for looped animations.
this.apply(skeleton, lastTime, Number.MAX_VALUE, firedEvents, alpha, setupPose, mixingOut);
lastTime = -1;
} else if (lastTime >= frames[frameCount - 1]) // Last time is after last frame.
return;
if (time < frames[0]) return; // Time is before first frame.
let frames = this.frames;
if (time < frames[0]) {
let vertexAttachment = <VertexAttachment>slotAttachment;
switch (pose) {
case MixPose.setup:
let zeroVertices: ArrayLike<number>;
if (vertexAttachment.bones == null) {
// Unweighted vertex positions (setup pose).
zeroVertices = vertexAttachment.vertices;
} else {
// Weighted deform offsets (zeros).
zeroVertices = zeros;
if (zeroVertices.length < vertexCount) zeros = zeroVertices = Utils.newFloatArray(vertexCount);
}
Utils.arrayCopy(zeroVertices, 0, vertices, 0, vertexCount);
return;
case MixPose.current:
if (alpha == 1) break;
if (vertexAttachment.bones == null) {
// Unweighted vertex positions.
let setupVertices = vertexAttachment.vertices;
for (let i = 0; i < vertexCount; i++)
vertices[i] += (setupVertices[i] - vertices[i]) * alpha;
} else {
// Weighted deform offsets.
alpha = 1 - alpha;
for (var i = 0; i < vertexCount; i++)
vertices[i] *= alpha;
}
}
return;
}
let frame = 0;
if (lastTime < frames[0])
frame = 0;
else {
frame = Animation.binarySearch(frames, lastTime);
let frameTime = frames[frame];
while (frame > 0) { // Fire multiple events with the same frame.
if (frames[frame - 1] != frameTime) break;
frame--;
}
}
for (; frame < frameCount && time >= frames[frame]; frame++)
firedEvents.push(this.events[frame]);
}
}
if (time >= frames[frames.length - 1]) { // Time is after last frame.
let lastVertices = frameVertices[frames.length - 1];
if (alpha == 1) {
Utils.arrayCopy(lastVertices, 0, vertices, 0, vertexCount);
} else if (pose == MixPose.setup) {
let vertexAttachment = slotAttachment as VertexAttachment;
if (vertexAttachment.bones == null) {
// Unweighted vertex positions, with alpha.
let setupVertices = vertexAttachment.vertices;
for (let i = 0; i < vertexCount; i++) {
let setup = setupVertices[i];
vertices[i] = setup + (lastVertices[i] - setup) * alpha;
}
} else {
// Weighted deform offsets, with alpha.
for (let i = 0; i < vertexCount; i++)
vertices[i] = lastVertices[i] * alpha;
}
} else {
for (let i = 0; i < vertexCount; i++)
vertices[i] += (lastVertices[i] - vertices[i]) * alpha;
}
return;
}
export class DrawOrderTimeline implements Timeline {
frames: ArrayLike<number>; // time, ...
drawOrders: Array<Array<number>>;
// Interpolate between the previous frame and the current frame.
let frame = Animation.binarySearch(frames, time);
let prevVertices = frameVertices[frame - 1];
let nextVertices = frameVertices[frame];
let frameTime = frames[frame];
let percent = this.getCurvePercent(frame - 1, 1 - (time - frameTime) / (frames[frame - 1] - frameTime));
constructor (frameCount: number) {
this.frames = Utils.newFloatArray(frameCount);
this.drawOrders = new Array<Array<number>>(frameCount);
}
if (alpha == 1) {
for (let i = 0; i < vertexCount; i++) {
let prev = prevVertices[i];
vertices[i] = prev + (nextVertices[i] - prev) * percent;
}
} else if (pose == MixPose.setup) {
let vertexAttachment = slotAttachment as VertexAttachment;
if (vertexAttachment.bones == null) {
// Unweighted vertex positions, with alpha.
let setupVertices = vertexAttachment.vertices;
for (let i = 0; i < vertexCount; i++) {
let prev = prevVertices[i], setup = setupVertices[i];
vertices[i] = setup + (prev + (nextVertices[i] - prev) * percent - setup) * alpha;
}
} else {
// Weighted deform offsets, with alpha.
for (let i = 0; i < vertexCount; i++) {
let prev = prevVertices[i];
vertices[i] = (prev + (nextVertices[i] - prev) * percent) * alpha;
}
}
} else {
// Vertex positions or deform offsets, with alpha.
for (let i = 0; i < vertexCount; i++) {
let prev = prevVertices[i];
vertices[i] += (prev + (nextVertices[i] - prev) * percent - vertices[i]) * alpha;
}
}
}
}
getPropertyId () {
return TimelineType.drawOrder << 24;
}
export class EventTimeline implements Timeline {
frames: ArrayLike<number>; // time, ...
events: Array<Event>;
getFrameCount () {
return this.frames.length;
}
constructor (frameCount: number) {
this.frames = Utils.newFloatArray(frameCount);
this.events = new Array<Event>(frameCount);
}
/** Sets the time of the specified keyframe.
* @param drawOrder May be null to use bind pose draw order. */
setFrame (frameIndex: number, time: number, drawOrder: Array<number>) {
this.frames[frameIndex] = time;
this.drawOrders[frameIndex] = drawOrder;
}
getPropertyId () {
return TimelineType.event << 24;
}
apply (skeleton: Skeleton, lastTime: number, time: number, firedEvents: Array<Event>, alpha: number, setupPose: boolean, mixingOut: boolean) {
let drawOrder: Array<Slot> = skeleton.drawOrder;
let slots: Array<Slot> = skeleton.slots;
if (mixingOut && setupPose) {
Utils.arrayCopy(skeleton.slots, 0, skeleton.drawOrder, 0, skeleton.slots.length);
return;
}
getFrameCount () {
return this.frames.length;
}
let frames = this.frames;
if (time < frames[0]) {
if (setupPose) Utils.arrayCopy(skeleton.slots, 0, skeleton.drawOrder, 0, skeleton.slots.length);
return;
}
/** Sets the time of the specified keyframe. */
setFrame (frameIndex: number, event: Event) {
this.frames[frameIndex] = event.time;
this.events[frameIndex] = event;
}
let frame = 0;
if (time >= frames[frames.length - 1]) // Time is after last frame.
frame = frames.length - 1;
else
frame = Animation.binarySearch(frames, time) - 1;
/** Fires events for frames > lastTime and <= time. */
apply (skeleton: Skeleton, lastTime: number, time: number, firedEvents: Array<Event>, alpha: number, pose: MixPose, direction: MixDirection) {
if (firedEvents == null) return;
let frames = this.frames;
let frameCount = this.frames.length;
let drawOrderToSetupIndex = this.drawOrders[frame];
if (drawOrderToSetupIndex == null)
Utils.arrayCopy(slots, 0, drawOrder, 0, slots.length);
else {
for (let i = 0, n = drawOrderToSetupIndex.length; i < n; i++)
drawOrder[i] = slots[drawOrderToSetupIndex[i]];
}
}
}
if (lastTime > time) { // Fire events after last time for looped animations.
this.apply(skeleton, lastTime, Number.MAX_VALUE, firedEvents, alpha, pose, direction);
lastTime = -1;
} else if (lastTime >= frames[frameCount - 1]) // Last time is after last frame.
return;
if (time < frames[0]) return; // Time is before first frame.
export class IkConstraintTimeline extends CurveTimeline {
static ENTRIES = 3;
static PREV_TIME = -3; static PREV_MIX = -2; static PREV_BEND_DIRECTION = -1;
static MIX = 1; static BEND_DIRECTION = 2;
let frame = 0;
if (lastTime < frames[0])
frame = 0;
else {
frame = Animation.binarySearch(frames, lastTime);
let frameTime = frames[frame];
while (frame > 0) { // Fire multiple events with the same frame.
if (frames[frame - 1] != frameTime) break;
frame--;
}
}
for (; frame < frameCount && time >= frames[frame]; frame++)
firedEvents.push(this.events[frame]);
}
}
ikConstraintIndex: number;
frames: ArrayLike<number>; // time, mix, bendDirection, ...
export class DrawOrderTimeline implements Timeline {
frames: ArrayLike<number>; // time, ...
drawOrders: Array<Array<number>>;
constructor (frameCount: number) {
super(frameCount);
this.frames = Utils.newFloatArray(frameCount * IkConstraintTimeline.ENTRIES);
}
constructor (frameCount: number) {
this.frames = Utils.newFloatArray(frameCount);
this.drawOrders = new Array<Array<number>>(frameCount);
}
getPropertyId () {
return (TimelineType.ikConstraint << 24) + this.ikConstraintIndex;
}
getPropertyId () {
return TimelineType.drawOrder << 24;
}
/** Sets the time, mix and bend direction of the specified keyframe. */
setFrame (frameIndex: number, time: number, mix: number, bendDirection: number) {
frameIndex *= IkConstraintTimeline.ENTRIES;
this.frames[frameIndex] = time;
this.frames[frameIndex + IkConstraintTimeline.MIX] = mix;
this.frames[frameIndex + IkConstraintTimeline.BEND_DIRECTION] = bendDirection;
}
getFrameCount () {
return this.frames.length;
}
apply (skeleton: Skeleton, lastTime: number, time: number, firedEvents: Array<Event>, alpha: number, setupPose: boolean, mixingOut: boolean) {
let frames = this.frames;
let constraint: IkConstraint = skeleton.ikConstraints[this.ikConstraintIndex];
if (time < frames[0]) {
if (setupPose) {
constraint.mix = constraint.data.mix;
constraint.bendDirection = constraint.data.bendDirection;
}
return;
}
/** Sets the time of the specified keyframe.
* @param drawOrder May be null to use bind pose draw order. */
setFrame (frameIndex: number, time: number, drawOrder: Array<number>) {
this.frames[frameIndex] = time;
this.drawOrders[frameIndex] = drawOrder;
}
if (time >= frames[frames.length - IkConstraintTimeline.ENTRIES]) { // Time is after last frame.
if (setupPose) {
constraint.mix = constraint.data.mix + (frames[frames.length + IkConstraintTimeline.PREV_MIX] - constraint.data.mix) * alpha;
constraint.bendDirection = mixingOut ? constraint.data.bendDirection
: frames[frames.length + IkConstraintTimeline.PREV_BEND_DIRECTION];
} else {
constraint.mix += (frames[frames.length + IkConstraintTimeline.PREV_MIX] - constraint.mix) * alpha;
if (!mixingOut) constraint.bendDirection = frames[frames.length + IkConstraintTimeline.PREV_BEND_DIRECTION];
}
return;
}
apply (skeleton: Skeleton, lastTime: number, time: number, firedEvents: Array<Event>, alpha: number, pose: MixPose, direction: MixDirection) {
let drawOrder: Array<Slot> = skeleton.drawOrder;
let slots: Array<Slot> = skeleton.slots;
if (direction == MixDirection.out && pose == MixPose.setup) {
Utils.arrayCopy(skeleton.slots, 0, skeleton.drawOrder, 0, skeleton.slots.length);
return;
}
// Interpolate between the previous frame and the current frame.
let frame = Animation.binarySearch(frames, time, IkConstraintTimeline.ENTRIES);
let mix = frames[frame + IkConstraintTimeline.PREV_MIX];
let frameTime = frames[frame];
let percent = this.getCurvePercent(frame / IkConstraintTimeline.ENTRIES - 1,
1 - (time - frameTime) / (frames[frame + IkConstraintTimeline.PREV_TIME] - frameTime));
let frames = this.frames;
if (time < frames[0]) {
if (pose == MixPose.setup) Utils.arrayCopy(skeleton.slots, 0, skeleton.drawOrder, 0, skeleton.slots.length);
return;
}
if (setupPose) {
constraint.mix = constraint.data.mix + (mix + (frames[frame + IkConstraintTimeline.MIX] - mix) * percent - constraint.data.mix) * alpha;
constraint.bendDirection = mixingOut ? constraint.data.bendDirection : frames[frame + IkConstraintTimeline.PREV_BEND_DIRECTION];
} else {
constraint.mix += (mix + (frames[frame + IkConstraintTimeline.MIX] - mix) * percent - constraint.mix) * alpha;
if (!mixingOut) constraint.bendDirection = frames[frame + IkConstraintTimeline.PREV_BEND_DIRECTION];
}
}
}
let frame = 0;
if (time >= frames[frames.length - 1]) // Time is after last frame.
frame = frames.length - 1;
else
frame = Animation.binarySearch(frames, time) - 1;
export class TransformConstraintTimeline extends CurveTimeline {
static ENTRIES = 5;
static PREV_TIME = -5; static PREV_ROTATE = -4; static PREV_TRANSLATE = -3; static PREV_SCALE = -2; static PREV_SHEAR = -1;
static ROTATE = 1; static TRANSLATE = 2; static SCALE = 3; static SHEAR = 4;
let drawOrderToSetupIndex = this.drawOrders[frame];
if (drawOrderToSetupIndex == null)
Utils.arrayCopy(slots, 0, drawOrder, 0, slots.length);
else {
for (let i = 0, n = drawOrderToSetupIndex.length; i < n; i++)
drawOrder[i] = slots[drawOrderToSetupIndex[i]];
}
}
}
transformConstraintIndex: number;
frames: ArrayLike<number>; // time, rotate mix, translate mix, scale mix, shear mix, ...
export class IkConstraintTimeline extends CurveTimeline {
static ENTRIES = 3;
static PREV_TIME = -3; static PREV_MIX = -2; static PREV_BEND_DIRECTION = -1;
static MIX = 1; static BEND_DIRECTION = 2;
constructor (frameCount: number) {
super(frameCount);
this.frames = Utils.newFloatArray(frameCount * TransformConstraintTimeline.ENTRIES);
}
ikConstraintIndex: number;
frames: ArrayLike<number>; // time, mix, bendDirection, ...
getPropertyId () {
return (TimelineType.transformConstraint << 24) + this.transformConstraintIndex;
}
constructor (frameCount: number) {
super(frameCount);
this.frames = Utils.newFloatArray(frameCount * IkConstraintTimeline.ENTRIES);
}
/** Sets the time and mixes of the specified keyframe. */
setFrame (frameIndex: number, time: number, rotateMix: number, translateMix: number, scaleMix: number, shearMix: number) {
frameIndex *= TransformConstraintTimeline.ENTRIES;
this.frames[frameIndex] = time;
this.frames[frameIndex + TransformConstraintTimeline.ROTATE] = rotateMix;
this.frames[frameIndex + TransformConstraintTimeline.TRANSLATE] = translateMix;
this.frames[frameIndex + TransformConstraintTimeline.SCALE] = scaleMix;
this.frames[frameIndex + TransformConstraintTimeline.SHEAR] = shearMix;
}
getPropertyId () {
return (TimelineType.ikConstraint << 24) + this.ikConstraintIndex;
}
apply (skeleton: Skeleton, lastTime: number, time: number, firedEvents: Array<Event>, alpha: number, setupPose: boolean, mixingOut: boolean) {
let frames = this.frames;
/** Sets the time, mix and bend direction of the specified keyframe. */
setFrame (frameIndex: number, time: number, mix: number, bendDirection: number) {
frameIndex *= IkConstraintTimeline.ENTRIES;
this.frames[frameIndex] = time;
this.frames[frameIndex + IkConstraintTimeline.MIX] = mix;
this.frames[frameIndex + IkConstraintTimeline.BEND_DIRECTION] = bendDirection;
}
let constraint: TransformConstraint = skeleton.transformConstraints[this.transformConstraintIndex];
if (time < frames[0]) {
if (setupPose) {
let data = constraint.data;
constraint.rotateMix = data.rotateMix;
constraint.translateMix = data.rotateMix;
constraint.scaleMix = data.scaleMix;
constraint.shearMix = data.shearMix;
}
return;
}
apply (skeleton: Skeleton, lastTime: number, time: number, firedEvents: Array<Event>, alpha: number, pose: MixPose, direction: MixDirection) {
let frames = this.frames;
let constraint: IkConstraint = skeleton.ikConstraints[this.ikConstraintIndex];
if (time < frames[0]) {
switch (pose) {
case MixPose.setup:
constraint.mix = constraint.data.mix;
constraint.bendDirection = constraint.data.bendDirection;
return;
case MixPose.current:
constraint.mix += (constraint.data.mix - constraint.mix) * alpha;
constraint.bendDirection = constraint.data.bendDirection;
}
return;
}
let rotate = 0, translate = 0, scale = 0, shear = 0;
if (time >= frames[frames.length - TransformConstraintTimeline.ENTRIES]) { // Time is after last frame.
let i = frames.length;
rotate = frames[i + TransformConstraintTimeline.PREV_ROTATE];
translate = frames[i + TransformConstraintTimeline.PREV_TRANSLATE];
scale = frames[i + TransformConstraintTimeline.PREV_SCALE];
shear = frames[i + TransformConstraintTimeline.PREV_SHEAR];
} else {
// Interpolate between the previous frame and the current frame.
let frame = Animation.binarySearch(frames, time, TransformConstraintTimeline.ENTRIES);
rotate = frames[frame + TransformConstraintTimeline.PREV_ROTATE];
translate = frames[frame + TransformConstraintTimeline.PREV_TRANSLATE];
scale = frames[frame + TransformConstraintTimeline.PREV_SCALE];
shear = frames[frame + TransformConstraintTimeline.PREV_SHEAR];
let frameTime = frames[frame];
let percent = this.getCurvePercent(frame / TransformConstraintTimeline.ENTRIES - 1,
1 - (time - frameTime) / (frames[frame + TransformConstraintTimeline.PREV_TIME] - frameTime));
if (time >= frames[frames.length - IkConstraintTimeline.ENTRIES]) { // Time is after last frame.
if (pose == MixPose.setup) {
constraint.mix = constraint.data.mix + (frames[frames.length + IkConstraintTimeline.PREV_MIX] - constraint.data.mix) * alpha;
constraint.bendDirection = direction == MixDirection.out ? constraint.data.bendDirection
: frames[frames.length + IkConstraintTimeline.PREV_BEND_DIRECTION];
} else {
constraint.mix += (frames[frames.length + IkConstraintTimeline.PREV_MIX] - constraint.mix) * alpha;
if (direction == MixDirection.in) constraint.bendDirection = frames[frames.length + IkConstraintTimeline.PREV_BEND_DIRECTION];
}
return;
}
rotate += (frames[frame + TransformConstraintTimeline.ROTATE] - rotate) * percent;
translate += (frames[frame + TransformConstraintTimeline.TRANSLATE] - translate) * percent;
scale += (frames[frame + TransformConstraintTimeline.SCALE] - scale) * percent;
shear += (frames[frame + TransformConstraintTimeline.SHEAR] - shear) * percent;
}
if (setupPose) {
let data = constraint.data;
constraint.rotateMix = data.rotateMix + (rotate - data.rotateMix) * alpha;
constraint.translateMix = data.translateMix + (translate - data.translateMix) * alpha;
constraint.scaleMix = data.scaleMix + (scale - data.scaleMix) * alpha;
constraint.shearMix = data.shearMix + (shear - data.shearMix) * alpha;
} else {
constraint.rotateMix += (rotate - constraint.rotateMix) * alpha;
constraint.translateMix += (translate - constraint.translateMix) * alpha;
constraint.scaleMix += (scale - constraint.scaleMix) * alpha;
constraint.shearMix += (shear - constraint.shearMix) * alpha;
}
}
}
// Interpolate between the previous frame and the current frame.
let frame = Animation.binarySearch(frames, time, IkConstraintTimeline.ENTRIES);
let mix = frames[frame + IkConstraintTimeline.PREV_MIX];
let frameTime = frames[frame];
let percent = this.getCurvePercent(frame / IkConstraintTimeline.ENTRIES - 1,
1 - (time - frameTime) / (frames[frame + IkConstraintTimeline.PREV_TIME] - frameTime));
export class PathConstraintPositionTimeline extends CurveTimeline {
static ENTRIES = 2;
static PREV_TIME = -2; static PREV_VALUE = -1;
static VALUE = 1;
if (pose == MixPose.setup) {
constraint.mix = constraint.data.mix + (mix + (frames[frame + IkConstraintTimeline.MIX] - mix) * percent - constraint.data.mix) * alpha;
constraint.bendDirection = direction == MixDirection.out ? constraint.data.bendDirection : frames[frame + IkConstraintTimeline.PREV_BEND_DIRECTION];
} else {
constraint.mix += (mix + (frames[frame + IkConstraintTimeline.MIX] - mix) * percent - constraint.mix) * alpha;
if (direction == MixDirection.in) constraint.bendDirection = frames[frame + IkConstraintTimeline.PREV_BEND_DIRECTION];
}
}
}
pathConstraintIndex: number;
export class TransformConstraintTimeline extends CurveTimeline {
static ENTRIES = 5;
static PREV_TIME = -5; static PREV_ROTATE = -4; static PREV_TRANSLATE = -3; static PREV_SCALE = -2; static PREV_SHEAR = -1;
static ROTATE = 1; static TRANSLATE = 2; static SCALE = 3; static SHEAR = 4;
frames: ArrayLike<number>; // time, position, ...
transformConstraintIndex: number;
frames: ArrayLike<number>; // time, rotate mix, translate mix, scale mix, shear mix, ...
constructor (frameCount: number) {
super(frameCount);
this.frames = Utils.newFloatArray(frameCount * PathConstraintPositionTimeline.ENTRIES);
}
constructor (frameCount: number) {
super(frameCount);
this.frames = Utils.newFloatArray(frameCount * TransformConstraintTimeline.ENTRIES);
}
getPropertyId () {
return (TimelineType.pathConstraintPosition << 24) + this.pathConstraintIndex;
}
getPropertyId () {
return (TimelineType.transformConstraint << 24) + this.transformConstraintIndex;
}
/** Sets the time and value of the specified keyframe. */
setFrame (frameIndex: number, time: number, value: number) {
frameIndex *= PathConstraintPositionTimeline.ENTRIES;
this.frames[frameIndex] = time;
this.frames[frameIndex + PathConstraintPositionTimeline.VALUE] = value;
}
/** Sets the time and mixes of the specified keyframe. */
setFrame (frameIndex: number, time: number, rotateMix: number, translateMix: number, scaleMix: number, shearMix: number) {
frameIndex *= TransformConstraintTimeline.ENTRIES;
this.frames[frameIndex] = time;
this.frames[frameIndex + TransformConstraintTimeline.ROTATE] = rotateMix;
this.frames[frameIndex + TransformConstraintTimeline.TRANSLATE] = translateMix;
this.frames[frameIndex + TransformConstraintTimeline.SCALE] = scaleMix;
this.frames[frameIndex + TransformConstraintTimeline.SHEAR] = shearMix;
}
apply (skeleton: Skeleton, lastTime: number, time: number, firedEvents: Array<Event>, alpha: number, setupPose: boolean, mixingOut: boolean) {
let frames = this.frames;
let constraint: PathConstraint = skeleton.pathConstraints[this.pathConstraintIndex];
if (time < frames[0]) {
if (setupPose) constraint.position = constraint.data.position;
return;
}
apply (skeleton: Skeleton, lastTime: number, time: number, firedEvents: Array<Event>, alpha: number, pose: MixPose, direction: MixDirection) {
let frames = this.frames;
let position = 0;
if (time >= frames[frames.length - PathConstraintPositionTimeline.ENTRIES]) // Time is after last frame.
position = frames[frames.length + PathConstraintPositionTimeline.PREV_VALUE];
else {
// Interpolate between the previous frame and the current frame.
let frame = Animation.binarySearch(frames, time, PathConstraintPositionTimeline.ENTRIES);
position = frames[frame + PathConstraintPositionTimeline.PREV_VALUE];
let frameTime = frames[frame];
let percent = this.getCurvePercent(frame / PathConstraintPositionTimeline.ENTRIES - 1,
1 - (time - frameTime) / (frames[frame + PathConstraintPositionTimeline.PREV_TIME] - frameTime));
let constraint: TransformConstraint = skeleton.transformConstraints[this.transformConstraintIndex];
if (time < frames[0]) {
let data = constraint.data;
switch (pose) {
case MixPose.setup:
constraint.rotateMix = data.rotateMix;
constraint.translateMix = data.translateMix;
constraint.scaleMix = data.scaleMix;
constraint.shearMix = data.shearMix;
return;
case MixPose.current:
constraint.rotateMix += (data.rotateMix - constraint.rotateMix) * alpha;
constraint.translateMix += (data.translateMix - constraint.translateMix) * alpha;
constraint.scaleMix += (data.scaleMix - constraint.scaleMix) * alpha;
constraint.shearMix += (data.shearMix - constraint.shearMix) * alpha;
}
return;
}
position += (frames[frame + PathConstraintPositionTimeline.VALUE] - position) * percent;
}
if (setupPose)
constraint.position = constraint.data.position + (position - constraint.data.position) * alpha;
else
constraint.position += (position - constraint.position) * alpha;
}
}
let rotate = 0, translate = 0, scale = 0, shear = 0;
if (time >= frames[frames.length - TransformConstraintTimeline.ENTRIES]) { // Time is after last frame.
let i = frames.length;
rotate = frames[i + TransformConstraintTimeline.PREV_ROTATE];
translate = frames[i + TransformConstraintTimeline.PREV_TRANSLATE];
scale = frames[i + TransformConstraintTimeline.PREV_SCALE];
shear = frames[i + TransformConstraintTimeline.PREV_SHEAR];
} else {
// Interpolate between the previous frame and the current frame.
let frame = Animation.binarySearch(frames, time, TransformConstraintTimeline.ENTRIES);
rotate = frames[frame + TransformConstraintTimeline.PREV_ROTATE];
translate = frames[frame + TransformConstraintTimeline.PREV_TRANSLATE];
scale = frames[frame + TransformConstraintTimeline.PREV_SCALE];
shear = frames[frame + TransformConstraintTimeline.PREV_SHEAR];
let frameTime = frames[frame];
let percent = this.getCurvePercent(frame / TransformConstraintTimeline.ENTRIES - 1,
1 - (time - frameTime) / (frames[frame + TransformConstraintTimeline.PREV_TIME] - frameTime));
export class PathConstraintSpacingTimeline extends PathConstraintPositionTimeline {
constructor (frameCount: number) {
super(frameCount);
}
rotate += (frames[frame + TransformConstraintTimeline.ROTATE] - rotate) * percent;
translate += (frames[frame + TransformConstraintTimeline.TRANSLATE] - translate) * percent;
scale += (frames[frame + TransformConstraintTimeline.SCALE] - scale) * percent;
shear += (frames[frame + TransformConstraintTimeline.SHEAR] - shear) * percent;
}
if (pose == MixPose.setup) {
let data = constraint.data;
constraint.rotateMix = data.rotateMix + (rotate - data.rotateMix) * alpha;
constraint.translateMix = data.translateMix + (translate - data.translateMix) * alpha;
constraint.scaleMix = data.scaleMix + (scale - data.scaleMix) * alpha;
constraint.shearMix = data.shearMix + (shear - data.shearMix) * alpha;
} else {
constraint.rotateMix += (rotate - constraint.rotateMix) * alpha;
constraint.translateMix += (translate - constraint.translateMix) * alpha;
constraint.scaleMix += (scale - constraint.scaleMix) * alpha;
constraint.shearMix += (shear - constraint.shearMix) * alpha;
}
}
}
getPropertyId () {
return (TimelineType.pathConstraintSpacing << 24) + this.pathConstraintIndex;
}
export class PathConstraintPositionTimeline extends CurveTimeline {
static ENTRIES = 2;
static PREV_TIME = -2; static PREV_VALUE = -1;
static VALUE = 1;
apply (skeleton: Skeleton, lastTime: number, time: number, firedEvents: Array<Event>, alpha: number, setupPose: boolean, mixingOut: boolean) {
let frames = this.frames;
let constraint: PathConstraint = skeleton.pathConstraints[this.pathConstraintIndex];
if (time < frames[0]) {
if (setupPose) constraint.spacing = constraint.data.spacing;
return;
}
pathConstraintIndex: number;
let spacing = 0;
if (time >= frames[frames.length - PathConstraintSpacingTimeline.ENTRIES]) // Time is after last frame.
spacing = frames[frames.length + PathConstraintSpacingTimeline.PREV_VALUE];
else {
// Interpolate between the previous frame and the current frame.
let frame = Animation.binarySearch(frames, time, PathConstraintSpacingTimeline.ENTRIES);
spacing = frames[frame + PathConstraintSpacingTimeline.PREV_VALUE];
let frameTime = frames[frame];
let percent = this.getCurvePercent(frame / PathConstraintSpacingTimeline.ENTRIES - 1,
1 - (time - frameTime) / (frames[frame + PathConstraintSpacingTimeline.PREV_TIME] - frameTime));
frames: ArrayLike<number>; // time, position, ...
spacing += (frames[frame + PathConstraintSpacingTimeline.VALUE] - spacing) * percent;
}
constructor (frameCount: number) {
super(frameCount);
this.frames = Utils.newFloatArray(frameCount * PathConstraintPositionTimeline.ENTRIES);
}
if (setupPose)
constraint.spacing = constraint.data.spacing + (spacing - constraint.data.spacing) * alpha;
else
constraint.spacing += (spacing - constraint.spacing) * alpha;
}
}
getPropertyId () {
return (TimelineType.pathConstraintPosition << 24) + this.pathConstraintIndex;
}
export class PathConstraintMixTimeline extends CurveTimeline {
static ENTRIES = 3;
static PREV_TIME = -3; static PREV_ROTATE = -2; static PREV_TRANSLATE = -1;
static ROTATE = 1; static TRANSLATE = 2;
/** Sets the time and value of the specified keyframe. */
setFrame (frameIndex: number, time: number, value: number) {
frameIndex *= PathConstraintPositionTimeline.ENTRIES;
this.frames[frameIndex] = time;
this.frames[frameIndex + PathConstraintPositionTimeline.VALUE] = value;
}
pathConstraintIndex: number;
apply (skeleton: Skeleton, lastTime: number, time: number, firedEvents: Array<Event>, alpha: number, pose: MixPose, direction: MixDirection) {
let frames = this.frames;
let constraint: PathConstraint = skeleton.pathConstraints[this.pathConstraintIndex];
if (time < frames[0]) {
switch (pose) {
case MixPose.setup:
constraint.position = constraint.data.position;
return;
case MixPose.current:
constraint.position += (constraint.data.position - constraint.position) * alpha;
}
return;
}
frames: ArrayLike<number>; // time, rotate mix, translate mix, ...
let position = 0;
if (time >= frames[frames.length - PathConstraintPositionTimeline.ENTRIES]) // Time is after last frame.
position = frames[frames.length + PathConstraintPositionTimeline.PREV_VALUE];
else {
// Interpolate between the previous frame and the current frame.
let frame = Animation.binarySearch(frames, time, PathConstraintPositionTimeline.ENTRIES);
position = frames[frame + PathConstraintPositionTimeline.PREV_VALUE];
let frameTime = frames[frame];
let percent = this.getCurvePercent(frame / PathConstraintPositionTimeline.ENTRIES - 1,
1 - (time - frameTime) / (frames[frame + PathConstraintPositionTimeline.PREV_TIME] - frameTime));
constructor (frameCount: number) {
super(frameCount);
this.frames = Utils.newFloatArray(frameCount * PathConstraintMixTimeline.ENTRIES);
}
position += (frames[frame + PathConstraintPositionTimeline.VALUE] - position) * percent;
}
if (pose == MixPose.setup)
constraint.position = constraint.data.position + (position - constraint.data.position) * alpha;
else
constraint.position += (position - constraint.position) * alpha;
}
}
getPropertyId () {
return (TimelineType.pathConstraintMix << 24) + this.pathConstraintIndex;
}
export class PathConstraintSpacingTimeline extends PathConstraintPositionTimeline {
constructor (frameCount: number) {
super(frameCount);
}
/** Sets the time and mixes of the specified keyframe. */
setFrame (frameIndex: number, time: number, rotateMix: number, translateMix: number) {
frameIndex *= PathConstraintMixTimeline.ENTRIES;
this.frames[frameIndex] = time;
this.frames[frameIndex + PathConstraintMixTimeline.ROTATE] = rotateMix;
this.frames[frameIndex + PathConstraintMixTimeline.TRANSLATE] = translateMix;
}
getPropertyId () {
return (TimelineType.pathConstraintSpacing << 24) + this.pathConstraintIndex;
}
apply (skeleton: Skeleton, lastTime: number, time: number, firedEvents: Array<Event>, alpha: number, setupPose: boolean, mixingOut: boolean) {
let frames = this.frames;
let constraint: PathConstraint = skeleton.pathConstraints[this.pathConstraintIndex];
apply (skeleton: Skeleton, lastTime: number, time: number, firedEvents: Array<Event>, alpha: number, pose: MixPose, direction: MixDirection) {
let frames = this.frames;
let constraint: PathConstraint = skeleton.pathConstraints[this.pathConstraintIndex];
if (time < frames[0]) {
switch (pose) {
case MixPose.setup:
constraint.spacing = constraint.data.spacing;
return;
case MixPose.current:
constraint.spacing += (constraint.data.spacing - constraint.spacing) * alpha;
}
return;
}
if (time < frames[0]) {
if (setupPose) {
constraint.rotateMix = constraint.data.rotateMix;
constraint.translateMix = constraint.data.translateMix;
}
return;
}
let spacing = 0;
if (time >= frames[frames.length - PathConstraintSpacingTimeline.ENTRIES]) // Time is after last frame.
spacing = frames[frames.length + PathConstraintSpacingTimeline.PREV_VALUE];
else {
// Interpolate between the previous frame and the current frame.
let frame = Animation.binarySearch(frames, time, PathConstraintSpacingTimeline.ENTRIES);
spacing = frames[frame + PathConstraintSpacingTimeline.PREV_VALUE];
let frameTime = frames[frame];
let percent = this.getCurvePercent(frame / PathConstraintSpacingTimeline.ENTRIES - 1,
1 - (time - frameTime) / (frames[frame + PathConstraintSpacingTimeline.PREV_TIME] - frameTime));
let rotate = 0, translate = 0;
if (time >= frames[frames.length - PathConstraintMixTimeline.ENTRIES]) { // Time is after last frame.
rotate = frames[frames.length + PathConstraintMixTimeline.PREV_ROTATE];
translate = frames[frames.length + PathConstraintMixTimeline.PREV_TRANSLATE];
} else {
// Interpolate between the previous frame and the current frame.
let frame = Animation.binarySearch(frames, time, PathConstraintMixTimeline.ENTRIES);
rotate = frames[frame + PathConstraintMixTimeline.PREV_ROTATE];
translate = frames[frame + PathConstraintMixTimeline.PREV_TRANSLATE];
let frameTime = frames[frame];
let percent = this.getCurvePercent(frame / PathConstraintMixTimeline.ENTRIES - 1,
1 - (time - frameTime) / (frames[frame + PathConstraintMixTimeline.PREV_TIME] - frameTime));
spacing += (frames[frame + PathConstraintSpacingTimeline.VALUE] - spacing) * percent;
}
rotate += (frames[frame + PathConstraintMixTimeline.ROTATE] - rotate) * percent;
translate += (frames[frame + PathConstraintMixTimeline.TRANSLATE] - translate) * percent;
}
if (pose == MixPose.setup)
constraint.spacing = constraint.data.spacing + (spacing - constraint.data.spacing) * alpha;
else
constraint.spacing += (spacing - constraint.spacing) * alpha;
}
}
if (setupPose) {
constraint.rotateMix = constraint.data.rotateMix + (rotate - constraint.data.rotateMix) * alpha;
constraint.translateMix = constraint.data.translateMix + (translate - constraint.data.translateMix) * alpha;
} else {
constraint.rotateMix += (rotate - constraint.rotateMix) * alpha;
constraint.translateMix += (translate - constraint.translateMix) * alpha;
}
}
}
export class PathConstraintMixTimeline extends CurveTimeline {
static ENTRIES = 3;
static PREV_TIME = -3; static PREV_ROTATE = -2; static PREV_TRANSLATE = -1;
static ROTATE = 1; static TRANSLATE = 2;
pathConstraintIndex: number;
frames: ArrayLike<number>; // time, rotate mix, translate mix, ...
constructor (frameCount: number) {
super(frameCount);
this.frames = Utils.newFloatArray(frameCount * PathConstraintMixTimeline.ENTRIES);
}
getPropertyId () {
return (TimelineType.pathConstraintMix << 24) + this.pathConstraintIndex;
}
/** Sets the time and mixes of the specified keyframe. */
setFrame (frameIndex: number, time: number, rotateMix: number, translateMix: number) {
frameIndex *= PathConstraintMixTimeline.ENTRIES;
this.frames[frameIndex] = time;
this.frames[frameIndex + PathConstraintMixTimeline.ROTATE] = rotateMix;
this.frames[frameIndex + PathConstraintMixTimeline.TRANSLATE] = translateMix;
}
apply (skeleton: Skeleton, lastTime: number, time: number, firedEvents: Array<Event>, alpha: number, pose: MixPose, direction: MixDirection) {
let frames = this.frames;
let constraint: PathConstraint = skeleton.pathConstraints[this.pathConstraintIndex];
if (time < frames[0]) {
switch (pose) {
case MixPose.setup:
constraint.rotateMix = constraint.data.rotateMix;
constraint.translateMix = constraint.data.translateMix;
return;
case MixPose.current:
constraint.rotateMix += (constraint.data.rotateMix - constraint.rotateMix) * alpha;
constraint.translateMix += (constraint.data.translateMix - constraint.translateMix) * alpha;
}
return;
}
let rotate = 0, translate = 0;
if (time >= frames[frames.length - PathConstraintMixTimeline.ENTRIES]) { // Time is after last frame.
rotate = frames[frames.length + PathConstraintMixTimeline.PREV_ROTATE];
translate = frames[frames.length + PathConstraintMixTimeline.PREV_TRANSLATE];
} else {
// Interpolate between the previous frame and the current frame.
let frame = Animation.binarySearch(frames, time, PathConstraintMixTimeline.ENTRIES);
rotate = frames[frame + PathConstraintMixTimeline.PREV_ROTATE];
translate = frames[frame + PathConstraintMixTimeline.PREV_TRANSLATE];
let frameTime = frames[frame];
let percent = this.getCurvePercent(frame / PathConstraintMixTimeline.ENTRIES - 1,
1 - (time - frameTime) / (frames[frame + PathConstraintMixTimeline.PREV_TIME] - frameTime));
rotate += (frames[frame + PathConstraintMixTimeline.ROTATE] - rotate) * percent;
translate += (frames[frame + PathConstraintMixTimeline.TRANSLATE] - translate) * percent;
}
if (pose == MixPose.setup) {
constraint.rotateMix = constraint.data.rotateMix + (rotate - constraint.data.rotateMix) * alpha;
constraint.translateMix = constraint.data.translateMix + (translate - constraint.data.translateMix) * alpha;
} else {
constraint.rotateMix += (rotate - constraint.rotateMix) * alpha;
constraint.translateMix += (translate - constraint.translateMix) * alpha;
}
}
}
}

@@ -32,81 +32,95 @@ /******************************************************************************

namespace pixi_spine.core {
export class AnimationState {
static emptyAnimation = new Animation("<empty>", [], 0);
export class AnimationState {
static emptyAnimation = new Animation("<empty>", [], 0);
static SUBSEQUENT = 0;
static FIRST = 1;
static DIP = 2;
static DIP_MIX = 3;
data: AnimationStateData;
tracks = new Array<TrackEntry>();
events = new Array<Event>();
listeners = new Array<AnimationStateListener2>();
queue = new EventQueue(this);
propertyIDs = new IntSet();
animationsChanged = false;
timeScale = 1;
data: AnimationStateData;
tracks = new Array<TrackEntry>();
events = new Array<Event>();
listeners = new Array<AnimationStateListener2>();
queue = new EventQueue(this);
propertyIDs = new IntSet();
mixingTo = new Array<TrackEntry>();
animationsChanged = false;
timeScale = 1;
trackEntryPool = new Pool<TrackEntry>(() => new TrackEntry());
trackEntryPool = new Pool<TrackEntry>(() => new TrackEntry());
constructor (data: AnimationStateData) {
this.data = data;
}
constructor (data: AnimationStateData) {
this.data = data;
}
update (delta: number) {
delta *= this.timeScale;
let tracks = this.tracks;
for (let i = 0, n = tracks.length; i < n; i++) {
let current = tracks[i];
if (current == null) continue;
update (delta: number) {
delta *= this.timeScale;
let tracks = this.tracks;
for (let i = 0, n = tracks.length; i < n; i++) {
let current = tracks[i];
if (current == null) continue;
current.animationLast = current.nextAnimationLast;
current.trackLast = current.nextTrackLast;
current.animationLast = current.nextAnimationLast;
current.trackLast = current.nextTrackLast;
let currentDelta = delta * current.timeScale;
let currentDelta = delta * current.timeScale;
if (current.delay > 0) {
current.delay -= currentDelta;
if (current.delay > 0) continue;
currentDelta = -current.delay;
current.delay = 0;
}
if (current.delay > 0) {
current.delay -= currentDelta;
if (current.delay > 0) continue;
currentDelta = -current.delay;
current.delay = 0;
}
let next = current.next;
if (next != null) {
// When the next entry's delay is passed, change to the next entry, preserving leftover time.
let nextTime = current.trackLast - next.delay;
if (nextTime >= 0) {
next.delay = 0;
next.trackTime = nextTime + delta * next.timeScale;
current.trackTime += currentDelta;
this.setCurrent(i, next, true);
while (next.mixingFrom != null) {
next.mixTime += currentDelta;
next = next.mixingFrom;
}
continue;
}
} else {
// Clear the track when there is no next entry, the track end time is reached, and there is no mixingFrom.
if (current.trackLast >= current.trackEnd && current.mixingFrom == null) {
tracks[i] = null;
this.queue.end(current);
this.disposeNext(current);
continue;
}
}
this.updateMixingFrom(current, delta);
let next = current.next;
if (next != null) {
// When the next entry's delay is passed, change to the next entry, preserving leftover time.
let nextTime = current.trackLast - next.delay;
if (nextTime >= 0) {
next.delay = 0;
next.trackTime = nextTime + delta * next.timeScale;
current.trackTime += currentDelta;
this.setCurrent(i, next, true);
while (next.mixingFrom != null) {
next.mixTime += currentDelta;
next = next.mixingFrom;
}
continue;
}
} else if (current.trackLast >= current.trackEnd && current.mixingFrom == null) {
tracks[i] = null;
this.queue.end(current);
this.disposeNext(current);
continue;
}
if (current.mixingFrom != null && this.updateMixingFrom(current, delta)) {
// End mixing from entries once all have completed.
let from = current.mixingFrom;
current.mixingFrom = null;
while (from != null) {
this.queue.end(from);
from = from.mixingFrom;
}
}
current.trackTime += currentDelta;
}
current.trackTime += currentDelta;
}
this.queue.drain();
}
this.queue.drain();
}
updateMixingFrom (entry: TrackEntry, delta: number) {
let from = entry.mixingFrom;
if (from == null) return;
updateMixingFrom (to: TrackEntry, delta: number): boolean {
let from = to.mixingFrom;
if (from == null) return true;
this.updateMixingFrom(from, delta);
let finished = this.updateMixingFrom(from, delta);
if (entry.mixTime >= entry.mixDuration && from.mixingFrom != null && entry.mixTime > 0) {
entry.mixingFrom = null;
this.queue.end(from);
return;
// Require mixTime > 0 to ensure the mixing from entry was applied at least once.
if (to.mixTime > 0 && (to.mixTime >= to.mixDuration || to.timeScale == 0)) {
if (from.totalAlpha == 0) {
to.mixingFrom = from.mixingFrom;
to.interruptAlpha = from.interruptAlpha;
this.queue.end(from);
}
return finished;
}

@@ -117,194 +131,221 @@

from.trackTime += delta * from.timeScale;
entry.mixTime += delta * from.timeScale;
to.mixTime += delta * to.timeScale;
return false;
}
apply (skeleton: Skeleton) {
if (skeleton == null) throw new Error("skeleton cannot be null.");
if (this.animationsChanged) this._animationsChanged();
apply (skeleton: Skeleton) : boolean {
if (skeleton == null) throw new Error("skeleton cannot be null.");
if (this.animationsChanged) this._animationsChanged();
let events = this.events;
let tracks = this.tracks;
let events = this.events;
let tracks = this.tracks;
let applied = false;
for (let i = 0, n = tracks.length; i < n; i++) {
let current = tracks[i];
if (current == null || current.delay > 0) continue;
for (let i = 0, n = tracks.length; i < n; i++) {
let current = tracks[i];
if (current == null || current.delay > 0) continue;
applied = true;
let currentPose = i == 0 ? MixPose.current : MixPose.currentLayered;
// Apply mixing from entries first.
let mix = current.alpha;
// Apply mixing from entries first.
let mix = current.alpha;
if (current.mixingFrom != null)
mix *= this.applyMixingFrom(current, skeleton);
else if (current.trackTime >= current.trackEnd)
mix *= this.applyMixingFrom(current, skeleton, currentPose);
else if (current.trackTime >= current.trackEnd && current.next == null)
mix = 0;
// Apply current entry.
let animationLast = current.animationLast, animationTime = current.getAnimationTime();
let timelineCount = current.animation.timelines.length;
let timelines = current.animation.timelines;
if (mix == 1) {
for (let ii = 0; ii < timelineCount; ii++)
timelines[ii].apply(skeleton, animationLast, animationTime, events, 1, true, false);
} else {
let firstFrame = current.timelinesRotation.length == 0;
if (firstFrame) Utils.setArraySize(current.timelinesRotation, timelineCount << 1, null);
let timelinesRotation = current.timelinesRotation;
// Apply current entry.
let animationLast = current.animationLast, animationTime = current.getAnimationTime();
let timelineCount = current.animation.timelines.length;
let timelines = current.animation.timelines;
if (mix == 1) {
for (let ii = 0; ii < timelineCount; ii++)
timelines[ii].apply(skeleton, animationLast, animationTime, events, 1, MixPose.setup, MixDirection.in);
} else {
let timelineData = current.timelineData;
let timelinesFirst = current.timelinesFirst;
for (let ii = 0; ii < timelineCount; ii++) {
let timeline = timelines[ii];
if (timeline instanceof RotateTimeline) {
this.applyRotateTimeline(timeline, skeleton, animationTime, mix, timelinesFirst[ii], timelinesRotation, ii << 1,
firstFrame);
} else
timeline.apply(skeleton, animationLast, animationTime, events, mix, timelinesFirst[ii], false);
}
}
this.queueEvents(current, animationTime);
let firstFrame = current.timelinesRotation.length == 0;
if (firstFrame) Utils.setArraySize(current.timelinesRotation, timelineCount << 1, null);
let timelinesRotation = current.timelinesRotation;
for (let ii = 0; ii < timelineCount; ii++) {
let timeline = timelines[ii];
let pose = timelineData[ii] >= AnimationState.FIRST ? MixPose.setup : currentPose;
if (timeline instanceof RotateTimeline) {
this.applyRotateTimeline(timeline, skeleton, animationTime, mix, pose, timelinesRotation, ii << 1, firstFrame);
} else
timeline.apply(skeleton, animationLast, animationTime, events, mix, pose, MixDirection.in);
}
}
this.queueEvents(current, animationTime);
events.length = 0;
current.nextAnimationLast = animationTime;
current.nextTrackLast = current.trackTime;
}
current.nextAnimationLast = animationTime;
current.nextTrackLast = current.trackTime;
}
this.queue.drain();
}
this.queue.drain();
return applied;
}
applyMixingFrom (entry: TrackEntry, skeleton: Skeleton) {
let from = entry.mixingFrom;
if (from.mixingFrom != null) this.applyMixingFrom(from, skeleton);
applyMixingFrom (to: TrackEntry, skeleton: Skeleton, currentPose: MixPose) {
let from = to.mixingFrom;
if (from.mixingFrom != null) this.applyMixingFrom(from, skeleton, currentPose);
let mix = 0;
if (entry.mixDuration == 0) // Single frame mix to undo mixingFrom changes.
mix = 1;
else {
mix = entry.mixTime / entry.mixDuration;
if (mix > 1) mix = 1;
}
let mix = 0;
if (to.mixDuration == 0) // Single frame mix to undo mixingFrom changes.
mix = 1;
else {
mix = to.mixTime / to.mixDuration;
if (mix > 1) mix = 1;
}
let events = mix < from.eventThreshold ? this.events : null;
let attachments = mix < from.attachmentThreshold, drawOrder = mix < from.drawOrderThreshold;
let animationLast = from.animationLast, animationTime = from.getAnimationTime();
let timelineCount = from.animation.timelines.length;
let timelines = from.animation.timelines;
let timelinesFirst = from.timelinesFirst;
let alpha = from.alpha * entry.mixAlpha * (1 - mix);
let events = mix < from.eventThreshold ? this.events : null;
let attachments = mix < from.attachmentThreshold, drawOrder = mix < from.drawOrderThreshold;
let animationLast = from.animationLast, animationTime = from.getAnimationTime();
let timelineCount = from.animation.timelines.length;
let timelines = from.animation.timelines;
let timelineData = from.timelineData;
let timelineDipMix = from.timelineDipMix;
let firstFrame = from.timelinesRotation.length == 0;
if (firstFrame) Utils.setArraySize(from.timelinesRotation, timelineCount << 1, null);
let timelinesRotation = from.timelinesRotation;
let firstFrame = from.timelinesRotation.length == 0;
if (firstFrame) Utils.setArraySize(from.timelinesRotation, timelineCount << 1, null);
let timelinesRotation = from.timelinesRotation;
for (let i = 0; i < timelineCount; i++) {
let timeline = timelines[i];
let setupPose = timelinesFirst[i];
if (timeline instanceof RotateTimeline)
this.applyRotateTimeline(timeline, skeleton, animationTime, alpha, setupPose, timelinesRotation, i << 1, firstFrame);
else {
if (!setupPose) {
if (!attachments && timeline instanceof AttachmentTimeline) continue;
if (!drawOrder && timeline instanceof DrawOrderTimeline) continue;
}
timeline.apply(skeleton, animationLast, animationTime, events, alpha, setupPose, true);
}
}
let pose: MixPose;
let alphaDip = from.alpha * to.interruptAlpha, alphaMix = alphaDip * (1 - mix), alpha = 0;
from.totalAlpha = 0;
for (var i = 0; i < timelineCount; i++) {
let timeline = timelines[i];
switch (timelineData[i]) {
case AnimationState.SUBSEQUENT:
if (!attachments && timeline instanceof AttachmentTimeline) continue;
if (!drawOrder && timeline instanceof DrawOrderTimeline) continue;
pose = currentPose;
alpha = alphaMix;
break;
case AnimationState.FIRST:
pose = MixPose.setup
alpha = alphaMix;
break;
case AnimationState.DIP:
pose = MixPose.setup;
alpha = alphaDip;
break;
default:
pose = MixPose.setup;
alpha = alphaDip;
let dipMix = timelineDipMix[i];
alpha *= Math.max(0, 1 - dipMix.mixTime / dipMix.mixDuration);
break;
}
from.totalAlpha += alpha;
if (timeline instanceof RotateTimeline)
this.applyRotateTimeline(timeline, skeleton, animationTime, alpha, pose, timelinesRotation, i << 1, firstFrame);
else {
timeline.apply(skeleton, animationLast, animationTime, events, alpha, pose, MixDirection.out);
}
}
if (entry.mixDuration > 0) this.queueEvents(from, animationTime);
if (to.mixDuration > 0) this.queueEvents(from, animationTime);
this.events.length = 0;
from.nextAnimationLast = animationTime;
from.nextTrackLast = from.trackTime;
from.nextAnimationLast = animationTime;
from.nextTrackLast = from.trackTime;
return mix;
}
return mix;
}
applyRotateTimeline (timeline: Timeline, skeleton: Skeleton, time: number, alpha: number, setupPose: boolean,
timelinesRotation: Array<number>, i: number, firstFrame: boolean) {
applyRotateTimeline (timeline: Timeline, skeleton: Skeleton, time: number, alpha: number, pose: MixPose,
timelinesRotation: Array<number>, i: number, firstFrame: boolean) {
if (firstFrame) timelinesRotation[i] = 0;
if (alpha == 1) {
timeline.apply(skeleton, 0, time, null, 1, setupPose, false);
return;
}
if (alpha == 1) {
timeline.apply(skeleton, 0, time, null, 1, pose, MixDirection.in);
return;
}
let rotateTimeline = timeline as RotateTimeline;
let frames = rotateTimeline.frames;
let bone = skeleton.bones[rotateTimeline.boneIndex];
if (time < frames[0]) {
if (setupPose) bone.rotation = bone.data.rotation;
return;
}
let rotateTimeline = timeline as RotateTimeline;
let frames = rotateTimeline.frames;
let bone = skeleton.bones[rotateTimeline.boneIndex];
if (time < frames[0]) {
if (pose == MixPose.setup) bone.rotation = bone.data.rotation;
return;
}
let r2 = 0;
if (time >= frames[frames.length - RotateTimeline.ENTRIES]) // Time is after last frame.
r2 = bone.data.rotation + frames[frames.length + RotateTimeline.PREV_ROTATION];
else {
// Interpolate between the previous frame and the current frame.
let frame = Animation.binarySearch(frames, time, RotateTimeline.ENTRIES);
let prevRotation = frames[frame + RotateTimeline.PREV_ROTATION];
let frameTime = frames[frame];
let percent = rotateTimeline.getCurvePercent((frame >> 1) - 1,
1 - (time - frameTime) / (frames[frame + RotateTimeline.PREV_TIME] - frameTime));
let r2 = 0;
if (time >= frames[frames.length - RotateTimeline.ENTRIES]) // Time is after last frame.
r2 = bone.data.rotation + frames[frames.length + RotateTimeline.PREV_ROTATION];
else {
// Interpolate between the previous frame and the current frame.
let frame = Animation.binarySearch(frames, time, RotateTimeline.ENTRIES);
let prevRotation = frames[frame + RotateTimeline.PREV_ROTATION];
let frameTime = frames[frame];
let percent = rotateTimeline.getCurvePercent((frame >> 1) - 1,
1 - (time - frameTime) / (frames[frame + RotateTimeline.PREV_TIME] - frameTime));
r2 = frames[frame + RotateTimeline.ROTATION] - prevRotation;
r2 -= (16384 - ((16384.499999999996 - r2 / 360) | 0)) * 360;
r2 = prevRotation + r2 * percent + bone.data.rotation;
r2 -= (16384 - ((16384.499999999996 - r2 / 360) | 0)) * 360;
}
r2 = frames[frame + RotateTimeline.ROTATION] - prevRotation;
r2 -= (16384 - ((16384.499999999996 - r2 / 360) | 0)) * 360;
r2 = prevRotation + r2 * percent + bone.data.rotation;
r2 -= (16384 - ((16384.499999999996 - r2 / 360) | 0)) * 360;
}
// Mix between rotations using the direction of the shortest route on the first frame while detecting crosses.
let r1 = setupPose ? bone.data.rotation : bone.rotation;
let total = 0, diff = r2 - r1;
if (diff == 0) {
// Mix between rotations using the direction of the shortest route on the first frame while detecting crosses.
let r1 = pose == MixPose.setup ? bone.data.rotation : bone.rotation;
let total = 0, diff = r2 - r1;
if (diff == 0) {
total = timelinesRotation[i];
} else {
diff -= (16384 - ((16384.499999999996 - diff / 360) | 0)) * 360;
let lastTotal = 0, lastDiff = 0;
if (firstFrame) {
lastTotal = 0;
lastDiff = diff;
} else {
lastTotal = timelinesRotation[i]; // Angle and direction of mix, including loops.
lastDiff = timelinesRotation[i + 1]; // Difference between bones.
}
let current = diff > 0, dir = lastTotal >= 0;
// Detect cross at 0 (not 180).
if (MathUtils.signum(lastDiff) != MathUtils.signum(diff) && Math.abs(lastDiff) <= 90) {
// A cross after a 360 rotation is a loop.
if (Math.abs(lastTotal) > 180) lastTotal += 360 * MathUtils.signum(lastTotal);
dir = current;
}
total = diff + lastTotal - lastTotal % 360; // Store loops as part of lastTotal.
if (dir != current) total += 360 * MathUtils.signum(lastTotal);
timelinesRotation[i] = total;
}
timelinesRotation[i + 1] = diff;
r1 += total * alpha;
bone.rotation = r1 - (16384 - ((16384.499999999996 - r1 / 360) | 0)) * 360;
}
} else {
diff -= (16384 - ((16384.499999999996 - diff / 360) | 0)) * 360;
let lastTotal = 0, lastDiff = 0;
if (firstFrame) {
lastTotal = 0;
lastDiff = diff;
} else {
lastTotal = timelinesRotation[i]; // Angle and direction of mix, including loops.
lastDiff = timelinesRotation[i + 1]; // Difference between bones.
}
let current = diff > 0, dir = lastTotal >= 0;
// Detect cross at 0 (not 180).
if (MathUtils.signum(lastDiff) != MathUtils.signum(diff) && Math.abs(lastDiff) <= 90) {
// A cross after a 360 rotation is a loop.
if (Math.abs(lastTotal) > 180) lastTotal += 360 * MathUtils.signum(lastTotal);
dir = current;
}
total = diff + lastTotal - lastTotal % 360; // Store loops as part of lastTotal.
if (dir != current) total += 360 * MathUtils.signum(lastTotal);
timelinesRotation[i] = total;
}
timelinesRotation[i + 1] = diff;
r1 += total * alpha;
bone.rotation = r1 - (16384 - ((16384.499999999996 - r1 / 360) | 0)) * 360;
}
queueEvents (entry: TrackEntry, animationTime: number) {
let animationStart = entry.animationStart, animationEnd = entry.animationEnd;
let duration = animationEnd - animationStart;
let trackLastWrapped = entry.trackLast % duration;
queueEvents (entry: TrackEntry, animationTime: number) {
let animationStart = entry.animationStart, animationEnd = entry.animationEnd;
let duration = animationEnd - animationStart;
let trackLastWrapped = entry.trackLast % duration;
// Queue events before complete.
let events = this.events;
let i = 0, n = events.length;
for (; i < n; i++) {
let event = events[i];
if (event.time < trackLastWrapped) break;
if (event.time > animationEnd) continue; // Discard events outside animation start/end.
this.queue.event(entry, event);
}
// Queue events before complete.
let events = this.events;
let i = 0, n = events.length;
for (; i < n; i++) {
let event = events[i];
if (event.time < trackLastWrapped) break;
if (event.time > animationEnd) continue; // Discard events outside animation start/end.
this.queue.event(entry, event);
}
// Queue complete if completed a loop iteration or the animation.
if (entry.loop ? (trackLastWrapped > entry.trackTime % duration)
: (animationTime >= animationEnd && entry.animationLast < animationEnd)) {
this.queue.complete(entry);
}
// Queue complete if completed a loop iteration or the animation.
if (entry.loop ? (trackLastWrapped > entry.trackTime % duration)
: (animationTime >= animationEnd && entry.animationLast < animationEnd)) {
this.queue.complete(entry);
}
// Queue events after complete.
for (; i < n; i++) {
let event = events[i];
if (event.time < animationStart) continue; // Discard events outside animation start/end.
this.queue.event(entry, events[i]);
}
}
// Queue events after complete.
for (; i < n; i++) {
let event = events[i];
if (event.time < animationStart) continue; // Discard events outside animation start/end.
this.queue.event(entry, events[i]);
}
}

@@ -321,120 +362,121 @@ clearTracks () {

clearTrack (trackIndex: number) {
if (trackIndex >= this.tracks.length) return;
let current = this.tracks[trackIndex];
if (current == null) return;
clearTrack (trackIndex: number) {
if (trackIndex >= this.tracks.length) return;
let current = this.tracks[trackIndex];
if (current == null) return;
this.queue.end(current);
this.queue.end(current);
this.disposeNext(current);
this.disposeNext(current);
let entry = current;
while (true) {
let from = entry.mixingFrom;
if (from == null) break;
this.queue.end(from);
entry.mixingFrom = null;
entry = from;
}
let entry = current;
while (true) {
let from = entry.mixingFrom;
if (from == null) break;
this.queue.end(from);
entry.mixingFrom = null;
entry = from;
}
this.tracks[current.trackIndex] = null;
this.tracks[current.trackIndex] = null;
this.queue.drain();
}
this.queue.drain();
}
setCurrent (index: number, current: TrackEntry, interrupt: boolean) {
let from = this.expandToIndex(index);
this.tracks[index] = current;
setCurrent (index: number, current: TrackEntry, interrupt: boolean) {
let from = this.expandToIndex(index);
this.tracks[index] = current;
if (from != null) {
if (from != null) {
if (interrupt) this.queue.interrupt(from);
current.mixingFrom = from;
current.mixTime = 0;
current.mixingFrom = from;
current.mixTime = 0;
from.timelinesRotation.length = 0;
// Store the interrupted mix percentage.
if (from.mixingFrom != null && from.mixDuration > 0)
current.interruptAlpha *= Math.min(1, from.mixTime / from.mixDuration);
// If not completely mixed in, set mixAlpha so mixing out happens from current mix to zero.
if (from.mixingFrom != null && from.mixDuration > 0) current.mixAlpha *= Math.min(from.mixTime / from.mixDuration, 1);
}
from.timelinesRotation.length = 0; // Reset rotation for mixing out, in case entry was mixed in.
}
this.queue.start(current);
}
this.queue.start(current);
}
setAnimation (trackIndex: number, animationName: string, loop: boolean) {
let animation = this.data.skeletonData.findAnimation(animationName);
if (animation == null) throw new Error("Animation not found: " + animationName);
return this.setAnimationWith(trackIndex, animation, loop);
}
setAnimation (trackIndex: number, animationName: string, loop: boolean) {
let animation = this.data.skeletonData.findAnimation(animationName);
if (animation == null) throw new Error("Animation not found: " + animationName);
return this.setAnimationWith(trackIndex, animation, loop);
}
setAnimationWith (trackIndex: number, animation: Animation, loop: boolean) {
if (animation == null) throw new Error("animation cannot be null.");
setAnimationWith (trackIndex: number, animation: Animation, loop: boolean) {
if (animation == null) throw new Error("animation cannot be null.");
let interrupt = true;
let current = this.expandToIndex(trackIndex);
if (current != null) {
if (current.nextTrackLast == -1) {
// Don't mix from an entry that was never applied.
this.tracks[trackIndex] = current.mixingFrom;
this.queue.interrupt(current);
this.queue.end(current);
this.disposeNext(current);
current = current.mixingFrom;
let current = this.expandToIndex(trackIndex);
if (current != null) {
if (current.nextTrackLast == -1) {
// Don't mix from an entry that was never applied.
this.tracks[trackIndex] = current.mixingFrom;
this.queue.interrupt(current);
this.queue.end(current);
this.disposeNext(current);
current = current.mixingFrom;
interrupt = false;
} else
this.disposeNext(current);
}
let entry = this.trackEntry(trackIndex, animation, loop, current);
this.setCurrent(trackIndex, entry, interrupt);
this.queue.drain();
return entry;
}
} else
this.disposeNext(current);
}
let entry = this.trackEntry(trackIndex, animation, loop, current);
this.setCurrent(trackIndex, entry, interrupt);
this.queue.drain();
return entry;
}
addAnimation (trackIndex: number, animationName: string, loop: boolean, delay: number) {
let animation = this.data.skeletonData.findAnimation(animationName);
if (animation == null) throw new Error("Animation not found: " + animationName);
return this.addAnimationWith(trackIndex, animation, loop, delay);
}
addAnimation (trackIndex: number, animationName: string, loop: boolean, delay: number) {
let animation = this.data.skeletonData.findAnimation(animationName);
if (animation == null) throw new Error("Animation not found: " + animationName);
return this.addAnimationWith(trackIndex, animation, loop, delay);
}
addAnimationWith (trackIndex: number, animation: Animation, loop: boolean, delay: number) {
if (animation == null) throw new Error("animation cannot be null.");
addAnimationWith (trackIndex: number, animation: Animation, loop: boolean, delay: number) {
if (animation == null) throw new Error("animation cannot be null.");
let last = this.expandToIndex(trackIndex);
if (last != null) {
while (last.next != null)
last = last.next;
}
let last = this.expandToIndex(trackIndex);
if (last != null) {
while (last.next != null)
last = last.next;
}
let entry = this.trackEntry(trackIndex, animation, loop, last);
let entry = this.trackEntry(trackIndex, animation, loop, last);
if (last == null) {
this.setCurrent(trackIndex, entry, true);
this.queue.drain();
} else {
last.next = entry;
if (delay <= 0) {
let duration = last.animationEnd - last.animationStart;
if (duration != 0)
delay += duration * (1 + ((last.trackTime / duration) | 0)) - this.data.getMix(last.animation, animation);
else
delay = 0;
}
}
if (last == null) {
this.setCurrent(trackIndex, entry, true);
this.queue.drain();
} else {
last.next = entry;
if (delay <= 0) {
let duration = last.animationEnd - last.animationStart;
if (duration != 0)
delay += duration * (1 + ((last.trackTime / duration) | 0)) - this.data.getMix(last.animation, animation);
else
delay = 0;
}
}
entry.delay = delay;
return entry;
}
entry.delay = delay;
return entry;
}
setEmptyAnimation (trackIndex: number, mixDuration: number) {
let entry = this.setAnimationWith(trackIndex, AnimationState.emptyAnimation, false);
entry.mixDuration = mixDuration;
entry.trackEnd = mixDuration;
return entry;
}
setEmptyAnimation (trackIndex: number, mixDuration: number) {
let entry = this.setAnimationWith(trackIndex, AnimationState.emptyAnimation, false);
entry.mixDuration = mixDuration;
entry.trackEnd = mixDuration;
return entry;
}
addEmptyAnimation (trackIndex: number, mixDuration: number, delay: number) {
if (delay <= 0) delay -= mixDuration;
let entry = this.addAnimationWith(trackIndex, AnimationState.emptyAnimation, false, delay);
entry.mixDuration = mixDuration;
entry.trackEnd = mixDuration;
return entry;
}
addEmptyAnimation (trackIndex: number, mixDuration: number, delay: number) {
if (delay <= 0) delay -= mixDuration;
let entry = this.addAnimationWith(trackIndex, AnimationState.emptyAnimation, false, delay);
entry.mixDuration = mixDuration;
entry.trackEnd = mixDuration;
return entry;
}

@@ -452,122 +494,85 @@ setEmptyAnimations (mixDuration: number) {

expandToIndex (index: number) {
if (index < this.tracks.length) return this.tracks[index];
Utils.ensureArrayCapacity(this.tracks, index - this.tracks.length + 1, null);
this.tracks.length = index + 1;
return null;
}
expandToIndex (index: number) {
if (index < this.tracks.length) return this.tracks[index];
Utils.ensureArrayCapacity(this.tracks, index - this.tracks.length + 1, null);
this.tracks.length = index + 1;
return null;
}
trackEntry (trackIndex: number, animation: Animation, loop: boolean, last: TrackEntry) {
let entry = this.trackEntryPool.obtain();
entry.trackIndex = trackIndex;
entry.animation = animation;
entry.loop = loop;
trackEntry (trackIndex: number, animation: Animation, loop: boolean, last: TrackEntry) {
let entry = this.trackEntryPool.obtain();
entry.trackIndex = trackIndex;
entry.animation = animation;
entry.loop = loop;
entry.eventThreshold = 0;
entry.attachmentThreshold = 0;
entry.drawOrderThreshold = 0;
entry.eventThreshold = 0;
entry.attachmentThreshold = 0;
entry.drawOrderThreshold = 0;
entry.animationStart = 0;
entry.animationEnd = animation.duration;
entry.animationLast = -1;
entry.nextAnimationLast = -1;
entry.animationStart = 0;
entry.animationEnd = animation.duration;
entry.animationLast = -1;
entry.nextAnimationLast = -1;
entry.delay = 0;
entry.trackTime = 0;
entry.trackLast = -1;
entry.nextTrackLast = -1;
entry.delay = 0;
entry.trackTime = 0;
entry.trackLast = -1;
entry.nextTrackLast = -1;
entry.trackEnd = Number.MAX_VALUE;
entry.timeScale = 1;
entry.timeScale = 1;
entry.alpha = 1;
entry.mixAlpha = 1;
entry.mixTime = 0;
entry.mixDuration = last == null ? 0 : this.data.getMix(last.animation, animation);
return entry;
}
entry.alpha = 1;
entry.interruptAlpha = 1;
entry.mixTime = 0;
entry.mixDuration = last == null ? 0 : this.data.getMix(last.animation, animation);
return entry;
}
disposeNext (entry: TrackEntry) {
let next = entry.next;
while (next != null) {
this.queue.dispose(next);
next = next.next;
}
entry.next = null;
}
disposeNext (entry: TrackEntry) {
let next = entry.next;
while (next != null) {
this.queue.dispose(next);
next = next.next;
}
entry.next = null;
}
_animationsChanged () {
this.animationsChanged = false;
_animationsChanged () {
this.animationsChanged = false;
let propertyIDs = this.propertyIDs;
let propertyIDs = this.propertyIDs;
propertyIDs.clear();
let mixingTo = this.mixingTo;
// Compute timelinesFirst from lowest to highest track entries.
let i = 0, n = this.tracks.length;
propertyIDs.clear();
for (; i < n; i++) { // Find first non-null entry.
let entry = this.tracks[i];
if (entry == null) continue;
this.setTimelinesFirst(entry);
i++;
break;
}
for (; i < n; i++) { // Rest of entries.
let entry = this.tracks[i];
if (entry != null) this.checkTimelinesFirst(entry);
}
}
let lastEntry: TrackEntry = null;
for (let i = 0, n = this.tracks.length; i < n; i++) {
let entry = this.tracks[i];
if (entry != null) entry.setTimelineData(null, mixingTo, propertyIDs);
}
}
setTimelinesFirst (entry: TrackEntry) {
if (entry.mixingFrom != null) {
this.setTimelinesFirst(entry.mixingFrom);
this.checkTimelinesUsage(entry, entry.timelinesFirst);
return;
}
let propertyIDs = this.propertyIDs;
let timelines = entry.animation.timelines;
let n = timelines.length;
let usage = Utils.setArraySize(entry.timelinesFirst, n, false);
for (let i = 0; i < n; i++) {
propertyIDs.add(timelines[i].getPropertyId());
usage[i] = true;
}
}
getCurrent (trackIndex: number) {
if (trackIndex >= this.tracks.length) return null;
return this.tracks[trackIndex];
}
checkTimelinesFirst (entry: TrackEntry) {
if (entry.mixingFrom != null) this.checkTimelinesFirst(entry.mixingFrom);
this.checkTimelinesUsage(entry, entry.timelinesFirst);
}
addListener (listener: AnimationStateListener2) {
if (listener == null) throw new Error("listener cannot be null.");
this.listeners.push(listener);
}
checkTimelinesUsage (entry: TrackEntry, usageArray: Array<boolean>) {
let propertyIDs = this.propertyIDs;
let timelines = entry.animation.timelines;
let n = timelines.length;
let usage = Utils.setArraySize(usageArray, n);
for (let i = 0; i < n; i++)
usage[i] = propertyIDs.add(timelines[i].getPropertyId());
}
/** Removes the listener added with {@link #addListener(AnimationStateListener)}. */
removeListener (listener: AnimationStateListener2) {
let index = this.listeners.indexOf(listener);
if (index >= 0) this.listeners.splice(index, 1);
}
getCurrent (trackIndex: number) {
if (trackIndex >= this.tracks.length) return null;
return this.tracks[trackIndex];
}
clearListeners () {
this.listeners.length = 0;
}
addListener (listener: AnimationStateListener2) {
if (listener == null) throw new Error("listener cannot be null.");
this.listeners.push(listener);
}
clearListenerNotifications () {
this.queue.clear();
}
/** Removes the listener added with {@link #addListener(AnimationStateListener)}. */
removeListener (listener: AnimationStateListener2) {
let index = this.listeners.indexOf(listener);
if (index >= 0) this.listeners.splice(index, 1);
}
clearListeners () {
this.listeners.length = 0;
}
clearListenerNotifications () {
this.queue.clear();
}
//deprecated stuff

@@ -613,48 +618,94 @@ onComplete: (trackIndex: number, loopCount: number) => any;

}
}
}
export class TrackEntry {
animation: Animation;
next: TrackEntry; mixingFrom: TrackEntry;
listener: AnimationStateListener2;
trackIndex: number;
loop: boolean;
eventThreshold: number; attachmentThreshold: number; drawOrderThreshold: number;
animationStart: number; animationEnd: number; animationLast: number; nextAnimationLast: number;
delay: number; trackTime: number; trackLast: number; nextTrackLast: number; trackEnd: number; timeScale: number;
alpha: number; mixTime: number; mixDuration: number; mixAlpha: number;
timelinesFirst = new Array<boolean>();
timelinesRotation = new Array<number>();
export class TrackEntry {
animation: Animation;
next: TrackEntry; mixingFrom: TrackEntry;
listener: AnimationStateListener2;
trackIndex: number;
loop: boolean;
eventThreshold: number; attachmentThreshold: number; drawOrderThreshold: number;
animationStart: number; animationEnd: number; animationLast: number; nextAnimationLast: number;
delay: number; trackTime: number; trackLast: number; nextTrackLast: number; trackEnd: number; timeScale: number;
alpha: number; mixTime: number; mixDuration: number; interruptAlpha: number; totalAlpha: number;
timelineData = new Array<number>();
timelineDipMix = new Array<TrackEntry>();
timelinesRotation = new Array<number>();
reset () {
this.next = null;
this.mixingFrom = null;
this.animation = null;
this.listener = null;
this.timelinesFirst.length = 0;
this.timelinesRotation.length = 0;
}
reset () {
this.next = null;
this.mixingFrom = null;
this.animation = null;
this.listener = null;
this.timelineData.length = 0;
this.timelineDipMix.length = 0;
this.timelinesRotation.length = 0;
}
getAnimationTime () {
if (this.loop) {
let duration = this.animationEnd - this.animationStart;
if (duration == 0) return this.animationStart;
return (this.trackTime % duration) + this.animationStart;
}
return Math.min(this.trackTime + this.animationStart, this.animationEnd);
}
setTimelineData (to: TrackEntry, mixingToArray: Array<TrackEntry>, propertyIDs: IntSet) : TrackEntry {
if (to != null) mixingToArray.push(to);
let lastEntry = this.mixingFrom != null ? this.mixingFrom.setTimelineData(this, mixingToArray, propertyIDs) : this;
if (to != null) mixingToArray.pop();
setAnimationLast(animationLast: number) {
this.animationLast = animationLast;
this.nextAnimationLast = animationLast;
}
let mixingTo = mixingToArray;
let mixingToLast = mixingToArray.length - 1;
let timelines = this.animation.timelines;
let timelinesCount = this.animation.timelines.length;
let timelineData = Utils.setArraySize(this.timelineData, timelinesCount);
this.timelineDipMix.length = 0;
let timelineDipMix = Utils.setArraySize(this.timelineDipMix, timelinesCount);
isComplete () {
return this.trackTime >= this.animationEnd - this.animationStart;
}
outer:
for (var i = 0; i < timelinesCount; i++) {
let id = timelines[i].getPropertyId();
if (!propertyIDs.add(id))
timelineData[i] = AnimationState.SUBSEQUENT;
else if (to == null || !to.hasTimeline(id))
timelineData[i] = AnimationState.FIRST;
else {
for (var ii = mixingToLast; ii >= 0; ii--) {
let entry = mixingTo[ii];
if (!entry.hasTimeline(id)) {
if (entry.mixDuration > 0) {
timelineData[i] = AnimationState.DIP_MIX;
timelineDipMix[i] = entry;
continue outer;
}
}
}
timelineData[i] = AnimationState.DIP;
}
}
return lastEntry;
}
resetRotationDirections () {
this.timelinesRotation.length = 0;
}
hasTimeline (id: number) : boolean {
let timelines = this.animation.timelines;
for (var i = 0, n = timelines.length; i < n; i++)
if (timelines[i].getPropertyId() == id) return true;
return false;
}
getAnimationTime () {
if (this.loop) {
let duration = this.animationEnd - this.animationStart;
if (duration == 0) return this.animationStart;
return (this.trackTime % duration) + this.animationStart;
}
return Math.min(this.trackTime + this.animationStart, this.animationEnd);
}
setAnimationLast(animationLast: number) {
this.animationLast = animationLast;
this.nextAnimationLast = animationLast;
}
isComplete () {
return this.trackTime >= this.animationEnd - this.animationStart;
}
resetRotationDirections () {
this.timelinesRotation.length = 0;
}
//deprecated stuff

@@ -704,49 +755,49 @@ onComplete: (trackIndex: number, loopCount: number) => any;

}
}
}
export class EventQueue {
objects: Array<any> = [];
drainDisabled = false;
animState: AnimationState;
export class EventQueue {
objects: Array<any> = [];
drainDisabled = false;
animState: AnimationState;
constructor(animState: AnimationState) {
this.animState = animState;
}
constructor(animState: AnimationState) {
this.animState = animState;
}
start (entry: TrackEntry) {
this.objects.push(EventType.start);
this.objects.push(entry);
this.animState.animationsChanged = true;
}
start (entry: TrackEntry) {
this.objects.push(EventType.start);
this.objects.push(entry);
this.animState.animationsChanged = true;
}
interrupt (entry: TrackEntry) {
this.objects.push(EventType.interrupt);
this.objects.push(entry);
}
interrupt (entry: TrackEntry) {
this.objects.push(EventType.interrupt);
this.objects.push(entry);
}
end (entry: TrackEntry) {
this.objects.push(EventType.end);
this.objects.push(entry);
this.animState.animationsChanged = true;
}
end (entry: TrackEntry) {
this.objects.push(EventType.end);
this.objects.push(entry);
this.animState.animationsChanged = true;
}
dispose (entry: TrackEntry) {
this.objects.push(EventType.dispose);
this.objects.push(entry);
}
dispose (entry: TrackEntry) {
this.objects.push(EventType.dispose);
this.objects.push(entry);
}
complete (entry: TrackEntry) {
this.objects.push(EventType.complete);
this.objects.push(entry);
}
complete (entry: TrackEntry) {
this.objects.push(EventType.complete);
this.objects.push(entry);
}
event (entry: TrackEntry, event: Event) {
this.objects.push(EventType.event);
this.objects.push(entry);
this.objects.push(event);
}
event (entry: TrackEntry, event: Event) {
this.objects.push(EventType.event);
this.objects.push(entry);
this.objects.push(event);
}
private static deprecatedWarning1: Boolean = false;
deprecateStuff() {
deprecateStuff() {
if (!EventQueue.deprecatedWarning1) {

@@ -759,118 +810,116 @@ EventQueue.deprecatedWarning1 = true;

drain () {
if (this.drainDisabled) return;
this.drainDisabled = true;
drain () {
if (this.drainDisabled) return;
this.drainDisabled = true;
let objects = this.objects;
let listeners = this.animState.listeners;
let objects = this.objects;
let listeners = this.animState.listeners;
for (let i = 0; i < objects.length; i += 2) {
let type = objects[i] as EventType;
let entry = objects[i + 1] as TrackEntry;
switch (type) {
case EventType.start:
if (entry.listener != null && entry.listener.start) entry.listener.start(entry);
for (let ii = 0; ii < listeners.length; ii++)
if (listeners[ii].start) listeners[ii].start(entry);
//deprecation
entry.onStart && this.deprecateStuff() && entry.onStart(entry.trackIndex);
this.animState.onStart && this.deprecateStuff() && this.deprecateStuff && this.animState.onStart(entry.trackIndex);
break;
case EventType.interrupt:
if (entry.listener != null && entry.listener.interrupt) entry.listener.interrupt(entry);
for (let ii = 0; ii < listeners.length; ii++)
if (listeners[ii].interrupt) listeners[ii].interrupt(entry);
break;
case EventType.end:
if (entry.listener != null && entry.listener.end) entry.listener.end(entry);
for (let ii = 0; ii < listeners.length; ii++)
if (listeners[ii].end) listeners[ii].end(entry);
//deprecation
entry.onEnd && this.deprecateStuff() && entry.onEnd(entry.trackIndex);
this.animState.onEnd && this.deprecateStuff() && this.animState.onEnd(entry.trackIndex);
// Fall through.
case EventType.dispose:
if (entry.listener != null && entry.listener.dispose) entry.listener.dispose(entry);
for (let ii = 0; ii < listeners.length; ii++)
if (listeners[ii].dispose) listeners[ii].dispose(entry);
this.animState.trackEntryPool.free(entry);
break;
case EventType.complete:
if (entry.listener != null && entry.listener.complete) entry.listener.complete(entry);
for (let ii = 0; ii < listeners.length; ii++)
if (listeners[ii].complete) listeners[ii].complete(entry);
//deprecation
for (let i = 0; i < objects.length; i += 2) {
let type = objects[i] as EventType;
let entry = objects[i + 1] as TrackEntry;
switch (type) {
case EventType.start:
if (entry.listener != null && entry.listener.start) entry.listener.start(entry);
for (let ii = 0; ii < listeners.length; ii++)
if (listeners[ii].start) listeners[ii].start(entry);
//deprecation
entry.onStart && this.deprecateStuff() && entry.onStart(entry.trackIndex);
this.animState.onStart && this.deprecateStuff() && this.deprecateStuff && this.animState.onStart(entry.trackIndex);
break;
case EventType.interrupt:
if (entry.listener != null && entry.listener.interrupt) entry.listener.interrupt(entry);
for (let ii = 0; ii < listeners.length; ii++)
if (listeners[ii].interrupt) listeners[ii].interrupt(entry);
break;
case EventType.end:
if (entry.listener != null && entry.listener.end) entry.listener.end(entry);
for (let ii = 0; ii < listeners.length; ii++)
if (listeners[ii].end) listeners[ii].end(entry);
//deprecation
entry.onEnd && this.deprecateStuff() && entry.onEnd(entry.trackIndex);
this.animState.onEnd && this.deprecateStuff() && this.animState.onEnd(entry.trackIndex);
// Fall through.
case EventType.dispose:
if (entry.listener != null && entry.listener.dispose) entry.listener.dispose(entry);
for (let ii = 0; ii < listeners.length; ii++)
if (listeners[ii].dispose) listeners[ii].dispose(entry);
this.animState.trackEntryPool.free(entry);
break;
case EventType.complete:
if (entry.listener != null && entry.listener.complete) entry.listener.complete(entry);
for (let ii = 0; ii < listeners.length; ii++)
if (listeners[ii].complete) listeners[ii].complete(entry);
//deprecation
let count = MathUtils.toInt(entry.loopsCount()) ;
entry.onComplete && this.deprecateStuff() && entry.onComplete(entry.trackIndex, count);
this.animState.onComplete && this.deprecateStuff() && this.animState.onComplete(entry.trackIndex, count);
break;
case EventType.event:
let event = objects[i++ + 2] as Event;
if (entry.listener != null && entry.listener.event) entry.listener.event(entry, event);
for (let ii = 0; ii < listeners.length; ii++)
if (listeners[ii].event) listeners[ii].event(entry, event);
//deprecation
entry.onEvent && this.deprecateStuff() && entry.onEvent(entry.trackIndex, event);
this.animState.onEvent && this.deprecateStuff() && this.animState.onEvent(entry.trackIndex, event);
break;
}
}
this.clear();
let count = MathUtils.toInt(entry.loopsCount()) ;
entry.onComplete && this.deprecateStuff() && entry.onComplete(entry.trackIndex, count);
this.animState.onComplete && this.deprecateStuff() && this.animState.onComplete(entry.trackIndex, count);
break;
case EventType.event:
let event = objects[i++ + 2] as Event;
if (entry.listener != null && entry.listener.event) entry.listener.event(entry, event);
for (let ii = 0; ii < listeners.length; ii++)
if (listeners[ii].event) listeners[ii].event(entry, event);
//deprecation
entry.onEvent && this.deprecateStuff() && entry.onEvent(entry.trackIndex, event);
this.animState.onEvent && this.deprecateStuff() && this.animState.onEvent(entry.trackIndex, event);
break;
}
}
this.clear();
this.drainDisabled = false;
}
this.drainDisabled = false;
}
clear () {
this.objects.length = 0;
}
clear () {
this.objects.length = 0;
}
}
export enum EventType {
start, interrupt, end, dispose, complete, event
}
}
export interface AnimationStateListener2 {
/** Invoked when this entry has been set as the current entry. */
start? (entry: TrackEntry): void;
export enum EventType {
start, interrupt, end, dispose, complete, event
}
/** Invoked when another entry has replaced this entry as the current entry. This entry may continue being applied for
* mixing. */
interrupt? (entry: TrackEntry): void;
export interface AnimationStateListener2 {
/** Invoked when this entry has been set as the current entry. */
start? (entry: TrackEntry): void;
/** Invoked when this entry is no longer the current entry and will never be applied again. */
end? (entry: TrackEntry): void;
/** Invoked when another entry has replaced this entry as the current entry. This entry may continue being applied for
* mixing. */
interrupt? (entry: TrackEntry): void;
/** Invoked when this entry will be disposed. This may occur without the entry ever being set as the current entry.
* References to the entry should not be kept after dispose is called, as it may be destroyed or reused. */
dispose? (entry: TrackEntry): void;
/** Invoked when this entry is no longer the current entry and will never be applied again. */
end? (entry: TrackEntry): void;
/** Invoked every time this entry's animation completes a loop. */
complete? (entry: TrackEntry): void;
/** Invoked when this entry will be disposed. This may occur without the entry ever being set as the current entry.
* References to the entry should not be kept after dispose is called, as it may be destroyed or reused. */
dispose? (entry: TrackEntry): void;
/** Invoked when this entry's animation triggers an event. */
event? (entry: TrackEntry, event: Event): void;
}
/** Invoked every time this entry's animation completes a loop. */
complete? (entry: TrackEntry): void;
export abstract class AnimationStateAdapter2 implements AnimationStateListener2 {
start (entry: TrackEntry) {
}
/** Invoked when this entry's animation triggers an event. */
event? (entry: TrackEntry, event: Event): void;
}
interrupt (entry: TrackEntry) {
}
export abstract class AnimationStateAdapter2 implements AnimationStateListener2 {
start (entry: TrackEntry) {
}
end (entry: TrackEntry) {
}
interrupt (entry: TrackEntry) {
}
dispose (entry: TrackEntry) {
}
end (entry: TrackEntry) {
}
complete (entry: TrackEntry) {
}
dispose (entry: TrackEntry) {
}
complete (entry: TrackEntry) {
}
event (entry: TrackEntry, event: Event) {
}
}
event (entry: TrackEntry, event: Event) {
}
}
}

@@ -67,3 +67,11 @@ /******************************************************************************

}
newPointAttachment(skin: Skin, name: string): PointAttachment {
return new PointAttachment(name);
}
newClippingAttachment(skin: Skin, name: string): ClippingAttachment {
return new ClippingAttachment(name);
}
}
}

@@ -43,2 +43,5 @@ /******************************************************************************

export abstract class VertexAttachment extends Attachment {
private static nextID = 0;
id = (VertexAttachment.nextID++ & 65535) << 11;
bones: Array<number>;

@@ -52,4 +55,4 @@ vertices: ArrayLike<number>;

computeWorldVertices(slot: Slot, worldVertices: ArrayLike<number>) {
this.computeWorldVerticesWith(slot, 0, this.worldVerticesLength, worldVertices, 0);
computeWorldVerticesOld(slot: Slot, worldVertices: ArrayLike<number>) {
this.computeWorldVertices(slot, 0, this.worldVerticesLength, worldVertices, 0, 2);
}

@@ -62,4 +65,4 @@

* @param offset The worldVertices index to begin writing values. */
computeWorldVerticesWith(slot: Slot, start: number, count: number, worldVertices: ArrayLike<number>, offset: number) {
count += offset;
computeWorldVertices(slot: Slot, start: number, count: number, worldVertices: ArrayLike<number>, offset: number, stride: number) {
count = offset + (count >> 1) * stride;
let skeleton = slot.bone.skeleton;

@@ -71,8 +74,7 @@ let deformArray = slot.attachmentVertices;

if (deformArray.length > 0) vertices = deformArray;
let bone = slot.bone;
let m = bone.matrix;
let x = m.tx;
let y = m.ty;
let a = m.a, b = m.c, c = m.b, d = m.d;
for (let v = start, w = offset; w < count; v += 2, w += 2) {
let mat = slot.bone.matrix;
let x = mat.tx;
let y = mat.ty;
let a = mat.a, b = mat.c, c = mat.b, d = mat.d;
for (let v = start, w = offset; w < count; v += 2, w += stride) {
let vx = vertices[v], vy = vertices[v + 1];

@@ -92,3 +94,3 @@ worldVertices[w] = vx * a + vy * b + x;

if (deformArray.length == 0) {
for (let w = offset, b = skip * 3; w < count; w += 2) {
for (let w = offset, b = skip * 3; w < count; w += stride) {
let wx = 0, wy = 0;

@@ -98,7 +100,6 @@ let n = bones[v++];

for (; v < n; v++, b += 3) {
let bone = skeletonBones[bones[v]];
let m = bone.matrix;
let mat = skeletonBones[bones[v]].matrix;
let vx = vertices[b], vy = vertices[b + 1], weight = vertices[b + 2];
wx += (vx * m.a + vy * m.c + m.tx) * weight;
wy += (vx * m.b + vy * m.d + m.ty) * weight;
wx += (vx * mat.a + vy * mat.c + mat.tx) * weight;
wy += (vx * mat.b + vy * mat.d + mat.ty) * weight;
}

@@ -110,3 +111,3 @@ worldVertices[w] = wx;

let deform = deformArray;
for (let w = offset, b = skip * 3, f = skip << 1; w < count; w += 2) {
for (let w = offset, b = skip * 3, f = skip << 1; w < count; w += stride) {
let wx = 0, wy = 0;

@@ -116,7 +117,7 @@ let n = bones[v++];

for (; v < n; v++, b += 3, f += 2) {
let bone = skeletonBones[bones[v]];
let m = bone.matrix;
let vx = vertices[b] + deform[f], vy = vertices[b + 1] + deform[f + 1], weight = vertices[b + 2];
wx += (vx * m.a + vy * m.c + m.tx) * weight;
wy += (vx * m.b + vy * m.d + m.ty) * weight;
let mat = skeletonBones[bones[v]].matrix;
let vx = vertices[b] + deform[f], vy = vertices[b + 1] + deform[f + 1],
weight = vertices[b + 2];
wx += (vx * mat.a + vy * mat.c + mat.tx) * weight;
wy += (vx * mat.b + vy * mat.d + mat.ty) * weight;
}

@@ -123,0 +124,0 @@ worldVertices[w] = wx;

@@ -45,3 +45,9 @@ /******************************************************************************

newPathAttachment(skin: Skin, name: string): PathAttachment;
/** @return May be null to not load an attachment */
newPointAttachment(skin: Skin, name: string): PointAttachment;
/** @return May be null to not load an attachment */
newClippingAttachment(skin: Skin, name: string): ClippingAttachment;
}
}

@@ -34,4 +34,4 @@ /******************************************************************************

export enum AttachmentType {
Region, BoundingBox, Mesh, LinkedMesh, Path
Region, BoundingBox, Mesh, LinkedMesh, Path, Point
}
}

@@ -36,3 +36,3 @@ /******************************************************************************

path: string;
regionUVs: ArrayLike<number>;
regionUVs: ArrayLike<number>; uvs: ArrayLike<number>;
triangles: Array<number>;

@@ -45,11 +45,6 @@ color = new Color(1, 1, 1, 1);

constructor(name: string) {
constructor (name: string) {
super(name);
}
updateWorldVertices(slot: Slot, premultipliedAlpha: boolean): ArrayLike<number> {
return [];
//nothing
}
updateUVs(region: TextureRegion, uvs: ArrayLike<number>): ArrayLike<number> {

@@ -82,7 +77,7 @@ let regionUVs = this.regionUVs;

applyDeform(sourceAttachment: VertexAttachment): boolean {
applyDeform (sourceAttachment: VertexAttachment): boolean {
return this == sourceAttachment || (this.inheritDeform && this.parentMesh == sourceAttachment);
}
getParentMesh() {
getParentMesh () {
return this.parentMesh;

@@ -92,3 +87,3 @@ }

/** @param parentMesh May be null. */
setParentMesh(parentMesh: MeshAttachment) {
setParentMesh (parentMesh: MeshAttachment) {
this.parentMesh = parentMesh;

@@ -98,9 +93,12 @@ if (parentMesh != null) {

this.vertices = parentMesh.vertices;
this.worldVerticesLength = parentMesh.worldVerticesLength;
this.regionUVs = parentMesh.regionUVs;
this.triangles = parentMesh.triangles;
this.hullLength = parentMesh.hullLength;
this.worldVerticesLength = parentMesh.worldVerticesLength;
this.worldVerticesLength = parentMesh.worldVerticesLength
}
}
//computeWorldVerticesWith(slot, 0, this.worldVerticesLength, worldVertices, 0);
}
}

@@ -34,2 +34,47 @@ /******************************************************************************

export class RegionAttachment extends Attachment {
static OX1 = 0;
static OY1 = 1;
static OX2 = 2;
static OY2 = 3;
static OX3 = 4;
static OY3 = 5;
static OX4 = 6;
static OY4 = 7;
static X1 = 0;
static Y1 = 1;
static C1R = 2;
static C1G = 3;
static C1B = 4;
static C1A = 5;
static U1 = 6;
static V1 = 7;
static X2 = 8;
static Y2 = 9;
static C2R = 10;
static C2G = 11;
static C2B = 12;
static C2A = 13;
static U2 = 14;
static V2 = 15;
static X3 = 16;
static Y3 = 17;
static C3R = 18;
static C3G = 19;
static C3B = 20;
static C3A = 21;
static U3 = 22;
static V3 = 23;
static X4 = 24;
static Y4 = 25;
static C4R = 26;
static C4G = 27;
static C4B = 28;
static C4A = 29;
static U4 = 30;
static V4 = 31;
x = 0;

@@ -45,4 +90,10 @@ y = 0;

path: string;
rendererObject: any;
region: TextureRegion;
offset = Utils.newFloatArray(8);
uvs = Utils.newFloatArray(8);
tempColor = new Color(1, 1, 1, 1);
constructor(name: string) {

@@ -52,7 +103,86 @@ super(name);

updateWorldVertices(slot: Slot, premultipliedAlpha: boolean): ArrayLike<number> {
return [];
//nothing
updateOffset(): void {
let regionScaleX = this.width / this.region.originalWidth * this.scaleX;
let regionScaleY = this.height / this.region.originalHeight * this.scaleY;
let localX = -this.width / 2 * this.scaleX + this.region.offsetX * regionScaleX;
let localY = -this.height / 2 * this.scaleY + this.region.offsetY * regionScaleY;
let localX2 = localX + this.region.width * regionScaleX;
let localY2 = localY + this.region.height * regionScaleY;
let radians = this.rotation * Math.PI / 180;
let cos = Math.cos(radians);
let sin = Math.sin(radians);
let localXCos = localX * cos + this.x;
let localXSin = localX * sin;
let localYCos = localY * cos + this.y;
let localYSin = localY * sin;
let localX2Cos = localX2 * cos + this.x;
let localX2Sin = localX2 * sin;
let localY2Cos = localY2 * cos + this.y;
let localY2Sin = localY2 * sin;
let offset = this.offset;
offset[RegionAttachment.OX1] = localXCos - localYSin;
offset[RegionAttachment.OY1] = localYCos + localXSin;
offset[RegionAttachment.OX2] = localXCos - localY2Sin;
offset[RegionAttachment.OY2] = localY2Cos + localXSin;
offset[RegionAttachment.OX3] = localX2Cos - localY2Sin;
offset[RegionAttachment.OY3] = localY2Cos + localX2Sin;
offset[RegionAttachment.OX4] = localX2Cos - localYSin;
offset[RegionAttachment.OY4] = localYCos + localX2Sin;
}
setRegion(region: TextureRegion): void {
this.region = region;
let uvs = this.uvs;
if (region.rotate) {
uvs[2] = region.u;
uvs[3] = region.v2;
uvs[4] = region.u;
uvs[5] = region.v;
uvs[6] = region.u2;
uvs[7] = region.v;
uvs[0] = region.u2;
uvs[1] = region.v2;
} else {
uvs[0] = region.u;
uvs[1] = region.v2;
uvs[2] = region.u;
uvs[3] = region.v;
uvs[4] = region.u2;
uvs[5] = region.v;
uvs[6] = region.u2;
uvs[7] = region.v2;
}
}
computeWorldVertices(bone: Bone, worldVertices: ArrayLike<number>, offset: number, stride: number) {
let vertexOffset = this.offset;
let mat = bone.matrix;
let x = mat.tx, y = mat.ty;
let a = mat.a, b = mat.c, c = mat.b, d = mat.d;
let offsetX = 0, offsetY = 0;
offsetX = vertexOffset[RegionAttachment.OX1];
offsetY = vertexOffset[RegionAttachment.OY1];
worldVertices[offset] = offsetX * a + offsetY * b + x; // br
worldVertices[offset + 1] = offsetX * c + offsetY * d + y;
offset += stride;
offsetX = vertexOffset[RegionAttachment.OX2];
offsetY = vertexOffset[RegionAttachment.OY2];
worldVertices[offset] = offsetX * a + offsetY * b + x; // bl
worldVertices[offset + 1] = offsetX * c + offsetY * d + y;
offset += stride;
offsetX = vertexOffset[RegionAttachment.OX3];
offsetY = vertexOffset[RegionAttachment.OY3];
worldVertices[offset] = offsetX * a + offsetY * b + x; // ul
worldVertices[offset + 1] = offsetX * c + offsetY * d + y;
offset += stride;
offsetX = vertexOffset[RegionAttachment.OX4];
offsetY = vertexOffset[RegionAttachment.OY4];
worldVertices[offset] = offsetX * a + offsetY * b + x; // ur
worldVertices[offset + 1] = offsetX * c + offsetY * d + y;
}
}
}

@@ -34,7 +34,7 @@ /******************************************************************************

export enum BlendMode {
Normal,
Additive,
Multiply,
Screen
Normal = 0,
Additive = 1,
Multiply = 2,
Screen = 3
}
}

@@ -244,27 +244,2 @@ /******************************************************************************

worldToLocalRotationX() {
let parent = this.parent;
if (parent == null) return this.arotation;
let pm = parent.matrix, m = this.matrix;
return Math.atan2(pm.a * m.b - pm.b * m.a, pm.d * m.a - pm.c * m.b) * MathUtils.radDeg;
}
worldToLocalRotationY() {
let parent = this.parent;
if (parent == null) return this.arotation;
let pm = parent.matrix, m = this.matrix;
return Math.atan2(pm.a * m.d - pm.b * m.c, pm.d * m.c - pm.c * m.d) * MathUtils.radDeg;
}
rotateWorld(degrees: number) {
let m = this.matrix;
let a = this.matrix.a, b = m.c, c = m.b, d = m.d;
let cos = MathUtils.cosDeg(degrees), sin = MathUtils.sinDeg(degrees);
m.a = cos * a - sin * c;
m.c = cos * b - sin * d;
m.b = sin * a + cos * c;
m.d = sin * b + cos * d;
this.appliedValid = false;
}
/** Computes the individual applied transform values from the world transform. This can be useful to perform processing using

@@ -333,3 +308,26 @@ * the applied transform after the world transform has been modified directly (eg, by a constraint).

}
worldToLocalRotation (worldRotation: number) {
let sin = MathUtils.sinDeg(worldRotation), cos = MathUtils.cosDeg(worldRotation);
let mat = this.matrix;
return Math.atan2(mat.a * sin - mat.b * cos, mat.d * cos - mat.c * sin) * MathUtils.radDeg;
}
localToWorldRotation (localRotation: number) {
let sin = MathUtils.sinDeg(localRotation), cos = MathUtils.cosDeg(localRotation);
let mat = this.matrix;
return Math.atan2(cos * mat.b + sin * mat.d, cos * mat.a + sin * mat.c) * MathUtils.radDeg;
}
rotateWorld (degrees: number) {
let mat = this.matrix;
let a = mat.a, b = mat.c, c = mat.b, d = mat.d;
let cos = MathUtils.cosDeg(degrees), sin = MathUtils.sinDeg(degrees);
mat.a = cos * a - sin * c;
mat.c = cos * b - sin * d;
mat.b = sin * a + cos * c;
mat.d = sin * b + cos * d;
this.appliedValid = false;
}
}
}

@@ -40,5 +40,3 @@ /******************************************************************************

level = 0;
constructor(data: IkConstraintData, skeleton: Skeleton) {
constructor (data: IkConstraintData, skeleton: Skeleton) {
if (data == null) throw new Error("data cannot be null.");

@@ -56,11 +54,11 @@ if (skeleton == null) throw new Error("skeleton cannot be null.");

getOrder() {
getOrder () {
return this.data.order;
}
apply() {
apply () {
this.update();
}
update() {
update () {
let target = this.target;

@@ -80,8 +78,8 @@ let bones = this.bones;

* coordinate system. */
apply1(bone: Bone, targetX: number, targetY: number, alpha: number) {
apply1 (bone: Bone, targetX: number, targetY: number, alpha: number) {
if (!bone.appliedValid) bone.updateAppliedTransform();
let pp = bone.parent.matrix;
let id = 1 / (pp.a * pp.d - pp.b * pp.c);
let x = targetX - pp.tx, y = targetY - pp.ty;
let tx = (x * pp.d - y * pp.c) * id - bone.ax, ty = (y * pp.a - x * pp.b) * id - bone.ay;
let p = bone.parent.matrix;
let id = 1 / (p.a * p.d - p.b * p.c);
let x = targetX - p.tx, y = targetY - p.ty;
let tx = (x * p.d - y * p.c) * id - bone.ax, ty = (y * p.a - x * p.b) * id - bone.ay;
let rotationIK = Math.atan2(ty, tx) * MathUtils.radDeg - bone.ashearX - bone.arotation;

@@ -99,3 +97,3 @@ if (bone.ascaleX < 0) rotationIK += 180;

* @param child A direct descendant of the parent bone. */
apply2(parent: Bone, child: Bone, targetX: number, targetY: number, bendDir: number, alpha: number) {
apply2 (parent: Bone, child: Bone, targetX: number, targetY: number, bendDir: number, alpha: number) {
if (alpha == 0) {

@@ -108,3 +106,3 @@ child.updateWorldTransform();

let px = parent.ax, py = parent.ay, psx = parent.ascaleX, psy = parent.ascaleY, csx = child.ascaleX;
let pmat = parent.matrix;
let os1 = 0, os2 = 0, s2 = 0;

@@ -128,24 +126,22 @@ if (psx < 0) {

os2 = 0;
let pm = parent.matrix;
let cx = child.ax, cy = 0, cwx = 0, cwy = 0, a = pm.a, b = pm.c, c = pm.b, d = pm.d;
let cx = child.ax, cy = 0, cwx = 0, cwy = 0, a = pmat.a, b = pmat.c, c = pmat.b, d = pmat.d;
let u = Math.abs(psx - psy) <= 0.0001;
if (!u) {
cy = 0;
cwx = a * cx + pm.tx;
cwy = c * cx + pm.ty;
cwx = a * cx + pmat.tx;
cwy = c * cx + pmat.ty;
} else {
cy = child.ay;
cwx = a * cx + b * cy + pm.tx;
cwy = c * cx + d * cy + pm.ty;
cwx = a * cx + b * cy + pmat.tx;
cwy = c * cx + d * cy + pmat.ty;
}
let pp = parent.parent;
let ppm = parent.parent.matrix;
a = ppm.a;
b = ppm.c;
c = ppm.b;
d = ppm.d;
let id = 1 / (a * d - b * c), x = targetX - ppm.tx, y = targetY - ppm.ty;
let pp = parent.parent.matrix;
a = pp.a;
b = pp.c;
c = pp.b;
d = pp.d;
let id = 1 / (a * d - b * c), x = targetX - pp.tx, y = targetY - pp.ty;
let tx = (x * d - y * b) * id - px, ty = (y * a - x * c) * id - py;
x = cwx - ppm.tx;
y = cwy - ppm.ty;
x = cwx - pp.tx;
y = cwy - pp.ty;
let dx = (x * d - y * b) * id - px, dy = (y * a - x * c) * id - py;

@@ -184,34 +180,23 @@ let l1 = Math.sqrt(dx * dx + dy * dy), l2 = child.data.length * csx, a1 = 0, a2 = 0;

}
let minAngle = 0, minDist = Number.MAX_VALUE, minX = 0, minY = 0;
let maxAngle = 0, maxDist = 0, maxX = 0, maxY = 0;
x = l1 + a;
d = x * x;
if (d > maxDist) {
maxAngle = 0;
maxDist = d;
maxX = x;
let minAngle = MathUtils.PI, minX = l1 - a, minDist = minX * minX, minY = 0;
let maxAngle = 0, maxX = l1 + a, maxDist = maxX * maxX, maxY = 0;
c = -a * l1 / (aa - bb);
if (c >= -1 && c <= 1) {
c = Math.acos(c);
x = a * Math.cos(c) + l1;
y = b * Math.sin(c);
d = x * x + y * y;
if (d < minDist) {
minAngle = c;
minDist = d;
minX = x;
minY = y;
}
if (d > maxDist) {
maxAngle = c;
maxDist = d;
maxX = x;
maxY = y;
}
}
x = l1 - a;
d = x * x;
if (d < minDist) {
minAngle = MathUtils.PI;
minDist = d;
minX = x;
}
let angle = Math.acos(-a * l1 / (aa - bb));
x = a * Math.cos(angle) + l1;
y = b * Math.sin(angle);
d = x * x + y * y;
if (d < minDist) {
minAngle = angle;
minDist = d;
minX = x;
minY = y;
}
if (d > maxDist) {
maxAngle = angle;
maxDist = d;
maxX = x;
maxY = y;
}
if (dd <= (minDist + maxDist) / 2) {

@@ -218,0 +203,0 @@ a1 = ta - Math.atan2(minY * bendDir, minX);

@@ -34,5 +34,3 @@ /******************************************************************************

export class PathConstraint implements Constraint {
static NONE = -1;
static BEFORE = -2;
static AFTER = -3;
static NONE = -1; static BEFORE = -2; static AFTER = -3;

@@ -42,15 +40,9 @@ data: PathConstraintData;

target: Slot;
position = 0;
spacing = 0;
rotateMix = 0;
translateMix = 0;
position = 0; spacing = 0; rotateMix = 0; translateMix = 0;
spaces = new Array<number>();
positions = new Array<number>();
world = new Array<number>();
curves = new Array<number>();
lengths = new Array<number>();
spaces = new Array<number>(); positions = new Array<number>();
world = new Array<number>(); curves = new Array<number>(); lengths = new Array<number>();
segments = new Array<number>();
constructor(data: PathConstraintData, skeleton: Skeleton) {
constructor (data: PathConstraintData, skeleton: Skeleton) {
if (data == null) throw new Error("data cannot be null.");

@@ -69,7 +61,7 @@ if (skeleton == null) throw new Error("skeleton cannot be null.");

apply() {
apply () {
this.update();
}
update() {
update () {
let attachment = this.target.getAttachment();

@@ -95,7 +87,6 @@ if (!(attachment instanceof PathAttachment)) return;

let bone = bones[i];
let m = bone.matrix;
let length = bone.data.length, x = length * m.a, y = length * m.b;
length = Math.sqrt(x * x + y * y);
let setupLength = bone.data.length, x = setupLength * bone.matrix.a, y = setupLength * bone.matrix.b;
let length = Math.sqrt(x * x + y * y);
if (scale) lengths[i] = length;
spaces[++i] = lengthSpacing ? Math.max(0, length + spacing) : spacing;
spaces[++i] = (lengthSpacing ? setupLength + spacing : spacing) * length / setupLength;
}

@@ -115,10 +106,10 @@ } else {

tip = false;
let pm = this.target.bone.matrix;
offsetRotation *= pm.a * pm.d - pm.b * pm.c > 0 ? MathUtils.degRad : -MathUtils.degRad;
let p = this.target.bone.matrix;
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) {
let bone = bones[i];
let m = bone.matrix;
m.tx += (boneX - m.tx) * translateMix;
m.ty += (boneY - m.ty) * translateMix;
let mat = bone.matrix;
mat.tx += (boneX - mat.tx) * translateMix;
mat.ty += (boneY - mat.ty) * translateMix;
let x = positions[p], y = positions[p + 1], dx = x - boneX, dy = y - boneY;

@@ -129,4 +120,4 @@ if (scale) {

let s = (Math.sqrt(dx * dx + dy * dy) / length - 1) * rotateMix + 1;
m.a *= s;
m.b *= s;
mat.a *= s;
mat.b *= s;
}

@@ -137,3 +128,3 @@ }

if (rotate) {
let a = m.a, b = m.c, c = m.b, d = m.d, r = 0, cos = 0, sin = 0;
let a = mat.a, b = mat.c, c = mat.b, d = mat.d, r = 0, cos = 0, sin = 0;
if (tangents)

@@ -157,3 +148,3 @@ r = positions[p - 1];

r -= MathUtils.PI2;
else if (r < -MathUtils.PI)
else if (r < -MathUtils.PI) //
r += MathUtils.PI2;

@@ -163,6 +154,6 @@ r *= rotateMix;

sin = Math.sin(r);
m.a = cos * a - sin * c;
m.c = cos * b - sin * d;
m.b = sin * a + cos * c;
m.d = sin * b + cos * d;
mat.a = cos * a - sin * c;
mat.c = cos * b - sin * d;
mat.b = sin * a + cos * c;
mat.d = sin * b + cos * d;
}

@@ -173,4 +164,4 @@ bone.appliedValid = false;

computeWorldPositions(path: PathAttachment, spacesCount: number, tangents: boolean, percentPosition: boolean,
percentSpacing: boolean) {
computeWorldPositions (path: PathAttachment, spacesCount: number, tangents: boolean, percentPosition: boolean,
percentSpacing: boolean) {
let target = this.target;

@@ -204,3 +195,3 @@ let position = this.position;

prevCurve = PathConstraint.BEFORE;
path.computeWorldVerticesWith(target, 2, 4, world, 0);
path.computeWorldVertices(target, 2, 4, world, 0, 2);
}

@@ -212,3 +203,3 @@ this.addBeforePosition(p, world, 0, out, o);

prevCurve = PathConstraint.AFTER;
path.computeWorldVerticesWith(target, verticesLength - 6, 4, world, 0);
path.computeWorldVertices(target, verticesLength - 6, 4, world, 0, 2);
}

@@ -220,3 +211,3 @@ this.addAfterPosition(p - pathLength, world, 0, out, o);

// Determine curve containing position.
for (; ; curve++) {
for (;; curve++) {
let length = lengths[curve];

@@ -235,6 +226,6 @@ if (p > length) continue;

if (closed && curve == curveCount) {
path.computeWorldVerticesWith(target, verticesLength - 4, 4, world, 0);
path.computeWorldVerticesWith(target, 0, 4, world, 4);
path.computeWorldVertices(target, verticesLength - 4, 4, world, 0, 2);
path.computeWorldVertices(target, 0, 4, world, 4, 2);
} else
path.computeWorldVerticesWith(target, curve * 6 + 2, 8, world, 0);
path.computeWorldVertices(target, curve * 6 + 2, 8, world, 0, 2);
}

@@ -251,4 +242,4 @@ this.addCurvePosition(p, world[0], world[1], world[2], world[3], world[4], world[5], world[6], world[7], out, o,

world = Utils.setArraySize(this.world, verticesLength);
path.computeWorldVerticesWith(target, 2, verticesLength - 4, world, 0);
path.computeWorldVerticesWith(target, 0, 2, world, verticesLength - 4);
path.computeWorldVertices(target, 2, verticesLength - 4, world, 0, 2);
path.computeWorldVertices(target, 0, 2, world, verticesLength - 4, 2);
world[verticesLength - 2] = world[0];

@@ -260,3 +251,3 @@ world[verticesLength - 1] = world[1];

world = Utils.setArraySize(this.world, verticesLength);
path.computeWorldVerticesWith(target, 2, verticesLength, world, 0);
path.computeWorldVertices(target, 2, verticesLength, world, 0, 2);
}

@@ -326,3 +317,3 @@

// Determine curve containing position.
for (; ; curve++) {
for (;; curve++) {
let length = curves[curve];

@@ -382,3 +373,3 @@ if (p > length) continue;

p *= curveLength;
for (; ; segment++) {
for (;; segment++) {
let length = segments[segment];

@@ -399,3 +390,3 @@ if (p > length) continue;

addBeforePosition(p: number, temp: Array<number>, i: number, out: Array<number>, o: number) {
addBeforePosition (p: number, temp: Array<number>, i: number, out: Array<number>, o: number) {
let x1 = temp[i], y1 = temp[i + 1], dx = temp[i + 2] - x1, dy = temp[i + 3] - y1, r = Math.atan2(dy, dx);

@@ -407,3 +398,3 @@ out[o] = x1 + p * Math.cos(r);

addAfterPosition(p: number, temp: Array<number>, i: number, out: Array<number>, o: number) {
addAfterPosition (p: number, temp: Array<number>, i: number, out: Array<number>, o: number) {
let x1 = temp[i + 2], y1 = temp[i + 3], dx = x1 - temp[i], dy = y1 - temp[i + 1], r = Math.atan2(dy, dx);

@@ -415,4 +406,4 @@ out[o] = x1 + p * Math.cos(r);

addCurvePosition(p: number, x1: number, y1: number, cx1: number, cy1: number, cx2: number, cy2: number, x2: number, y2: number,
out: Array<number>, o: number, tangents: boolean) {
addCurvePosition (p: number, x1: number, y1: number, cx1: number, cy1: number, cx2: number, cy2: number, x2: number, y2: number,
out: Array<number>, o: number, tangents: boolean) {
if (p == 0 || isNaN(p)) p = 0.0001;

@@ -427,3 +418,3 @@ let tt = p * p, ttt = tt * p, u = 1 - p, uu = u * u, uuu = uu * u;

getOrder() {
getOrder () {
return this.data.order;

@@ -430,0 +421,0 @@ }

@@ -46,8 +46,6 @@ /******************************************************************************

time = 0;
flipX = false;
flipY = false;
x = 0;
y = 0;
flipX = false; flipY = false;
x = 0; y = 0;
constructor(data: SkeletonData) {
constructor (data: SkeletonData) {
if (data == null) throw new Error("data cannot be null.");

@@ -102,3 +100,3 @@ this.data = data;

updateCache() {
updateCache () {
let updateCache = this._updateCache;

@@ -112,2 +110,3 @@ updateCache.length = 0;

// IK first, lowest hierarchy depth first.
let ikConstraints = this.ikConstraints;

@@ -148,3 +147,3 @@ let transformConstraints = this.transformConstraints;

sortIkConstraint(constraint: IkConstraint) {
sortIkConstraint (constraint: IkConstraint) {
let target = constraint.target;

@@ -168,3 +167,3 @@ this.sortBone(target);

sortPathConstraint(constraint: PathConstraint) {
sortPathConstraint (constraint: PathConstraint) {
let slot = constraint.target;

@@ -176,4 +175,4 @@ let slotIndex = slot.data.index;

this.sortPathConstraintAttachment(this.data.defaultSkin, slotIndex, slotBone);
for (let ii = 0, nn = this.data.skins.length; ii < nn; ii++)
this.sortPathConstraintAttachment(this.data.skins[ii], slotIndex, slotBone);
for (let i = 0, n = this.data.skins.length; i < n; i++)
this.sortPathConstraintAttachment(this.data.skins[i], slotIndex, slotBone);

@@ -185,14 +184,14 @@ let attachment = slot.getAttachment();

let boneCount = constrained.length;
for (let ii = 0; ii < boneCount; ii++)
this.sortBone(constrained[ii]);
for (let i = 0; i < boneCount; i++)
this.sortBone(constrained[i]);
this._updateCache.push(constraint);
for (let ii = 0; ii < boneCount; ii++)
this.sortReset(constrained[ii].children);
for (let ii = 0; ii < boneCount; ii++)
constrained[ii].sorted = true;
for (let i = 0; i < boneCount; i++)
this.sortReset(constrained[i].children);
for (let i = 0; i < boneCount; i++)
constrained[i].sorted = true;
}
sortTransformConstraint(constraint: TransformConstraint) {
sortTransformConstraint (constraint: TransformConstraint) {
this.sortBone(constraint.target);

@@ -202,4 +201,13 @@

let boneCount = constrained.length;
for (let ii = 0; ii < boneCount; ii++)
this.sortBone(constrained[ii]);
if (constraint.data.local) {
for (let i = 0; i < boneCount; i++) {
let child = constrained[i];
this.sortBone(child.parent);
if (!(this._updateCache.indexOf(child) > -1)) this.updateCacheReset.push(child);
}
} else {
for (let i = 0; i < boneCount; i++) {
this.sortBone(constrained[i]);
}
}

@@ -214,3 +222,3 @@ this._updateCache.push(constraint);

sortPathConstraintAttachment(skin: Skin, slotIndex: number, slotBone: Bone) {
sortPathConstraintAttachment (skin: Skin, slotIndex: number, slotBone: Bone) {
let attachments = skin.attachments[slotIndex];

@@ -223,3 +231,3 @@ if (!attachments) return;

sortPathConstraintAttachmentWith(attachment: Attachment, slotBone: Bone) {
sortPathConstraintAttachmentWith (attachment: Attachment, slotBone: Bone) {
if (!(attachment instanceof PathAttachment)) return;

@@ -242,3 +250,3 @@ let pathBones = (<PathAttachment>attachment).bones;

sortBone(bone: Bone) {
sortBone (bone: Bone) {
if (bone.sorted) return;

@@ -251,3 +259,3 @@ let parent = bone.parent;

sortReset(bones: Array<Bone>) {
sortReset (bones: Array<Bone>) {
for (let i = 0, n = bones.length; i < n; i++) {

@@ -261,3 +269,3 @@ let bone = bones[i];

/** Updates the world transform for each bone and applies constraints. */
updateWorldTransform() {
updateWorldTransform () {
let updateCacheReset = this.updateCacheReset;

@@ -281,3 +289,3 @@ for (let i = 0, n = updateCacheReset.length; i < n; i++) {

/** Sets the bones, constraints, and slots to their setup pose values. */
setToSetupPose() {
setToSetupPose () {
this.setBonesToSetupPose();

@@ -288,3 +296,3 @@ this.setSlotsToSetupPose();

/** Sets the bones and constraints to their setup pose values. */
setBonesToSetupPose() {
setBonesToSetupPose () {
let bones = this.bones;

@@ -322,3 +330,3 @@ for (let i = 0, n = bones.length; i < n; i++)

setSlotsToSetupPose() {
setSlotsToSetupPose () {
let slots = this.slots;

@@ -331,3 +339,3 @@ Utils.arrayCopy(slots, 0, this.drawOrder, 0, slots.length);

/** @return May return null. */
getRootBone() {
getRootBone () {
if (this.bones.length == 0) return null;

@@ -338,3 +346,3 @@ return this.bones[0];

/** @return May be null. */
findBone(boneName: string) {
findBone (boneName: string) {
if (boneName == null) throw new Error("boneName cannot be null.");

@@ -350,3 +358,3 @@ let bones = this.bones;

/** @return -1 if the bone was not found. */
findBoneIndex(boneName: string) {
findBoneIndex (boneName: string) {
if (boneName == null) throw new Error("boneName cannot be null.");

@@ -360,3 +368,3 @@ let bones = this.bones;

/** @return May be null. */
findSlot(slotName: string) {
findSlot (slotName: string) {
if (slotName == null) throw new Error("slotName cannot be null.");

@@ -372,3 +380,3 @@ let slots = this.slots;

/** @return -1 if the bone was not found. */
findSlotIndex(slotName: string) {
findSlotIndex (slotName: string) {
if (slotName == null) throw new Error("slotName cannot be null.");

@@ -383,3 +391,3 @@ let slots = this.slots;

* @see #setSkin(Skin) */
setSkinByName(skinName: string) {
setSkinByName (skinName: string) {
let skin = this.data.findSkin(skinName);

@@ -394,3 +402,3 @@ if (skin == null) throw new Error("Skin not found: " + skinName);

* @param newSkin May be null. */
setSkin(newSkin: Skin) {
setSkin (newSkin: Skin) {
if (newSkin != null) {

@@ -415,3 +423,3 @@ if (this.skin != null)

/** @return May be null. */
getAttachmentByName(slotName: string, attachmentName: string): Attachment {
getAttachmentByName (slotName: string, attachmentName: string): Attachment {
return this.getAttachment(this.data.findSlotIndex(slotName), attachmentName);

@@ -421,3 +429,3 @@ }

/** @return May be null. */
getAttachment(slotIndex: number, attachmentName: string): Attachment {
getAttachment (slotIndex: number, attachmentName: string): Attachment {
if (attachmentName == null) throw new Error("attachmentName cannot be null.");

@@ -433,3 +441,3 @@ if (this.skin != null) {

/** @param attachmentName May be null. */
setAttachment(slotName: string, attachmentName: string) {
setAttachment (slotName: string, attachmentName: string) {
if (slotName == null) throw new Error("slotName cannot be null.");

@@ -454,3 +462,3 @@ let slots = this.slots;

/** @return May be null. */
findIkConstraint(constraintName: string) {
findIkConstraint (constraintName: string) {
if (constraintName == null) throw new Error("constraintName cannot be null.");

@@ -466,3 +474,3 @@ let ikConstraints = this.ikConstraints;

/** @return May be null. */
findTransformConstraint(constraintName: string) {
findTransformConstraint (constraintName: string) {
if (constraintName == null) throw new Error("constraintName cannot be null.");

@@ -478,3 +486,3 @@ let transformConstraints = this.transformConstraints;

/** @return May be null. */
findPathConstraint(constraintName: string) {
findPathConstraint (constraintName: string) {
if (constraintName == null) throw new Error("constraintName cannot be null.");

@@ -491,4 +499,5 @@ let pathConstraints = this.pathConstraints;

* @param offset The distance from the skeleton origin to the bottom left corner of the AABB.
* @param size The width and height of the AABB. */
getBounds(offset: Vector2, size: Vector2) {
* @param size The width and height of the AABB.
* @param temp Working memory */
getBounds (offset: Vector2, size: Vector2, temp: Array<number>) {
if (offset == null) throw new Error("offset cannot be null.");

@@ -500,10 +509,17 @@ if (size == null) throw new Error("size cannot be null.");

let slot = drawOrder[i];
let verticesLength = 0;
let vertices: ArrayLike<number> = null;
let attachment = slot.getAttachment();
if (attachment instanceof RegionAttachment)
vertices = (<RegionAttachment>attachment).updateWorldVertices(slot, false);
else if (attachment instanceof MeshAttachment)
vertices = (<MeshAttachment>attachment).updateWorldVertices(slot, true);
if (attachment instanceof RegionAttachment) {
verticesLength = 8;
vertices = Utils.setArraySize(temp, verticesLength, 0);
(<RegionAttachment>attachment).computeWorldVertices(slot.bone, vertices, 0, 2);
} else if (attachment instanceof MeshAttachment) {
let mesh = (<MeshAttachment>attachment);
verticesLength = mesh.worldVerticesLength;
vertices = Utils.setArraySize(temp, verticesLength, 0);
mesh.computeWorldVertices(slot, 0, verticesLength, vertices, 0, 2);
}
if (vertices != null) {
for (let ii = 0, nn = vertices.length; ii < nn; ii += 8) {
for (let ii = 0, nn = vertices.length; ii < nn; ii += 2) {
let x = vertices[ii], y = vertices[ii + 1];

@@ -521,3 +537,3 @@ minX = Math.min(minX, x);

update(delta: number) {
update (delta: number) {
this.time += delta;

@@ -524,0 +540,0 @@ }

@@ -34,6 +34,3 @@ /******************************************************************************

export class SkeletonBounds {
minX = 0;
minY = 0;
maxX = 0;
maxY = 0;
minX = 0; minY = 0; maxX = 0; maxY = 0;
boundingBoxes = new Array<BoundingBoxAttachment>();

@@ -45,3 +42,3 @@ polygons = new Array<ArrayLike<number>>();

update(skeleton: Skeleton, updateAabb: boolean) {
update (skeleton: Skeleton, updateAabb: boolean) {
if (skeleton == null) throw new Error("skeleton cannot be null.");

@@ -70,10 +67,17 @@ let boundingBoxes = this.boundingBoxes;

polygons.push(polygon);
boundingBox.computeWorldVertices(slot, polygon);
boundingBox.computeWorldVertices(slot, 0, boundingBox.worldVerticesLength, polygon, 0, 2);
}
}
if (updateAabb) this.aabbCompute();
if (updateAabb) {
this.aabbCompute();
} else {
this.minX = Number.POSITIVE_INFINITY;
this.minY = Number.POSITIVE_INFINITY;
this.maxX = Number.NEGATIVE_INFINITY;
this.maxY = Number.NEGATIVE_INFINITY;
}
}
aabbCompute() {
aabbCompute () {
let minX = Number.POSITIVE_INFINITY, minY = Number.POSITIVE_INFINITY, maxX = Number.NEGATIVE_INFINITY, maxY = Number.NEGATIVE_INFINITY;

@@ -100,3 +104,3 @@ let polygons = this.polygons;

/** Returns true if the axis aligned bounding box contains the point. */
aabbContainsPoint(x: number, y: number) {
aabbContainsPoint (x: number, y: number) {
return x >= this.minX && x <= this.maxX && y >= this.minY && y <= this.maxY;

@@ -106,3 +110,3 @@ }

/** Returns true if the axis aligned bounding box intersects the line segment. */
aabbIntersectsSegment(x1: number, y1: number, x2: number, y2: number) {
aabbIntersectsSegment (x1: number, y1: number, x2: number, y2: number) {
let minX = this.minX;

@@ -127,3 +131,3 @@ let minY = this.minY;

/** Returns true if the axis aligned bounding box intersects the axis aligned bounding box of the specified bounds. */
aabbIntersectsSkeleton(bounds: SkeletonBounds) {
aabbIntersectsSkeleton (bounds: SkeletonBounds) {
return this.minX < bounds.maxX && this.maxX > bounds.minX && this.minY < bounds.maxY && this.maxY > bounds.minY;

@@ -134,3 +138,3 @@ }

* efficient to only call this method if {@link #aabbContainsPoint(float, float)} returns true. */
containsPoint(x: number, y: number): BoundingBoxAttachment {
containsPoint (x: number, y: number): BoundingBoxAttachment {
let polygons = this.polygons;

@@ -143,3 +147,3 @@ for (let i = 0, n = polygons.length; i < n; i++)

/** Returns true if the polygon contains the point. */
containsPointPolygon(polygon: ArrayLike<number>, x: number, y: number) {
containsPointPolygon (polygon: ArrayLike<number>, x: number, y: number) {
let vertices = polygon;

@@ -165,3 +169,3 @@ let nn = polygon.length;

* true. */
intersectsSegment(x1: number, y1: number, x2: number, y2: number) {
intersectsSegment (x1: number, y1: number, x2: number, y2: number) {
let polygons = this.polygons;

@@ -174,3 +178,3 @@ for (let i = 0, n = polygons.length; i < n; i++)

/** Returns true if the polygon contains any part of the line segment. */
intersectsSegmentPolygon(polygon: ArrayLike<number>, x1: number, y1: number, x2: number, y2: number) {
intersectsSegmentPolygon (polygon: ArrayLike<number>, x1: number, y1: number, x2: number, y2: number) {
let vertices = polygon;

@@ -199,3 +203,3 @@ let nn = polygon.length;

/** Returns the polygon for the specified bounding box, or null. */
getPolygon(boundingBox: BoundingBoxAttachment) {
getPolygon (boundingBox: BoundingBoxAttachment) {
if (boundingBox == null) throw new Error("boundingBox cannot be null.");

@@ -206,7 +210,7 @@ let index = this.boundingBoxes.indexOf(boundingBox);

getWidth() {
getWidth () {
return this.maxX - this.minX;
}
getHeight() {
getHeight () {
return this.maxY - this.minY;

@@ -213,0 +217,0 @@ }

@@ -38,7 +38,7 @@ /******************************************************************************

constructor(attachmentLoader: AttachmentLoader) {
constructor (attachmentLoader: AttachmentLoader) {
this.attachmentLoader = attachmentLoader;
}
readSkeletonData(json: string | any): SkeletonData {
readSkeletonData (json: string | any): SkeletonData {
let scale = this.scale;

@@ -79,14 +79,4 @@ let skeletonData = new SkeletonData();

data.shearY = this.getValue(boneMap, "shearY", 0);
data.transformMode = SkeletonJson.transformModeFromString(this.getValue(boneMap, "transform", "normal"));
//this is legacy
if (boneMap.hasOwnProperty("inheritScale") || boneMap.hasOwnProperty("inheritRotation")) {
//before 3.5
data.transformMode = SkeletonJson.transformModeLegacy(
this.getValue(boneMap, "inheritRotation", true),
this.getValue(boneMap, "inheritScale", true));
} else {
//after 3.5
data.transformMode = SkeletonJson.transformModeFromString(this.getValue(boneMap, "transform", "normal"));
}
skeletonData.bones.push(data);

@@ -109,2 +99,8 @@ }

let dark: string = this.getValue(slotMap, "dark", null);
if (dark != null) {
data.darkColor = new Color(1, 1, 1, 1);
data.darkColor.setFromString(dark);
}
data.attachmentName = this.getValue(slotMap, "attachment", null);

@@ -159,2 +155,4 @@ data.blendMode = SkeletonJson.blendModeFromString(this.getValue(slotMap, "blend", "normal"));

data.local = this.getValue(constraintMap, "local", false);
data.relative = this.getValue(constraintMap, "relative", false);
data.offsetRotation = this.getValue(constraintMap, "rotation", 0);

@@ -219,3 +217,3 @@ data.offsetX = this.getValue(constraintMap, "x", 0) * scale;

for (let entryName in slotMap) {
let attachment = this.readAttachment(slotMap[entryName], skin, slotIndex, entryName);
let attachment = this.readAttachment(slotMap[entryName], skin, slotIndex, entryName, skeletonData);
if (attachment != null) skin.addAttachment(slotIndex, entryName, attachment);

@@ -237,3 +235,3 @@ }

linkedMesh.mesh.setParentMesh(<MeshAttachment> parent);
// linkedMesh.mesh.updateUVs();
//linkedMesh.mesh.updateUVs();
}

@@ -265,3 +263,3 @@ this.linkedMeshes.length = 0;

readAttachment(map: any, skin: Skin, slotIndex: number, name: string): Attachment {
readAttachment (map: any, skin: Skin, slotIndex: number, name: string, skeletonData: SkeletonData): Attachment {
let scale = this.scale;

@@ -289,2 +287,3 @@ name = this.getValue(map, "name", name);

//region.updateOffset();
return region;

@@ -300,5 +299,2 @@ }

}
//weightedmesh is deprecated but who cares
case "weightedmesh":
case "skinnedmesh":
case "mesh":

@@ -325,3 +321,3 @@ case "linkedmesh": {

mesh.regionUVs = uvs;
// mesh.updateUVs();
//mesh.updateUVs();

@@ -342,3 +338,3 @@ mesh.hullLength = this.getValue(map, "hull", 0) * 2;

for (let i = 0; i < map.lengths.length; i++)
lengths[i++] = map.lengths[i] * scale;
lengths[i] = map.lengths[i] * scale;
path.lengths = lengths;

@@ -350,2 +346,31 @@

}
case "point": {
let point = this.attachmentLoader.newPointAttachment(skin, name);
if (point == null) return null;
point.x = this.getValue(map, "x", 0) * scale;
point.y = this.getValue(map, "y", 0) * scale;
point.rotation = this.getValue(map, "rotation", 0);
let color = this.getValue(map, "color", null);
if (color != null) point.color.setFromString(color);
return point;
}
case "clipping": {
let clip = this.attachmentLoader.newClippingAttachment(skin, name);
if (clip == null) return null;
let end = this.getValue(map, "end", null);
if (end != null) {
let slot = skeletonData.findSlot(end);
if (slot == null) throw new Error("Clipping end slot not found: " + end);
clip.endSlot = slot;
}
let vertexCount = map.vertexCount;
this.readVertices(map, clip, vertexCount << 1);
let color: string = this.getValue(map, "color", null);
if (color != null) clip.color.setFromString(color);
return clip;
}
}

@@ -355,3 +380,3 @@ return null;

readVertices(map: any, attachment: VertexAttachment, verticesLength: number) {
readVertices (map: any, attachment: VertexAttachment, verticesLength: number) {
let scale = this.scale;

@@ -361,7 +386,8 @@ attachment.worldVerticesLength = verticesLength;

if (verticesLength == vertices.length) {
let scaledVertices = Utils.toFloatArray(vertices);
if (scale != 1) {
for (let i = 0, n = vertices.length; i < n; i++)
vertices[i] *= scale;
scaledVertices[i] *= scale;
}
attachment.vertices = Utils.toFloatArray(vertices);
attachment.vertices = scaledVertices;
return;

@@ -385,3 +411,3 @@ }

readAnimation(map: any, name: string, skeletonData: SkeletonData) {
readAnimation (map: any, name: string, skeletonData: SkeletonData) {
let scale = this.scale;

@@ -399,3 +425,14 @@ let timelines = new Array<Timeline>();

let timelineMap = slotMap[timelineName];
if (timelineName == "color") {
if (timelineName == "attachment") {
let timeline = new AttachmentTimeline(timelineMap.length);
timeline.slotIndex = slotIndex;
let frameIndex = 0;
for (let i = 0; i < timelineMap.length; i++) {
let valueMap = timelineMap[i];
timeline.setFrame(frameIndex++, valueMap.time, valueMap.name);
}
timelines.push(timeline);
duration = Math.max(duration, timeline.frames[timeline.getFrameCount() - 1]);
} else if (timelineName == "color") {
let timeline = new ColorTimeline(timelineMap.length);

@@ -416,4 +453,4 @@ timeline.slotIndex = slotIndex;

} else if (timelineName = "attachment") {
let timeline = new AttachmentTimeline(timelineMap.length);
} else if (timelineName == "twoColor") {
let timeline = new TwoColorTimeline(timelineMap.length);
timeline.slotIndex = slotIndex;

@@ -424,6 +461,13 @@

let valueMap = timelineMap[i];
timeline.setFrame(frameIndex++, valueMap.time, valueMap.name);
let light = new Color();
let dark = new Color();
light.setFromString(valueMap.light);
dark.setFromString(valueMap.dark);
timeline.setFrame(frameIndex, valueMap.time, light.r, light.g, light.b, light.a, dark.r, dark.g, dark.b);
this.readCurve(valueMap, timeline, frameIndex);
frameIndex++;
}
timelines.push(timeline);
duration = Math.max(duration, timeline.frames[timeline.getFrameCount() - 1]);
duration = Math.max(duration, timeline.frames[(timeline.getFrameCount() - 1) * TwoColorTimeline.ENTRIES]);
} else

@@ -677,3 +721,3 @@ throw new Error("Invalid timeline type for a slot: " + timelineName + " (" + slotName + ")");

if (eventData == null) throw new Error("Event not found: " + eventMap.name);
let event = new Event(eventMap.time, eventData);
let event = new Event(Utils.toSinglePrecision(eventMap.time), eventData);
event.intValue = this.getValue(eventMap, "int", eventData.intValue);

@@ -695,3 +739,3 @@ event.floatValue = this.getValue(eventMap, "float", eventData.floatValue);

readCurve(map: any, timeline: CurveTimeline, frameIndex: number) {
readCurve (map: any, timeline: CurveTimeline, frameIndex: number) {
if (!map.curve) return;

@@ -706,15 +750,16 @@ if (map.curve === "stepped")

getValue(map: any, prop: string, defaultValue: any) {
getValue (map: any, prop: string, defaultValue: any) {
return map[prop] !== undefined ? map[prop] : defaultValue;
}
static blendModeFromString(str: string): number {
if (str === 'multiply') return PIXI.BLEND_MODES.MULTIPLY;
if (str === 'additive') return PIXI.BLEND_MODES.ADD;
if (str === 'screen') return PIXI.BLEND_MODES.SCREEN;
if (str === 'normal') return PIXI.BLEND_MODES.NORMAL;
static blendModeFromString (str: string) {
str = str.toLowerCase();
if (str == "normal") return BlendMode.Normal;
if (str == "additive") return BlendMode.Additive;
if (str == "multiply") return BlendMode.Multiply;
if (str == "screen") return BlendMode.Screen;
throw new Error(`Unknown blend mode: ${str}`);
}
static positionModeFromString(str: string) {
static positionModeFromString (str: string) {
str = str.toLowerCase();

@@ -726,3 +771,3 @@ if (str == "fixed") return PositionMode.Fixed;

static spacingModeFromString(str: string) {
static spacingModeFromString (str: string) {
str = str.toLowerCase();

@@ -735,3 +780,3 @@ if (str == "length") return SpacingMode.Length;

static rotateModeFromString(str: string) {
static rotateModeFromString (str: string) {
str = str.toLowerCase();

@@ -753,24 +798,10 @@ if (str == "tangent") return RotateMode.Tangent;

}
static transformModeLegacy(inheritRotation: boolean, inheritScale: boolean) {
console.log("Deprecation Warning: re-export your model with spine 3.5, or downgrade to pixi-spine 1.1 branch. There were many breaking changes, place breakpoint here if you want to know which model is broken");
if (inheritRotation && inheritScale) {
return TransformMode.Normal;
} else if (inheritRotation) {
return TransformMode.NoScaleOrReflection;
} else if (inheritScale) {
return TransformMode.NoRotationOrReflection;
} else {
return TransformMode.OnlyTranslation;
}
}
}
class LinkedMesh {
parent: string;
skin: string;
parent: string; skin: string;
slotIndex: number;
mesh: MeshAttachment;
constructor(mesh: MeshAttachment, skin: string, slotIndex: number, parent: string) {
constructor (mesh: MeshAttachment, skin: string, slotIndex: number, parent: string) {
this.mesh = mesh;

@@ -777,0 +808,0 @@ this.skin = skin;

@@ -37,2 +37,5 @@ /******************************************************************************

currentSprite: any;
currentGraphics: any;
clippingContainer: any;
meshes: any;

@@ -42,2 +45,3 @@ currentMeshName: string;

currentSpriteName: string;
blendMode: number;

@@ -48,6 +52,7 @@ //assign hack region a bit later

//canon
//this is canon
data: SlotData;
bone: Bone;
color: Color;
darkColor: Color;
attachment: Attachment;

@@ -57,3 +62,3 @@ private attachmentTime: number;

constructor(data: SlotData, bone: Bone) {
constructor (data: SlotData, bone: Bone) {
if (data == null) throw new Error("data cannot be null.");

@@ -64,8 +69,10 @@ if (bone == null) throw new Error("bone cannot be null.");

this.color = new Color();
this.blendMode = data.blendMode;
this.darkColor = data.darkColor == null ? null : new Color();
this.setToSetupPose();
this.blendMode = this.data.blendMode;
}
/** @return May be null. */
getAttachment(): Attachment {
getAttachment (): Attachment {
return this.attachment;

@@ -76,3 +83,3 @@ }

* @param attachment May be null. */
setAttachment(attachment: Attachment) {
setAttachment (attachment: Attachment) {
if (this.attachment == attachment) return;

@@ -84,3 +91,3 @@ this.attachment = attachment;

setAttachmentTime(time: number) {
setAttachmentTime (time: number) {
this.attachmentTime = this.bone.skeleton.time - time;

@@ -90,8 +97,9 @@ }

/** Returns the time since the attachment was set. */
getAttachmentTime(): number {
getAttachmentTime (): number {
return this.bone.skeleton.time - this.attachmentTime;
}
setToSetupPose() {
setToSetupPose () {
this.color.setFromColor(this.data.color);
if (this.darkColor != null) this.darkColor.setFromColor(this.data.darkColor);
if (this.data.attachmentName == null)

@@ -98,0 +106,0 @@ this.attachment = null;

@@ -38,6 +38,7 @@ /******************************************************************************

color = new Color(1, 1, 1, 1);
darkColor: Color;
attachmentName: string;
blendMode: number;
blendMode: BlendMode;
constructor(index: number, name: string, boneData: BoneData) {
constructor (index: number, name: string, boneData: BoneData) {
if (index < 0) throw new Error("index must be >= 0.");

@@ -44,0 +45,0 @@ if (name == null) throw new Error("name cannot be null.");

@@ -192,4 +192,4 @@ /******************************************************************************

if (PIXI.VERSION[0] == '4') {
// pixi v4.0.0
if (PIXI.VERSION[0] != '3') {
// pixi v4 or v5
region.texture = new PIXI.Texture(region.page.baseTexture, frame, orig, trim, rotate);

@@ -196,0 +196,0 @@ } else {

@@ -62,14 +62,34 @@ /******************************************************************************

update() {
let rotateMix = this.rotateMix, translateMix = this.translateMix, scaleMix = this.scaleMix, shearMix = this.shearMix;
if (this.data.local) {
if (this.data.relative)
this.applyRelativeLocal();
else
this.applyAbsoluteLocal();
} else {
if (this.data.relative)
this.applyRelativeWorld();
else
this.applyAbsoluteWorld();
}
}
applyAbsoluteWorld() {
let rotateMix = this.rotateMix, translateMix = this.translateMix, scaleMix = this.scaleMix,
shearMix = this.shearMix;
let target = this.target;
let ta = target.matrix.a, tb = target.matrix.c, tc = target.matrix.b, td = target.matrix.d;
let targetMat = target.matrix;
let ta = targetMat.a, tb = targetMat.c, tc = targetMat.b, td = targetMat.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];
let m = bone.matrix;
let modified = false;
let mat = bone.matrix;
if (rotateMix != 0) {
let a = m.a, b = m.c, c = m.b, d = m.d;
let r = Math.atan2(tc, ta) - Math.atan2(c, a) + this.data.offsetRotation * MathUtils.degRad;
let a = mat.a, b = mat.c, c = mat.b, d = mat.d;
let r = Math.atan2(tc, ta) - Math.atan2(c, a) + offsetRotation;
if (r > MathUtils.PI)

@@ -81,6 +101,6 @@ r -= MathUtils.PI2;

let cos = Math.cos(r), sin = Math.sin(r);
m.a = cos * a - sin * c;
m.c = cos * b - sin * d;
m.b = sin * a + cos * c;
m.d = sin * b + cos * d;
mat.a = cos * a - sin * c;
mat.c = cos * b - sin * d;
mat.b = sin * a + cos * c;
mat.d = sin * b + cos * d;
modified = true;

@@ -92,4 +112,4 @@ }

target.localToWorld(temp.set(this.data.offsetX, this.data.offsetY));
m.tx += (temp.x - m.tx) * translateMix;
m.ty += (temp.y - m.ty) * translateMix;
mat.tx += (temp.x - mat.tx) * translateMix;
mat.ty += (temp.y - mat.ty) * translateMix;
modified = true;

@@ -99,12 +119,12 @@ }

if (scaleMix > 0) {
let s = Math.sqrt(m.a * m.a + m.b * m.b);
let s = Math.sqrt(mat.a * mat.a + mat.b * mat.b);
let ts = Math.sqrt(ta * ta + tc * tc);
if (s > 0.00001) s = (s + (ts - s + this.data.offsetScaleX) * scaleMix) / s;
m.a *= s;
m.b *= s;
s = Math.sqrt(m.c * m.c + m.d * m.d);
mat.a *= s;
mat.b *= s;
s = Math.sqrt(mat.c * mat.c + mat.d * mat.d);
ts = Math.sqrt(tb * tb + td * td);
if (s > 0.00001) s = (s + (ts - s + this.data.offsetScaleY) * scaleMix) / s;
m.c *= s;
m.d *= s;
mat.c *= s;
mat.d *= s;
modified = true;

@@ -114,5 +134,5 @@ }

if (shearMix > 0) {
let b = m.c, d = m.d;
let b = mat.c, d = mat.d;
let by = Math.atan2(d, b);
let r = Math.atan2(td, tb) - Math.atan2(tc, ta) - (by - Math.atan2(m.b, m.a));
let r = Math.atan2(td, tb) - Math.atan2(tc, ta) - (by - Math.atan2(mat.b, mat.a));
if (r > MathUtils.PI)

@@ -122,6 +142,6 @@ r -= MathUtils.PI2;

r += MathUtils.PI2;
r = by + (r + this.data.offsetShearY * MathUtils.degRad) * shearMix;
r = by + (r + offsetShearY) * shearMix;
let s = Math.sqrt(b * b + d * d);
m.c = Math.cos(r) * s;
m.d = Math.sin(r) * s;
mat.c = Math.cos(r) * s;
mat.d = Math.sin(r) * s;
modified = true;

@@ -134,2 +154,139 @@ }

applyRelativeWorld() {
let rotateMix = this.rotateMix, translateMix = this.translateMix, scaleMix = this.scaleMix,
shearMix = this.shearMix;
let target = this.target;
let targetMat = target.matrix;
let ta = targetMat.a, tb = targetMat.c, tc = targetMat.b, td = targetMat.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];
let modified = false;
let mat = bone.matrix;
if (rotateMix != 0) {
let a = mat.a, b = mat.c, c = mat.b, d = mat.d;
let r = Math.atan2(tc, ta) + offsetRotation;
if (r > MathUtils.PI)
r -= MathUtils.PI2;
else if (r < -MathUtils.PI) r += MathUtils.PI2;
r *= rotateMix;
let cos = Math.cos(r), sin = Math.sin(r);
mat.a = cos * a - sin * c;
mat.c = cos * b - sin * d;
mat.b = sin * a + cos * c;
mat.d = sin * b + cos * d;
modified = true;
}
if (translateMix != 0) {
let temp = this.temp;
target.localToWorld(temp.set(this.data.offsetX, this.data.offsetY));
mat.tx += temp.x * translateMix;
mat.ty += temp.y * translateMix;
modified = true;
}
if (scaleMix > 0) {
let s = (Math.sqrt(ta * ta + tc * tc) - 1 + this.data.offsetScaleX) * scaleMix + 1;
mat.a *= s;
mat.b *= s;
s = (Math.sqrt(tb * tb + td * td) - 1 + this.data.offsetScaleY) * scaleMix + 1;
mat.c *= s;
mat.d *= s;
modified = true;
}
if (shearMix > 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 = mat.c, d = mat.d;
r = Math.atan2(d, b) + (r - MathUtils.PI / 2 + offsetShearY) * shearMix;
let s = Math.sqrt(b * b + d * d);
mat.c = Math.cos(r) * s;
mat.d = Math.sin(r) * s;
modified = true;
}
if (modified) bone.appliedValid = false;
}
}
applyAbsoluteLocal() {
let rotateMix = this.rotateMix, translateMix = this.translateMix, scaleMix = this.scaleMix,
shearMix = this.shearMix;
let target = this.target;
if (!target.appliedValid) target.updateAppliedTransform();
let bones = this.bones;
for (let i = 0, n = bones.length; i < n; i++) {
let bone = bones[i];
if (!bone.appliedValid) bone.updateAppliedTransform();
let rotation = bone.arotation;
if (rotateMix != 0) {
let r = target.arotation - rotation + this.data.offsetRotation;
r -= (16384 - ((16384.499999999996 - r / 360) | 0)) * 360;
rotation += r * rotateMix;
}
let x = bone.ax, y = bone.ay;
if (translateMix != 0) {
x += (target.ax - x + this.data.offsetX) * translateMix;
y += (target.ay - y + this.data.offsetY) * translateMix;
}
let scaleX = bone.ascaleX, scaleY = bone.ascaleY;
if (scaleMix > 0) {
if (scaleX > 0.00001) scaleX = (scaleX + (target.ascaleX - scaleX + this.data.offsetScaleX) * scaleMix) / scaleX;
if (scaleY > 0.00001) scaleY = (scaleY + (target.ascaleY - scaleY + this.data.offsetScaleY) * scaleMix) / scaleY;
}
let shearY = bone.ashearY;
if (shearMix > 0) {
let r = target.ashearY - shearY + this.data.offsetShearY;
r -= (16384 - ((16384.499999999996 - r / 360) | 0)) * 360;
bone.shearY += r * shearMix;
}
bone.updateWorldTransformWith(x, y, rotation, scaleX, scaleY, bone.ashearX, shearY);
}
}
applyRelativeLocal() {
let rotateMix = this.rotateMix, translateMix = this.translateMix, scaleMix = this.scaleMix,
shearMix = this.shearMix;
let target = this.target;
if (!target.appliedValid) target.updateAppliedTransform();
let bones = this.bones;
for (let i = 0, n = bones.length; i < n; i++) {
let bone = bones[i];
if (!bone.appliedValid) bone.updateAppliedTransform();
let rotation = bone.arotation;
if (rotateMix != 0) rotation += (target.arotation + this.data.offsetRotation) * rotateMix;
let x = bone.ax, y = bone.ay;
if (translateMix != 0) {
x += (target.ax + this.data.offsetX) * translateMix;
y += (target.ay + this.data.offsetY) * translateMix;
}
let scaleX = bone.ascaleX, scaleY = bone.ascaleY;
if (scaleMix > 0) {
if (scaleX > 0.00001) scaleX *= ((target.ascaleX - 1 + this.data.offsetScaleX) * scaleMix) + 1;
if (scaleY > 0.00001) scaleY *= ((target.ascaleY - 1 + this.data.offsetScaleY) * scaleMix) + 1;
}
let shearY = bone.ashearY;
if (shearMix > 0) shearY += (target.ashearY + this.data.offsetShearY) * shearMix;
bone.updateWorldTransformWith(x, y, rotation, scaleX, scaleY, bone.ashearX, shearY);
}
}
getOrder() {

@@ -139,2 +296,3 @@ return this.data.order;

}
}

@@ -38,14 +38,8 @@ /******************************************************************************

target: BoneData;
rotateMix = 0;
translateMix = 0;
scaleMix = 0;
shearMix = 0;
offsetRotation = 0;
offsetX = 0;
offsetY = 0;
offsetScaleX = 0;
offsetScaleY = 0;
offsetShearY = 0;
rotateMix = 0; translateMix = 0; scaleMix = 0; shearMix = 0;
offsetRotation = 0; offsetX = 0; offsetY = 0; offsetScaleX = 0; offsetScaleY = 0; offsetShearY = 0;
relative = false;
local = false;
constructor(name: string) {
constructor (name: string) {
if (name == null) throw new Error("name cannot be null.");

@@ -52,0 +46,0 @@ this.name = name;

@@ -32,270 +32,370 @@ /******************************************************************************

namespace pixi_spine.core {
export interface Map<T> {
[key: string]: T;
}
export interface Map<T> {
[key: string]: T;
}
export class IntSet {
array = new Array<number>();
export class IntSet {
array = new Array<number>();
add (value: number): boolean {
let contains = this.contains(value);
this.array[value | 0] = value | 0;
return !contains;
}
add (value: number): boolean {
let contains = this.contains(value);
this.array[value | 0] = value | 0;
return !contains;
}
contains (value: number) {
return this.array[value | 0] != undefined;
}
contains (value: number) {
return this.array[value | 0] != undefined;
}
remove (value: number) {
this.array[value | 0] = undefined;
}
remove (value: number) {
this.array[value | 0] = undefined;
}
clear () {
this.array.length = 0;
}
}
clear () {
this.array.length = 0;
}
}
export interface Disposable {
dispose (): void;
}
export interface Disposable {
dispose (): void;
}
export class Color {
public static WHITE = new Color(1, 1, 1, 1);
public static RED = new Color(1, 0, 0, 1);
public static GREEN = new Color(0, 1, 0, 1);
public static BLUE = new Color(0, 0, 1, 1);
public static MAGENTA = new Color(1, 0, 1, 1);
export interface Restorable {
restore (): void;
}
constructor (public r: number = 0, public g: number = 0, public b: number = 0, public a: number = 0) {
}
export class Color {
public static WHITE = new Color(1, 1, 1, 1);
public static RED = new Color(1, 0, 0, 1);
public static GREEN = new Color(0, 1, 0, 1);
public static BLUE = new Color(0, 0, 1, 1);
public static MAGENTA = new Color(1, 0, 1, 1);
set (r: number, g: number, b: number, a: number) {
this.r = r;
this.g = g;
this.b = b;
this.a = a;
this.clamp();
return this;
}
constructor (public r: number = 0, public g: number = 0, public b: number = 0, public a: number = 0) {
}
setFromColor (c: Color) {
this.r = c.r;
this.g = c.g;
this.b = c.b;
this.a = c.a;
return this;
}
set (r: number, g: number, b: number, a: number) {
this.r = r;
this.g = g;
this.b = b;
this.a = a;
this.clamp();
return this;
}
setFromString (hex: string) {
hex = hex.charAt(0) == '#' ? hex.substr(1) : hex;
this.r = parseInt(hex.substr(0, 2), 16) / 255.0;
this.g = parseInt(hex.substr(2, 2), 16) / 255.0;
this.b = parseInt(hex.substr(4, 2), 16) / 255.0;
this.a = (hex.length != 8 ? 255 : parseInt(hex.substr(6, 2), 16)) / 255.0;
return this;
}
setFromColor (c: Color) {
this.r = c.r;
this.g = c.g;
this.b = c.b;
this.a = c.a;
return this;
}
add (r: number, g: number, b: number, a: number) {
this.r += r;
this.g += g;
this.b += b;
this.a += a;
this.clamp();
return this;
}
setFromString (hex: string) {
hex = hex.charAt(0) == '#' ? hex.substr(1) : hex;
this.r = parseInt(hex.substr(0, 2), 16) / 255.0;
this.g = parseInt(hex.substr(2, 2), 16) / 255.0;
this.b = parseInt(hex.substr(4, 2), 16) / 255.0;
this.a = (hex.length != 8 ? 255 : parseInt(hex.substr(6, 2), 16)) / 255.0;
return this;
}
clamp () {
if (this.r < 0) this.r = 0;
else if (this.r > 1) this.r = 1;
add (r: number, g: number, b: number, a: number) {
this.r += r;
this.g += g;
this.b += b;
this.a += a;
this.clamp();
return this;
}
if (this.g < 0) this.g = 0;
else if (this.g > 1) this.g = 1;
clamp () {
if (this.r < 0) this.r = 0;
else if (this.r > 1) this.r = 1;
if (this.b < 0) this.b = 0;
else if (this.b > 1) this.b = 1;
if (this.g < 0) this.g = 0;
else if (this.g > 1) this.g = 1;
if (this.a < 0) this.a = 0;
else if (this.a > 1) this.a = 1;
return this;
}
}
if (this.b < 0) this.b = 0;
else if (this.b > 1) this.b = 1;
export class MathUtils {
static PI = 3.1415927;
static PI2 = MathUtils.PI * 2;
static radiansToDegrees = 180 / MathUtils.PI;
static radDeg = MathUtils.radiansToDegrees;
static degreesToRadians = MathUtils.PI / 180;
static degRad = MathUtils.degreesToRadians;
if (this.a < 0) this.a = 0;
else if (this.a > 1) this.a = 1;
return this;
}
}
static clamp (value: number, min: number, max: number) {
if (value < min) return min;
if (value > max) return max;
return value;
}
export class MathUtils {
static PI = 3.1415927;
static PI2 = MathUtils.PI * 2;
static radiansToDegrees = 180 / MathUtils.PI;
static radDeg = MathUtils.radiansToDegrees;
static degreesToRadians = MathUtils.PI / 180;
static degRad = MathUtils.degreesToRadians;
static cosDeg (degrees: number) {
return Math.cos(degrees * MathUtils.degRad);
}
static clamp (value: number, min: number, max: number) {
if (value < min) return min;
if (value > max) return max;
return value;
}
static sinDeg (degrees: number) {
return Math.sin(degrees * MathUtils.degRad);
}
static cosDeg (degrees: number) {
return Math.cos(degrees * MathUtils.degRad);
}
static signum (value: number): number {
return value > 0 ? 1 : value < 0 ? -1 : 0;
}
static sinDeg (degrees: number) {
return Math.sin(degrees * MathUtils.degRad);
}
static toInt (x: number) {
return x > 0 ? Math.floor(x) : Math.ceil(x);
}
static signum (value: number): number {
return value > 0 ? 1 : value < 0 ? -1 : 0;
}
static cbrt (x: number) {
var y = Math.pow(Math.abs(x), 1/3);
return x < 0 ? -y : y;
}
}
static toInt (x: number) {
return x > 0 ? Math.floor(x) : Math.ceil(x);
}
export class Utils {
static SUPPORTS_TYPED_ARRAYS = typeof(Float32Array) !== "undefined";
static cbrt (x: number) {
let y = Math.pow(Math.abs(x), 1/3);
return x < 0 ? -y : y;
}
static arrayCopy<T> (source: ArrayLike<T>, sourceStart: number, dest: ArrayLike<T>, destStart: number, numElements: number) {
for (let i = sourceStart, j = destStart; i < sourceStart + numElements; i++, j++) {
dest[j] = source[i];
}
}
static randomTriangular (min: number, max: number): number {
return MathUtils.randomTriangularWith(min, max, (min + max) * 0.5);
}
static setArraySize<T> (array: Array<T>, size: number, value: any = 0): Array<T> {
let oldSize = array.length;
if (oldSize == size) return array;
array.length = size;
if (oldSize < size) {
for (let i = oldSize; i < size; i++) array[i] = value;
}
return array;
}
static randomTriangularWith (min: number, max: number, mode: number): number {
let u = Math.random();
let d = max - min;
if (u <= (mode - min) / d) return min + Math.sqrt(u * d * (mode - min));
return max - Math.sqrt((1 - u) * d * (max - mode));
}
}
static ensureArrayCapacity<T> (array: Array<T>, size: number, value: any = 0): Array<T> {
if (array.length >= size) return array;
return Utils.setArraySize(array, size, value);
}
export abstract class Interpolation {
protected abstract applyInternal (a: number): number;
apply(start: number, end: number, a: number): number {
return start + (end - start) * this.applyInternal(a);
}
}
static newArray<T> (size: number, defaultValue: T): Array<T> {
let array = new Array<T>(size);
for (let i = 0; i < size; i++) array[i] = defaultValue;
return array;
}
export class Pow extends Interpolation {
protected power = 2;
static newFloatArray (size: number): ArrayLike<number> {
if (Utils.SUPPORTS_TYPED_ARRAYS) {
return new Float32Array(size)
} else {
let array = new Array<number>(size);
for (let i = 0; i < array.length; i++) array[i] = 0;
return array;
}
}
constructor (power: number) {
super();
this.power = power;
}
static toFloatArray (array: Array<number>) {
return Utils.SUPPORTS_TYPED_ARRAYS ? new Float32Array(array) : array;
}
}
applyInternal (a: number): number {
if (a <= 0.5) return Math.pow(a * 2, this.power) / 2;
return Math.pow((a - 1) * 2, this.power) / (this.power % 2 == 0 ? -2 : 2) + 1;
}
}
export class DebugUtils {
static logBones(skeleton: Skeleton) {
for (let i = 0; i < skeleton.bones.length; i++) {
let bone = skeleton.bones[i]
let mat = bone.matrix
console.log(bone.data.name + ", " + mat.a + ", " + mat.b + ", " + mat.c + ", " + mat.d + ", " + mat.tx + ", " + mat.ty);
}
}
}
export class PowOut extends Pow {
constructor (power: number) {
super(power);
}
export class Pool<T> {
private items = new Array<T>();
private instantiator: () => T;
applyInternal (a: number) : number {
return Math.pow(a - 1, this.power) * (this.power % 2 == 0 ? -1 : 1) + 1;
}
}
constructor (instantiator: () => T) {
this.instantiator = instantiator;
}
export class Utils {
static SUPPORTS_TYPED_ARRAYS = typeof(Float32Array) !== "undefined";
obtain () {
return this.items.length > 0 ? this.items.pop() : this.instantiator();
}
static arrayCopy<T> (source: ArrayLike<T>, sourceStart: number, dest: ArrayLike<T>, destStart: number, numElements: number) {
for (let i = sourceStart, j = destStart; i < sourceStart + numElements; i++, j++) {
dest[j] = source[i];
}
}
free (item: T) {
if ((item as any).reset) (item as any).reset();
this.items.push(item);
}
static setArraySize<T> (array: Array<T>, size: number, value: any = 0): Array<T> {
let oldSize = array.length;
if (oldSize == size) return array;
array.length = size;
if (oldSize < size) {
for (let i = oldSize; i < size; i++) array[i] = value;
}
return array;
}
freeAll (items: ArrayLike<T>) {
for (let i = 0; i < items.length; i++) {
if ((items[i] as any).reset) (items[i] as any).reset();
this.items[i] = items[i];
}
}
static ensureArrayCapacity<T> (array: Array<T>, size: number, value: any = 0): Array<T> {
if (array.length >= size) return array;
return Utils.setArraySize(array, size, value);
}
clear () {
this.items.length = 0;
}
}
static newArray<T> (size: number, defaultValue: T): Array<T> {
let array = new Array<T>(size);
for (let i = 0; i < size; i++) array[i] = defaultValue;
return array;
}
export class Vector2 {
constructor (public x = 0, public y = 0) {
}
static newFloatArray (size: number): ArrayLike<number> {
if (Utils.SUPPORTS_TYPED_ARRAYS) {
return new Float32Array(size)
} else {
let array = new Array<number>(size);
for (let i = 0; i < array.length; i++) array[i] = 0;
return array;
}
}
set (x: number, y: number): Vector2 {
this.x = x;
this.y = y;
return this;
}
static newShortArray (size: number): ArrayLike<number> {
if (Utils.SUPPORTS_TYPED_ARRAYS) {
return new Int16Array(size)
} else {
let array = new Array<number>(size);
for (let i = 0; i < array.length; i++) array[i] = 0;
return array;
}
}
length () {
let x = this.x;
let y = this.y;
return Math.sqrt(x * x + y * y);
}
static toFloatArray (array: Array<number>) {
return Utils.SUPPORTS_TYPED_ARRAYS ? new Float32Array(array) : array;
}
normalize () {
let len = this.length();
if (len != 0) {
this.x /= len;
this.y /= len;
}
return this;
}
}
static toSinglePrecision (value: number) {
return Utils.SUPPORTS_TYPED_ARRAYS ? Math.fround(value) : value;
}
}
export class TimeKeeper {
maxDelta = 0.064;
framesPerSecond = 0;
delta = 0;
totalTime = 0;
export class DebugUtils {
static logBones(skeleton: Skeleton) {
for (let i = 0; i < skeleton.bones.length; i++) {
let bone = skeleton.bones[i];
let mat = bone.matrix;
console.log(bone.data.name + ", " + mat.a + ", " + mat.b + ", " + mat.c + ", " + mat.d + ", " + mat.tx + ", " + mat.ty);
}
}
}
private lastTime = Date.now() / 1000;
private frameCount = 0;
private frameTime = 0;
export class Pool<T> {
private items = new Array<T>();
private instantiator: () => T;
update () {
var now = Date.now() / 1000;
this.delta = now - this.lastTime;
this.frameTime += this.delta;
this.totalTime += this.delta;
if (this.delta > this.maxDelta) this.delta = this.maxDelta;
this.lastTime = now;
constructor (instantiator: () => T) {
this.instantiator = instantiator;
}
this.frameCount++;
if (this.frameTime > 1) {
this.framesPerSecond = this.frameCount / this.frameTime;
this.frameTime = 0;
this.frameCount = 0;
}
}
}
obtain () {
return this.items.length > 0 ? this.items.pop() : this.instantiator();
}
export interface ArrayLike<T> {
length: number;
[n: number]: T;
}
free (item: T) {
if ((item as any).reset) (item as any).reset();
this.items.push(item);
}
freeAll (items: ArrayLike<T>) {
for (let i = 0; i < items.length; i++) {
if ((items[i] as any).reset) (items[i] as any).reset();
this.items[i] = items[i];
}
}
clear () {
this.items.length = 0;
}
}
export class Vector2 {
constructor (public x = 0, public y = 0) {
}
set (x: number, y: number): Vector2 {
this.x = x;
this.y = y;
return this;
}
length () {
let x = this.x;
let y = this.y;
return Math.sqrt(x * x + y * y);
}
normalize () {
let len = this.length();
if (len != 0) {
this.x /= len;
this.y /= len;
}
return this;
}
}
export class TimeKeeper {
maxDelta = 0.064;
framesPerSecond = 0;
delta = 0;
totalTime = 0;
private lastTime = Date.now() / 1000;
private frameCount = 0;
private frameTime = 0;
update () {
var now = Date.now() / 1000;
this.delta = now - this.lastTime;
this.frameTime += this.delta;
this.totalTime += this.delta;
if (this.delta > this.maxDelta) this.delta = this.maxDelta;
this.lastTime = now;
this.frameCount++;
if (this.frameTime > 1) {
this.framesPerSecond = this.frameCount / this.frameTime;
this.frameTime = 0;
this.frameCount = 0;
}
}
}
export interface ArrayLike<T> {
length: number;
[n: number]: T;
}
export class WindowedMean {
values: Array<number>;
addedValues = 0;
lastValue = 0;
mean = 0;
dirty = true;
constructor (windowSize: number = 32) {
this.values = new Array<number>(windowSize);
}
hasEnoughData () {
return this.addedValues >= this.values.length;
}
addValue (value: number) {
if (this.addedValues < this.values.length)
this.addedValues++;
this.values[this.lastValue++] = value;
if (this.lastValue > this.values.length - 1) this.lastValue = 0;
this.dirty = true;
}
getMean () {
if (this.hasEnoughData()) {
if (this.dirty) {
let mean = 0;
for (let i = 0; i < this.values.length; i++) {
mean += this.values[i];
}
this.mean = mean / this.values.length;
this.dirty = false;
}
return this.mean;
} else {
return 0;
}
}
}
}

@@ -18,6 +18,6 @@ namespace pixi_spine {

}
const metadata = resource.metadata || {};
const metadataSkeletonScale = metadata ? resource.metadata.spineSkeletonScale : null;
var metadataSkeletonScale = resource.metadata ? resource.metadata.spineSkeletonScale : null;
var metadataAtlas = resource.metadata ? resource.metadata.spineAtlas : null;
const metadataAtlas = metadata ? resource.metadata.spineAtlas : null;
if (metadataAtlas === false) {

@@ -37,6 +37,3 @@ return next();

var metadataAtlasSuffix = '.atlas';
if (resource.metadata && resource.metadata.spineAtlasSuffix) {
metadataAtlasSuffix = resource.metadata.spineAtlasSuffix;
}
const metadataAtlasSuffix = metadata.spineAtlasSuffix || '.atlas';

@@ -48,32 +45,33 @@ /**

*/
var atlasPath = resource.url.substr(0, resource.url.lastIndexOf('.')) + metadataAtlasSuffix;
let atlasPath = resource.url.substr(0, resource.url.lastIndexOf('.')) + metadataAtlasSuffix;
//remove the baseUrl
atlasPath = atlasPath.replace(this.baseUrl, '');
var atlasOptions = {
const atlasOptions = {
crossOrigin: resource.crossOrigin,
xhrType: PIXI.loaders.Resource.XHR_RESPONSE_TYPE.TEXT,
metadata: resource.metadata ? resource.metadata.spineMetadata : null,
metadata: metadata.spineMetadata || null,
parentResource: resource
};
var imageOptions = {
const imageOptions = {
crossOrigin: resource.crossOrigin,
metadata: resource.metadata ? resource.metadata.imageMetadata : null,
metadata: metadata.imageMetadata || null,
parentResource: resource
};
var baseUrl = resource.url.substr(0, resource.url.lastIndexOf('/') + 1);
let baseUrl = resource.url.substr(0, resource.url.lastIndexOf('/') + 1);
//remove the baseUrl
baseUrl = baseUrl.replace(this.baseUrl, '');
var adapter = imageLoaderAdapter(this, resource.name + '_atlas_page_', baseUrl, imageOptions);
const adapter = metadata.images ? staticImageLoader(metadata.images)
: metadata.image ? staticImageLoader({'default': metadata.image})
: metadata.imageLoader ? metadata.imageLoader(this, resource.name + '_atlas_page_', baseUrl, imageOptions)
: imageLoaderAdapter(this, resource.name + '_atlas_page_', baseUrl, imageOptions);
this.add(resource.name + '_atlas', atlasPath, atlasOptions, function (atlasResource: PIXI.loaders.Resource) {
new core.TextureAtlas(atlasResource.xhr.responseText, adapter, function (spineAtlas) {
var spineJsonParser = new core.SkeletonJson(new core.AtlasAttachmentLoader(spineAtlas));
let spineJsonParser = new core.SkeletonJson(new core.AtlasAttachmentLoader(spineAtlas));
if (metadataSkeletonScale) {
spineJsonParser.scale = metadataSkeletonScale;
}
var skeletonData = spineJsonParser.readSkeletonData(resource.data);
resource.spineData = skeletonData;
resource.spineData = spineJsonParser.readSkeletonData(resource.data);
resource.spineAtlas = spineAtlas;

@@ -92,4 +90,4 @@

return function (line: string, callback: (baseTexture: PIXI.BaseTexture) => any) {
var name = namePrefix + line;
var url = baseUrl + line;
const name = namePrefix + line;
const url = baseUrl + line;
loader.add(name, url, imageOptions, (resource: PIXI.loaders.Resource) => {

@@ -110,4 +108,14 @@ callback(resource.texture.baseTexture);

export function staticImageLoader(pages: { [key: string]: (PIXI.BaseTexture | PIXI.Texture) }) {
return function (line: any, callback: any) {
let page = pages[line] || pages['default'] as any;
if (page && page.baseTexture)
callback(page.baseTexture);
else
callback(page);
}
}
PIXI.loaders.Loader.addPixiMiddleware(atlasParser);
PIXI.loader.use(atlasParser());
}
/// <reference types="pixi.js" />
/// <reference path="polyfills.ts" />
namespace pixi_spine {

@@ -31,3 +31,3 @@ /* Esoteric Software SPINE wrapper for pixi.js */

* ```js
* var spineAnimation = new spine(spineData);
* let spineAnimation = new spine(spineData);
* ```

@@ -49,2 +49,3 @@ *

slotContainers: Array<PIXI.Container>;
tempClipContainers: Array<PIXI.Container>;

@@ -98,12 +99,15 @@ constructor(spineData: core.SkeletonData) {

for (var i = 0, n = this.skeleton.slots.length; i < n; i++) {
var slot = this.skeleton.slots[i];
var attachment: any = slot.attachment;
var slotContainer = new PIXI.Container();
this.tempClipContainers = [];
for (let i = 0, n = this.skeleton.slots.length; i < n; i++) {
let slot = this.skeleton.slots[i];
let attachment: any = slot.attachment;
let slotContainer = new PIXI.Container();
this.slotContainers.push(slotContainer);
this.addChild(slotContainer);
this.tempClipContainers.push(null);
if (attachment instanceof core.RegionAttachment) {
var spriteName = (attachment.region as core.TextureAtlasRegion).name;
var sprite = this.createSprite(slot, attachment, spriteName);
let spriteName = (attachment.region as core.TextureAtlasRegion).name;
let sprite = this.createSprite(slot, attachment, spriteName);
slot.currentSprite = sprite;

@@ -114,3 +118,3 @@ slot.currentSpriteName = spriteName;

else if (attachment instanceof core.MeshAttachment) {
var mesh = this.createMesh(slot, attachment);
let mesh = this.createMesh(slot, attachment);
slot.currentMesh = mesh;

@@ -120,2 +124,7 @@ slot.currentMeshName = attachment.name;

}
else if (attachment instanceof core.ClippingAttachment) {
this.createGraphics(slot, attachment);
slotContainer.addChild(slot.clippingContainer);
slotContainer.addChild(slot.currentGraphics);
}
else {

@@ -187,18 +196,13 @@ continue;

let drawOrder = this.skeleton.drawOrder;
let slots = this.skeleton.slots;
for (var i = 0, n = drawOrder.length; i < n; i++) {
this.children[i] = this.slotContainers[drawOrder[i].data.index];
}
let r0 = this.tintRgb[0];
let g0 = this.tintRgb[1];
let b0 = this.tintRgb[2];
var r0 = this.tintRgb[0];
var g0 = this.tintRgb[1];
var b0 = this.tintRgb[2];
for (let i = 0, n = slots.length; i < n; i++) {
let slot = slots[i];
let attachment = slot.attachment;
let slotContainer = this.slotContainers[i];
for (i = 0, n = slots.length; i < n; i++) {
var slot = slots[i];
var attachment = slot.attachment;
var slotContainer = this.slotContainers[i];
if (!attachment) {

@@ -209,3 +213,3 @@ slotContainer.visible = false;

var attColor = (attachment as any).color;
let attColor = (attachment as any).color;
if (attachment instanceof core.RegionAttachment) {

@@ -221,3 +225,3 @@ let region = (attachment as core.RegionAttachment).region;

if (!slot.currentSpriteName || slot.currentSpriteName !== ar.name) {
var spriteName = ar.name;
let spriteName = ar.name;
if (slot.currentSprite) {

@@ -231,3 +235,3 @@ slot.currentSprite.visible = false;

else {
var sprite = this.createSprite(slot, attachment, spriteName);
let sprite = this.createSprite(slot, attachment, spriteName);
slotContainer.addChild(sprite);

@@ -243,4 +247,4 @@ }

let transform = slotContainer.transform;
let transAny : any = transform;
let lt: PIXI.Matrix;
let transAny: any = transform;
let lt: PIXI.Matrix = null;
if (transAny.matrix2d) {

@@ -254,12 +258,21 @@ //gameofbombs pixi fork, sorry for that, we really use it :)

} else {
if (transAny.position) {
transform = new PIXI.TransformBase();
slotContainer.transform = transform;
if (PIXI.TransformBase) {
if (transAny.position) {
transform = new PIXI.TransformBase();
slotContainer.transform = transform;
}
lt = transform.localTransform;
} else {
// if (transAny.autoUpdateLocal) {
// transAny.autoUpdateLocal = false;
// }
transAny.setFromMatrix(slot.bone.matrix);
}
lt = transform.localTransform;
}
slot.bone.matrix.copy(lt);
if (lt) {
slot.bone.matrix.copy(lt);
}
} else {
//PIXI v3
var lt = slotContainer.localTransform || new PIXI.Matrix();
let lt = slotContainer.localTransform || new PIXI.Matrix();
slot.bone.matrix.copy(lt);

@@ -291,3 +304,3 @@ slotContainer.localTransform = lt;

if (!slot.currentMeshName || slot.currentMeshName !== attachment.name) {
var meshName = attachment.name;
let meshName = attachment.name;
if (slot.currentMesh) {

@@ -303,3 +316,3 @@ slot.currentMesh.visible = false;

else {
var mesh = this.createMesh(slot, attachment);
let mesh = this.createMesh(slot, attachment);
slotContainer.addChild(mesh);

@@ -311,3 +324,3 @@ }

}
(attachment as core.VertexAttachment).computeWorldVertices(slot, slot.currentMesh.vertices);
(attachment as core.VertexAttachment).computeWorldVerticesOld(slot, slot.currentMesh.vertices);
if (PIXI.VERSION[0] !== '3') {

@@ -317,3 +330,3 @@ // PIXI version 4

//only for PIXI v4
var tintRgb = slot.currentMesh.tintRgb;
let tintRgb = slot.currentMesh.tintRgb;
tintRgb[0] = r0 * slot.color.r * attColor.r;

@@ -325,2 +338,10 @@ tintRgb[1] = g0 * slot.color.g * attColor.g;

}
else if (attachment instanceof core.ClippingAttachment) {
if (!slot.currentGraphics) {
this.createGraphics(slot, attachment);
slotContainer.addChild(slot.clippingContainer);
slotContainer.addChild(slot.currentGraphics);
}
this.updateGraphics(slot, attachment);
}
else {

@@ -334,2 +355,55 @@ slotContainer.visible = false;

}
//== this is clipping implementation ===
//TODO: remove parent hacks when pixi masks allow it
let drawOrder = this.skeleton.drawOrder;
let clippingAttachment: core.ClippingAttachment = null;
let clippingContainer: PIXI.Container = null;
for (let i = 0, n = drawOrder.length; i < n; i++) {
let slot = slots[drawOrder[i].data.index];
let slotContainer = this.slotContainers[drawOrder[i].data.index];
if (!clippingContainer) {
if (slotContainer.parent !== this) {
slotContainer.parent.removeChild(slotContainer);
//silend add hack
slotContainer.parent = this;
}
}
if (slot.currentGraphics) {
clippingContainer = slot.clippingContainer;
clippingAttachment = slot.attachment as core.ClippingAttachment;
clippingContainer.children.length = 0;
this.children[i] = slotContainer;
if (clippingAttachment.endSlot == slot.data) {
clippingContainer.renderable = false;
clippingContainer = null;
clippingAttachment = null;
}
} else {
if (clippingContainer) {
let c = this.tempClipContainers[i];
if (!c) {
c = this.tempClipContainers[i] = new PIXI.Container();
c.visible = false;
}
this.children[i] = c;
//silent remove hack
slotContainer.parent = null;
clippingContainer.addChild(slotContainer);
if (clippingAttachment.endSlot == slot.data) {
clippingContainer.renderable = true;
clippingContainer = null;
clippingAttachment = null;
}
} else {
this.children[i] = slotContainer;
}
}
}
};

@@ -373,3 +447,3 @@

this.lastTime = this.lastTime || Date.now();
var timeDelta = (Date.now() - this.lastTime) * 0.001;
let timeDelta = (Date.now() - this.lastTime) * 0.001;
this.lastTime = Date.now();

@@ -398,4 +472,4 @@ this.update(timeDelta);

}
var texture = region.texture;
var sprite = new SpineSprite(texture);
let texture = region.texture;
let sprite = new SpineSprite(texture);
sprite.rotation = attachment.rotation * core.MathUtils.degRad;

@@ -448,2 +522,26 @@ sprite.anchor.x = 0.5;

static clippingPolygon: Array<number> = [];
createGraphics(slot: core.Slot, clip: core.ClippingAttachment) {
let graphics = new PIXI.Graphics();
let poly = new PIXI.Polygon([]);
graphics.clear();
graphics.beginFill(0xffffff, 1);
graphics.drawPolygon(poly as any);
graphics.renderable = false;
slot.currentGraphics = graphics;
slot.clippingContainer = new PIXI.Container();
slot.clippingContainer.mask = slot.currentGraphics;
return graphics;
}
updateGraphics(slot: core.Slot, clip: core.ClippingAttachment) {
let vertices = (slot.currentGraphics.graphicsData[0].shape as PIXI.Polygon).points;
let n = clip.worldVerticesLength;
vertices.length = n;
clip.computeWorldVertices(slot, 0, n, vertices, 0, 2);
slot.currentGraphics.dirty++;
}
/**

@@ -460,8 +558,8 @@ * Changes texture in attachment in specific slot.

hackTextureBySlotIndex(slotIndex: number, texture: PIXI.Texture = null, size: PIXI.Rectangle = null) {
var slot = this.skeleton.slots[slotIndex];
let slot = this.skeleton.slots[slotIndex];
if (!slot) {
return false;
}
var attachment: any = slot.attachment;
var region: core.TextureRegion = attachment.region;
let attachment: any = slot.attachment;
let region: core.TextureRegion = attachment.region;
if (texture) {

@@ -495,3 +593,3 @@ region = new core.TextureRegion();

hackTextureBySlotName = function (slotName: string, texture: PIXI.Texture = null, size: PIXI.Rectangle = null) {
var index = this.skeleton.findSlotIndex(slotName);
let index = this.skeleton.findSlotIndex(slotName);
if (index == -1) {

@@ -505,5 +603,5 @@ return false;

function SlotContainerUpdateTransformV3() {
var pt = this.parent.worldTransform;
var wt = this.worldTransform;
var lt = this.localTransform;
let pt = this.parent.worldTransform;
let wt = this.worldTransform;
let lt = this.localTransform;
wt.a = lt.a * pt.a + lt.b * pt.c;

@@ -510,0 +608,0 @@ wt.b = lt.a * pt.b + lt.b * pt.d;

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc