pixi-spine
Advanced tools
Comparing version 1.4.2 to 1.5.1
@@ -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; |
{ | ||
"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()); | ||
} |
190
src/Spine.ts
/// <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
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
1552834
51
16350