pixi-spine
Advanced tools
+82
-55
@@ -7,3 +7,3 @@ declare module PIXI.spine.core { | ||
| constructor(name: string, timelines: Array<Timeline>, duration: number); | ||
| apply(skeleton: Skeleton, lastTime: number, time: number, loop: boolean, events: Array<Event>, alpha: number, pose: MixPose, direction: MixDirection): void; | ||
| apply(skeleton: Skeleton, lastTime: number, time: number, loop: boolean, events: Array<Event>, alpha: number, blend: MixBlend, direction: MixDirection): void; | ||
| static binarySearch(values: ArrayLike<number>, target: number, step?: number): number; | ||
@@ -13,13 +13,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, pose: MixPose, direction: MixDirection): void; | ||
| apply(skeleton: Skeleton, lastTime: number, time: number, events: Array<Event>, alpha: number, blend: MixBlend, direction: MixDirection): void; | ||
| getPropertyId(): number; | ||
| } | ||
| enum MixPose { | ||
| enum MixBlend { | ||
| setup = 0, | ||
| current = 1, | ||
| currentLayered = 2, | ||
| first = 1, | ||
| replace = 2, | ||
| add = 3 | ||
| } | ||
| enum MixDirection { | ||
| in = 0, | ||
| out = 1, | ||
| out = 1 | ||
| } | ||
@@ -41,3 +42,3 @@ enum TimelineType { | ||
| pathConstraintMix = 13, | ||
| twoColor = 14, | ||
| twoColor = 14 | ||
| } | ||
@@ -58,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, pose: MixPose, direction: MixDirection): void; | ||
| abstract apply(skeleton: Skeleton, lastTime: number, time: number, events: Array<Event>, alpha: number, blend: MixBlend, direction: MixDirection): void; | ||
| } | ||
@@ -71,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, pose: MixPose, direction: MixDirection): void; | ||
| apply(skeleton: Skeleton, lastTime: number, time: number, events: Array<Event>, alpha: number, blend: MixBlend, direction: MixDirection): void; | ||
| } | ||
@@ -86,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, pose: MixPose, direction: MixDirection): void; | ||
| apply(skeleton: Skeleton, lastTime: number, time: number, events: Array<Event>, alpha: number, blend: MixBlend, direction: MixDirection): void; | ||
| } | ||
@@ -92,3 +93,3 @@ class ScaleTimeline extends TranslateTimeline { | ||
| getPropertyId(): number; | ||
| apply(skeleton: Skeleton, lastTime: number, time: number, events: Array<Event>, alpha: number, pose: MixPose, direction: MixDirection): void; | ||
| apply(skeleton: Skeleton, lastTime: number, time: number, events: Array<Event>, alpha: number, blend: MixBlend, direction: MixDirection): void; | ||
| } | ||
@@ -98,3 +99,3 @@ class ShearTimeline extends TranslateTimeline { | ||
| getPropertyId(): number; | ||
| apply(skeleton: Skeleton, lastTime: number, time: number, events: Array<Event>, alpha: number, pose: MixPose, direction: MixDirection): void; | ||
| apply(skeleton: Skeleton, lastTime: number, time: number, events: Array<Event>, alpha: number, blend: MixBlend, direction: MixDirection): void; | ||
| } | ||
@@ -117,3 +118,3 @@ 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, pose: MixPose, direction: MixDirection): void; | ||
| apply(skeleton: Skeleton, lastTime: number, time: number, events: Array<Event>, alpha: number, blend: MixBlend, direction: MixDirection): void; | ||
| } | ||
@@ -142,3 +143,3 @@ class TwoColorTimeline extends CurveTimeline { | ||
| 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; | ||
| apply(skeleton: Skeleton, lastTime: number, time: number, events: Array<Event>, alpha: number, blend: MixBlend, direction: MixDirection): void; | ||
| } | ||
@@ -153,3 +154,3 @@ class AttachmentTimeline implements Timeline { | ||
| setFrame(frameIndex: number, time: number, attachmentName: string): void; | ||
| apply(skeleton: Skeleton, lastTime: number, time: number, events: Array<Event>, alpha: number, pose: MixPose, direction: MixDirection): void; | ||
| apply(skeleton: Skeleton, lastTime: number, time: number, events: Array<Event>, alpha: number, blend: MixBlend, direction: MixDirection): void; | ||
| } | ||
@@ -164,3 +165,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, pose: MixPose, direction: MixDirection): void; | ||
| apply(skeleton: Skeleton, lastTime: number, time: number, firedEvents: Array<Event>, alpha: number, blend: MixBlend, direction: MixDirection): void; | ||
| } | ||
@@ -174,3 +175,3 @@ class EventTimeline implements Timeline { | ||
| setFrame(frameIndex: number, event: Event): void; | ||
| apply(skeleton: Skeleton, lastTime: number, time: number, firedEvents: Array<Event>, alpha: number, pose: MixPose, direction: MixDirection): void; | ||
| apply(skeleton: Skeleton, lastTime: number, time: number, firedEvents: Array<Event>, alpha: number, blend: MixBlend, direction: MixDirection): void; | ||
| } | ||
@@ -184,3 +185,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, pose: MixPose, direction: MixDirection): void; | ||
| apply(skeleton: Skeleton, lastTime: number, time: number, firedEvents: Array<Event>, alpha: number, blend: MixBlend, direction: MixDirection): void; | ||
| } | ||
@@ -192,4 +193,8 @@ class IkConstraintTimeline extends CurveTimeline { | ||
| static PREV_BEND_DIRECTION: number; | ||
| static PREV_COMPRESS: number; | ||
| static PREV_STRETCH: number; | ||
| static MIX: number; | ||
| static BEND_DIRECTION: number; | ||
| static COMPRESS: number; | ||
| static STRETCH: number; | ||
| ikConstraintIndex: number; | ||
@@ -199,4 +204,4 @@ frames: ArrayLike<number>; | ||
| getPropertyId(): number; | ||
| setFrame(frameIndex: number, time: number, mix: number, bendDirection: number): void; | ||
| apply(skeleton: Skeleton, lastTime: number, time: number, firedEvents: Array<Event>, alpha: number, pose: MixPose, direction: MixDirection): void; | ||
| setFrame(frameIndex: number, time: number, mix: number, bendDirection: number, compress: boolean, stretch: boolean): void; | ||
| apply(skeleton: Skeleton, lastTime: number, time: number, firedEvents: Array<Event>, alpha: number, blend: MixBlend, direction: MixDirection): void; | ||
| } | ||
@@ -219,3 +224,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, pose: MixPose, direction: MixDirection): void; | ||
| apply(skeleton: Skeleton, lastTime: number, time: number, firedEvents: Array<Event>, alpha: number, blend: MixBlend, direction: MixDirection): void; | ||
| } | ||
@@ -232,3 +237,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, pose: MixPose, direction: MixDirection): void; | ||
| apply(skeleton: Skeleton, lastTime: number, time: number, firedEvents: Array<Event>, alpha: number, blend: MixBlend, direction: MixDirection): void; | ||
| } | ||
@@ -238,3 +243,3 @@ class PathConstraintSpacingTimeline extends PathConstraintPositionTimeline { | ||
| getPropertyId(): number; | ||
| apply(skeleton: Skeleton, lastTime: number, time: number, firedEvents: Array<Event>, alpha: number, pose: MixPose, direction: MixDirection): void; | ||
| apply(skeleton: Skeleton, lastTime: number, time: number, firedEvents: Array<Event>, alpha: number, blend: MixBlend, direction: MixDirection): void; | ||
| } | ||
@@ -253,3 +258,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, pose: MixPose, direction: MixDirection): void; | ||
| apply(skeleton: Skeleton, lastTime: number, time: number, firedEvents: Array<Event>, alpha: number, blend: MixBlend, direction: MixDirection): void; | ||
| } | ||
@@ -262,4 +267,4 @@ } | ||
| static FIRST: number; | ||
| static DIP: number; | ||
| static DIP_MIX: number; | ||
| static HOLD: number; | ||
| static HOLD_MIX: number; | ||
| data: AnimationStateData; | ||
@@ -271,3 +276,2 @@ tracks: TrackEntry[]; | ||
| propertyIDs: IntSet; | ||
| mixingTo: TrackEntry[]; | ||
| animationsChanged: boolean; | ||
@@ -280,4 +284,4 @@ timeScale: number; | ||
| 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; | ||
| applyMixingFrom(to: TrackEntry, skeleton: Skeleton, blend: MixBlend): number; | ||
| applyRotateTimeline(timeline: Timeline, skeleton: Skeleton, time: number, alpha: number, blend: MixBlend, timelinesRotation: Array<number>, i: number, firstFrame: boolean): void; | ||
| queueEvents(entry: TrackEntry, animationTime: number): void; | ||
@@ -298,2 +302,4 @@ clearTracks(): void; | ||
| _animationsChanged(): void; | ||
| setTimelineModes(entry: TrackEntry): void; | ||
| hasTimeline(entry: TrackEntry, id: number): boolean; | ||
| getCurrent(trackIndex: number): TrackEntry; | ||
@@ -320,5 +326,7 @@ addListener(listener: AnimationStateListener2): void; | ||
| mixingFrom: TrackEntry; | ||
| mixingTo: TrackEntry; | ||
| listener: AnimationStateListener2; | ||
| trackIndex: number; | ||
| loop: boolean; | ||
| holdPrevious: boolean; | ||
| eventThreshold: number; | ||
@@ -342,8 +350,7 @@ attachmentThreshold: number; | ||
| totalAlpha: number; | ||
| timelineData: number[]; | ||
| timelineDipMix: TrackEntry[]; | ||
| mixBlend: MixBlend; | ||
| timelineMode: number[]; | ||
| timelineHoldMix: TrackEntry[]; | ||
| timelinesRotation: number[]; | ||
| reset(): void; | ||
| setTimelineData(to: TrackEntry, mixingToArray: Array<TrackEntry>, propertyIDs: IntSet): TrackEntry; | ||
| hasTimeline(id: number): boolean; | ||
| getAnimationTime(): number; | ||
@@ -385,3 +392,3 @@ setAnimationLast(animationLast: number): void; | ||
| complete = 4, | ||
| event = 5, | ||
| event = 5 | ||
| } | ||
@@ -464,3 +471,3 @@ interface AnimationStateListener2 { | ||
| Path = 4, | ||
| Point = 5, | ||
| Point = 5 | ||
| } | ||
@@ -587,3 +594,3 @@ } | ||
| Multiply = 2, | ||
| Screen = 3, | ||
| Screen = 3 | ||
| } | ||
@@ -655,3 +662,3 @@ } | ||
| NoScale = 3, | ||
| NoScaleOrReflection = 4, | ||
| NoScaleOrReflection = 4 | ||
| } | ||
@@ -671,2 +678,4 @@ } | ||
| time: number; | ||
| volume: number; | ||
| balance: number; | ||
| constructor(time: number, data: EventData); | ||
@@ -681,2 +690,5 @@ } | ||
| stringValue: string; | ||
| audioPath: string; | ||
| volume: number; | ||
| balance: number; | ||
| constructor(name: string); | ||
@@ -690,4 +702,6 @@ } | ||
| target: Bone; | ||
| bendDirection: number; | ||
| compress: boolean; | ||
| stretch: boolean; | ||
| mix: number; | ||
| bendDirection: number; | ||
| constructor(data: IkConstraintData, skeleton: Skeleton); | ||
@@ -697,4 +711,4 @@ getOrder(): number; | ||
| update(): void; | ||
| apply1(bone: Bone, targetX: number, targetY: number, alpha: number): void; | ||
| apply2(parent: Bone, child: Bone, targetX: number, targetY: number, bendDir: number, alpha: number): void; | ||
| apply1(bone: Bone, targetX: number, targetY: number, compress: boolean, stretch: boolean, uniform: boolean, alpha: number): void; | ||
| apply2(parent: Bone, child: Bone, targetX: number, targetY: number, bendDir: number, stretch: boolean, alpha: number): void; | ||
| } | ||
@@ -709,2 +723,5 @@ } | ||
| bendDirection: number; | ||
| compress: boolean; | ||
| stretch: boolean; | ||
| uniform: boolean; | ||
| mix: number; | ||
@@ -719,2 +736,3 @@ constructor(name: string); | ||
| static AFTER: number; | ||
| static epsilon: number; | ||
| data: PathConstraintData; | ||
@@ -761,3 +779,3 @@ bones: Array<Bone>; | ||
| Fixed = 0, | ||
| Percent = 1, | ||
| Percent = 1 | ||
| } | ||
@@ -767,3 +785,3 @@ enum SpacingMode { | ||
| Fixed = 1, | ||
| Percent = 2, | ||
| Percent = 2 | ||
| } | ||
@@ -773,3 +791,3 @@ enum RotateMode { | ||
| Chain = 1, | ||
| ChainScale = 2, | ||
| ChainScale = 2 | ||
| } | ||
@@ -791,4 +809,4 @@ } | ||
| time: number; | ||
| flipX: boolean; | ||
| flipY: boolean; | ||
| scaleX: number; | ||
| scaleY: number; | ||
| x: number; | ||
@@ -815,3 +833,3 @@ y: number; | ||
| setSkinByName(skinName: string): void; | ||
| setSkin(newSkin: Skin): void; | ||
| setSkin(newSkin: Skin | null): void; | ||
| getAttachmentByName(slotName: string, attachmentName: string): Attachment; | ||
@@ -825,2 +843,5 @@ getAttachment(slotIndex: number, attachmentName: string): Attachment; | ||
| update(delta: number): void; | ||
| flipX: boolean; | ||
| flipY: boolean; | ||
| private static deprecatedWarning1; | ||
| } | ||
@@ -988,3 +1009,3 @@ } | ||
| MipMapNearestLinear = 9986, | ||
| MipMapLinearLinear = 9987, | ||
| MipMapLinearLinear = 9987 | ||
| } | ||
@@ -994,3 +1015,3 @@ enum TextureWrap { | ||
| ClampToEdge = 33071, | ||
| Repeat = 10497, | ||
| Repeat = 10497 | ||
| } | ||
@@ -1025,3 +1046,3 @@ class TextureRegion { | ||
| addSpineAtlas(atlasText: string, textureLoader: (path: string, loaderFunction: (tex: PIXI.BaseTexture) => any) => any, callback: (obj: TextureAtlas) => any): void; | ||
| private load(atlasText, textureLoader, callback); | ||
| private load; | ||
| findRegion(name: string): TextureAtlasRegion; | ||
@@ -1099,5 +1120,5 @@ dispose(): void; | ||
| 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); | ||
| private static isConcave; | ||
| private static positiveArea; | ||
| private static winding; | ||
| } | ||
@@ -1183,2 +1204,3 @@ } | ||
| static toSinglePrecision(value: number): number; | ||
| static webkit602BugfixHelper(alpha: number, blend: MixBlend): void; | ||
| } | ||
@@ -1282,3 +1304,3 @@ class DebugUtils { | ||
| } | ||
| class SpineMesh extends PIXI.Mesh { | ||
| class SpineMesh extends PIXI.SimpleMesh { | ||
| region: core.TextureRegion; | ||
@@ -1289,2 +1311,3 @@ constructor(texture: PIXI.Texture, vertices?: Float32Array, uvs?: Float32Array, indices?: Uint16Array, drawMode?: number); | ||
| static globalAutoUpdate: boolean; | ||
| static globalDelayLimit: number; | ||
| tintRgb: ArrayLike<number>; | ||
@@ -1297,8 +1320,10 @@ spineData: core.SkeletonData; | ||
| tempClipContainers: Array<PIXI.Container>; | ||
| localDelayLimit: number; | ||
| constructor(spineData: core.SkeletonData); | ||
| autoUpdate: boolean; | ||
| tint: number; | ||
| readonly delayLimit: number; | ||
| update(dt: number): void; | ||
| private setSpriteRegion(attachment, sprite, region); | ||
| private setMeshRegion(attachment, mesh, region); | ||
| private setSpriteRegion; | ||
| private setMeshRegion; | ||
| protected lastTime: number; | ||
@@ -1318,3 +1343,5 @@ autoUpdateTransform(): void; | ||
| transformHack(): number; | ||
| hackAttachmentGroups(nameSuffix: string, group: any, outGroup: any): any[][]; | ||
| destroy(options?: any): void; | ||
| } | ||
| } |
+9
-9
| { | ||
| "name": "pixi-spine", | ||
| "version": "2.0.0-alpha", | ||
| "description": "Spine implementation for pixi v^3 and v^4", | ||
| "version": "2.0.0", | ||
| "description": "Spine implementation for pixi v^5", | ||
| "author": "Mat Groves", | ||
@@ -32,5 +32,5 @@ "contributors": [ | ||
| "docs": "typedoc --out ./docs/ --readme README.md ./src/", | ||
| "check:browserify": "yarn checkpack -- browserify -e test/checkpack.ts", | ||
| "check:webpack": "yarn checkpack -- webpack -e test/checkpack.ts", | ||
| "check:vanillajs": "yarn checkpack -- vanillajs -e test/checkpack.ts", | ||
| "check:browserify": "yarn checkpack browserify -e test/checkpack.ts", | ||
| "check:webpack": "yarn checkpack webpack -e test/checkpack.ts", | ||
| "check:vanillajs": "yarn checkpack vanillajs -e test/checkpack.ts", | ||
| "check:all": "yarn build && yarn check:browserify && yarn check:webpack && yarn check:vanillajs" | ||
@@ -40,3 +40,2 @@ }, | ||
| "bin/", | ||
| "src/", | ||
| "SPINE-LICENSE", | ||
@@ -47,3 +46,4 @@ "package.json", | ||
| "devDependencies": { | ||
| "checkpack": "~0.1.0", | ||
| "@types/lodash": "^4.14.108", | ||
| "checkpack": "^0.3", | ||
| "del": "~2.2.0", | ||
@@ -53,3 +53,3 @@ "glob": "~7.1.1", | ||
| "parallelshell": "~2.0.0", | ||
| "pixi.js": "next", | ||
| "pixi.js-legacy": "~5.0.1", | ||
| "rimraf": "~2.5.3", | ||
@@ -59,4 +59,4 @@ "tmp": "^0.0.33", | ||
| "typedoc": "^0.9.0", | ||
| "typescript": "~2.4" | ||
| "typescript": "^2.8.3" | ||
| } | ||
| } |
+7
-5
@@ -5,4 +5,6 @@ # pixi-spine | ||
| Spine implementation for pixi v5. | ||
| Spine implementation for PixiJS v5. | ||
| For v4 please see [v4.x branch](https://github.com/pixijs/pixi-spine/tree/v4.x) and use npm version `1.5.21` | ||
| ## Usage | ||
@@ -12,3 +14,3 @@ | ||
| If you are just including the built files, pixi spine adds itself to a pixi namespace: | ||
| If you are just including the built files, pixi spine adds itself to a `PIXI` namespace: | ||
@@ -26,3 +28,3 @@ ```js | ||
| PIXI.loader | ||
| app.loader | ||
| .add('spineCharacter', 'spine-data-1/HERO.json') | ||
@@ -63,5 +65,5 @@ .load(function (loader, resources) { | ||
| Pixi-spine 1.3.x works ONLY with data exported from Spine 3.5. | ||
| We aim to support the latest stable version of spine. | ||
| Please enable "beta updates" and re-export everything from the spine editor. | ||
| If you are below Spine 3.5, please please enable "beta updates" and re-export everything from the spine editor. | ||
@@ -68,0 +70,0 @@ According to spine runtime license, you can use runtime only if you have bought the editor, so exporting latest versions of animations shouldn't be a problem for you. |
| /****************************************************************************** | ||
| * Spine Runtimes Software License v2.5 | ||
| * | ||
| * Copyright (c) 2013-2016, Esoteric Software | ||
| * All rights reserved. | ||
| * | ||
| * You are granted a perpetual, non-exclusive, non-sublicensable, and | ||
| * non-transferable license to use, install, execute, and perform the Spine | ||
| * Runtimes software and derivative works solely for personal or internal | ||
| * use. Without the written permission of Esoteric Software (see Section 2 of | ||
| * the Spine Software License Agreement), you may not (a) modify, translate, | ||
| * adapt, or develop new applications using the Spine Runtimes or otherwise | ||
| * create derivative works or improvements of the Spine Runtimes or (b) remove, | ||
| * delete, alter, or obscure any trademarks or any copyright, trademark, patent, | ||
| * or other intellectual property or proprietary rights notices on or in the | ||
| * Software, including any copy thereof. Redistributions in binary or source | ||
| * form must include this license and terms. | ||
| * | ||
| * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR | ||
| * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF | ||
| * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO | ||
| * EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | ||
| * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, | ||
| * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF | ||
| * USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER | ||
| * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) | ||
| * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE | ||
| * POSSIBILITY OF SUCH DAMAGE. | ||
| *****************************************************************************/ | ||
| namespace pixi_spine.core { | ||
| 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; | ||
| } | ||
| 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; | ||
| } | ||
| 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 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, pose: MixPose, direction: MixDirection): void; | ||
| getPropertyId (): number; | ||
| } | ||
| export enum MixPose { | ||
| setup, | ||
| current, | ||
| currentLayered | ||
| } | ||
| export enum MixDirection { | ||
| in, out | ||
| } | ||
| export enum TimelineType { | ||
| rotate, translate, scale, shear, | ||
| attachment, color, deform, | ||
| event, drawOrder, | ||
| ikConstraint, transformConstraint, | ||
| pathConstraintPosition, pathConstraintSpacing, pathConstraintMix, | ||
| twoColor | ||
| } | ||
| export abstract class CurveTimeline implements Timeline { | ||
| static LINEAR = 0; static STEPPED = 1; static BEZIER = 2; | ||
| static BEZIER_SIZE = 10 * 2 - 1; | ||
| private curves: ArrayLike<number>; // type, x, y, ... | ||
| abstract getPropertyId(): number; | ||
| constructor (frameCount: number) { | ||
| if (frameCount <= 0) throw new Error("frameCount must be > 0: " + frameCount); | ||
| this.curves = Utils.newFloatArray((frameCount - 1) * CurveTimeline.BEZIER_SIZE); | ||
| } | ||
| getFrameCount () { | ||
| return this.curves.length / CurveTimeline.BEZIER_SIZE + 1; | ||
| } | ||
| setLinear (frameIndex: number) { | ||
| this.curves[frameIndex * CurveTimeline.BEZIER_SIZE] = CurveTimeline.LINEAR; | ||
| } | ||
| setStepped (frameIndex: number) { | ||
| this.curves[frameIndex * CurveTimeline.BEZIER_SIZE] = CurveTimeline.STEPPED; | ||
| } | ||
| 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; | ||
| } | ||
| /** 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; | ||
| let i = frameIndex * CurveTimeline.BEZIER_SIZE; | ||
| let curves = this.curves; | ||
| curves[i++] = 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; | ||
| } | ||
| } | ||
| 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. | ||
| } | ||
| abstract apply (skeleton: Skeleton, lastTime: number, time: number, events: Array<Event>, alpha: number, pose: MixPose, direction: MixDirection): void; | ||
| } | ||
| export class RotateTimeline extends CurveTimeline { | ||
| static ENTRIES = 2; | ||
| static PREV_TIME = -2; static PREV_ROTATION = -1; | ||
| static ROTATION = 1; | ||
| boneIndex: number; | ||
| frames: ArrayLike<number>; // time, degrees, ... | ||
| constructor (frameCount: number) { | ||
| super(frameCount); | ||
| this.frames = Utils.newFloatArray(frameCount << 1); | ||
| } | ||
| getPropertyId () { | ||
| return (TimelineType.rotate << 24) + this.boneIndex; | ||
| } | ||
| /** 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; | ||
| } | ||
| apply (skeleton: Skeleton, lastTime: number, time: number, events: Array<Event>, alpha: number, pose: MixPose, direction: MixDirection) { | ||
| let frames = this.frames; | ||
| 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; | ||
| } | ||
| 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; | ||
| } | ||
| // 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 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; | ||
| } | ||
| } | ||
| } | ||
| 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; | ||
| boneIndex: number; | ||
| frames: ArrayLike<number>; // time, x, y, ... | ||
| constructor (frameCount: number) { | ||
| super(frameCount); | ||
| this.frames = Utils.newFloatArray(frameCount * TranslateTimeline.ENTRIES); | ||
| } | ||
| getPropertyId () { | ||
| return (TimelineType.translate << 24) + this.boneIndex; | ||
| } | ||
| /** 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; | ||
| } | ||
| apply (skeleton: Skeleton, lastTime: number, time: number, events: Array<Event>, alpha: number, pose: MixPose, direction: MixDirection) { | ||
| let frames = this.frames; | ||
| 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; | ||
| } | ||
| 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)); | ||
| 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; | ||
| } | ||
| } | ||
| } | ||
| export class ScaleTimeline extends TranslateTimeline { | ||
| constructor (frameCount: number) { | ||
| super(frameCount); | ||
| } | ||
| getPropertyId () { | ||
| return (TimelineType.scale << 24) + this.boneIndex; | ||
| } | ||
| apply (skeleton: Skeleton, lastTime: number, time: number, events: Array<Event>, alpha: number, pose: MixPose, direction: MixDirection) { | ||
| let frames = this.frames; | ||
| 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; | ||
| } | ||
| 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)); | ||
| 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; | ||
| } | ||
| } | ||
| } | ||
| export class ShearTimeline extends TranslateTimeline { | ||
| constructor (frameCount: number) { | ||
| super(frameCount); | ||
| } | ||
| getPropertyId () { | ||
| return (TimelineType.shear << 24) + this.boneIndex; | ||
| } | ||
| apply (skeleton: Skeleton, lastTime: number, time: number, events: Array<Event>, alpha: number, pose: MixPose, direction: MixDirection) { | ||
| let frames = this.frames; | ||
| 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; | ||
| } | ||
| 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)); | ||
| 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; | ||
| } | ||
| } | ||
| } | ||
| 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; | ||
| slotIndex: number; | ||
| frames: ArrayLike<number>; // time, r, g, b, a, ... | ||
| constructor (frameCount: number) { | ||
| super(frameCount); | ||
| this.frames = Utils.newFloatArray(frameCount * ColorTimeline.ENTRIES); | ||
| } | ||
| getPropertyId () { | ||
| return (TimelineType.color << 24) + this.slotIndex; | ||
| } | ||
| /** 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; | ||
| } | ||
| 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; | ||
| } | ||
| 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)); | ||
| 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); | ||
| } | ||
| } | ||
| } | ||
| 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; | ||
| slotIndex: number; | ||
| frames: ArrayLike<number>; // time, r, g, b, a, r2, g2, b2, ... | ||
| constructor (frameCount: number) { | ||
| super(frameCount); | ||
| this.frames = Utils.newFloatArray(frameCount * TwoColorTimeline.ENTRIES); | ||
| } | ||
| getPropertyId () { | ||
| return (TimelineType.twoColor << 24) + this.slotIndex; | ||
| } | ||
| /** 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; | ||
| } | ||
| 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 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)); | ||
| 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); | ||
| } | ||
| } | ||
| } | ||
| export class AttachmentTimeline implements Timeline { | ||
| slotIndex: number; | ||
| frames: ArrayLike<number> // time, ... | ||
| attachmentNames: Array<string>; | ||
| constructor (frameCount: number) { | ||
| this.frames = Utils.newFloatArray(frameCount); | ||
| this.attachmentNames = new Array<string>(frameCount); | ||
| } | ||
| getPropertyId () { | ||
| return (TimelineType.attachment << 24) + this.slotIndex; | ||
| } | ||
| getFrameCount () { | ||
| return this.frames.length; | ||
| } | ||
| /** Sets the time and value of the specified keyframe. */ | ||
| setFrame (frameIndex: number, time: number, attachmentName: string) { | ||
| this.frames[frameIndex] = time; | ||
| this.attachmentNames[frameIndex] = attachmentName; | ||
| } | ||
| 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; | ||
| } | ||
| 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; | ||
| } | ||
| 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; | ||
| let attachmentName = this.attachmentNames[frameIndex]; | ||
| skeleton.slots[this.slotIndex] | ||
| .setAttachment(attachmentName == null ? null : skeleton.getAttachment(this.slotIndex, attachmentName)); | ||
| } | ||
| } | ||
| let zeros : ArrayLike<number> = null; | ||
| export class DeformTimeline extends CurveTimeline { | ||
| slotIndex: number; | ||
| attachment: VertexAttachment; | ||
| frames: ArrayLike<number>; // time, ... | ||
| frameVertices: Array<ArrayLike<number>>; | ||
| 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.deform << 27) + + this.attachment.id + this.slotIndex; | ||
| } | ||
| /** Sets the time of the specified keyframe. */ | ||
| setFrame (frameIndex: number, time: number, vertices: ArrayLike<number>) { | ||
| this.frames[frameIndex] = time; | ||
| this.frameVertices[frameIndex] = vertices; | ||
| } | ||
| 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; | ||
| let verticesArray: Array<number> = slot.attachmentVertices; | ||
| let frameVertices = this.frameVertices; | ||
| let vertexCount = frameVertices[0].length; | ||
| let vertices: Array<number> = Utils.setArraySize(verticesArray, vertexCount); | ||
| 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; | ||
| } | ||
| 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; | ||
| } | ||
| // 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)); | ||
| 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; | ||
| } | ||
| } | ||
| } | ||
| } | ||
| export class EventTimeline implements Timeline { | ||
| frames: ArrayLike<number>; // time, ... | ||
| events: Array<Event>; | ||
| constructor (frameCount: number) { | ||
| this.frames = Utils.newFloatArray(frameCount); | ||
| this.events = new Array<Event>(frameCount); | ||
| } | ||
| getPropertyId () { | ||
| return TimelineType.event << 24; | ||
| } | ||
| getFrameCount () { | ||
| return this.frames.length; | ||
| } | ||
| /** Sets the time of the specified keyframe. */ | ||
| setFrame (frameIndex: number, event: Event) { | ||
| this.frames[frameIndex] = event.time; | ||
| this.events[frameIndex] = event; | ||
| } | ||
| /** 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; | ||
| 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. | ||
| 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]); | ||
| } | ||
| } | ||
| export class DrawOrderTimeline implements Timeline { | ||
| frames: ArrayLike<number>; // time, ... | ||
| drawOrders: Array<Array<number>>; | ||
| constructor (frameCount: number) { | ||
| this.frames = Utils.newFloatArray(frameCount); | ||
| this.drawOrders = new Array<Array<number>>(frameCount); | ||
| } | ||
| getPropertyId () { | ||
| return TimelineType.drawOrder << 24; | ||
| } | ||
| getFrameCount () { | ||
| return this.frames.length; | ||
| } | ||
| /** 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; | ||
| } | ||
| 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; | ||
| } | ||
| let frames = this.frames; | ||
| if (time < frames[0]) { | ||
| if (pose == MixPose.setup) Utils.arrayCopy(skeleton.slots, 0, skeleton.drawOrder, 0, skeleton.slots.length); | ||
| return; | ||
| } | ||
| 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; | ||
| 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]]; | ||
| } | ||
| } | ||
| } | ||
| 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; | ||
| ikConstraintIndex: number; | ||
| frames: ArrayLike<number>; // time, mix, bendDirection, ... | ||
| constructor (frameCount: number) { | ||
| super(frameCount); | ||
| this.frames = Utils.newFloatArray(frameCount * IkConstraintTimeline.ENTRIES); | ||
| } | ||
| getPropertyId () { | ||
| return (TimelineType.ikConstraint << 24) + this.ikConstraintIndex; | ||
| } | ||
| /** 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; | ||
| } | ||
| 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; | ||
| } | ||
| 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; | ||
| } | ||
| // 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)); | ||
| 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]; | ||
| } | ||
| } | ||
| } | ||
| 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; | ||
| transformConstraintIndex: number; | ||
| frames: ArrayLike<number>; // time, rotate mix, translate mix, scale mix, shear mix, ... | ||
| constructor (frameCount: number) { | ||
| super(frameCount); | ||
| this.frames = Utils.newFloatArray(frameCount * TransformConstraintTimeline.ENTRIES); | ||
| } | ||
| getPropertyId () { | ||
| return (TimelineType.transformConstraint << 24) + this.transformConstraintIndex; | ||
| } | ||
| /** 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, pose: MixPose, direction: MixDirection) { | ||
| let frames = this.frames; | ||
| 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; | ||
| } | ||
| 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)); | ||
| 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; | ||
| } | ||
| } | ||
| } | ||
| export class PathConstraintPositionTimeline extends CurveTimeline { | ||
| static ENTRIES = 2; | ||
| static PREV_TIME = -2; static PREV_VALUE = -1; | ||
| static VALUE = 1; | ||
| pathConstraintIndex: number; | ||
| frames: ArrayLike<number>; // time, position, ... | ||
| constructor (frameCount: number) { | ||
| super(frameCount); | ||
| this.frames = Utils.newFloatArray(frameCount * PathConstraintPositionTimeline.ENTRIES); | ||
| } | ||
| getPropertyId () { | ||
| return (TimelineType.pathConstraintPosition << 24) + this.pathConstraintIndex; | ||
| } | ||
| /** 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; | ||
| } | ||
| 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; | ||
| } | ||
| 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)); | ||
| 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; | ||
| } | ||
| } | ||
| export class PathConstraintSpacingTimeline extends PathConstraintPositionTimeline { | ||
| constructor (frameCount: number) { | ||
| super(frameCount); | ||
| } | ||
| getPropertyId () { | ||
| return (TimelineType.pathConstraintSpacing << 24) + 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; | ||
| } | ||
| 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)); | ||
| spacing += (frames[frame + PathConstraintSpacingTimeline.VALUE] - spacing) * percent; | ||
| } | ||
| if (pose == MixPose.setup) | ||
| constraint.spacing = constraint.data.spacing + (spacing - constraint.data.spacing) * alpha; | ||
| else | ||
| constraint.spacing += (spacing - constraint.spacing) * 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; | ||
| } | ||
| } | ||
| } | ||
| } |
| /****************************************************************************** | ||
| * Spine Runtimes Software License v2.5 | ||
| * | ||
| * Copyright (c) 2013-2016, Esoteric Software | ||
| * All rights reserved. | ||
| * | ||
| * You are granted a perpetual, non-exclusive, non-sublicensable, and | ||
| * non-transferable license to use, install, execute, and perform the Spine | ||
| * Runtimes software and derivative works solely for personal or internal | ||
| * use. Without the written permission of Esoteric Software (see Section 2 of | ||
| * the Spine Software License Agreement), you may not (a) modify, translate, | ||
| * adapt, or develop new applications using the Spine Runtimes or otherwise | ||
| * create derivative works or improvements of the Spine Runtimes or (b) remove, | ||
| * delete, alter, or obscure any trademarks or any copyright, trademark, patent, | ||
| * or other intellectual property or proprietary rights notices on or in the | ||
| * Software, including any copy thereof. Redistributions in binary or source | ||
| * form must include this license and terms. | ||
| * | ||
| * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR | ||
| * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF | ||
| * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO | ||
| * EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | ||
| * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, | ||
| * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF | ||
| * USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER | ||
| * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) | ||
| * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE | ||
| * POSSIBILITY OF SUCH DAMAGE. | ||
| *****************************************************************************/ | ||
| namespace pixi_spine.core { | ||
| 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(); | ||
| mixingTo = new Array<TrackEntry>(); | ||
| animationsChanged = false; | ||
| timeScale = 1; | ||
| trackEntryPool = new Pool<TrackEntry>(() => new TrackEntry()); | ||
| 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; | ||
| current.animationLast = current.nextAnimationLast; | ||
| current.trackLast = current.nextTrackLast; | ||
| let currentDelta = delta * current.timeScale; | ||
| 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 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; | ||
| } | ||
| this.queue.drain(); | ||
| } | ||
| updateMixingFrom (to: TrackEntry, delta: number): boolean { | ||
| let from = to.mixingFrom; | ||
| if (from == null) return true; | ||
| let finished = this.updateMixingFrom(from, delta); | ||
| // 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; | ||
| } | ||
| from.animationLast = from.nextAnimationLast; | ||
| from.trackLast = from.nextTrackLast; | ||
| from.trackTime += delta * from.timeScale; | ||
| to.mixTime += delta * to.timeScale; | ||
| return false; | ||
| } | ||
| 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 applied = false; | ||
| 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; | ||
| if (current.mixingFrom != null) | ||
| 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, MixPose.setup, MixDirection.in); | ||
| } else { | ||
| let timelineData = current.timelineData; | ||
| 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; | ||
| } | ||
| this.queue.drain(); | ||
| return applied; | ||
| } | ||
| applyMixingFrom (to: TrackEntry, skeleton: Skeleton, currentPose: MixPose) { | ||
| let from = to.mixingFrom; | ||
| if (from.mixingFrom != null) this.applyMixingFrom(from, skeleton, currentPose); | ||
| 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 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 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 (to.mixDuration > 0) this.queueEvents(from, animationTime); | ||
| this.events.length = 0; | ||
| from.nextAnimationLast = animationTime; | ||
| from.nextTrackLast = from.trackTime; | ||
| return mix; | ||
| } | ||
| 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, pose, MixDirection.in); | ||
| 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)); | ||
| 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 = 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; | ||
| } | ||
| 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 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]); | ||
| } | ||
| } | ||
| clearTracks () { | ||
| let oldDrainDisabled = this.queue.drainDisabled; | ||
| this.queue.drainDisabled = true; | ||
| for (let i = 0, n = this.tracks.length; i < n; i++) | ||
| this.clearTrack(i); | ||
| this.tracks.length = 0; | ||
| this.queue.drainDisabled = oldDrainDisabled; | ||
| this.queue.drain(); | ||
| } | ||
| clearTrack (trackIndex: number) { | ||
| if (trackIndex >= this.tracks.length) return; | ||
| let current = this.tracks[trackIndex]; | ||
| if (current == null) return; | ||
| this.queue.end(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; | ||
| } | ||
| this.tracks[current.trackIndex] = null; | ||
| this.queue.drain(); | ||
| } | ||
| setCurrent (index: number, current: TrackEntry, interrupt: boolean) { | ||
| let from = this.expandToIndex(index); | ||
| this.tracks[index] = current; | ||
| if (from != null) { | ||
| if (interrupt) this.queue.interrupt(from); | ||
| current.mixingFrom = from; | ||
| current.mixTime = 0; | ||
| // Store the interrupted mix percentage. | ||
| if (from.mixingFrom != null && from.mixDuration > 0) | ||
| current.interruptAlpha *= Math.min(1, from.mixTime / from.mixDuration); | ||
| from.timelinesRotation.length = 0; // Reset rotation for mixing out, in case entry was mixed in. | ||
| } | ||
| 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); | ||
| } | ||
| 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; | ||
| interrupt = false; | ||
| } 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); | ||
| } | ||
| 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 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; | ||
| } | ||
| } | ||
| 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; | ||
| } | ||
| 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; | ||
| } | ||
| setEmptyAnimations (mixDuration: number) { | ||
| let oldDrainDisabled = this.queue.drainDisabled; | ||
| this.queue.drainDisabled = true; | ||
| for (let i = 0, n = this.tracks.length; i < n; i++) { | ||
| let current = this.tracks[i]; | ||
| if (current != null) this.setEmptyAnimation(current.trackIndex, mixDuration); | ||
| } | ||
| this.queue.drainDisabled = oldDrainDisabled; | ||
| this.queue.drain(); | ||
| } | ||
| 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; | ||
| entry.eventThreshold = 0; | ||
| entry.attachmentThreshold = 0; | ||
| entry.drawOrderThreshold = 0; | ||
| 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.trackEnd = Number.MAX_VALUE; | ||
| entry.timeScale = 1; | ||
| 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; | ||
| } | ||
| _animationsChanged () { | ||
| this.animationsChanged = false; | ||
| let propertyIDs = this.propertyIDs; | ||
| propertyIDs.clear(); | ||
| let mixingTo = this.mixingTo; | ||
| 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); | ||
| } | ||
| } | ||
| getCurrent (trackIndex: number) { | ||
| if (trackIndex >= this.tracks.length) return null; | ||
| return this.tracks[trackIndex]; | ||
| } | ||
| addListener (listener: AnimationStateListener2) { | ||
| if (listener == null) throw new Error("listener cannot be null."); | ||
| this.listeners.push(listener); | ||
| } | ||
| /** 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 | ||
| onComplete: (trackIndex: number, loopCount: number) => any; | ||
| onEvent: (trackIndex: number, event: Event) => any; | ||
| onStart: (trackIndex: number) => any; | ||
| onEnd: (trackIndex: number) => any; | ||
| private static deprecatedWarning1: boolean = false; | ||
| setAnimationByName(trackIndex: number, animationName: string, loop: boolean) { | ||
| if (!AnimationState.deprecatedWarning1) { | ||
| AnimationState.deprecatedWarning1 = true; | ||
| console.warn("Deprecation Warning: AnimationState.setAnimationByName is deprecated, please use setAnimation from now on."); | ||
| } | ||
| this.setAnimation(trackIndex, animationName, loop); | ||
| } | ||
| private static deprecatedWarning2: boolean = false; | ||
| addAnimationByName(trackIndex: number, animationName: string, loop: boolean, delay: number) { | ||
| if (!AnimationState.deprecatedWarning2) { | ||
| AnimationState.deprecatedWarning2 = true; | ||
| console.warn("Deprecation Warning: AnimationState.addAnimationByName is deprecated, please use addAnimation from now on."); | ||
| } | ||
| this.addAnimation(trackIndex, animationName, loop, delay); | ||
| } | ||
| private static deprecatedWarning3: boolean = false; | ||
| hasAnimation(animationName: string): boolean { | ||
| let animation = this.data.skeletonData.findAnimation(animationName); | ||
| return animation !== null; | ||
| } | ||
| hasAnimationByName(animationName: string): boolean { | ||
| if (!AnimationState.deprecatedWarning3) { | ||
| AnimationState.deprecatedWarning3 = true; | ||
| console.warn("Deprecation Warning: AnimationState.hasAnimationByName is deprecated, please use hasAnimation from now on."); | ||
| } | ||
| return this.hasAnimation(animationName); | ||
| } | ||
| } | ||
| 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.timelineData.length = 0; | ||
| this.timelineDipMix.length = 0; | ||
| this.timelinesRotation.length = 0; | ||
| } | ||
| 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(); | ||
| 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); | ||
| 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; | ||
| } | ||
| 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 | ||
| onComplete: (trackIndex: number, loopCount: number) => any; | ||
| onEvent: (trackIndex: number, event: Event) => any; | ||
| onStart: (trackIndex: number) => any; | ||
| onEnd: (trackIndex: number) => any; | ||
| private static deprecatedWarning1: Boolean = false; | ||
| private static deprecatedWarning2: Boolean = false; | ||
| get time() { | ||
| if (!TrackEntry.deprecatedWarning1) { | ||
| TrackEntry.deprecatedWarning1 = true; | ||
| console.warn("Deprecation Warning: TrackEntry.time is deprecated, please use trackTime from now on."); | ||
| } | ||
| return this.trackTime; | ||
| } | ||
| set time(value: number) { | ||
| if (!TrackEntry.deprecatedWarning1) { | ||
| TrackEntry.deprecatedWarning1 = true; | ||
| console.warn("Deprecation Warning: TrackEntry.time is deprecated, please use trackTime from now on."); | ||
| } | ||
| this.trackTime = value; | ||
| } | ||
| get endTime() { | ||
| if (!TrackEntry.deprecatedWarning2) { | ||
| TrackEntry.deprecatedWarning2 = true; | ||
| console.warn("Deprecation Warning: TrackEntry.endTime is deprecated, please use trackEnd from now on."); | ||
| } | ||
| return this.trackTime; | ||
| } | ||
| set endTime(value: number) { | ||
| if (!TrackEntry.deprecatedWarning2) { | ||
| TrackEntry.deprecatedWarning2 = true; | ||
| console.warn("Deprecation Warning: TrackEntry.endTime is deprecated, please use trackEnd from now on."); | ||
| } | ||
| this.trackTime = value; | ||
| } | ||
| loopsCount() { | ||
| return Math.floor(this.trackTime / this.trackEnd); | ||
| } | ||
| } | ||
| export class EventQueue { | ||
| objects: Array<any> = []; | ||
| drainDisabled = false; | ||
| animState: AnimationState; | ||
| constructor(animState: AnimationState) { | ||
| this.animState = animState; | ||
| } | ||
| 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); | ||
| } | ||
| 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); | ||
| } | ||
| 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); | ||
| } | ||
| private static deprecatedWarning1: Boolean = false; | ||
| deprecateStuff() { | ||
| if (!EventQueue.deprecatedWarning1) { | ||
| EventQueue.deprecatedWarning1 = true; | ||
| console.warn("Deprecation Warning: onComplete, onStart, onEnd, onEvent art deprecated, please use listeners from now on. 'state.addListener({ complete: function(track, event) { } })'"); | ||
| } | ||
| return true; | ||
| } | ||
| drain () { | ||
| if (this.drainDisabled) return; | ||
| this.drainDisabled = true; | ||
| 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 | ||
| 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; | ||
| } | ||
| 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; | ||
| /** 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 is no longer the current entry and will never be applied again. */ | ||
| end? (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 every time this entry's animation completes a loop. */ | ||
| complete? (entry: TrackEntry): void; | ||
| /** Invoked when this entry's animation triggers an event. */ | ||
| event? (entry: TrackEntry, event: Event): void; | ||
| } | ||
| export abstract class AnimationStateAdapter2 implements AnimationStateListener2 { | ||
| start (entry: TrackEntry) { | ||
| } | ||
| interrupt (entry: TrackEntry) { | ||
| } | ||
| end (entry: TrackEntry) { | ||
| } | ||
| dispose (entry: TrackEntry) { | ||
| } | ||
| complete (entry: TrackEntry) { | ||
| } | ||
| event (entry: TrackEntry, event: Event) { | ||
| } | ||
| } | ||
| } |
| /****************************************************************************** | ||
| * Spine Runtimes Software License | ||
| * Version 2.5 | ||
| * | ||
| * Copyright (c) 2013-2016, Esoteric Software | ||
| * All rights reserved. | ||
| * | ||
| * You are granted a perpetual, non-exclusive, non-sublicensable, and | ||
| * non-transferable license to use, install, execute, and perform the Spine | ||
| * Runtimes software and derivative works solely for personal or internal | ||
| * use. Without the written permission of Esoteric Software (see Section 2 of | ||
| * the Spine Software License Agreement), you may not (a) modify, translate, | ||
| * adapt, or develop new applications using the Spine Runtimes or otherwise | ||
| * create derivative works or improvements of the Spine Runtimes or (b) remove, | ||
| * delete, alter, or obscure any trademarks or any copyright, trademark, patent, | ||
| * or other intellectual property or proprietary rights notices on or in the | ||
| * Software, including any copy thereof. Redistributions in binary or source | ||
| * form must include this license and terms. | ||
| * | ||
| * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR | ||
| * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF | ||
| * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO | ||
| * EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | ||
| * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, | ||
| * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF | ||
| * USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER | ||
| * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) | ||
| * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE | ||
| * POSSIBILITY OF SUCH DAMAGE. | ||
| *****************************************************************************/ | ||
| namespace pixi_spine.core { | ||
| export class AnimationStateData { | ||
| skeletonData: SkeletonData; | ||
| animationToMixTime: Map<number> = {}; | ||
| defaultMix = 0; | ||
| constructor(skeletonData: SkeletonData) { | ||
| if (skeletonData == null) throw new Error("skeletonData cannot be null."); | ||
| this.skeletonData = skeletonData; | ||
| } | ||
| setMix(fromName: string, toName: string, duration: number) { | ||
| let from = this.skeletonData.findAnimation(fromName); | ||
| if (from == null) throw new Error("Animation not found: " + fromName); | ||
| let to = this.skeletonData.findAnimation(toName); | ||
| if (to == null) throw new Error("Animation not found: " + toName); | ||
| this.setMixWith(from, to, duration); | ||
| } | ||
| private static deprecatedWarning1: boolean = false; | ||
| setMixByName(fromName: string, toName: string, duration: number) { | ||
| if (!AnimationStateData.deprecatedWarning1) { | ||
| AnimationStateData.deprecatedWarning1 = true; | ||
| console.warn("Deprecation Warning: AnimationStateData.setMixByName is deprecated, please use setMix from now on."); | ||
| } | ||
| this.setMix(fromName, toName, duration); | ||
| } | ||
| setMixWith(from: Animation, to: Animation, duration: number) { | ||
| if (from == null) throw new Error("from cannot be null."); | ||
| if (to == null) throw new Error("to cannot be null."); | ||
| let key = from.name + to.name; | ||
| this.animationToMixTime[key] = duration; | ||
| } | ||
| getMix(from: Animation, to: Animation) { | ||
| let key = from.name + to.name; | ||
| let value = this.animationToMixTime[key]; | ||
| return value === undefined ? this.defaultMix : value; | ||
| } | ||
| } | ||
| } |
| /****************************************************************************** | ||
| * Spine Runtimes Software License | ||
| * Version 2.5 | ||
| * | ||
| * Copyright (c) 2013-2016, Esoteric Software | ||
| * All rights reserved. | ||
| * | ||
| * You are granted a perpetual, non-exclusive, non-sublicensable, and | ||
| * non-transferable license to use, install, execute, and perform the Spine | ||
| * Runtimes software and derivative works solely for personal or internal | ||
| * use. Without the written permission of Esoteric Software (see Section 2 of | ||
| * the Spine Software License Agreement), you may not (a) modify, translate, | ||
| * adapt, or develop new applications using the Spine Runtimes or otherwise | ||
| * create derivative works or improvements of the Spine Runtimes or (b) remove, | ||
| * delete, alter, or obscure any trademarks or any copyright, trademark, patent, | ||
| * or other intellectual property or proprietary rights notices on or in the | ||
| * Software, including any copy thereof. Redistributions in binary or source | ||
| * form must include this license and terms. | ||
| * | ||
| * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR | ||
| * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF | ||
| * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO | ||
| * EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | ||
| * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, | ||
| * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF | ||
| * USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER | ||
| * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) | ||
| * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE | ||
| * POSSIBILITY OF SUCH DAMAGE. | ||
| *****************************************************************************/ | ||
| namespace pixi_spine.core { | ||
| export class AtlasAttachmentLoader implements AttachmentLoader { | ||
| atlas: TextureAtlas; | ||
| constructor(atlas: TextureAtlas) { | ||
| this.atlas = atlas; | ||
| } | ||
| /** @return May be null to not load an attachment. */ | ||
| newRegionAttachment(skin: Skin, name: string, path: string): RegionAttachment { | ||
| let region = this.atlas.findRegion(path); | ||
| if (region == null) throw new Error("Region not found in atlas: " + path + " (region attachment: " + name + ")"); | ||
| let attachment = new RegionAttachment(name); | ||
| attachment.region = region; | ||
| return attachment; | ||
| } | ||
| /** @return May be null to not load an attachment. */ | ||
| newMeshAttachment(skin: Skin, name: string, path: string): MeshAttachment { | ||
| let region = this.atlas.findRegion(path); | ||
| if (region == null) throw new Error("Region not found in atlas: " + path + " (mesh attachment: " + name + ")"); | ||
| let attachment = new MeshAttachment(name); | ||
| attachment.region = region; | ||
| return attachment; | ||
| } | ||
| /** @return May be null to not load an attachment. */ | ||
| newBoundingBoxAttachment(skin: Skin, name: string): BoundingBoxAttachment { | ||
| return new BoundingBoxAttachment(name); | ||
| } | ||
| /** @return May be null to not load an attachment */ | ||
| newPathAttachment(skin: Skin, name: string): PathAttachment { | ||
| return new PathAttachment(name); | ||
| } | ||
| newPointAttachment(skin: Skin, name: string): PointAttachment { | ||
| return new PointAttachment(name); | ||
| } | ||
| newClippingAttachment(skin: Skin, name: string): ClippingAttachment { | ||
| return new ClippingAttachment(name); | ||
| } | ||
| } | ||
| } |
| /****************************************************************************** | ||
| * Spine Runtimes Software License | ||
| * Version 2.5 | ||
| * | ||
| * Copyright (c) 2013-2016, Esoteric Software | ||
| * All rights reserved. | ||
| * | ||
| * You are granted a perpetual, non-exclusive, non-sublicensable, and | ||
| * non-transferable license to use, install, execute, and perform the Spine | ||
| * Runtimes software and derivative works solely for personal or internal | ||
| * use. Without the written permission of Esoteric Software (see Section 2 of | ||
| * the Spine Software License Agreement), you may not (a) modify, translate, | ||
| * adapt, or develop new applications using the Spine Runtimes or otherwise | ||
| * create derivative works or improvements of the Spine Runtimes or (b) remove, | ||
| * delete, alter, or obscure any trademarks or any copyright, trademark, patent, | ||
| * or other intellectual property or proprietary rights notices on or in the | ||
| * Software, including any copy thereof. Redistributions in binary or source | ||
| * form must include this license and terms. | ||
| * | ||
| * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR | ||
| * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF | ||
| * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO | ||
| * EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | ||
| * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, | ||
| * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF | ||
| * USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER | ||
| * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) | ||
| * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE | ||
| * POSSIBILITY OF SUCH DAMAGE. | ||
| *****************************************************************************/ | ||
| namespace pixi_spine.core { | ||
| export abstract class Attachment { | ||
| name: string; | ||
| constructor(name: string) { | ||
| if (name == null) throw new Error("name cannot be null."); | ||
| this.name = name; | ||
| } | ||
| } | ||
| export abstract class VertexAttachment extends Attachment { | ||
| private static nextID = 0; | ||
| id = (VertexAttachment.nextID++ & 65535) << 11; | ||
| bones: Array<number>; | ||
| vertices: ArrayLike<number>; | ||
| worldVerticesLength = 0; | ||
| constructor(name: string) { | ||
| super(name); | ||
| } | ||
| computeWorldVerticesOld(slot: Slot, worldVertices: ArrayLike<number>) { | ||
| this.computeWorldVertices(slot, 0, this.worldVerticesLength, worldVertices, 0, 2); | ||
| } | ||
| /** Transforms local vertices to world coordinates. | ||
| * @param start The index of the first local vertex value to transform. Each vertex has 2 values, x and y. | ||
| * @param count The number of world vertex values to output. Must be <= {@link #getWorldVerticesLength()} - start. | ||
| * @param worldVertices The output world vertices. Must have a length >= offset + count. | ||
| * @param offset The worldVertices index to begin writing values. */ | ||
| computeWorldVertices(slot: Slot, start: number, count: number, worldVertices: ArrayLike<number>, offset: number, stride: number) { | ||
| count = offset + (count >> 1) * stride; | ||
| let skeleton = slot.bone.skeleton; | ||
| let deformArray = slot.attachmentVertices; | ||
| let vertices = this.vertices; | ||
| let bones = this.bones; | ||
| if (bones == null) { | ||
| if (deformArray.length > 0) vertices = deformArray; | ||
| 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]; | ||
| worldVertices[w] = vx * a + vy * b + x; | ||
| worldVertices[w + 1] = vx * c + vy * d + y; | ||
| } | ||
| return; | ||
| } | ||
| let v = 0, skip = 0; | ||
| for (let i = 0; i < start; i += 2) { | ||
| let n = bones[v]; | ||
| v += n + 1; | ||
| skip += n; | ||
| } | ||
| let skeletonBones = skeleton.bones; | ||
| if (deformArray.length == 0) { | ||
| for (let w = offset, b = skip * 3; w < count; w += stride) { | ||
| let wx = 0, wy = 0; | ||
| let n = bones[v++]; | ||
| n += v; | ||
| for (; v < n; v++, b += 3) { | ||
| let mat = skeletonBones[bones[v]].matrix; | ||
| let vx = vertices[b], vy = vertices[b + 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; | ||
| } | ||
| worldVertices[w] = wx; | ||
| worldVertices[w + 1] = wy; | ||
| } | ||
| } else { | ||
| let deform = deformArray; | ||
| for (let w = offset, b = skip * 3, f = skip << 1; w < count; w += stride) { | ||
| let wx = 0, wy = 0; | ||
| let n = bones[v++]; | ||
| n += v; | ||
| for (; v < n; v++, b += 3, f += 2) { | ||
| 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; | ||
| } | ||
| worldVertices[w] = wx; | ||
| worldVertices[w + 1] = wy; | ||
| } | ||
| } | ||
| } | ||
| /** Returns true if a deform originally applied to the specified attachment should be applied to this attachment. */ | ||
| applyDeform(sourceAttachment: VertexAttachment) { | ||
| return this == sourceAttachment; | ||
| } | ||
| } | ||
| } |
| /****************************************************************************** | ||
| * Spine Runtimes Software License | ||
| * Version 2.5 | ||
| * | ||
| * Copyright (c) 2013-2016, Esoteric Software | ||
| * All rights reserved. | ||
| * | ||
| * You are granted a perpetual, non-exclusive, non-sublicensable, and | ||
| * non-transferable license to use, install, execute, and perform the Spine | ||
| * Runtimes software and derivative works solely for personal or internal | ||
| * use. Without the written permission of Esoteric Software (see Section 2 of | ||
| * the Spine Software License Agreement), you may not (a) modify, translate, | ||
| * adapt, or develop new applications using the Spine Runtimes or otherwise | ||
| * create derivative works or improvements of the Spine Runtimes or (b) remove, | ||
| * delete, alter, or obscure any trademarks or any copyright, trademark, patent, | ||
| * or other intellectual property or proprietary rights notices on or in the | ||
| * Software, including any copy thereof. Redistributions in binary or source | ||
| * form must include this license and terms. | ||
| * | ||
| * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR | ||
| * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF | ||
| * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO | ||
| * EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | ||
| * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, | ||
| * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF | ||
| * USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER | ||
| * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) | ||
| * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE | ||
| * POSSIBILITY OF SUCH DAMAGE. | ||
| *****************************************************************************/ | ||
| namespace pixi_spine.core { | ||
| export interface AttachmentLoader { | ||
| /** @return May be null to not load an attachment. */ | ||
| newRegionAttachment (skin: Skin, name: string, path: string): RegionAttachment; | ||
| /** @return May be null to not load an attachment. */ | ||
| newMeshAttachment (skin: Skin, name: string, path: string): MeshAttachment; | ||
| /** @return May be null to not load an attachment. */ | ||
| newBoundingBoxAttachment (skin: Skin, name: string): BoundingBoxAttachment; | ||
| /** @return May be null to not load an attachment */ | ||
| 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; | ||
| } | ||
| } |
| /****************************************************************************** | ||
| * Spine Runtimes Software License | ||
| * Version 2.5 | ||
| * | ||
| * Copyright (c) 2013-2016, Esoteric Software | ||
| * All rights reserved. | ||
| * | ||
| * You are granted a perpetual, non-exclusive, non-sublicensable, and | ||
| * non-transferable license to use, install, execute, and perform the Spine | ||
| * Runtimes software and derivative works solely for personal or internal | ||
| * use. Without the written permission of Esoteric Software (see Section 2 of | ||
| * the Spine Software License Agreement), you may not (a) modify, translate, | ||
| * adapt, or develop new applications using the Spine Runtimes or otherwise | ||
| * create derivative works or improvements of the Spine Runtimes or (b) remove, | ||
| * delete, alter, or obscure any trademarks or any copyright, trademark, patent, | ||
| * or other intellectual property or proprietary rights notices on or in the | ||
| * Software, including any copy thereof. Redistributions in binary or source | ||
| * form must include this license and terms. | ||
| * | ||
| * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR | ||
| * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF | ||
| * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO | ||
| * EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | ||
| * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, | ||
| * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF | ||
| * USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER | ||
| * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) | ||
| * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE | ||
| * POSSIBILITY OF SUCH DAMAGE. | ||
| *****************************************************************************/ | ||
| namespace pixi_spine.core { | ||
| export enum AttachmentType { | ||
| Region, BoundingBox, Mesh, LinkedMesh, Path, Point | ||
| } | ||
| } |
| /****************************************************************************** | ||
| * Spine Runtimes Software License | ||
| * Version 2.5 | ||
| * | ||
| * Copyright (c) 2013-2016, Esoteric Software | ||
| * All rights reserved. | ||
| * | ||
| * You are granted a perpetual, non-exclusive, non-sublicensable, and | ||
| * non-transferable license to use, install, execute, and perform the Spine | ||
| * Runtimes software and derivative works solely for personal or internal | ||
| * use. Without the written permission of Esoteric Software (see Section 2 of | ||
| * the Spine Software License Agreement), you may not (a) modify, translate, | ||
| * adapt, or develop new applications using the Spine Runtimes or otherwise | ||
| * create derivative works or improvements of the Spine Runtimes or (b) remove, | ||
| * delete, alter, or obscure any trademarks or any copyright, trademark, patent, | ||
| * or other intellectual property or proprietary rights notices on or in the | ||
| * Software, including any copy thereof. Redistributions in binary or source | ||
| * form must include this license and terms. | ||
| * | ||
| * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR | ||
| * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF | ||
| * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO | ||
| * EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | ||
| * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, | ||
| * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF | ||
| * USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER | ||
| * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) | ||
| * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE | ||
| * POSSIBILITY OF SUCH DAMAGE. | ||
| *****************************************************************************/ | ||
| namespace pixi_spine.core { | ||
| export class BoundingBoxAttachment extends VertexAttachment { | ||
| color = new Color(1, 1, 1, 1); | ||
| constructor(name: string) { | ||
| super(name); | ||
| } | ||
| } | ||
| } |
| /****************************************************************************** | ||
| * Spine Runtimes Software License v2.5 | ||
| * | ||
| * Copyright (c) 2013-2016, Esoteric Software | ||
| * All rights reserved. | ||
| * | ||
| * You are granted a perpetual, non-exclusive, non-sublicensable, and | ||
| * non-transferable license to use, install, execute, and perform the Spine | ||
| * Runtimes software and derivative works solely for personal or internal | ||
| * use. Without the written permission of Esoteric Software (see Section 2 of | ||
| * the Spine Software License Agreement), you may not (a) modify, translate, | ||
| * adapt, or develop new applications using the Spine Runtimes or otherwise | ||
| * create derivative works or improvements of the Spine Runtimes or (b) remove, | ||
| * delete, alter, or obscure any trademarks or any copyright, trademark, patent, | ||
| * or other intellectual property or proprietary rights notices on or in the | ||
| * Software, including any copy thereof. Redistributions in binary or source | ||
| * form must include this license and terms. | ||
| * | ||
| * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR | ||
| * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF | ||
| * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO | ||
| * EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | ||
| * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, | ||
| * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF | ||
| * USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER | ||
| * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) | ||
| * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE | ||
| * POSSIBILITY OF SUCH DAMAGE. | ||
| *****************************************************************************/ | ||
| namespace pixi_spine.core { | ||
| export class ClippingAttachment extends VertexAttachment { | ||
| endSlot: SlotData; | ||
| // Nonessential. | ||
| color = new Color(0.2275, 0.2275, 0.8078, 1); // ce3a3aff | ||
| constructor(name: string) { | ||
| super(name); | ||
| } | ||
| } | ||
| } |
| /****************************************************************************** | ||
| * Spine Runtimes Software License | ||
| * Version 2.5 | ||
| * | ||
| * Copyright (c) 2013-2016, Esoteric Software | ||
| * All rights reserved. | ||
| * | ||
| * You are granted a perpetual, non-exclusive, non-sublicensable, and | ||
| * non-transferable license to use, install, execute, and perform the Spine | ||
| * Runtimes software and derivative works solely for personal or internal | ||
| * use. Without the written permission of Esoteric Software (see Section 2 of | ||
| * the Spine Software License Agreement), you may not (a) modify, translate, | ||
| * adapt, or develop new applications using the Spine Runtimes or otherwise | ||
| * create derivative works or improvements of the Spine Runtimes or (b) remove, | ||
| * delete, alter, or obscure any trademarks or any copyright, trademark, patent, | ||
| * or other intellectual property or proprietary rights notices on or in the | ||
| * Software, including any copy thereof. Redistributions in binary or source | ||
| * form must include this license and terms. | ||
| * | ||
| * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR | ||
| * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF | ||
| * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO | ||
| * EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | ||
| * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, | ||
| * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF | ||
| * USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER | ||
| * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) | ||
| * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE | ||
| * POSSIBILITY OF SUCH DAMAGE. | ||
| *****************************************************************************/ | ||
| namespace pixi_spine.core { | ||
| export class MeshAttachment extends VertexAttachment { | ||
| region: TextureRegion; | ||
| path: string; | ||
| regionUVs: ArrayLike<number>; uvs: ArrayLike<number>; | ||
| triangles: Array<number>; | ||
| color = new Color(1, 1, 1, 1); | ||
| hullLength: number; | ||
| private parentMesh: MeshAttachment; | ||
| inheritDeform = false; | ||
| tempColor = new Color(0, 0, 0, 0); | ||
| constructor (name: string) { | ||
| super(name); | ||
| } | ||
| updateUVs(region: TextureRegion, uvs: ArrayLike<number>): ArrayLike<number> { | ||
| let regionUVs = this.regionUVs; | ||
| let n = regionUVs.length; | ||
| if (!uvs || uvs.length != n) { | ||
| uvs = Utils.newFloatArray(n); | ||
| } | ||
| if (region == null) { | ||
| return; | ||
| } | ||
| let texture = region.texture; | ||
| let r = (texture as any)._uvs; | ||
| let w1 = region.width, h1 = region.height, w2 = region.originalWidth, h2 = region.originalHeight; | ||
| let x = region.offsetX, y = region.pixiOffsetY; | ||
| for (let i = 0; i < n; i += 2) { | ||
| let u = this.regionUVs[i], v = this.regionUVs[i + 1]; | ||
| u = (u * w2 - x) / w1; | ||
| v = (v * h2 - y) / h1; | ||
| uvs[i] = (r.x0 * (1 - u) + r.x1 * u) * (1 - v) + (r.x3 * (1 - u) + r.x2 * u) * v; | ||
| uvs[i + 1] = (r.y0 * (1 - u) + r.y1 * u) * (1 - v) + (r.y3 * (1 - u) + r.y2 * u) * v; | ||
| } | ||
| return uvs; | ||
| } | ||
| applyDeform (sourceAttachment: VertexAttachment): boolean { | ||
| return this == sourceAttachment || (this.inheritDeform && this.parentMesh == sourceAttachment); | ||
| } | ||
| getParentMesh () { | ||
| return this.parentMesh; | ||
| } | ||
| /** @param parentMesh May be null. */ | ||
| setParentMesh (parentMesh: MeshAttachment) { | ||
| this.parentMesh = parentMesh; | ||
| if (parentMesh != null) { | ||
| this.bones = parentMesh.bones; | ||
| this.vertices = parentMesh.vertices; | ||
| this.worldVerticesLength = parentMesh.worldVerticesLength; | ||
| this.regionUVs = parentMesh.regionUVs; | ||
| this.triangles = parentMesh.triangles; | ||
| this.hullLength = parentMesh.hullLength; | ||
| this.worldVerticesLength = parentMesh.worldVerticesLength | ||
| } | ||
| } | ||
| //computeWorldVerticesWith(slot, 0, this.worldVerticesLength, worldVertices, 0); | ||
| } | ||
| } |
| /****************************************************************************** | ||
| * Spine Runtimes Software License | ||
| * Version 2.5 | ||
| * | ||
| * Copyright (c) 2013-2016, Esoteric Software | ||
| * All rights reserved. | ||
| * | ||
| * You are granted a perpetual, non-exclusive, non-sublicensable, and | ||
| * non-transferable license to use, install, execute, and perform the Spine | ||
| * Runtimes software and derivative works solely for personal or internal | ||
| * use. Without the written permission of Esoteric Software (see Section 2 of | ||
| * the Spine Software License Agreement), you may not (a) modify, translate, | ||
| * adapt, or develop new applications using the Spine Runtimes or otherwise | ||
| * create derivative works or improvements of the Spine Runtimes or (b) remove, | ||
| * delete, alter, or obscure any trademarks or any copyright, trademark, patent, | ||
| * or other intellectual property or proprietary rights notices on or in the | ||
| * Software, including any copy thereof. Redistributions in binary or source | ||
| * form must include this license and terms. | ||
| * | ||
| * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR | ||
| * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF | ||
| * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO | ||
| * EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | ||
| * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, | ||
| * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF | ||
| * USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER | ||
| * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) | ||
| * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE | ||
| * POSSIBILITY OF SUCH DAMAGE. | ||
| *****************************************************************************/ | ||
| namespace pixi_spine.core { | ||
| export class PathAttachment extends VertexAttachment { | ||
| lengths: Array<number>; | ||
| closed = false; | ||
| constantSpeed = false; | ||
| color = new Color(1, 1, 1, 1); | ||
| constructor(name: string) { | ||
| super(name); | ||
| } | ||
| } | ||
| } |
| /****************************************************************************** | ||
| * Spine Runtimes Software License v2.5 | ||
| * | ||
| * Copyright (c) 2013-2016, Esoteric Software | ||
| * All rights reserved. | ||
| * | ||
| * You are granted a perpetual, non-exclusive, non-sublicensable, and | ||
| * non-transferable license to use, install, execute, and perform the Spine | ||
| * Runtimes software and derivative works solely for personal or internal | ||
| * use. Without the written permission of Esoteric Software (see Section 2 of | ||
| * the Spine Software License Agreement), you may not (a) modify, translate, | ||
| * adapt, or develop new applications using the Spine Runtimes or otherwise | ||
| * create derivative works or improvements of the Spine Runtimes or (b) remove, | ||
| * delete, alter, or obscure any trademarks or any copyright, trademark, patent, | ||
| * or other intellectual property or proprietary rights notices on or in the | ||
| * Software, including any copy thereof. Redistributions in binary or source | ||
| * form must include this license and terms. | ||
| * | ||
| * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR | ||
| * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF | ||
| * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO | ||
| * EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | ||
| * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, | ||
| * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF | ||
| * USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER | ||
| * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) | ||
| * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE | ||
| * POSSIBILITY OF SUCH DAMAGE. | ||
| *****************************************************************************/ | ||
| namespace pixi_spine.core { | ||
| export class PointAttachment extends VertexAttachment { | ||
| x: number; | ||
| y: number; | ||
| rotation: number; | ||
| color = new Color(0.38, 0.94, 0, 1); | ||
| constructor(name: string) { | ||
| super(name); | ||
| } | ||
| computeWorldPosition(bone: Bone, point: Vector2) { | ||
| const mat = bone.matrix; | ||
| point.x = this.x * mat.a + this.y * mat.c + bone.worldX; | ||
| point.y = this.x * mat.b + this.y * mat.d + bone.worldY; | ||
| return point; | ||
| } | ||
| computeWorldRotation(bone: Bone) { | ||
| const mat = bone.matrix; | ||
| let cos = MathUtils.cosDeg(this.rotation), sin = MathUtils.sinDeg(this.rotation); | ||
| let x = cos * mat.a + sin * mat.c; | ||
| let y = cos * mat.b + sin * mat.d; | ||
| return Math.atan2(y, x) * MathUtils.radDeg; | ||
| } | ||
| } | ||
| } |
| /****************************************************************************** | ||
| * Spine Runtimes Software License | ||
| * Version 2.5 | ||
| * | ||
| * Copyright (c) 2013-2016, Esoteric Software | ||
| * All rights reserved. | ||
| * | ||
| * You are granted a perpetual, non-exclusive, non-sublicensable, and | ||
| * non-transferable license to use, install, execute, and perform the Spine | ||
| * Runtimes software and derivative works solely for personal or internal | ||
| * use. Without the written permission of Esoteric Software (see Section 2 of | ||
| * the Spine Software License Agreement), you may not (a) modify, translate, | ||
| * adapt, or develop new applications using the Spine Runtimes or otherwise | ||
| * create derivative works or improvements of the Spine Runtimes or (b) remove, | ||
| * delete, alter, or obscure any trademarks or any copyright, trademark, patent, | ||
| * or other intellectual property or proprietary rights notices on or in the | ||
| * Software, including any copy thereof. Redistributions in binary or source | ||
| * form must include this license and terms. | ||
| * | ||
| * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR | ||
| * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF | ||
| * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO | ||
| * EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | ||
| * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, | ||
| * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF | ||
| * USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER | ||
| * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) | ||
| * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE | ||
| * POSSIBILITY OF SUCH DAMAGE. | ||
| *****************************************************************************/ | ||
| namespace pixi_spine.core { | ||
| 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; | ||
| y = 0; | ||
| scaleX = 1; | ||
| scaleY = 1; | ||
| rotation = 0; | ||
| width = 0; | ||
| height = 0; | ||
| color = new Color(1, 1, 1, 1); | ||
| path: string; | ||
| rendererObject: any; | ||
| region: TextureRegion; | ||
| offset = Utils.newFloatArray(8); | ||
| uvs = Utils.newFloatArray(8); | ||
| tempColor = new Color(1, 1, 1, 1); | ||
| constructor(name: string) { | ||
| super(name); | ||
| } | ||
| 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; | ||
| } | ||
| } | ||
| } |
| /****************************************************************************** | ||
| * Spine Runtimes Software License | ||
| * Version 2.5 | ||
| * | ||
| * Copyright (c) 2013-2016, Esoteric Software | ||
| * All rights reserved. | ||
| * | ||
| * You are granted a perpetual, non-exclusive, non-sublicensable, and | ||
| * non-transferable license to use, install, execute, and perform the Spine | ||
| * Runtimes software and derivative works solely for personal or internal | ||
| * use. Without the written permission of Esoteric Software (see Section 2 of | ||
| * the Spine Software License Agreement), you may not (a) modify, translate, | ||
| * adapt, or develop new applications using the Spine Runtimes or otherwise | ||
| * create derivative works or improvements of the Spine Runtimes or (b) remove, | ||
| * delete, alter, or obscure any trademarks or any copyright, trademark, patent, | ||
| * or other intellectual property or proprietary rights notices on or in the | ||
| * Software, including any copy thereof. Redistributions in binary or source | ||
| * form must include this license and terms. | ||
| * | ||
| * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR | ||
| * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF | ||
| * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO | ||
| * EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | ||
| * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, | ||
| * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF | ||
| * USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER | ||
| * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) | ||
| * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE | ||
| * POSSIBILITY OF SUCH DAMAGE. | ||
| *****************************************************************************/ | ||
| namespace pixi_spine.core { | ||
| export enum BlendMode { | ||
| Normal = 0, | ||
| Additive = 1, | ||
| Multiply = 2, | ||
| Screen = 3 | ||
| } | ||
| } |
-331
| /****************************************************************************** | ||
| * Spine Runtimes Software License | ||
| * Version 2.5 | ||
| * | ||
| * Copyright (c) 2013-2016, Esoteric Software | ||
| * All rights reserved. | ||
| * | ||
| * You are granted a perpetual, non-exclusive, non-sublicensable, and | ||
| * non-transferable license to use, install, execute, and perform the Spine | ||
| * Runtimes software and derivative works solely for personal or internal | ||
| * use. Without the written permission of Esoteric Software (see Section 2 of | ||
| * the Spine Software License Agreement), you may not (a) modify, translate, | ||
| * adapt, or develop new applications using the Spine Runtimes or otherwise | ||
| * create derivative works or improvements of the Spine Runtimes or (b) remove, | ||
| * delete, alter, or obscure any trademarks or any copyright, trademark, patent, | ||
| * or other intellectual property or proprietary rights notices on or in the | ||
| * Software, including any copy thereof. Redistributions in binary or source | ||
| * form must include this license and terms. | ||
| * | ||
| * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR | ||
| * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF | ||
| * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO | ||
| * EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | ||
| * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, | ||
| * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF | ||
| * USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER | ||
| * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) | ||
| * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE | ||
| * POSSIBILITY OF SUCH DAMAGE. | ||
| *****************************************************************************/ | ||
| namespace pixi_spine.core { | ||
| export class Bone implements Updatable { | ||
| static yDown: boolean = false; | ||
| //be careful! Spine b,c is c,b in pixi matrix | ||
| matrix = new PIXI.Matrix(); | ||
| get worldX(): number { | ||
| return this.matrix.tx; | ||
| } | ||
| get worldY(): number { | ||
| return this.matrix.ty; | ||
| } | ||
| data: BoneData; | ||
| skeleton: Skeleton; | ||
| parent: Bone; | ||
| children = new Array<Bone>(); | ||
| x = 0; | ||
| y = 0; | ||
| rotation = 0; | ||
| scaleX = 0; | ||
| scaleY = 0; | ||
| shearX = 0; | ||
| shearY = 0; | ||
| ax = 0; | ||
| ay = 0; | ||
| arotation = 0; | ||
| ascaleX = 0; | ||
| ascaleY = 0; | ||
| ashearX = 0; | ||
| ashearY = 0; | ||
| appliedValid = false; | ||
| sorted = false; | ||
| /** @param parent May be null. */ | ||
| constructor(data: BoneData, skeleton: Skeleton, parent: Bone) { | ||
| if (data == null) throw new Error("data cannot be null."); | ||
| if (skeleton == null) throw new Error("skeleton cannot be null."); | ||
| this.data = data; | ||
| this.skeleton = skeleton; | ||
| this.parent = parent; | ||
| this.setToSetupPose(); | ||
| } | ||
| /** Same as {@link #updateWorldTransform()}. This method exists for Bone to implement {@link Updatable}. */ | ||
| update() { | ||
| this.updateWorldTransformWith(this.x, this.y, this.rotation, this.scaleX, this.scaleY, this.shearX, this.shearY); | ||
| } | ||
| /** Computes the world transform using the parent bone and this bone's local transform. */ | ||
| updateWorldTransform() { | ||
| this.updateWorldTransformWith(this.x, this.y, this.rotation, this.scaleX, this.scaleY, this.shearX, this.shearY); | ||
| } | ||
| /** Computes the world transform using the parent bone and the specified local transform. */ | ||
| updateWorldTransformWith(x: number, y: number, rotation: number, scaleX: number, scaleY: number, shearX: number, shearY: number) { | ||
| this.ax = x; | ||
| this.ay = y; | ||
| this.arotation = rotation; | ||
| this.ascaleX = scaleX; | ||
| this.ascaleY = scaleY; | ||
| this.ashearX = shearX; | ||
| this.ashearY = shearY; | ||
| this.appliedValid = true; | ||
| let parent = this.parent; | ||
| let m = this.matrix; | ||
| if (parent == null) { // Root bone. | ||
| let rotationY = rotation + 90 + shearY; | ||
| let la = MathUtils.cosDeg(rotation + shearX) * scaleX; | ||
| let lb = MathUtils.cosDeg(rotationY) * scaleY; | ||
| let lc = MathUtils.sinDeg(rotation + shearX) * scaleX; | ||
| let ld = MathUtils.sinDeg(rotationY) * scaleY; | ||
| let skeleton = this.skeleton; | ||
| if (skeleton.flipX) { | ||
| x = -x; | ||
| la = -la; | ||
| lb = -lb; | ||
| } | ||
| if (skeleton.flipY !== Bone.yDown) { | ||
| y = -y; | ||
| lc = -lc; | ||
| ld = -ld; | ||
| } | ||
| m.a = la; | ||
| m.c = lb; | ||
| m.b = lc; | ||
| m.d = ld; | ||
| m.tx = x + skeleton.x; | ||
| m.ty = y + skeleton.y; | ||
| return; | ||
| } | ||
| let pa = parent.matrix.a, pb = parent.matrix.c, pc = parent.matrix.b, pd = parent.matrix.d; | ||
| m.tx = pa * x + pb * y + parent.matrix.tx; | ||
| m.ty = pc * x + pd * y + parent.matrix.ty; | ||
| switch (this.data.transformMode) { | ||
| case TransformMode.Normal: { | ||
| let rotationY = rotation + 90 + shearY; | ||
| let la = MathUtils.cosDeg(rotation + shearX) * scaleX; | ||
| let lb = MathUtils.cosDeg(rotationY) * scaleY; | ||
| let lc = MathUtils.sinDeg(rotation + shearX) * scaleX; | ||
| let ld = MathUtils.sinDeg(rotationY) * scaleY; | ||
| m.a = pa * la + pb * lc; | ||
| m.c = pa * lb + pb * ld; | ||
| m.b = pc * la + pd * lc; | ||
| m.d = pc * lb + pd * ld; | ||
| return; | ||
| } | ||
| case TransformMode.OnlyTranslation: { | ||
| let rotationY = rotation + 90 + shearY; | ||
| m.a = MathUtils.cosDeg(rotation + shearX) * scaleX; | ||
| m.c = MathUtils.cosDeg(rotationY) * scaleY; | ||
| m.b = MathUtils.sinDeg(rotation + shearX) * scaleX; | ||
| m.d = MathUtils.sinDeg(rotationY) * scaleY; | ||
| break; | ||
| } | ||
| case TransformMode.NoRotationOrReflection: { | ||
| let s = pa * pa + pc * pc; | ||
| let prx = 0; | ||
| if (s > 0.0001) { | ||
| s = Math.abs(pa * pd - pb * pc) / s; | ||
| pb = pc * s; | ||
| pd = pa * s; | ||
| prx = Math.atan2(pc, pa) * MathUtils.radDeg; | ||
| } else { | ||
| pa = 0; | ||
| pc = 0; | ||
| prx = 90 - Math.atan2(pd, pb) * MathUtils.radDeg; | ||
| } | ||
| let rx = rotation + shearX - prx; | ||
| let ry = rotation + shearY - prx + 90; | ||
| let la = MathUtils.cosDeg(rx) * scaleX; | ||
| let lb = MathUtils.cosDeg(ry) * scaleY; | ||
| let lc = MathUtils.sinDeg(rx) * scaleX; | ||
| let ld = MathUtils.sinDeg(ry) * scaleY; | ||
| m.a = pa * la - pb * lc; | ||
| m.c = pa * lb - pb * ld; | ||
| m.b = pc * la + pd * lc; | ||
| m.d = pc * lb + pd * ld; | ||
| break; | ||
| } | ||
| case TransformMode.NoScale: | ||
| case TransformMode.NoScaleOrReflection: { | ||
| let cos = MathUtils.cosDeg(rotation); | ||
| let sin = MathUtils.sinDeg(rotation); | ||
| let za = pa * cos + pb * sin; | ||
| let zc = pc * cos + pd * sin; | ||
| let s = Math.sqrt(za * za + zc * zc); | ||
| if (s > 0.00001) s = 1 / s; | ||
| za *= s; | ||
| zc *= s; | ||
| s = Math.sqrt(za * za + zc * zc); | ||
| let r = Math.PI / 2 + Math.atan2(zc, za); | ||
| let zb = Math.cos(r) * s; | ||
| let zd = Math.sin(r) * s; | ||
| let la = MathUtils.cosDeg(shearX) * scaleX; | ||
| let lb = MathUtils.cosDeg(90 + shearY) * scaleY; | ||
| let lc = MathUtils.sinDeg(shearX) * scaleX; | ||
| let ld = MathUtils.sinDeg(90 + shearY) * scaleY; | ||
| m.a = za * la + zb * lc; | ||
| m.c = za * lb + zb * ld; | ||
| m.b = zc * la + zd * lc; | ||
| m.d = zc * lb + zd * ld; | ||
| if (this.data.transformMode != TransformMode.NoScaleOrReflection ? pa * pd - pb * pc < 0 : ((this.skeleton.flipX != this.skeleton.flipY) != Bone.yDown)) { | ||
| m.c = -m.c; | ||
| m.d = -m.d; | ||
| } | ||
| return; | ||
| } | ||
| } | ||
| if (this.skeleton.flipX) { | ||
| m.a = -m.a; | ||
| m.c = -m.c; | ||
| } | ||
| if (this.skeleton.flipY != Bone.yDown) { | ||
| m.b = -m.b; | ||
| m.d = -m.d; | ||
| } | ||
| } | ||
| setToSetupPose() { | ||
| let data = this.data; | ||
| this.x = data.x; | ||
| this.y = data.y; | ||
| this.rotation = data.rotation; | ||
| this.scaleX = data.scaleX; | ||
| this.scaleY = data.scaleY; | ||
| this.shearX = data.shearX; | ||
| this.shearY = data.shearY; | ||
| } | ||
| getWorldRotationX() { | ||
| return Math.atan2(this.matrix.b, this.matrix.a) * MathUtils.radDeg; | ||
| } | ||
| getWorldRotationY() { | ||
| return Math.atan2(this.matrix.d, this.matrix.c) * MathUtils.radDeg; | ||
| } | ||
| getWorldScaleX() { | ||
| let m = this.matrix; | ||
| return Math.sqrt(m.a * m.a + m.c * m.c); | ||
| } | ||
| getWorldScaleY() { | ||
| let m = this.matrix; | ||
| return Math.sqrt(m.b * m.b + m.d * m.d); | ||
| } | ||
| /** Computes the individual applied transform values from the world transform. This can be useful to perform processing using | ||
| * the applied transform after the world transform has been modified directly (eg, by a constraint). | ||
| * <p> | ||
| * Some information is ambiguous in the world transform, such as -1,-1 scale versus 180 rotation. */ | ||
| updateAppliedTransform() { | ||
| this.appliedValid = true; | ||
| let parent = this.parent; | ||
| let m = this.matrix; | ||
| if (parent == null) { | ||
| this.ax = m.tx; | ||
| this.ay = m.ty; | ||
| this.arotation = Math.atan2(m.b, m.a) * MathUtils.radDeg; | ||
| this.ascaleX = Math.sqrt(m.a * m.a + m.b * m.b); | ||
| this.ascaleY = Math.sqrt(m.c * m.c + m.d * m.d); | ||
| this.ashearX = 0; | ||
| this.ashearY = Math.atan2(m.a * m.c + m.b * m.d, m.a * m.d - m.b * m.c) * MathUtils.radDeg; | ||
| return; | ||
| } | ||
| let pm = parent.matrix; | ||
| let pid = 1 / (pm.a * pm.d - pm.b * pm.c); | ||
| let dx = m.tx - pm.tx, dy = m.ty - pm.ty; | ||
| this.ax = (dx * pm.d * pid - dy * pm.c * pid); | ||
| this.ay = (dy * pm.a * pid - dx * pm.b * pid); | ||
| let ia = pid * pm.d; | ||
| let id = pid * pm.a; | ||
| let ib = pid * pm.c; | ||
| let ic = pid * pm.b; | ||
| let ra = ia * m.a - ib * m.b; | ||
| let rb = ia * m.c - ib * m.d; | ||
| let rc = id * m.b - ic * m.a; | ||
| let rd = id * m.d - ic * m.c; | ||
| this.ashearX = 0; | ||
| this.ascaleX = Math.sqrt(ra * ra + rc * rc); | ||
| if (this.ascaleX > 0.0001) { | ||
| let det = ra * rd - rb * rc; | ||
| this.ascaleY = det / this.ascaleX; | ||
| this.ashearY = Math.atan2(ra * rb + rc * rd, det) * MathUtils.radDeg; | ||
| this.arotation = Math.atan2(rc, ra) * MathUtils.radDeg; | ||
| } else { | ||
| this.ascaleX = 0; | ||
| this.ascaleY = Math.sqrt(rb * rb + rd * rd); | ||
| this.ashearY = 0; | ||
| this.arotation = 90 - Math.atan2(rd, rb) * MathUtils.radDeg; | ||
| } | ||
| } | ||
| worldToLocal(world: Vector2) { | ||
| let m = this.matrix; | ||
| let a = m.a, b = m.c, c = m.b, d = m.d; | ||
| let invDet = 1 / (a * d - b * c); | ||
| let x = world.x - m.tx, y = world.y - m.ty; | ||
| world.x = (x * d * invDet - y * b * invDet); | ||
| world.y = (y * a * invDet - x * c * invDet); | ||
| return world; | ||
| } | ||
| localToWorld(local: Vector2) { | ||
| let m = this.matrix; | ||
| let x = local.x, y = local.y; | ||
| local.x = x * m.a + y * m.c + m.tx; | ||
| local.y = x * m.b + y * m.d + m.ty; | ||
| return local; | ||
| } | ||
| 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; | ||
| } | ||
| } | ||
| } |
| /****************************************************************************** | ||
| * Spine Runtimes Software License | ||
| * Version 2.5 | ||
| * | ||
| * Copyright (c) 2013-2016, Esoteric Software | ||
| * All rights reserved. | ||
| * | ||
| * You are granted a perpetual, non-exclusive, non-sublicensable, and | ||
| * non-transferable license to use, install, execute, and perform the Spine | ||
| * Runtimes software and derivative works solely for personal or internal | ||
| * use. Without the written permission of Esoteric Software (see Section 2 of | ||
| * the Spine Software License Agreement), you may not (a) modify, translate, | ||
| * adapt, or develop new applications using the Spine Runtimes or otherwise | ||
| * create derivative works or improvements of the Spine Runtimes or (b) remove, | ||
| * delete, alter, or obscure any trademarks or any copyright, trademark, patent, | ||
| * or other intellectual property or proprietary rights notices on or in the | ||
| * Software, including any copy thereof. Redistributions in binary or source | ||
| * form must include this license and terms. | ||
| * | ||
| * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR | ||
| * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF | ||
| * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO | ||
| * EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | ||
| * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, | ||
| * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF | ||
| * USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER | ||
| * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) | ||
| * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE | ||
| * POSSIBILITY OF SUCH DAMAGE. | ||
| *****************************************************************************/ | ||
| namespace pixi_spine.core { | ||
| export class BoneData { | ||
| index: number; | ||
| name: string; | ||
| parent: BoneData; | ||
| length: number; | ||
| x = 0; | ||
| y = 0; | ||
| rotation = 0; | ||
| scaleX = 1; | ||
| scaleY = 1; | ||
| shearX = 0; | ||
| shearY = 0; | ||
| transformMode = TransformMode.Normal; | ||
| constructor(index: number, name: string, parent: BoneData) { | ||
| if (index < 0) throw new Error("index must be >= 0."); | ||
| if (name == null) throw new Error("name cannot be null."); | ||
| this.index = index; | ||
| this.name = name; | ||
| this.parent = parent; | ||
| } | ||
| } | ||
| export enum TransformMode { | ||
| Normal, OnlyTranslation, NoRotationOrReflection, NoScale, NoScaleOrReflection | ||
| } | ||
| } |
| /****************************************************************************** | ||
| * Spine Runtimes Software License v2.5 | ||
| * | ||
| * Copyright (c) 2013-2016, Esoteric Software | ||
| * All rights reserved. | ||
| * | ||
| * You are granted a perpetual, non-exclusive, non-sublicensable, and | ||
| * non-transferable license to use, install, execute, and perform the Spine | ||
| * Runtimes software and derivative works solely for personal or internal | ||
| * use. Without the written permission of Esoteric Software (see Section 2 of | ||
| * the Spine Software License Agreement), you may not (a) modify, translate, | ||
| * adapt, or develop new applications using the Spine Runtimes or otherwise | ||
| * create derivative works or improvements of the Spine Runtimes or (b) remove, | ||
| * delete, alter, or obscure any trademarks or any copyright, trademark, patent, | ||
| * or other intellectual property or proprietary rights notices on or in the | ||
| * Software, including any copy thereof. Redistributions in binary or source | ||
| * form must include this license and terms. | ||
| * | ||
| * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR | ||
| * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF | ||
| * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO | ||
| * EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | ||
| * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, | ||
| * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF | ||
| * USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER | ||
| * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) | ||
| * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE | ||
| * POSSIBILITY OF SUCH DAMAGE. | ||
| *****************************************************************************/ | ||
| namespace pixi_spine.core { | ||
| export interface Constraint extends Updatable { | ||
| getOrder(): number; | ||
| } | ||
| } |
| /****************************************************************************** | ||
| * Spine Runtimes Software License | ||
| * Version 2.5 | ||
| * | ||
| * Copyright (c) 2013-2016, Esoteric Software | ||
| * All rights reserved. | ||
| * | ||
| * You are granted a perpetual, non-exclusive, non-sublicensable, and | ||
| * non-transferable license to use, install, execute, and perform the Spine | ||
| * Runtimes software and derivative works solely for personal or internal | ||
| * use. Without the written permission of Esoteric Software (see Section 2 of | ||
| * the Spine Software License Agreement), you may not (a) modify, translate, | ||
| * adapt, or develop new applications using the Spine Runtimes or otherwise | ||
| * create derivative works or improvements of the Spine Runtimes or (b) remove, | ||
| * delete, alter, or obscure any trademarks or any copyright, trademark, patent, | ||
| * or other intellectual property or proprietary rights notices on or in the | ||
| * Software, including any copy thereof. Redistributions in binary or source | ||
| * form must include this license and terms. | ||
| * | ||
| * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR | ||
| * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF | ||
| * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO | ||
| * EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | ||
| * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, | ||
| * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF | ||
| * USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER | ||
| * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) | ||
| * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE | ||
| * POSSIBILITY OF SUCH DAMAGE. | ||
| *****************************************************************************/ | ||
| namespace pixi_spine.core { | ||
| export class Event { | ||
| data: EventData; | ||
| intValue: number; | ||
| floatValue: number; | ||
| stringValue: string; | ||
| time: number; | ||
| constructor(time: number, data: EventData) { | ||
| if (data == null) throw new Error("data cannot be null."); | ||
| this.time = time; | ||
| this.data = data; | ||
| } | ||
| } | ||
| } |
| /****************************************************************************** | ||
| * Spine Runtimes Software License | ||
| * Version 2.5 | ||
| * | ||
| * Copyright (c) 2013-2016, Esoteric Software | ||
| * All rights reserved. | ||
| * | ||
| * You are granted a perpetual, non-exclusive, non-sublicensable, and | ||
| * non-transferable license to use, install, execute, and perform the Spine | ||
| * Runtimes software and derivative works solely for personal or internal | ||
| * use. Without the written permission of Esoteric Software (see Section 2 of | ||
| * the Spine Software License Agreement), you may not (a) modify, translate, | ||
| * adapt, or develop new applications using the Spine Runtimes or otherwise | ||
| * create derivative works or improvements of the Spine Runtimes or (b) remove, | ||
| * delete, alter, or obscure any trademarks or any copyright, trademark, patent, | ||
| * or other intellectual property or proprietary rights notices on or in the | ||
| * Software, including any copy thereof. Redistributions in binary or source | ||
| * form must include this license and terms. | ||
| * | ||
| * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR | ||
| * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF | ||
| * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO | ||
| * EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | ||
| * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, | ||
| * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF | ||
| * USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER | ||
| * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) | ||
| * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE | ||
| * POSSIBILITY OF SUCH DAMAGE. | ||
| *****************************************************************************/ | ||
| namespace pixi_spine.core { | ||
| export class EventData { | ||
| name: string; | ||
| intValue: number; | ||
| floatValue: number; | ||
| stringValue: string; | ||
| constructor (name: string) { | ||
| this.name = name; | ||
| } | ||
| } | ||
| } |
| /****************************************************************************** | ||
| * Spine Runtimes Software License | ||
| * Version 2.5 | ||
| * | ||
| * Copyright (c) 2013-2016, Esoteric Software | ||
| * All rights reserved. | ||
| * | ||
| * You are granted a perpetual, non-exclusive, non-sublicensable, and | ||
| * non-transferable license to use, install, execute, and perform the Spine | ||
| * Runtimes software and derivative works solely for personal or internal | ||
| * use. Without the written permission of Esoteric Software (see Section 2 of | ||
| * the Spine Software License Agreement), you may not (a) modify, translate, | ||
| * adapt, or develop new applications using the Spine Runtimes or otherwise | ||
| * create derivative works or improvements of the Spine Runtimes or (b) remove, | ||
| * delete, alter, or obscure any trademarks or any copyright, trademark, patent, | ||
| * or other intellectual property or proprietary rights notices on or in the | ||
| * Software, including any copy thereof. Redistributions in binary or source | ||
| * form must include this license and terms. | ||
| * | ||
| * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR | ||
| * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF | ||
| * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO | ||
| * EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | ||
| * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, | ||
| * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF | ||
| * USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER | ||
| * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) | ||
| * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE | ||
| * POSSIBILITY OF SUCH DAMAGE. | ||
| *****************************************************************************/ | ||
| namespace pixi_spine.core { | ||
| export class IkConstraint implements Constraint { | ||
| data: IkConstraintData; | ||
| bones: Array<Bone>; | ||
| target: Bone; | ||
| mix = 1; | ||
| bendDirection = 0; | ||
| constructor (data: IkConstraintData, skeleton: Skeleton) { | ||
| if (data == null) throw new Error("data cannot be null."); | ||
| if (skeleton == null) throw new Error("skeleton cannot be null."); | ||
| this.data = data; | ||
| this.mix = data.mix; | ||
| this.bendDirection = data.bendDirection; | ||
| this.bones = new Array<Bone>(); | ||
| for (let i = 0; i < data.bones.length; i++) | ||
| this.bones.push(skeleton.findBone(data.bones[i].name)); | ||
| this.target = skeleton.findBone(data.target.name); | ||
| } | ||
| getOrder () { | ||
| return this.data.order; | ||
| } | ||
| apply () { | ||
| this.update(); | ||
| } | ||
| update () { | ||
| let target = this.target; | ||
| let bones = this.bones; | ||
| switch (bones.length) { | ||
| case 1: | ||
| this.apply1(bones[0], target.worldX, target.worldY, this.mix); | ||
| break; | ||
| case 2: | ||
| this.apply2(bones[0], bones[1], target.worldX, target.worldY, this.bendDirection, this.mix); | ||
| break; | ||
| } | ||
| } | ||
| /** Adjusts the bone rotation so the tip is as close to the target position as possible. The target is specified in the world | ||
| * coordinate system. */ | ||
| apply1 (bone: Bone, targetX: number, targetY: number, alpha: number) { | ||
| if (!bone.appliedValid) bone.updateAppliedTransform(); | ||
| 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; | ||
| if (bone.ascaleX < 0) rotationIK += 180; | ||
| if (rotationIK > 180) | ||
| rotationIK -= 360; | ||
| else if (rotationIK < -180) rotationIK += 360; | ||
| bone.updateWorldTransformWith(bone.ax, bone.ay, bone.arotation + rotationIK * alpha, bone.ascaleX, bone.ascaleY, bone.ashearX, | ||
| bone.ashearY); | ||
| } | ||
| /** Adjusts the parent and child bone rotations so the tip of the child is as close to the target position as possible. The | ||
| * target is specified in the world coordinate system. | ||
| * @param child A direct descendant of the parent bone. */ | ||
| apply2 (parent: Bone, child: Bone, targetX: number, targetY: number, bendDir: number, alpha: number) { | ||
| if (alpha == 0) { | ||
| child.updateWorldTransform(); | ||
| return; | ||
| } | ||
| if (!parent.appliedValid) parent.updateAppliedTransform(); | ||
| if (!child.appliedValid) child.updateAppliedTransform(); | ||
| 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; | ||
| if (psx < 0) { | ||
| psx = -psx; | ||
| os1 = 180; | ||
| s2 = -1; | ||
| } else { | ||
| os1 = 0; | ||
| s2 = 1; | ||
| } | ||
| if (psy < 0) { | ||
| psy = -psy; | ||
| s2 = -s2; | ||
| } | ||
| if (csx < 0) { | ||
| csx = -csx; | ||
| os2 = 180; | ||
| } else | ||
| os2 = 0; | ||
| 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 + pmat.tx; | ||
| cwy = c * cx + pmat.ty; | ||
| } else { | ||
| cy = child.ay; | ||
| cwx = a * cx + b * cy + pmat.tx; | ||
| cwy = c * cx + d * cy + pmat.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 - pp.tx; | ||
| y = cwy - pp.ty; | ||
| let dx = (x * d - y * b) * id - px, dy = (y * a - x * c) * id - py; | ||
| let l1 = Math.sqrt(dx * dx + dy * dy), l2 = child.data.length * csx, a1 = 0, a2 = 0; | ||
| outer: | ||
| if (u) { | ||
| l2 *= psx; | ||
| let cos = (tx * tx + ty * ty - l1 * l1 - l2 * l2) / (2 * l1 * l2); | ||
| if (cos < -1) | ||
| cos = -1; | ||
| else if (cos > 1) cos = 1; | ||
| a2 = Math.acos(cos) * bendDir; | ||
| a = l1 + l2 * cos; | ||
| b = l2 * Math.sin(a2); | ||
| a1 = Math.atan2(ty * a - tx * b, tx * a + ty * b); | ||
| } else { | ||
| a = psx * l2; | ||
| b = psy * l2; | ||
| let aa = a * a, bb = b * b, dd = tx * tx + ty * ty, ta = Math.atan2(ty, tx); | ||
| c = bb * l1 * l1 + aa * dd - aa * bb; | ||
| let c1 = -2 * bb * l1, c2 = bb - aa; | ||
| d = c1 * c1 - 4 * c2 * c; | ||
| if (d >= 0) { | ||
| let q = Math.sqrt(d); | ||
| if (c1 < 0) q = -q; | ||
| q = -(c1 + q) / 2; | ||
| let r0 = q / c2, r1 = c / q; | ||
| let r = Math.abs(r0) < Math.abs(r1) ? r0 : r1; | ||
| if (r * r <= dd) { | ||
| y = Math.sqrt(dd - r * r) * bendDir; | ||
| a1 = ta - Math.atan2(y, r); | ||
| a2 = Math.atan2(y / psy, (r - l1) / psx); | ||
| break outer; | ||
| } | ||
| } | ||
| 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; | ||
| } | ||
| } | ||
| if (dd <= (minDist + maxDist) / 2) { | ||
| a1 = ta - Math.atan2(minY * bendDir, minX); | ||
| a2 = minAngle * bendDir; | ||
| } else { | ||
| a1 = ta - Math.atan2(maxY * bendDir, maxX); | ||
| a2 = maxAngle * bendDir; | ||
| } | ||
| } | ||
| let os = Math.atan2(cy, cx) * s2; | ||
| let rotation = parent.arotation; | ||
| a1 = (a1 - os) * MathUtils.radDeg + os1 - rotation; | ||
| if (a1 > 180) | ||
| a1 -= 360; | ||
| else if (a1 < -180) a1 += 360; | ||
| parent.updateWorldTransformWith(px, py, rotation + a1 * alpha, parent.ascaleX, parent.ascaleY, 0, 0); | ||
| rotation = child.arotation; | ||
| a2 = ((a2 + os) * MathUtils.radDeg - child.ashearX) * s2 + os2 - rotation; | ||
| if (a2 > 180) | ||
| a2 -= 360; | ||
| else if (a2 < -180) a2 += 360; | ||
| child.updateWorldTransformWith(cx, cy, rotation + a2 * alpha, child.ascaleX, child.ascaleY, child.ashearX, child.ashearY); | ||
| } | ||
| } | ||
| } |
| /****************************************************************************** | ||
| * Spine Runtimes Software License | ||
| * Version 2.5 | ||
| * | ||
| * Copyright (c) 2013-2016, Esoteric Software | ||
| * All rights reserved. | ||
| * | ||
| * You are granted a perpetual, non-exclusive, non-sublicensable, and | ||
| * non-transferable license to use, install, execute, and perform the Spine | ||
| * Runtimes software and derivative works solely for personal or internal | ||
| * use. Without the written permission of Esoteric Software (see Section 2 of | ||
| * the Spine Software License Agreement), you may not (a) modify, translate, | ||
| * adapt, or develop new applications using the Spine Runtimes or otherwise | ||
| * create derivative works or improvements of the Spine Runtimes or (b) remove, | ||
| * delete, alter, or obscure any trademarks or any copyright, trademark, patent, | ||
| * or other intellectual property or proprietary rights notices on or in the | ||
| * Software, including any copy thereof. Redistributions in binary or source | ||
| * form must include this license and terms. | ||
| * | ||
| * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR | ||
| * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF | ||
| * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO | ||
| * EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | ||
| * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, | ||
| * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF | ||
| * USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER | ||
| * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) | ||
| * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE | ||
| * POSSIBILITY OF SUCH DAMAGE. | ||
| *****************************************************************************/ | ||
| namespace pixi_spine.core { | ||
| export class IkConstraintData { | ||
| name: string; | ||
| order = 0; | ||
| bones = new Array<BoneData>(); | ||
| target: BoneData; | ||
| bendDirection = 1; | ||
| mix = 1; | ||
| constructor(name: string) { | ||
| this.name = name; | ||
| } | ||
| } | ||
| } |
| /****************************************************************************** | ||
| * Spine Runtimes Software License | ||
| * Version 2.5 | ||
| * | ||
| * Copyright (c) 2013-2016, Esoteric Software | ||
| * All rights reserved. | ||
| * | ||
| * You are granted a perpetual, non-exclusive, non-sublicensable, and | ||
| * non-transferable license to use, install, execute, and perform the Spine | ||
| * Runtimes software and derivative works solely for personal or internal | ||
| * use. Without the written permission of Esoteric Software (see Section 2 of | ||
| * the Spine Software License Agreement), you may not (a) modify, translate, | ||
| * adapt, or develop new applications using the Spine Runtimes or otherwise | ||
| * create derivative works or improvements of the Spine Runtimes or (b) remove, | ||
| * delete, alter, or obscure any trademarks or any copyright, trademark, patent, | ||
| * or other intellectual property or proprietary rights notices on or in the | ||
| * Software, including any copy thereof. Redistributions in binary or source | ||
| * form must include this license and terms. | ||
| * | ||
| * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR | ||
| * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF | ||
| * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO | ||
| * EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | ||
| * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, | ||
| * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF | ||
| * USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER | ||
| * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) | ||
| * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE | ||
| * POSSIBILITY OF SUCH DAMAGE. | ||
| *****************************************************************************/ | ||
| namespace pixi_spine.core { | ||
| export class PathConstraint implements Constraint { | ||
| static NONE = -1; static BEFORE = -2; static AFTER = -3; | ||
| data: PathConstraintData; | ||
| bones: Array<Bone>; | ||
| target: Slot; | ||
| 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>(); | ||
| segments = new Array<number>(); | ||
| constructor (data: PathConstraintData, skeleton: Skeleton) { | ||
| if (data == null) throw new Error("data cannot be null."); | ||
| if (skeleton == null) throw new Error("skeleton cannot be null."); | ||
| this.data = data; | ||
| this.bones = new Array<Bone>(); | ||
| for (let i = 0, n = data.bones.length; i < n; i++) | ||
| this.bones.push(skeleton.findBone(data.bones[i].name)); | ||
| this.target = skeleton.findSlot(data.target.name); | ||
| this.position = data.position; | ||
| this.spacing = data.spacing; | ||
| this.rotateMix = data.rotateMix; | ||
| this.translateMix = data.translateMix; | ||
| } | ||
| apply () { | ||
| this.update(); | ||
| } | ||
| update () { | ||
| let attachment = this.target.getAttachment(); | ||
| if (!(attachment instanceof PathAttachment)) return; | ||
| let rotateMix = this.rotateMix, translateMix = this.translateMix; | ||
| let translate = translateMix > 0, rotate = rotateMix > 0; | ||
| if (!translate && !rotate) return; | ||
| let data = this.data; | ||
| let spacingMode = data.spacingMode; | ||
| let lengthSpacing = spacingMode == SpacingMode.Length; | ||
| let rotateMode = data.rotateMode; | ||
| let tangents = rotateMode == RotateMode.Tangent, scale = rotateMode == RotateMode.ChainScale; | ||
| let boneCount = this.bones.length, spacesCount = tangents ? boneCount : boneCount + 1; | ||
| let bones = this.bones; | ||
| let spaces = Utils.setArraySize(this.spaces, spacesCount), lengths: Array<number> = null; | ||
| let spacing = this.spacing; | ||
| if (scale || lengthSpacing) { | ||
| if (scale) lengths = Utils.setArraySize(this.lengths, boneCount); | ||
| for (let i = 0, n = spacesCount - 1; i < n;) { | ||
| let bone = bones[i]; | ||
| 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 ? setupLength + spacing : spacing) * length / setupLength; | ||
| } | ||
| } else { | ||
| for (let i = 1; i < spacesCount; i++) | ||
| spaces[i] = spacing; | ||
| } | ||
| let positions = this.computeWorldPositions(<PathAttachment>attachment, spacesCount, tangents, | ||
| data.positionMode == PositionMode.Percent, spacingMode == SpacingMode.Percent); | ||
| let boneX = positions[0], boneY = positions[1], offsetRotation = data.offsetRotation; | ||
| let tip = false; | ||
| if (offsetRotation == 0) | ||
| tip = rotateMode == RotateMode.Chain; | ||
| else { | ||
| tip = false; | ||
| 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 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; | ||
| if (scale) { | ||
| let length = lengths[i]; | ||
| if (length != 0) { | ||
| let s = (Math.sqrt(dx * dx + dy * dy) / length - 1) * rotateMix + 1; | ||
| mat.a *= s; | ||
| mat.b *= s; | ||
| } | ||
| } | ||
| boneX = x; | ||
| boneY = y; | ||
| if (rotate) { | ||
| let a = mat.a, b = mat.c, c = mat.b, d = mat.d, r = 0, cos = 0, sin = 0; | ||
| if (tangents) | ||
| r = positions[p - 1]; | ||
| else if (spaces[i + 1] == 0) | ||
| r = positions[p + 2]; | ||
| else | ||
| r = Math.atan2(dy, dx); | ||
| r -= Math.atan2(c, a); | ||
| if (tip) { | ||
| cos = Math.cos(r); | ||
| sin = Math.sin(r); | ||
| let length = bone.data.length; | ||
| boneX += (length * (cos * a - sin * c) - dx) * rotateMix; | ||
| boneY += (length * (sin * a + cos * c) - dy) * rotateMix; | ||
| } else { | ||
| r += offsetRotation; | ||
| } | ||
| if (r > MathUtils.PI) | ||
| r -= MathUtils.PI2; | ||
| else if (r < -MathUtils.PI) // | ||
| r += MathUtils.PI2; | ||
| r *= rotateMix; | ||
| 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; | ||
| } | ||
| bone.appliedValid = false; | ||
| } | ||
| } | ||
| computeWorldPositions (path: PathAttachment, spacesCount: number, tangents: boolean, percentPosition: boolean, | ||
| percentSpacing: boolean) { | ||
| let target = this.target; | ||
| let position = this.position; | ||
| let spaces = this.spaces, out = Utils.setArraySize(this.positions, spacesCount * 3 + 2), world: Array<number> = null; | ||
| let closed = path.closed; | ||
| let verticesLength = path.worldVerticesLength, curveCount = verticesLength / 6, prevCurve = PathConstraint.NONE; | ||
| if (!path.constantSpeed) { | ||
| let lengths = path.lengths; | ||
| curveCount -= closed ? 1 : 2; | ||
| let pathLength = lengths[curveCount]; | ||
| if (percentPosition) position *= pathLength; | ||
| if (percentSpacing) { | ||
| for (let i = 0; i < spacesCount; i++) | ||
| spaces[i] *= pathLength; | ||
| } | ||
| world = Utils.setArraySize(this.world, 8); | ||
| for (let i = 0, o = 0, curve = 0; i < spacesCount; i++, o += 3) { | ||
| let space = spaces[i]; | ||
| position += space; | ||
| let p = position; | ||
| if (closed) { | ||
| p %= pathLength; | ||
| if (p < 0) p += pathLength; | ||
| curve = 0; | ||
| } else if (p < 0) { | ||
| if (prevCurve != PathConstraint.BEFORE) { | ||
| prevCurve = PathConstraint.BEFORE; | ||
| path.computeWorldVertices(target, 2, 4, world, 0, 2); | ||
| } | ||
| this.addBeforePosition(p, world, 0, out, o); | ||
| continue; | ||
| } else if (p > pathLength) { | ||
| if (prevCurve != PathConstraint.AFTER) { | ||
| prevCurve = PathConstraint.AFTER; | ||
| path.computeWorldVertices(target, verticesLength - 6, 4, world, 0, 2); | ||
| } | ||
| this.addAfterPosition(p - pathLength, world, 0, out, o); | ||
| continue; | ||
| } | ||
| // Determine curve containing position. | ||
| for (;; curve++) { | ||
| let length = lengths[curve]; | ||
| if (p > length) continue; | ||
| if (curve == 0) | ||
| p /= length; | ||
| else { | ||
| let prev = lengths[curve - 1]; | ||
| p = (p - prev) / (length - prev); | ||
| } | ||
| break; | ||
| } | ||
| if (curve != prevCurve) { | ||
| prevCurve = curve; | ||
| if (closed && curve == curveCount) { | ||
| path.computeWorldVertices(target, verticesLength - 4, 4, world, 0, 2); | ||
| path.computeWorldVertices(target, 0, 4, world, 4, 2); | ||
| } else | ||
| path.computeWorldVertices(target, curve * 6 + 2, 8, world, 0, 2); | ||
| } | ||
| this.addCurvePosition(p, world[0], world[1], world[2], world[3], world[4], world[5], world[6], world[7], out, o, | ||
| tangents || (i > 0 && space == 0)); | ||
| } | ||
| return out; | ||
| } | ||
| // World vertices. | ||
| if (closed) { | ||
| verticesLength += 2; | ||
| world = Utils.setArraySize(this.world, verticesLength); | ||
| path.computeWorldVertices(target, 2, verticesLength - 4, world, 0, 2); | ||
| path.computeWorldVertices(target, 0, 2, world, verticesLength - 4, 2); | ||
| world[verticesLength - 2] = world[0]; | ||
| world[verticesLength - 1] = world[1]; | ||
| } else { | ||
| curveCount--; | ||
| verticesLength -= 4; | ||
| world = Utils.setArraySize(this.world, verticesLength); | ||
| path.computeWorldVertices(target, 2, verticesLength, world, 0, 2); | ||
| } | ||
| // Curve lengths. | ||
| let curves = Utils.setArraySize(this.curves, curveCount); | ||
| let pathLength = 0; | ||
| let x1 = world[0], y1 = world[1], cx1 = 0, cy1 = 0, cx2 = 0, cy2 = 0, x2 = 0, y2 = 0; | ||
| let tmpx = 0, tmpy = 0, dddfx = 0, dddfy = 0, ddfx = 0, ddfy = 0, dfx = 0, dfy = 0; | ||
| for (let i = 0, w = 2; i < curveCount; i++, w += 6) { | ||
| cx1 = world[w]; | ||
| cy1 = world[w + 1]; | ||
| cx2 = world[w + 2]; | ||
| cy2 = world[w + 3]; | ||
| x2 = world[w + 4]; | ||
| y2 = world[w + 5]; | ||
| tmpx = (x1 - cx1 * 2 + cx2) * 0.1875; | ||
| tmpy = (y1 - cy1 * 2 + cy2) * 0.1875; | ||
| dddfx = ((cx1 - cx2) * 3 - x1 + x2) * 0.09375; | ||
| dddfy = ((cy1 - cy2) * 3 - y1 + y2) * 0.09375; | ||
| ddfx = tmpx * 2 + dddfx; | ||
| ddfy = tmpy * 2 + dddfy; | ||
| dfx = (cx1 - x1) * 0.75 + tmpx + dddfx * 0.16666667; | ||
| dfy = (cy1 - y1) * 0.75 + tmpy + dddfy * 0.16666667; | ||
| pathLength += Math.sqrt(dfx * dfx + dfy * dfy); | ||
| dfx += ddfx; | ||
| dfy += ddfy; | ||
| ddfx += dddfx; | ||
| ddfy += dddfy; | ||
| pathLength += Math.sqrt(dfx * dfx + dfy * dfy); | ||
| dfx += ddfx; | ||
| dfy += ddfy; | ||
| pathLength += Math.sqrt(dfx * dfx + dfy * dfy); | ||
| dfx += ddfx + dddfx; | ||
| dfy += ddfy + dddfy; | ||
| pathLength += Math.sqrt(dfx * dfx + dfy * dfy); | ||
| curves[i] = pathLength; | ||
| x1 = x2; | ||
| y1 = y2; | ||
| } | ||
| if (percentPosition) position *= pathLength; | ||
| if (percentSpacing) { | ||
| for (let i = 0; i < spacesCount; i++) | ||
| spaces[i] *= pathLength; | ||
| } | ||
| let segments = this.segments; | ||
| let curveLength = 0; | ||
| for (let i = 0, o = 0, curve = 0, segment = 0; i < spacesCount; i++, o += 3) { | ||
| let space = spaces[i]; | ||
| position += space; | ||
| let p = position; | ||
| if (closed) { | ||
| p %= pathLength; | ||
| if (p < 0) p += pathLength; | ||
| curve = 0; | ||
| } else if (p < 0) { | ||
| this.addBeforePosition(p, world, 0, out, o); | ||
| continue; | ||
| } else if (p > pathLength) { | ||
| this.addAfterPosition(p - pathLength, world, verticesLength - 4, out, o); | ||
| continue; | ||
| } | ||
| // Determine curve containing position. | ||
| for (;; curve++) { | ||
| let length = curves[curve]; | ||
| if (p > length) continue; | ||
| if (curve == 0) | ||
| p /= length; | ||
| else { | ||
| let prev = curves[curve - 1]; | ||
| p = (p - prev) / (length - prev); | ||
| } | ||
| break; | ||
| } | ||
| // Curve segment lengths. | ||
| if (curve != prevCurve) { | ||
| prevCurve = curve; | ||
| let ii = curve * 6; | ||
| x1 = world[ii]; | ||
| y1 = world[ii + 1]; | ||
| cx1 = world[ii + 2]; | ||
| cy1 = world[ii + 3]; | ||
| cx2 = world[ii + 4]; | ||
| cy2 = world[ii + 5]; | ||
| x2 = world[ii + 6]; | ||
| y2 = world[ii + 7]; | ||
| tmpx = (x1 - cx1 * 2 + cx2) * 0.03; | ||
| tmpy = (y1 - cy1 * 2 + cy2) * 0.03; | ||
| dddfx = ((cx1 - cx2) * 3 - x1 + x2) * 0.006; | ||
| dddfy = ((cy1 - cy2) * 3 - y1 + y2) * 0.006; | ||
| ddfx = tmpx * 2 + dddfx; | ||
| ddfy = tmpy * 2 + dddfy; | ||
| dfx = (cx1 - x1) * 0.3 + tmpx + dddfx * 0.16666667; | ||
| dfy = (cy1 - y1) * 0.3 + tmpy + dddfy * 0.16666667; | ||
| curveLength = Math.sqrt(dfx * dfx + dfy * dfy); | ||
| segments[0] = curveLength; | ||
| for (ii = 1; ii < 8; ii++) { | ||
| dfx += ddfx; | ||
| dfy += ddfy; | ||
| ddfx += dddfx; | ||
| ddfy += dddfy; | ||
| curveLength += Math.sqrt(dfx * dfx + dfy * dfy); | ||
| segments[ii] = curveLength; | ||
| } | ||
| dfx += ddfx; | ||
| dfy += ddfy; | ||
| curveLength += Math.sqrt(dfx * dfx + dfy * dfy); | ||
| segments[8] = curveLength; | ||
| dfx += ddfx + dddfx; | ||
| dfy += ddfy + dddfy; | ||
| curveLength += Math.sqrt(dfx * dfx + dfy * dfy); | ||
| segments[9] = curveLength; | ||
| segment = 0; | ||
| } | ||
| // Weight by segment length. | ||
| p *= curveLength; | ||
| for (;; segment++) { | ||
| let length = segments[segment]; | ||
| if (p > length) continue; | ||
| if (segment == 0) | ||
| p /= length; | ||
| else { | ||
| let prev = segments[segment - 1]; | ||
| p = segment + (p - prev) / (length - prev); | ||
| } | ||
| break; | ||
| } | ||
| this.addCurvePosition(p * 0.1, x1, y1, cx1, cy1, cx2, cy2, x2, y2, out, o, tangents || (i > 0 && space == 0)); | ||
| } | ||
| return out; | ||
| } | ||
| 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); | ||
| out[o] = x1 + p * Math.cos(r); | ||
| out[o + 1] = y1 + p * Math.sin(r); | ||
| out[o + 2] = r; | ||
| } | ||
| 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); | ||
| out[o] = x1 + p * Math.cos(r); | ||
| out[o + 1] = y1 + p * Math.sin(r); | ||
| out[o + 2] = 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) { | ||
| if (p == 0 || isNaN(p)) p = 0.0001; | ||
| let tt = p * p, ttt = tt * p, u = 1 - p, uu = u * u, uuu = uu * u; | ||
| let ut = u * p, ut3 = ut * 3, uut3 = u * ut3, utt3 = ut3 * p; | ||
| let x = x1 * uuu + cx1 * uut3 + cx2 * utt3 + x2 * ttt, y = y1 * uuu + cy1 * uut3 + cy2 * utt3 + y2 * ttt; | ||
| out[o] = x; | ||
| out[o + 1] = y; | ||
| if (tangents) out[o + 2] = Math.atan2(y - (y1 * uu + cy1 * ut * 2 + cy2 * tt), x - (x1 * uu + cx1 * ut * 2 + cx2 * tt)); | ||
| } | ||
| getOrder () { | ||
| return this.data.order; | ||
| } | ||
| } | ||
| } |
| /****************************************************************************** | ||
| * Spine Runtimes Software License | ||
| * Version 2.5 | ||
| * | ||
| * Copyright (c) 2013-2016, Esoteric Software | ||
| * All rights reserved. | ||
| * | ||
| * You are granted a perpetual, non-exclusive, non-sublicensable, and | ||
| * non-transferable license to use, install, execute, and perform the Spine | ||
| * Runtimes software and derivative works solely for personal or internal | ||
| * use. Without the written permission of Esoteric Software (see Section 2 of | ||
| * the Spine Software License Agreement), you may not (a) modify, translate, | ||
| * adapt, or develop new applications using the Spine Runtimes or otherwise | ||
| * create derivative works or improvements of the Spine Runtimes or (b) remove, | ||
| * delete, alter, or obscure any trademarks or any copyright, trademark, patent, | ||
| * or other intellectual property or proprietary rights notices on or in the | ||
| * Software, including any copy thereof. Redistributions in binary or source | ||
| * form must include this license and terms. | ||
| * | ||
| * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR | ||
| * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF | ||
| * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO | ||
| * EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | ||
| * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, | ||
| * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF | ||
| * USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER | ||
| * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) | ||
| * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE | ||
| * POSSIBILITY OF SUCH DAMAGE. | ||
| *****************************************************************************/ | ||
| namespace pixi_spine.core { | ||
| export class PathConstraintData { | ||
| name: string; | ||
| order = 0; | ||
| bones = new Array<BoneData>(); | ||
| target: SlotData; | ||
| positionMode: PositionMode; | ||
| spacingMode: SpacingMode; | ||
| rotateMode: RotateMode; | ||
| offsetRotation: number; | ||
| position: number; | ||
| spacing: number; | ||
| rotateMix: number; | ||
| translateMix: number; | ||
| constructor(name: string) { | ||
| this.name = name; | ||
| } | ||
| } | ||
| export enum PositionMode { | ||
| Fixed, Percent | ||
| } | ||
| export enum SpacingMode { | ||
| Length, Fixed, Percent | ||
| } | ||
| export enum RotateMode { | ||
| Tangent, Chain, ChainScale | ||
| } | ||
| } |
| /****************************************************************************** | ||
| * Spine Runtimes Software License | ||
| * Version 2.5 | ||
| * | ||
| * Copyright (c) 2013-2016, Esoteric Software | ||
| * All rights reserved. | ||
| * | ||
| * You are granted a perpetual, non-exclusive, non-sublicensable, and | ||
| * non-transferable license to use, install, execute, and perform the Spine | ||
| * Runtimes software and derivative works solely for personal or internal | ||
| * use. Without the written permission of Esoteric Software (see Section 2 of | ||
| * the Spine Software License Agreement), you may not (a) modify, translate, | ||
| * adapt, or develop new applications using the Spine Runtimes or otherwise | ||
| * create derivative works or improvements of the Spine Runtimes or (b) remove, | ||
| * delete, alter, or obscure any trademarks or any copyright, trademark, patent, | ||
| * or other intellectual property or proprietary rights notices on or in the | ||
| * Software, including any copy thereof. Redistributions in binary or source | ||
| * form must include this license and terms. | ||
| * | ||
| * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR | ||
| * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF | ||
| * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO | ||
| * EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | ||
| * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, | ||
| * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF | ||
| * USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER | ||
| * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) | ||
| * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE | ||
| * POSSIBILITY OF SUCH DAMAGE. | ||
| *****************************************************************************/ | ||
| namespace pixi_spine.core { | ||
| export class Skeleton { | ||
| data: SkeletonData; | ||
| bones: Array<Bone>; | ||
| slots: Array<Slot>; | ||
| drawOrder: Array<Slot>; | ||
| ikConstraints: Array<IkConstraint>; | ||
| transformConstraints: Array<TransformConstraint>; | ||
| pathConstraints: Array<PathConstraint>; | ||
| _updateCache = new Array<Updatable>(); | ||
| updateCacheReset = new Array<Updatable>(); | ||
| skin: Skin; | ||
| color: Color; | ||
| time = 0; | ||
| flipX = false; flipY = false; | ||
| x = 0; y = 0; | ||
| constructor (data: SkeletonData) { | ||
| if (data == null) throw new Error("data cannot be null."); | ||
| this.data = data; | ||
| this.bones = new Array<Bone>(); | ||
| for (let i = 0; i < data.bones.length; i++) { | ||
| let boneData = data.bones[i]; | ||
| let bone: Bone; | ||
| if (boneData.parent == null) | ||
| bone = new Bone(boneData, this, null); | ||
| else { | ||
| let parent = this.bones[boneData.parent.index]; | ||
| bone = new Bone(boneData, this, parent); | ||
| parent.children.push(bone); | ||
| } | ||
| this.bones.push(bone); | ||
| } | ||
| this.slots = new Array<Slot>(); | ||
| this.drawOrder = new Array<Slot>(); | ||
| for (let i = 0; i < data.slots.length; i++) { | ||
| let slotData = data.slots[i]; | ||
| let bone = this.bones[slotData.boneData.index]; | ||
| let slot = new Slot(slotData, bone); | ||
| this.slots.push(slot); | ||
| this.drawOrder.push(slot); | ||
| } | ||
| this.ikConstraints = new Array<IkConstraint>(); | ||
| for (let i = 0; i < data.ikConstraints.length; i++) { | ||
| let ikConstraintData = data.ikConstraints[i]; | ||
| this.ikConstraints.push(new IkConstraint(ikConstraintData, this)); | ||
| } | ||
| this.transformConstraints = new Array<TransformConstraint>(); | ||
| for (let i = 0; i < data.transformConstraints.length; i++) { | ||
| let transformConstraintData = data.transformConstraints[i]; | ||
| this.transformConstraints.push(new TransformConstraint(transformConstraintData, this)); | ||
| } | ||
| this.pathConstraints = new Array<PathConstraint>(); | ||
| for (let i = 0; i < data.pathConstraints.length; i++) { | ||
| let pathConstraintData = data.pathConstraints[i]; | ||
| this.pathConstraints.push(new PathConstraint(pathConstraintData, this)); | ||
| } | ||
| this.color = new Color(1, 1, 1, 1); | ||
| this.updateCache(); | ||
| } | ||
| updateCache () { | ||
| let updateCache = this._updateCache; | ||
| updateCache.length = 0; | ||
| this.updateCacheReset.length = 0; | ||
| let bones = this.bones; | ||
| for (let i = 0, n = bones.length; i < n; i++) | ||
| bones[i].sorted = false; | ||
| // IK first, lowest hierarchy depth first. | ||
| let ikConstraints = this.ikConstraints; | ||
| let transformConstraints = this.transformConstraints; | ||
| let pathConstraints = this.pathConstraints; | ||
| let ikCount = ikConstraints.length, transformCount = transformConstraints.length, pathCount = pathConstraints.length; | ||
| let constraintCount = ikCount + transformCount + pathCount; | ||
| outer: | ||
| for (let i = 0; i < constraintCount; i++) { | ||
| for (let ii = 0; ii < ikCount; ii++) { | ||
| let constraint = ikConstraints[ii]; | ||
| if (constraint.data.order == i) { | ||
| this.sortIkConstraint(constraint); | ||
| continue outer; | ||
| } | ||
| } | ||
| for (let ii = 0; ii < transformCount; ii++) { | ||
| let constraint = transformConstraints[ii]; | ||
| if (constraint.data.order == i) { | ||
| this.sortTransformConstraint(constraint); | ||
| continue outer; | ||
| } | ||
| } | ||
| for (let ii = 0; ii < pathCount; ii++) { | ||
| let constraint = pathConstraints[ii]; | ||
| if (constraint.data.order == i) { | ||
| this.sortPathConstraint(constraint); | ||
| continue outer; | ||
| } | ||
| } | ||
| } | ||
| for (let i = 0, n = bones.length; i < n; i++) | ||
| this.sortBone(bones[i]); | ||
| } | ||
| sortIkConstraint (constraint: IkConstraint) { | ||
| let target = constraint.target; | ||
| this.sortBone(target); | ||
| let constrained = constraint.bones; | ||
| let parent = constrained[0]; | ||
| this.sortBone(parent); | ||
| if (constrained.length > 1) { | ||
| let child = constrained[constrained.length - 1]; | ||
| if (!(this._updateCache.indexOf(child) > -1)) this.updateCacheReset.push(child); | ||
| } | ||
| this._updateCache.push(constraint); | ||
| this.sortReset(parent.children); | ||
| constrained[constrained.length - 1].sorted = true; | ||
| } | ||
| sortPathConstraint (constraint: PathConstraint) { | ||
| let slot = constraint.target; | ||
| let slotIndex = slot.data.index; | ||
| let slotBone = slot.bone; | ||
| if (this.skin != null) this.sortPathConstraintAttachment(this.skin, slotIndex, slotBone); | ||
| if (this.data.defaultSkin != null && this.data.defaultSkin != this.skin) | ||
| this.sortPathConstraintAttachment(this.data.defaultSkin, slotIndex, slotBone); | ||
| for (let i = 0, n = this.data.skins.length; i < n; i++) | ||
| this.sortPathConstraintAttachment(this.data.skins[i], slotIndex, slotBone); | ||
| let attachment = slot.getAttachment(); | ||
| if (attachment instanceof PathAttachment) this.sortPathConstraintAttachmentWith(attachment, slotBone); | ||
| let constrained = constraint.bones; | ||
| let boneCount = constrained.length; | ||
| for (let i = 0; i < boneCount; i++) | ||
| this.sortBone(constrained[i]); | ||
| this._updateCache.push(constraint); | ||
| 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) { | ||
| this.sortBone(constraint.target); | ||
| let constrained = constraint.bones; | ||
| let boneCount = constrained.length; | ||
| 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]); | ||
| } | ||
| } | ||
| 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; | ||
| } | ||
| sortPathConstraintAttachment (skin: Skin, slotIndex: number, slotBone: Bone) { | ||
| let attachments = skin.attachments[slotIndex]; | ||
| if (!attachments) return; | ||
| for (let key in attachments) { | ||
| this.sortPathConstraintAttachmentWith(attachments[key], slotBone); | ||
| } | ||
| } | ||
| sortPathConstraintAttachmentWith (attachment: Attachment, slotBone: Bone) { | ||
| if (!(attachment instanceof PathAttachment)) return; | ||
| let pathBones = (<PathAttachment>attachment).bones; | ||
| if (pathBones == null) | ||
| this.sortBone(slotBone); | ||
| else { | ||
| let bones = this.bones; | ||
| let i = 0; | ||
| while (i < pathBones.length) { | ||
| let boneCount = pathBones[i++]; | ||
| for (let n = i + boneCount; i < n; i++) { | ||
| let boneIndex = pathBones[i]; | ||
| this.sortBone(bones[boneIndex]); | ||
| } | ||
| } | ||
| } | ||
| } | ||
| sortBone (bone: Bone) { | ||
| if (bone.sorted) return; | ||
| let parent = bone.parent; | ||
| if (parent != null) this.sortBone(parent); | ||
| bone.sorted = true; | ||
| this._updateCache.push(bone); | ||
| } | ||
| sortReset (bones: Array<Bone>) { | ||
| for (let i = 0, n = bones.length; i < n; i++) { | ||
| let bone = bones[i]; | ||
| if (bone.sorted) this.sortReset(bone.children); | ||
| bone.sorted = false; | ||
| } | ||
| } | ||
| /** Updates the world transform for each bone and applies constraints. */ | ||
| updateWorldTransform () { | ||
| let updateCacheReset = this.updateCacheReset; | ||
| for (let i = 0, n = updateCacheReset.length; i < n; i++) { | ||
| let bone = updateCacheReset[i] as Bone; | ||
| bone.ax = bone.x; | ||
| bone.ay = bone.y; | ||
| bone.arotation = bone.rotation; | ||
| bone.ascaleX = bone.scaleX; | ||
| bone.ascaleY = bone.scaleY; | ||
| bone.ashearX = bone.shearX; | ||
| bone.ashearY = bone.shearY; | ||
| bone.appliedValid = true; | ||
| } | ||
| let updateCache = this._updateCache; | ||
| for (let i = 0, n = updateCache.length; i < n; i++) | ||
| updateCache[i].update(); | ||
| } | ||
| /** Sets the bones, constraints, and slots to their setup pose values. */ | ||
| setToSetupPose () { | ||
| this.setBonesToSetupPose(); | ||
| this.setSlotsToSetupPose(); | ||
| } | ||
| /** Sets the bones and constraints to their setup pose values. */ | ||
| setBonesToSetupPose () { | ||
| let bones = this.bones; | ||
| for (let i = 0, n = bones.length; i < n; i++) | ||
| bones[i].setToSetupPose(); | ||
| let ikConstraints = this.ikConstraints; | ||
| for (let i = 0, n = ikConstraints.length; i < n; i++) { | ||
| let constraint = ikConstraints[i]; | ||
| constraint.bendDirection = constraint.data.bendDirection; | ||
| constraint.mix = constraint.data.mix; | ||
| } | ||
| let transformConstraints = this.transformConstraints; | ||
| for (let i = 0, n = transformConstraints.length; i < n; i++) { | ||
| let constraint = transformConstraints[i]; | ||
| let data = constraint.data; | ||
| constraint.rotateMix = data.rotateMix; | ||
| constraint.translateMix = data.translateMix; | ||
| constraint.scaleMix = data.scaleMix; | ||
| constraint.shearMix = data.shearMix; | ||
| } | ||
| let pathConstraints = this.pathConstraints; | ||
| for (let i = 0, n = pathConstraints.length; i < n; i++) { | ||
| let constraint = pathConstraints[i]; | ||
| let data = constraint.data; | ||
| constraint.position = data.position; | ||
| constraint.spacing = data.spacing; | ||
| constraint.rotateMix = data.rotateMix; | ||
| constraint.translateMix = data.translateMix; | ||
| } | ||
| } | ||
| setSlotsToSetupPose () { | ||
| let slots = this.slots; | ||
| Utils.arrayCopy(slots, 0, this.drawOrder, 0, slots.length); | ||
| for (let i = 0, n = slots.length; i < n; i++) | ||
| slots[i].setToSetupPose(); | ||
| } | ||
| /** @return May return null. */ | ||
| getRootBone () { | ||
| if (this.bones.length == 0) return null; | ||
| return this.bones[0]; | ||
| } | ||
| /** @return May be null. */ | ||
| findBone (boneName: string) { | ||
| if (boneName == null) throw new Error("boneName cannot be null."); | ||
| let bones = this.bones; | ||
| for (let i = 0, n = bones.length; i < n; i++) { | ||
| let bone = bones[i]; | ||
| if (bone.data.name == boneName) return bone; | ||
| } | ||
| return null; | ||
| } | ||
| /** @return -1 if the bone was not found. */ | ||
| findBoneIndex (boneName: string) { | ||
| if (boneName == null) throw new Error("boneName cannot be null."); | ||
| let bones = this.bones; | ||
| for (let i = 0, n = bones.length; i < n; i++) | ||
| if (bones[i].data.name == boneName) return i; | ||
| return -1; | ||
| } | ||
| /** @return May be null. */ | ||
| findSlot (slotName: string) { | ||
| if (slotName == null) throw new Error("slotName cannot be null."); | ||
| let slots = this.slots; | ||
| for (let i = 0, n = slots.length; i < n; i++) { | ||
| let slot = slots[i]; | ||
| if (slot.data.name == slotName) return slot; | ||
| } | ||
| return null; | ||
| } | ||
| /** @return -1 if the bone was not found. */ | ||
| findSlotIndex (slotName: string) { | ||
| if (slotName == null) throw new Error("slotName cannot be null."); | ||
| let slots = this.slots; | ||
| for (let i = 0, n = slots.length; i < n; i++) | ||
| if (slots[i].data.name == slotName) return i; | ||
| return -1; | ||
| } | ||
| /** Sets a skin by name. | ||
| * @see #setSkin(Skin) */ | ||
| setSkinByName (skinName: string) { | ||
| let skin = this.data.findSkin(skinName); | ||
| if (skin == null) throw new Error("Skin not found: " + skinName); | ||
| this.setSkin(skin); | ||
| } | ||
| /** Sets the skin used to look up attachments before looking in the {@link SkeletonData#getDefaultSkin() default skin}. | ||
| * Attachments from the new skin are attached if the corresponding attachment from the old skin was attached. If there was no | ||
| * old skin, each slot's setup mode attachment is attached from the new skin. | ||
| * @param newSkin May be null. */ | ||
| setSkin (newSkin: Skin) { | ||
| if (newSkin != null) { | ||
| if (this.skin != null) | ||
| newSkin.attachAll(this, this.skin); | ||
| else { | ||
| let slots = this.slots; | ||
| for (let i = 0, n = slots.length; i < n; i++) { | ||
| let slot = slots[i]; | ||
| let name = slot.data.attachmentName; | ||
| if (name != null) { | ||
| let attachment: Attachment = newSkin.getAttachment(i, name); | ||
| if (attachment != null) slot.setAttachment(attachment); | ||
| } | ||
| } | ||
| } | ||
| } | ||
| this.skin = newSkin; | ||
| } | ||
| /** @return May be null. */ | ||
| getAttachmentByName (slotName: string, attachmentName: string): Attachment { | ||
| return this.getAttachment(this.data.findSlotIndex(slotName), attachmentName); | ||
| } | ||
| /** @return May be null. */ | ||
| getAttachment (slotIndex: number, attachmentName: string): Attachment { | ||
| if (attachmentName == null) throw new Error("attachmentName cannot be null."); | ||
| if (this.skin != null) { | ||
| let attachment: Attachment = this.skin.getAttachment(slotIndex, attachmentName); | ||
| if (attachment != null) return attachment; | ||
| } | ||
| if (this.data.defaultSkin != null) return this.data.defaultSkin.getAttachment(slotIndex, attachmentName); | ||
| return null; | ||
| } | ||
| /** @param attachmentName May be null. */ | ||
| setAttachment (slotName: string, attachmentName: string) { | ||
| if (slotName == null) throw new Error("slotName cannot be null."); | ||
| let slots = this.slots; | ||
| for (let i = 0, n = slots.length; i < n; i++) { | ||
| let slot = slots[i]; | ||
| if (slot.data.name == slotName) { | ||
| let attachment: Attachment = null; | ||
| if (attachmentName != null) { | ||
| attachment = this.getAttachment(i, attachmentName); | ||
| if (attachment == null) | ||
| throw new Error("Attachment not found: " + attachmentName + ", for slot: " + slotName); | ||
| } | ||
| slot.setAttachment(attachment); | ||
| return; | ||
| } | ||
| } | ||
| throw new Error("Slot not found: " + slotName); | ||
| } | ||
| /** @return May be null. */ | ||
| findIkConstraint (constraintName: string) { | ||
| if (constraintName == null) throw new Error("constraintName cannot be null."); | ||
| let ikConstraints = this.ikConstraints; | ||
| for (let i = 0, n = ikConstraints.length; i < n; i++) { | ||
| let ikConstraint = ikConstraints[i]; | ||
| if (ikConstraint.data.name == constraintName) return ikConstraint; | ||
| } | ||
| return null; | ||
| } | ||
| /** @return May be null. */ | ||
| findTransformConstraint (constraintName: string) { | ||
| if (constraintName == null) throw new Error("constraintName cannot be null."); | ||
| let transformConstraints = this.transformConstraints; | ||
| for (let i = 0, n = transformConstraints.length; i < n; i++) { | ||
| let constraint = transformConstraints[i]; | ||
| if (constraint.data.name == constraintName) return constraint; | ||
| } | ||
| return null; | ||
| } | ||
| /** @return May be null. */ | ||
| findPathConstraint (constraintName: string) { | ||
| if (constraintName == null) throw new Error("constraintName cannot be null."); | ||
| let pathConstraints = this.pathConstraints; | ||
| for (let i = 0, n = pathConstraints.length; i < n; i++) { | ||
| let constraint = pathConstraints[i]; | ||
| if (constraint.data.name == constraintName) return constraint; | ||
| } | ||
| return null; | ||
| } | ||
| /** Returns the axis aligned bounding box (AABB) of the region and mesh attachments for the current pose. | ||
| * @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. | ||
| * @param temp Working memory */ | ||
| getBounds (offset: Vector2, size: Vector2, temp: Array<number>) { | ||
| if (offset == null) throw new Error("offset cannot be null."); | ||
| if (size == null) throw new Error("size cannot be null."); | ||
| let drawOrder = this.drawOrder; | ||
| let minX = Number.POSITIVE_INFINITY, minY = Number.POSITIVE_INFINITY, maxX = Number.NEGATIVE_INFINITY, maxY = Number.NEGATIVE_INFINITY; | ||
| for (let i = 0, n = drawOrder.length; i < n; i++) { | ||
| let slot = drawOrder[i]; | ||
| let verticesLength = 0; | ||
| let vertices: ArrayLike<number> = null; | ||
| let attachment = slot.getAttachment(); | ||
| 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 += 2) { | ||
| let x = vertices[ii], y = vertices[ii + 1]; | ||
| minX = Math.min(minX, x); | ||
| minY = Math.min(minY, y); | ||
| maxX = Math.max(maxX, x); | ||
| maxY = Math.max(maxY, y); | ||
| } | ||
| } | ||
| } | ||
| offset.set(minX, minY); | ||
| size.set(maxX - minX, maxY - minY); | ||
| } | ||
| update (delta: number) { | ||
| this.time += delta; | ||
| } | ||
| } | ||
| } |
| /****************************************************************************** | ||
| * Spine Runtimes Software License | ||
| * Version 2.5 | ||
| * | ||
| * Copyright (c) 2013-2016, Esoteric Software | ||
| * All rights reserved. | ||
| * | ||
| * You are granted a perpetual, non-exclusive, non-sublicensable, and | ||
| * non-transferable license to use, install, execute, and perform the Spine | ||
| * Runtimes software and derivative works solely for personal or internal | ||
| * use. Without the written permission of Esoteric Software (see Section 2 of | ||
| * the Spine Software License Agreement), you may not (a) modify, translate, | ||
| * adapt, or develop new applications using the Spine Runtimes or otherwise | ||
| * create derivative works or improvements of the Spine Runtimes or (b) remove, | ||
| * delete, alter, or obscure any trademarks or any copyright, trademark, patent, | ||
| * or other intellectual property or proprietary rights notices on or in the | ||
| * Software, including any copy thereof. Redistributions in binary or source | ||
| * form must include this license and terms. | ||
| * | ||
| * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR | ||
| * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF | ||
| * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO | ||
| * EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | ||
| * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, | ||
| * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF | ||
| * USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER | ||
| * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) | ||
| * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE | ||
| * POSSIBILITY OF SUCH DAMAGE. | ||
| *****************************************************************************/ | ||
| namespace pixi_spine.core { | ||
| export class SkeletonBounds { | ||
| minX = 0; minY = 0; maxX = 0; maxY = 0; | ||
| boundingBoxes = new Array<BoundingBoxAttachment>(); | ||
| polygons = new Array<ArrayLike<number>>(); | ||
| private polygonPool = new Pool<ArrayLike<number>>(() => { | ||
| return Utils.newFloatArray(16); | ||
| }); | ||
| update (skeleton: Skeleton, updateAabb: boolean) { | ||
| if (skeleton == null) throw new Error("skeleton cannot be null."); | ||
| let boundingBoxes = this.boundingBoxes; | ||
| let polygons = this.polygons; | ||
| let polygonPool = this.polygonPool; | ||
| let slots = skeleton.slots; | ||
| let slotCount = slots.length; | ||
| boundingBoxes.length = 0; | ||
| polygonPool.freeAll(polygons); | ||
| polygons.length = 0; | ||
| for (let i = 0; i < slotCount; i++) { | ||
| let slot = slots[i]; | ||
| let attachment = slot.getAttachment(); | ||
| if (attachment instanceof BoundingBoxAttachment) { | ||
| let boundingBox = attachment as BoundingBoxAttachment; | ||
| boundingBoxes.push(boundingBox); | ||
| let polygon = polygonPool.obtain(); | ||
| if (polygon.length != boundingBox.worldVerticesLength) { | ||
| polygon = Utils.newFloatArray(boundingBox.worldVerticesLength); | ||
| } | ||
| polygons.push(polygon); | ||
| boundingBox.computeWorldVertices(slot, 0, boundingBox.worldVerticesLength, polygon, 0, 2); | ||
| } | ||
| } | ||
| 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 () { | ||
| let minX = Number.POSITIVE_INFINITY, minY = Number.POSITIVE_INFINITY, maxX = Number.NEGATIVE_INFINITY, maxY = Number.NEGATIVE_INFINITY; | ||
| let polygons = this.polygons; | ||
| for (let i = 0, n = polygons.length; i < n; i++) { | ||
| let polygon = polygons[i]; | ||
| let vertices = polygon; | ||
| for (let ii = 0, nn = polygon.length; ii < nn; ii += 2) { | ||
| let x = vertices[ii]; | ||
| let y = vertices[ii + 1]; | ||
| minX = Math.min(minX, x); | ||
| minY = Math.min(minY, y); | ||
| maxX = Math.max(maxX, x); | ||
| maxY = Math.max(maxY, y); | ||
| } | ||
| } | ||
| this.minX = minX; | ||
| this.minY = minY; | ||
| this.maxX = maxX; | ||
| this.maxY = maxY; | ||
| } | ||
| /** Returns true if the axis aligned bounding box contains the point. */ | ||
| aabbContainsPoint (x: number, y: number) { | ||
| return x >= this.minX && x <= this.maxX && y >= this.minY && y <= this.maxY; | ||
| } | ||
| /** Returns true if the axis aligned bounding box intersects the line segment. */ | ||
| aabbIntersectsSegment (x1: number, y1: number, x2: number, y2: number) { | ||
| let minX = this.minX; | ||
| let minY = this.minY; | ||
| let maxX = this.maxX; | ||
| let maxY = this.maxY; | ||
| if ((x1 <= minX && x2 <= minX) || (y1 <= minY && y2 <= minY) || (x1 >= maxX && x2 >= maxX) || (y1 >= maxY && y2 >= maxY)) | ||
| return false; | ||
| let m = (y2 - y1) / (x2 - x1); | ||
| let y = m * (minX - x1) + y1; | ||
| if (y > minY && y < maxY) return true; | ||
| y = m * (maxX - x1) + y1; | ||
| if (y > minY && y < maxY) return true; | ||
| let x = (minY - y1) / m + x1; | ||
| if (x > minX && x < maxX) return true; | ||
| x = (maxY - y1) / m + x1; | ||
| if (x > minX && x < maxX) return true; | ||
| return false; | ||
| } | ||
| /** Returns true if the axis aligned bounding box intersects the axis aligned bounding box of the specified bounds. */ | ||
| aabbIntersectsSkeleton (bounds: SkeletonBounds) { | ||
| return this.minX < bounds.maxX && this.maxX > bounds.minX && this.minY < bounds.maxY && this.maxY > bounds.minY; | ||
| } | ||
| /** Returns the first bounding box attachment that contains the point, or null. When doing many checks, it is usually more | ||
| * efficient to only call this method if {@link #aabbContainsPoint(float, float)} returns true. */ | ||
| containsPoint (x: number, y: number): BoundingBoxAttachment { | ||
| let polygons = this.polygons; | ||
| for (let i = 0, n = polygons.length; i < n; i++) | ||
| if (this.containsPointPolygon(polygons[i], x, y)) return this.boundingBoxes[i]; | ||
| return null; | ||
| } | ||
| /** Returns true if the polygon contains the point. */ | ||
| containsPointPolygon (polygon: ArrayLike<number>, x: number, y: number) { | ||
| let vertices = polygon; | ||
| let nn = polygon.length; | ||
| let prevIndex = nn - 2; | ||
| let inside = false; | ||
| for (let ii = 0; ii < nn; ii += 2) { | ||
| let vertexY = vertices[ii + 1]; | ||
| let prevY = vertices[prevIndex + 1]; | ||
| if ((vertexY < y && prevY >= y) || (prevY < y && vertexY >= y)) { | ||
| let vertexX = vertices[ii]; | ||
| if (vertexX + (y - vertexY) / (prevY - vertexY) * (vertices[prevIndex] - vertexX) < x) inside = !inside; | ||
| } | ||
| prevIndex = ii; | ||
| } | ||
| return inside; | ||
| } | ||
| /** Returns the first bounding box attachment that contains any part of the line segment, or null. When doing many checks, it | ||
| * is usually more efficient to only call this method if {@link #aabbIntersectsSegment(float, float, float, float)} returns | ||
| * true. */ | ||
| intersectsSegment (x1: number, y1: number, x2: number, y2: number) { | ||
| let polygons = this.polygons; | ||
| for (let i = 0, n = polygons.length; i < n; i++) | ||
| if (this.intersectsSegmentPolygon(polygons[i], x1, y1, x2, y2)) return this.boundingBoxes[i]; | ||
| return null; | ||
| } | ||
| /** Returns true if the polygon contains any part of the line segment. */ | ||
| intersectsSegmentPolygon (polygon: ArrayLike<number>, x1: number, y1: number, x2: number, y2: number) { | ||
| let vertices = polygon; | ||
| let nn = polygon.length; | ||
| let width12 = x1 - x2, height12 = y1 - y2; | ||
| let det1 = x1 * y2 - y1 * x2; | ||
| let x3 = vertices[nn - 2], y3 = vertices[nn - 1]; | ||
| for (let ii = 0; ii < nn; ii += 2) { | ||
| let x4 = vertices[ii], y4 = vertices[ii + 1]; | ||
| let det2 = x3 * y4 - y3 * x4; | ||
| let width34 = x3 - x4, height34 = y3 - y4; | ||
| let det3 = width12 * height34 - height12 * width34; | ||
| let x = (det1 * width34 - width12 * det2) / det3; | ||
| if (((x >= x3 && x <= x4) || (x >= x4 && x <= x3)) && ((x >= x1 && x <= x2) || (x >= x2 && x <= x1))) { | ||
| let y = (det1 * height34 - height12 * det2) / det3; | ||
| if (((y >= y3 && y <= y4) || (y >= y4 && y <= y3)) && ((y >= y1 && y <= y2) || (y >= y2 && y <= y1))) return true; | ||
| } | ||
| x3 = x4; | ||
| y3 = y4; | ||
| } | ||
| return false; | ||
| } | ||
| /** Returns the polygon for the specified bounding box, or null. */ | ||
| getPolygon (boundingBox: BoundingBoxAttachment) { | ||
| if (boundingBox == null) throw new Error("boundingBox cannot be null."); | ||
| let index = this.boundingBoxes.indexOf(boundingBox); | ||
| return index == -1 ? null : this.polygons[index]; | ||
| } | ||
| getWidth () { | ||
| return this.maxX - this.minX; | ||
| } | ||
| getHeight () { | ||
| return this.maxY - this.minY; | ||
| } | ||
| } | ||
| } |
| /****************************************************************************** | ||
| * Spine Runtimes Software License v2.5 | ||
| * | ||
| * Copyright (c) 2013-2016, Esoteric Software | ||
| * All rights reserved. | ||
| * | ||
| * You are granted a perpetual, non-exclusive, non-sublicensable, and | ||
| * non-transferable license to use, install, execute, and perform the Spine | ||
| * Runtimes software and derivative works solely for personal or internal | ||
| * use. Without the written permission of Esoteric Software (see Section 2 of | ||
| * the Spine Software License Agreement), you may not (a) modify, translate, | ||
| * adapt, or develop new applications using the Spine Runtimes or otherwise | ||
| * create derivative works or improvements of the Spine Runtimes or (b) remove, | ||
| * delete, alter, or obscure any trademarks or any copyright, trademark, patent, | ||
| * or other intellectual property or proprietary rights notices on or in the | ||
| * Software, including any copy thereof. Redistributions in binary or source | ||
| * form must include this license and terms. | ||
| * | ||
| * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR | ||
| * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF | ||
| * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO | ||
| * EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | ||
| * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, | ||
| * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF | ||
| * USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER | ||
| * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) | ||
| * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE | ||
| * POSSIBILITY OF SUCH DAMAGE. | ||
| *****************************************************************************/ | ||
| namespace pixi_spine.core { | ||
| export class SkeletonClipping { | ||
| private triangulator = new Triangulator(); | ||
| private clippingPolygon = new Array<number>(); | ||
| private clipOutput = new Array<number>(); | ||
| clippedVertices = new Array<number>(); | ||
| clippedTriangles = new Array<number>(); | ||
| private scratch = new Array<number>(); | ||
| private clipAttachment: ClippingAttachment; | ||
| private clippingPolygons: Array<Array<number>>; | ||
| clipStart (slot: Slot, clip: ClippingAttachment): number { | ||
| if (this.clipAttachment != null) return 0; | ||
| this.clipAttachment = clip; | ||
| let n = clip.worldVerticesLength; | ||
| let vertices = Utils.setArraySize(this.clippingPolygon, n); | ||
| clip.computeWorldVertices(slot, 0, n, vertices, 0, 2); | ||
| let clippingPolygon = this.clippingPolygon; | ||
| SkeletonClipping.makeClockwise(clippingPolygon); | ||
| let clippingPolygons = this.clippingPolygons = this.triangulator.decompose(clippingPolygon, this.triangulator.triangulate(clippingPolygon)); | ||
| for (let i = 0, n = clippingPolygons.length; i < n; i++) { | ||
| let polygon = clippingPolygons[i]; | ||
| SkeletonClipping.makeClockwise(polygon); | ||
| polygon.push(polygon[0]); | ||
| polygon.push(polygon[1]); | ||
| } | ||
| return clippingPolygons.length; | ||
| } | ||
| clipEndWithSlot (slot: Slot) { | ||
| if (this.clipAttachment != null && this.clipAttachment.endSlot == slot.data) this.clipEnd(); | ||
| } | ||
| clipEnd () { | ||
| if (this.clipAttachment == null) return; | ||
| this.clipAttachment = null; | ||
| this.clippingPolygons = null; | ||
| this.clippedVertices.length = 0; | ||
| this.clippedTriangles.length = 0; | ||
| this.clippingPolygon.length = 0; | ||
| } | ||
| isClipping (): boolean { | ||
| return this.clipAttachment != null; | ||
| } | ||
| clipTriangles (vertices: ArrayLike<number>, verticesLength: number, triangles: ArrayLike<number>, trianglesLength: number, uvs: ArrayLike<number>, | ||
| light: Color, dark: Color, twoColor: boolean) { | ||
| let clipOutput = this.clipOutput, clippedVertices = this.clippedVertices; | ||
| let clippedTriangles = this.clippedTriangles; | ||
| let polygons = this.clippingPolygons; | ||
| let polygonsCount = this.clippingPolygons.length; | ||
| let vertexSize = twoColor ? 12 : 8; | ||
| let index = 0; | ||
| clippedVertices.length = 0; | ||
| clippedTriangles.length = 0; | ||
| outer: | ||
| for (let i = 0; i < trianglesLength; i += 3) { | ||
| let vertexOffset = triangles[i] << 1; | ||
| let x1 = vertices[vertexOffset], y1 = vertices[vertexOffset + 1]; | ||
| let u1 = uvs[vertexOffset], v1 = uvs[vertexOffset + 1]; | ||
| vertexOffset = triangles[i + 1] << 1; | ||
| let x2 = vertices[vertexOffset], y2 = vertices[vertexOffset + 1]; | ||
| let u2 = uvs[vertexOffset], v2 = uvs[vertexOffset + 1]; | ||
| vertexOffset = triangles[i + 2] << 1; | ||
| let x3 = vertices[vertexOffset], y3 = vertices[vertexOffset + 1]; | ||
| let u3 = uvs[vertexOffset], v3 = uvs[vertexOffset + 1]; | ||
| for (let p = 0; p < polygonsCount; p++) { | ||
| let s = clippedVertices.length; | ||
| if (this.clip(x1, y1, x2, y2, x3, y3, polygons[p], clipOutput)) { | ||
| let clipOutputLength = clipOutput.length; | ||
| if (clipOutputLength == 0) continue; | ||
| let d0 = y2 - y3, d1 = x3 - x2, d2 = x1 - x3, d4 = y3 - y1; | ||
| let d = 1 / (d0 * d2 + d1 * (y1 - y3)); | ||
| let clipOutputCount = clipOutputLength >> 1; | ||
| let clipOutputItems = this.clipOutput; | ||
| let clippedVerticesItems = Utils.setArraySize(clippedVertices, s + clipOutputCount * vertexSize); | ||
| for (let ii = 0; ii < clipOutputLength; ii += 2) { | ||
| let x = clipOutputItems[ii], y = clipOutputItems[ii + 1]; | ||
| clippedVerticesItems[s] = x; | ||
| clippedVerticesItems[s + 1] = y; | ||
| clippedVerticesItems[s + 2] = light.r; | ||
| clippedVerticesItems[s + 3] = light.g; | ||
| clippedVerticesItems[s + 4] = light.b; | ||
| clippedVerticesItems[s + 5] = light.a; | ||
| let c0 = x - x3, c1 = y - y3; | ||
| let a = (d0 * c0 + d1 * c1) * d; | ||
| let b = (d4 * c0 + d2 * c1) * d; | ||
| let c = 1 - a - b; | ||
| clippedVerticesItems[s + 6] = u1 * a + u2 * b + u3 * c; | ||
| clippedVerticesItems[s + 7] = v1 * a + v2 * b + v3 * c; | ||
| if (twoColor) { | ||
| clippedVerticesItems[s + 8] = dark.r; | ||
| clippedVerticesItems[s + 9] = dark.g; | ||
| clippedVerticesItems[s + 10] = dark.b; | ||
| clippedVerticesItems[s + 11] = dark.a; | ||
| } | ||
| s += vertexSize; | ||
| } | ||
| s = clippedTriangles.length; | ||
| let clippedTrianglesItems = Utils.setArraySize(clippedTriangles, s + 3 * (clipOutputCount - 2)); | ||
| clipOutputCount--; | ||
| for (let ii = 1; ii < clipOutputCount; ii++) { | ||
| clippedTrianglesItems[s] = index; | ||
| clippedTrianglesItems[s + 1] = (index + ii); | ||
| clippedTrianglesItems[s + 2] = (index + ii + 1); | ||
| s += 3; | ||
| } | ||
| index += clipOutputCount + 1; | ||
| } else { | ||
| let clippedVerticesItems = Utils.setArraySize(clippedVertices, s + 3 * vertexSize); | ||
| clippedVerticesItems[s] = x1; | ||
| clippedVerticesItems[s + 1] = y1; | ||
| clippedVerticesItems[s + 2] = light.r; | ||
| clippedVerticesItems[s + 3] = light.g; | ||
| clippedVerticesItems[s + 4] = light.b; | ||
| clippedVerticesItems[s + 5] = light.a; | ||
| if (!twoColor) { | ||
| clippedVerticesItems[s + 6] = u1; | ||
| clippedVerticesItems[s + 7] = v1; | ||
| clippedVerticesItems[s + 8] = x2; | ||
| clippedVerticesItems[s + 9] = y2; | ||
| clippedVerticesItems[s + 10] = light.r; | ||
| clippedVerticesItems[s + 11] = light.g; | ||
| clippedVerticesItems[s + 12] = light.b; | ||
| clippedVerticesItems[s + 13] = light.a; | ||
| clippedVerticesItems[s + 14] = u2; | ||
| clippedVerticesItems[s + 15] = v2; | ||
| clippedVerticesItems[s + 16] = x3; | ||
| clippedVerticesItems[s + 17] = y3; | ||
| clippedVerticesItems[s + 18] = light.r; | ||
| clippedVerticesItems[s + 19] = light.g; | ||
| clippedVerticesItems[s + 20] = light.b; | ||
| clippedVerticesItems[s + 21] = light.a; | ||
| clippedVerticesItems[s + 22] = u3; | ||
| clippedVerticesItems[s + 23] = v3; | ||
| } else { | ||
| clippedVerticesItems[s + 6] = u1; | ||
| clippedVerticesItems[s + 7] = v1; | ||
| clippedVerticesItems[s + 8] = dark.r; | ||
| clippedVerticesItems[s + 9] = dark.g; | ||
| clippedVerticesItems[s + 10] = dark.b; | ||
| clippedVerticesItems[s + 11] = dark.a; | ||
| clippedVerticesItems[s + 12] = x2; | ||
| clippedVerticesItems[s + 13] = y2; | ||
| clippedVerticesItems[s + 14] = light.r; | ||
| clippedVerticesItems[s + 15] = light.g; | ||
| clippedVerticesItems[s + 16] = light.b; | ||
| clippedVerticesItems[s + 17] = light.a; | ||
| clippedVerticesItems[s + 18] = u2; | ||
| clippedVerticesItems[s + 19] = v2; | ||
| clippedVerticesItems[s + 20] = dark.r; | ||
| clippedVerticesItems[s + 21] = dark.g; | ||
| clippedVerticesItems[s + 22] = dark.b; | ||
| clippedVerticesItems[s + 23] = dark.a; | ||
| clippedVerticesItems[s + 24] = x3; | ||
| clippedVerticesItems[s + 25] = y3; | ||
| clippedVerticesItems[s + 26] = light.r; | ||
| clippedVerticesItems[s + 27] = light.g; | ||
| clippedVerticesItems[s + 28] = light.b; | ||
| clippedVerticesItems[s + 29] = light.a; | ||
| clippedVerticesItems[s + 30] = u3; | ||
| clippedVerticesItems[s + 31] = v3; | ||
| clippedVerticesItems[s + 32] = dark.r; | ||
| clippedVerticesItems[s + 33] = dark.g; | ||
| clippedVerticesItems[s + 34] = dark.b; | ||
| clippedVerticesItems[s + 35] = dark.a; | ||
| } | ||
| s = clippedTriangles.length; | ||
| let clippedTrianglesItems = Utils.setArraySize(clippedTriangles, s + 3); | ||
| clippedTrianglesItems[s] = index; | ||
| clippedTrianglesItems[s + 1] = (index + 1); | ||
| clippedTrianglesItems[s + 2] = (index + 2); | ||
| index += 3; | ||
| continue outer; | ||
| } | ||
| } | ||
| } | ||
| } | ||
| /** Clips the input triangle against the convex, clockwise clipping area. If the triangle lies entirely within the clipping | ||
| * area, false is returned. The clipping area must duplicate the first vertex at the end of the vertices list. */ | ||
| clip (x1: number, y1: number, x2: number, y2: number, x3: number, y3: number, clippingArea: Array<number>, output: Array<number>) { | ||
| let originalOutput = output; | ||
| let clipped = false; | ||
| // Avoid copy at the end. | ||
| let input: Array<number> = null; | ||
| if (clippingArea.length % 4 >= 2) { | ||
| input = output; | ||
| output = this.scratch; | ||
| } else | ||
| input = this.scratch; | ||
| input.length = 0; | ||
| input.push(x1); | ||
| input.push(y1); | ||
| input.push(x2); | ||
| input.push(y2); | ||
| input.push(x3); | ||
| input.push(y3); | ||
| input.push(x1); | ||
| input.push(y1); | ||
| output.length = 0; | ||
| let clippingVertices = clippingArea; | ||
| let clippingVerticesLast = clippingArea.length - 4; | ||
| for (let i = 0;; i += 2) { | ||
| let edgeX = clippingVertices[i], edgeY = clippingVertices[i + 1]; | ||
| let edgeX2 = clippingVertices[i + 2], edgeY2 = clippingVertices[i + 3]; | ||
| let deltaX = edgeX - edgeX2, deltaY = edgeY - edgeY2; | ||
| let inputVertices = input; | ||
| let inputVerticesLength = input.length - 2, outputStart = output.length; | ||
| for (let ii = 0; ii < inputVerticesLength; ii += 2) { | ||
| let inputX = inputVertices[ii], inputY = inputVertices[ii + 1]; | ||
| let inputX2 = inputVertices[ii + 2], inputY2 = inputVertices[ii + 3]; | ||
| let side2 = deltaX * (inputY2 - edgeY2) - deltaY * (inputX2 - edgeX2) > 0; | ||
| if (deltaX * (inputY - edgeY2) - deltaY * (inputX - edgeX2) > 0) { | ||
| if (side2) { // v1 inside, v2 inside | ||
| output.push(inputX2); | ||
| output.push(inputY2); | ||
| continue; | ||
| } | ||
| // v1 inside, v2 outside | ||
| let c0 = inputY2 - inputY, c2 = inputX2 - inputX; | ||
| let ua = (c2 * (edgeY - inputY) - c0 * (edgeX - inputX)) / (c0 * (edgeX2 - edgeX) - c2 * (edgeY2 - edgeY)); | ||
| output.push(edgeX + (edgeX2 - edgeX) * ua); | ||
| output.push(edgeY + (edgeY2 - edgeY) * ua); | ||
| } else if (side2) { // v1 outside, v2 inside | ||
| let c0 = inputY2 - inputY, c2 = inputX2 - inputX; | ||
| let ua = (c2 * (edgeY - inputY) - c0 * (edgeX - inputX)) / (c0 * (edgeX2 - edgeX) - c2 * (edgeY2 - edgeY)); | ||
| output.push(edgeX + (edgeX2 - edgeX) * ua); | ||
| output.push(edgeY + (edgeY2 - edgeY) * ua); | ||
| output.push(inputX2); | ||
| output.push(inputY2); | ||
| } | ||
| clipped = true; | ||
| } | ||
| if (outputStart == output.length) { // All edges outside. | ||
| originalOutput.length = 0; | ||
| return true; | ||
| } | ||
| output.push(output[0]); | ||
| output.push(output[1]); | ||
| if (i == clippingVerticesLast) break; | ||
| let temp = output; | ||
| output = input; | ||
| output.length = 0; | ||
| input = temp; | ||
| } | ||
| if (originalOutput != output) { | ||
| originalOutput.length = 0; | ||
| for (let i = 0, n = output.length - 2; i < n; i++) | ||
| originalOutput[i] = output[i]; | ||
| } else | ||
| originalOutput.length = originalOutput.length - 2; | ||
| return clipped; | ||
| } | ||
| public static makeClockwise (polygon: ArrayLike<number>) { | ||
| let vertices = polygon; | ||
| let verticeslength = polygon.length; | ||
| let area = vertices[verticeslength - 2] * vertices[1] - vertices[0] * vertices[verticeslength - 1], p1x = 0, p1y = 0, p2x = 0, p2y = 0; | ||
| for (let i = 0, n = verticeslength - 3; i < n; i += 2) { | ||
| p1x = vertices[i]; | ||
| p1y = vertices[i + 1]; | ||
| p2x = vertices[i + 2]; | ||
| p2y = vertices[i + 3]; | ||
| area += p1x * p2y - p2x * p1y; | ||
| } | ||
| if (area < 0) return; | ||
| for (let i = 0, lastX = verticeslength - 2, n = verticeslength >> 1; i < n; i += 2) { | ||
| let x = vertices[i], y = vertices[i + 1]; | ||
| let other = lastX - i; | ||
| vertices[i] = vertices[other]; | ||
| vertices[i + 1] = vertices[other + 1]; | ||
| vertices[other] = x; | ||
| vertices[other + 1] = y; | ||
| } | ||
| } | ||
| } | ||
| } |
| /****************************************************************************** | ||
| * Spine Runtimes Software License | ||
| * Version 2.5 | ||
| * | ||
| * Copyright (c) 2013-2016, Esoteric Software | ||
| * All rights reserved. | ||
| * | ||
| * You are granted a perpetual, non-exclusive, non-sublicensable, and | ||
| * non-transferable license to use, install, execute, and perform the Spine | ||
| * Runtimes software and derivative works solely for personal or internal | ||
| * use. Without the written permission of Esoteric Software (see Section 2 of | ||
| * the Spine Software License Agreement), you may not (a) modify, translate, | ||
| * adapt, or develop new applications using the Spine Runtimes or otherwise | ||
| * create derivative works or improvements of the Spine Runtimes or (b) remove, | ||
| * delete, alter, or obscure any trademarks or any copyright, trademark, patent, | ||
| * or other intellectual property or proprietary rights notices on or in the | ||
| * Software, including any copy thereof. Redistributions in binary or source | ||
| * form must include this license and terms. | ||
| * | ||
| * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR | ||
| * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF | ||
| * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO | ||
| * EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | ||
| * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, | ||
| * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF | ||
| * USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER | ||
| * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) | ||
| * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE | ||
| * POSSIBILITY OF SUCH DAMAGE. | ||
| *****************************************************************************/ | ||
| namespace pixi_spine.core { | ||
| export class SkeletonData { | ||
| name: string; | ||
| bones = new Array<BoneData>(); // Ordered parents first. | ||
| slots = new Array<SlotData>(); // Setup pose draw order. | ||
| skins = new Array<Skin>(); | ||
| defaultSkin: Skin; | ||
| events = new Array<EventData>(); | ||
| animations = new Array<Animation>(); | ||
| ikConstraints = new Array<IkConstraintData>(); | ||
| transformConstraints = new Array<TransformConstraintData>(); | ||
| pathConstraints = new Array<PathConstraintData>(); | ||
| width: number; | ||
| height: number; | ||
| version: string; | ||
| hash: string; | ||
| // Nonessential | ||
| fps = 0; | ||
| imagesPath: string; | ||
| findBone(boneName: string) { | ||
| if (boneName == null) throw new Error("boneName cannot be null."); | ||
| let bones = this.bones; | ||
| for (let i = 0, n = bones.length; i < n; i++) { | ||
| let bone = bones[i]; | ||
| if (bone.name == boneName) return bone; | ||
| } | ||
| return null; | ||
| } | ||
| findBoneIndex(boneName: string) { | ||
| if (boneName == null) throw new Error("boneName cannot be null."); | ||
| let bones = this.bones; | ||
| for (let i = 0, n = bones.length; i < n; i++) | ||
| if (bones[i].name == boneName) return i; | ||
| return -1; | ||
| } | ||
| findSlot(slotName: string) { | ||
| if (slotName == null) throw new Error("slotName cannot be null."); | ||
| let slots = this.slots; | ||
| for (let i = 0, n = slots.length; i < n; i++) { | ||
| let slot = slots[i]; | ||
| if (slot.name == slotName) return slot; | ||
| } | ||
| return null; | ||
| } | ||
| findSlotIndex(slotName: string) { | ||
| if (slotName == null) throw new Error("slotName cannot be null."); | ||
| let slots = this.slots; | ||
| for (let i = 0, n = slots.length; i < n; i++) | ||
| if (slots[i].name == slotName) return i; | ||
| return -1; | ||
| } | ||
| findSkin(skinName: string) { | ||
| if (skinName == null) throw new Error("skinName cannot be null."); | ||
| let skins = this.skins; | ||
| for (let i = 0, n = skins.length; i < n; i++) { | ||
| let skin = skins[i]; | ||
| if (skin.name == skinName) return skin; | ||
| } | ||
| return null; | ||
| } | ||
| findEvent(eventDataName: string) { | ||
| if (eventDataName == null) throw new Error("eventDataName cannot be null."); | ||
| let events = this.events; | ||
| for (let i = 0, n = events.length; i < n; i++) { | ||
| let event = events[i]; | ||
| if (event.name == eventDataName) return event; | ||
| } | ||
| return null; | ||
| } | ||
| findAnimation(animationName: string) { | ||
| if (animationName == null) throw new Error("animationName cannot be null."); | ||
| let animations = this.animations; | ||
| for (let i = 0, n = animations.length; i < n; i++) { | ||
| let animation = animations[i]; | ||
| if (animation.name == animationName) return animation; | ||
| } | ||
| return null; | ||
| } | ||
| findIkConstraint(constraintName: string) { | ||
| if (constraintName == null) throw new Error("constraintName cannot be null."); | ||
| let ikConstraints = this.ikConstraints; | ||
| for (let i = 0, n = ikConstraints.length; i < n; i++) { | ||
| let constraint = ikConstraints[i]; | ||
| if (constraint.name == constraintName) return constraint; | ||
| } | ||
| return null; | ||
| } | ||
| findTransformConstraint(constraintName: string) { | ||
| if (constraintName == null) throw new Error("constraintName cannot be null."); | ||
| let transformConstraints = this.transformConstraints; | ||
| for (let i = 0, n = transformConstraints.length; i < n; i++) { | ||
| let constraint = transformConstraints[i]; | ||
| if (constraint.name == constraintName) return constraint; | ||
| } | ||
| return null; | ||
| } | ||
| findPathConstraint(constraintName: string) { | ||
| if (constraintName == null) throw new Error("constraintName cannot be null."); | ||
| let pathConstraints = this.pathConstraints; | ||
| for (let i = 0, n = pathConstraints.length; i < n; i++) { | ||
| let constraint = pathConstraints[i]; | ||
| if (constraint.name == constraintName) return constraint; | ||
| } | ||
| return null; | ||
| } | ||
| findPathConstraintIndex(pathConstraintName: string) { | ||
| if (pathConstraintName == null) throw new Error("pathConstraintName cannot be null."); | ||
| let pathConstraints = this.pathConstraints; | ||
| for (let i = 0, n = pathConstraints.length; i < n; i++) | ||
| if (pathConstraints[i].name == pathConstraintName) return i; | ||
| return -1; | ||
| } | ||
| } | ||
| } |
| /****************************************************************************** | ||
| * Spine Runtimes Software License | ||
| * Version 2.5 | ||
| * | ||
| * Copyright (c) 2013-2016, Esoteric Software | ||
| * All rights reserved. | ||
| * | ||
| * You are granted a perpetual, non-exclusive, non-sublicensable, and | ||
| * non-transferable license to use, install, execute, and perform the Spine | ||
| * Runtimes software and derivative works solely for personal or internal | ||
| * use. Without the written permission of Esoteric Software (see Section 2 of | ||
| * the Spine Software License Agreement), you may not (a) modify, translate, | ||
| * adapt, or develop new applications using the Spine Runtimes or otherwise | ||
| * create derivative works or improvements of the Spine Runtimes or (b) remove, | ||
| * delete, alter, or obscure any trademarks or any copyright, trademark, patent, | ||
| * or other intellectual property or proprietary rights notices on or in the | ||
| * Software, including any copy thereof. Redistributions in binary or source | ||
| * form must include this license and terms. | ||
| * | ||
| * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR | ||
| * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF | ||
| * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO | ||
| * EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | ||
| * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, | ||
| * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF | ||
| * USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER | ||
| * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) | ||
| * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE | ||
| * POSSIBILITY OF SUCH DAMAGE. | ||
| *****************************************************************************/ | ||
| namespace pixi_spine.core { | ||
| export class SkeletonJson { | ||
| attachmentLoader: AttachmentLoader; | ||
| scale = 1; | ||
| private linkedMeshes = new Array<LinkedMesh>(); | ||
| constructor (attachmentLoader: AttachmentLoader) { | ||
| this.attachmentLoader = attachmentLoader; | ||
| } | ||
| readSkeletonData (json: string | any): SkeletonData { | ||
| let scale = this.scale; | ||
| let skeletonData = new SkeletonData(); | ||
| let root = typeof(json) === "string" ? JSON.parse(json) : json; | ||
| // Skeleton | ||
| let skeletonMap = root.skeleton; | ||
| if (skeletonMap != null) { | ||
| skeletonData.hash = skeletonMap.hash; | ||
| skeletonData.version = skeletonMap.spine; | ||
| skeletonData.width = skeletonMap.width; | ||
| skeletonData.height = skeletonMap.height; | ||
| skeletonData.fps = skeletonMap.fps; | ||
| skeletonData.imagesPath = skeletonMap.images; | ||
| } | ||
| // Bones | ||
| if (root.bones) { | ||
| for (let i = 0; i < root.bones.length; i++) { | ||
| let boneMap = root.bones[i]; | ||
| let parent: BoneData = null; | ||
| let parentName: string = this.getValue(boneMap, "parent", null); | ||
| if (parentName != null) { | ||
| parent = skeletonData.findBone(parentName); | ||
| if (parent == null) throw new Error("Parent bone not found: " + parentName); | ||
| } | ||
| let data = new BoneData(skeletonData.bones.length, boneMap.name, parent); | ||
| data.length = this.getValue(boneMap, "length", 0) * scale; | ||
| data.x = this.getValue(boneMap, "x", 0) * scale; | ||
| data.y = this.getValue(boneMap, "y", 0) * scale; | ||
| data.rotation = this.getValue(boneMap, "rotation", 0); | ||
| data.scaleX = this.getValue(boneMap, "scaleX", 1); | ||
| data.scaleY = this.getValue(boneMap, "scaleY", 1); | ||
| data.shearX = this.getValue(boneMap, "shearX", 0); | ||
| data.shearY = this.getValue(boneMap, "shearY", 0); | ||
| data.transformMode = SkeletonJson.transformModeFromString(this.getValue(boneMap, "transform", "normal")); | ||
| skeletonData.bones.push(data); | ||
| } | ||
| } | ||
| // Slots. | ||
| if (root.slots) { | ||
| for (let i = 0; i < root.slots.length; i++) { | ||
| let slotMap = root.slots[i]; | ||
| let slotName: string = slotMap.name; | ||
| let boneName: string = slotMap.bone; | ||
| let boneData = skeletonData.findBone(boneName); | ||
| if (boneData == null) throw new Error("Slot bone not found: " + boneName); | ||
| let data = new SlotData(skeletonData.slots.length, slotName, boneData); | ||
| let color: string = this.getValue(slotMap, "color", null); | ||
| if (color != null) data.color.setFromString(color); | ||
| 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); | ||
| data.blendMode = SkeletonJson.blendModeFromString(this.getValue(slotMap, "blend", "normal")); | ||
| skeletonData.slots.push(data); | ||
| } | ||
| } | ||
| // IK constraints | ||
| if (root.ik) { | ||
| for (let i = 0; i < root.ik.length; i++) { | ||
| let constraintMap = root.ik[i]; | ||
| let data = new IkConstraintData(constraintMap.name); | ||
| data.order = this.getValue(constraintMap, "order", 0); | ||
| for (let j = 0; j < constraintMap.bones.length; j++) { | ||
| let boneName = constraintMap.bones[j]; | ||
| let bone = skeletonData.findBone(boneName); | ||
| if (bone == null) throw new Error("IK bone not found: " + boneName); | ||
| data.bones.push(bone); | ||
| } | ||
| let targetName: string = constraintMap.target; | ||
| data.target = skeletonData.findBone(targetName); | ||
| if (data.target == null) throw new Error("IK target bone not found: " + targetName); | ||
| data.bendDirection = this.getValue(constraintMap, "bendPositive", true) ? 1 : -1; | ||
| data.mix = this.getValue(constraintMap, "mix", 1); | ||
| skeletonData.ikConstraints.push(data); | ||
| } | ||
| } | ||
| // Transform constraints. | ||
| if (root.transform) { | ||
| for (let i = 0; i < root.transform.length; i++) { | ||
| let constraintMap = root.transform[i]; | ||
| let data = new TransformConstraintData(constraintMap.name); | ||
| data.order = this.getValue(constraintMap, "order", 0); | ||
| for (let j = 0; j < constraintMap.bones.length; j++) { | ||
| let boneName = constraintMap.bones[j]; | ||
| let bone = skeletonData.findBone(boneName); | ||
| if (bone == null) throw new Error("Transform constraint bone not found: " + boneName); | ||
| data.bones.push(bone); | ||
| } | ||
| let targetName: string = constraintMap.target; | ||
| data.target = skeletonData.findBone(targetName); | ||
| if (data.target == null) throw new Error("Transform constraint target bone not found: " + targetName); | ||
| data.local = this.getValue(constraintMap, "local", false); | ||
| data.relative = this.getValue(constraintMap, "relative", false); | ||
| data.offsetRotation = this.getValue(constraintMap, "rotation", 0); | ||
| data.offsetX = this.getValue(constraintMap, "x", 0) * scale; | ||
| data.offsetY = this.getValue(constraintMap, "y", 0) * scale; | ||
| data.offsetScaleX = this.getValue(constraintMap, "scaleX", 0); | ||
| data.offsetScaleY = this.getValue(constraintMap, "scaleY", 0); | ||
| data.offsetShearY = this.getValue(constraintMap, "shearY", 0); | ||
| data.rotateMix = this.getValue(constraintMap, "rotateMix", 1); | ||
| data.translateMix = this.getValue(constraintMap, "translateMix", 1); | ||
| data.scaleMix = this.getValue(constraintMap, "scaleMix", 1); | ||
| data.shearMix = this.getValue(constraintMap, "shearMix", 1); | ||
| skeletonData.transformConstraints.push(data); | ||
| } | ||
| } | ||
| // Path constraints. | ||
| if (root.path) { | ||
| for (let i = 0; i < root.path.length; i++) { | ||
| let constraintMap = root.path[i]; | ||
| let data = new PathConstraintData(constraintMap.name); | ||
| data.order = this.getValue(constraintMap, "order", 0); | ||
| for (let j = 0; j < constraintMap.bones.length; j++) { | ||
| let boneName = constraintMap.bones[j]; | ||
| let bone = skeletonData.findBone(boneName); | ||
| if (bone == null) throw new Error("Transform constraint bone not found: " + boneName); | ||
| data.bones.push(bone); | ||
| } | ||
| let targetName: string = constraintMap.target; | ||
| data.target = skeletonData.findSlot(targetName); | ||
| if (data.target == null) throw new Error("Path target slot not found: " + targetName); | ||
| data.positionMode = SkeletonJson.positionModeFromString(this.getValue(constraintMap, "positionMode", "percent")); | ||
| data.spacingMode = SkeletonJson.spacingModeFromString(this.getValue(constraintMap, "spacingMode", "length")); | ||
| data.rotateMode = SkeletonJson.rotateModeFromString(this.getValue(constraintMap, "rotateMode", "tangent")); | ||
| data.offsetRotation = this.getValue(constraintMap, "rotation", 0); | ||
| data.position = this.getValue(constraintMap, "position", 0); | ||
| if (data.positionMode == PositionMode.Fixed) data.position *= scale; | ||
| data.spacing = this.getValue(constraintMap, "spacing", 0); | ||
| if (data.spacingMode == SpacingMode.Length || data.spacingMode == SpacingMode.Fixed) data.spacing *= scale; | ||
| data.rotateMix = this.getValue(constraintMap, "rotateMix", 1); | ||
| data.translateMix = this.getValue(constraintMap, "translateMix", 1); | ||
| skeletonData.pathConstraints.push(data); | ||
| } | ||
| } | ||
| // Skins. | ||
| if (root.skins) { | ||
| for (let skinName in root.skins) { | ||
| let skinMap = root.skins[skinName] | ||
| let skin = new Skin(skinName); | ||
| for (let slotName in skinMap) { | ||
| let slotIndex = skeletonData.findSlotIndex(slotName); | ||
| if (slotIndex == -1) throw new Error("Slot not found: " + slotName); | ||
| let slotMap = skinMap[slotName]; | ||
| for (let entryName in slotMap) { | ||
| let attachment = this.readAttachment(slotMap[entryName], skin, slotIndex, entryName, skeletonData); | ||
| if (attachment != null) skin.addAttachment(slotIndex, entryName, attachment); | ||
| } | ||
| } | ||
| skeletonData.skins.push(skin); | ||
| if (skin.name == "default") skeletonData.defaultSkin = skin; | ||
| } | ||
| } | ||
| // Linked meshes. | ||
| for (let i = 0, n = this.linkedMeshes.length; i < n; i++) { | ||
| let linkedMesh = this.linkedMeshes[i]; | ||
| let skin = linkedMesh.skin == null ? skeletonData.defaultSkin : skeletonData.findSkin(linkedMesh.skin); | ||
| if (skin == null) throw new Error("Skin not found: " + linkedMesh.skin); | ||
| let parent = skin.getAttachment(linkedMesh.slotIndex, linkedMesh.parent); | ||
| if (parent == null) throw new Error("Parent mesh not found: " + linkedMesh.parent); | ||
| linkedMesh.mesh.setParentMesh(<MeshAttachment> parent); | ||
| //linkedMesh.mesh.updateUVs(); | ||
| } | ||
| this.linkedMeshes.length = 0; | ||
| // Events. | ||
| if (root.events) { | ||
| for (let eventName in root.events) { | ||
| let eventMap = root.events[eventName]; | ||
| let data = new EventData(eventName); | ||
| data.intValue = this.getValue(eventMap, "int", 0); | ||
| data.floatValue = this.getValue(eventMap, "float", 0); | ||
| data.stringValue = this.getValue(eventMap, "string", ""); | ||
| skeletonData.events.push(data); | ||
| } | ||
| } | ||
| // Animations. | ||
| if (root.animations) { | ||
| for (let animationName in root.animations) { | ||
| let animationMap = root.animations[animationName]; | ||
| this.readAnimation(animationMap, animationName, skeletonData); | ||
| } | ||
| } | ||
| return skeletonData; | ||
| } | ||
| readAttachment (map: any, skin: Skin, slotIndex: number, name: string, skeletonData: SkeletonData): Attachment { | ||
| let scale = this.scale; | ||
| name = this.getValue(map, "name", name); | ||
| let type = this.getValue(map, "type", "region"); | ||
| switch (type) { | ||
| case "region": { | ||
| let path = this.getValue(map, "path", name); | ||
| let region = this.attachmentLoader.newRegionAttachment(skin, name, path); | ||
| if (region == null) return null; | ||
| region.path = path; | ||
| region.x = this.getValue(map, "x", 0) * scale; | ||
| region.y = this.getValue(map, "y", 0) * scale; | ||
| region.scaleX = this.getValue(map, "scaleX", 1); | ||
| region.scaleY = this.getValue(map, "scaleY", 1); | ||
| region.rotation = this.getValue(map, "rotation", 0); | ||
| region.width = map.width * scale; | ||
| region.height = map.height * scale; | ||
| let color: string = this.getValue(map, "color", null); | ||
| if (color != null) region.color.setFromString(color); | ||
| //region.updateOffset(); | ||
| return region; | ||
| } | ||
| case "boundingbox": { | ||
| let box = this.attachmentLoader.newBoundingBoxAttachment(skin, name); | ||
| if (box == null) return null; | ||
| this.readVertices(map, box, map.vertexCount << 1); | ||
| let color: string = this.getValue(map, "color", null); | ||
| if (color != null) box.color.setFromString(color); | ||
| return box; | ||
| } | ||
| case "mesh": | ||
| case "linkedmesh": { | ||
| let path = this.getValue(map, "path", name); | ||
| let mesh = this.attachmentLoader.newMeshAttachment(skin, name, path); | ||
| if (mesh == null) return null; | ||
| mesh.path = path; | ||
| let color = this.getValue(map, "color", null); | ||
| if (color != null) mesh.color.setFromString(color); | ||
| let parent: string = this.getValue(map, "parent", null); | ||
| if (parent != null) { | ||
| mesh.inheritDeform = this.getValue(map, "deform", true); | ||
| this.linkedMeshes.push(new LinkedMesh(mesh, <string> this.getValue(map, "skin", null), slotIndex, parent)); | ||
| return mesh; | ||
| } | ||
| let uvs: Array<number> = map.uvs; | ||
| this.readVertices(map, mesh, uvs.length); | ||
| mesh.triangles = map.triangles; | ||
| mesh.regionUVs = uvs; | ||
| //mesh.updateUVs(); | ||
| mesh.hullLength = this.getValue(map, "hull", 0) * 2; | ||
| return mesh; | ||
| } | ||
| case "path": { | ||
| let path = this.attachmentLoader.newPathAttachment(skin, name); | ||
| if (path == null) return null; | ||
| path.closed = this.getValue(map, "closed", false); | ||
| path.constantSpeed = this.getValue(map, "constantSpeed", true); | ||
| let vertexCount = map.vertexCount; | ||
| this.readVertices(map, path, vertexCount << 1); | ||
| let lengths: Array<number> = Utils.newArray(vertexCount / 3, 0); | ||
| for (let i = 0; i < map.lengths.length; i++) | ||
| lengths[i] = map.lengths[i] * scale; | ||
| path.lengths = lengths; | ||
| let color: string = this.getValue(map, "color", null); | ||
| if (color != null) path.color.setFromString(color); | ||
| return path; | ||
| } | ||
| 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; | ||
| } | ||
| } | ||
| return null; | ||
| } | ||
| readVertices (map: any, attachment: VertexAttachment, verticesLength: number) { | ||
| let scale = this.scale; | ||
| attachment.worldVerticesLength = verticesLength; | ||
| let vertices: Array<number> = map.vertices; | ||
| if (verticesLength == vertices.length) { | ||
| let scaledVertices = Utils.toFloatArray(vertices); | ||
| if (scale != 1) { | ||
| for (let i = 0, n = vertices.length; i < n; i++) | ||
| scaledVertices[i] *= scale; | ||
| } | ||
| attachment.vertices = scaledVertices; | ||
| return; | ||
| } | ||
| let weights = new Array<number>(); | ||
| let bones = new Array<number>(); | ||
| for (let i = 0, n = vertices.length; i < n;) { | ||
| let boneCount = vertices[i++]; | ||
| bones.push(boneCount); | ||
| for (let nn = i + boneCount * 4; i < nn; i += 4) { | ||
| bones.push(vertices[i]); | ||
| weights.push(vertices[i + 1] * scale); | ||
| weights.push(vertices[i + 2] * scale); | ||
| weights.push(vertices[i + 3]); | ||
| } | ||
| } | ||
| attachment.bones = bones; | ||
| attachment.vertices = Utils.toFloatArray(weights); | ||
| } | ||
| readAnimation (map: any, name: string, skeletonData: SkeletonData) { | ||
| let scale = this.scale; | ||
| let timelines = new Array<Timeline>(); | ||
| let duration = 0; | ||
| // Slot timelines. | ||
| if (map.slots) { | ||
| for (let slotName in map.slots) { | ||
| let slotMap = map.slots[slotName]; | ||
| let slotIndex = skeletonData.findSlotIndex(slotName); | ||
| if (slotIndex == -1) throw new Error("Slot not found: " + slotName); | ||
| for (let timelineName in slotMap) { | ||
| let timelineMap = slotMap[timelineName]; | ||
| 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); | ||
| timeline.slotIndex = slotIndex; | ||
| let frameIndex = 0; | ||
| for (let i = 0; i < timelineMap.length; i++) { | ||
| let valueMap = timelineMap[i]; | ||
| let color = new Color(); | ||
| color.setFromString(valueMap.color || "ffffffff"); | ||
| timeline.setFrame(frameIndex, valueMap.time, color.r, color.g, color.b, color.a); | ||
| this.readCurve(valueMap, timeline, frameIndex); | ||
| frameIndex++; | ||
| } | ||
| timelines.push(timeline); | ||
| duration = Math.max(duration, timeline.frames[(timeline.getFrameCount() - 1) * ColorTimeline.ENTRIES]); | ||
| } else if (timelineName == "twoColor") { | ||
| let timeline = new TwoColorTimeline(timelineMap.length); | ||
| timeline.slotIndex = slotIndex; | ||
| let frameIndex = 0; | ||
| for (let i = 0; i < timelineMap.length; i++) { | ||
| let valueMap = timelineMap[i]; | ||
| 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) * TwoColorTimeline.ENTRIES]); | ||
| } else | ||
| throw new Error("Invalid timeline type for a slot: " + timelineName + " (" + slotName + ")"); | ||
| } | ||
| } | ||
| } | ||
| // Bone timelines. | ||
| if (map.bones) { | ||
| for (let boneName in map.bones) { | ||
| let boneMap = map.bones[boneName]; | ||
| let boneIndex = skeletonData.findBoneIndex(boneName); | ||
| if (boneIndex == -1) throw new Error("Bone not found: " + boneName); | ||
| for (let timelineName in boneMap) { | ||
| let timelineMap = boneMap[timelineName]; | ||
| if (timelineName === "rotate") { | ||
| let timeline = new RotateTimeline(timelineMap.length); | ||
| timeline.boneIndex = boneIndex; | ||
| let frameIndex = 0; | ||
| for (let i = 0; i < timelineMap.length; i++) { | ||
| let valueMap = timelineMap[i]; | ||
| timeline.setFrame(frameIndex, valueMap.time, valueMap.angle); | ||
| this.readCurve(valueMap, timeline, frameIndex); | ||
| frameIndex++; | ||
| } | ||
| timelines.push(timeline); | ||
| duration = Math.max(duration, timeline.frames[(timeline.getFrameCount() - 1) * RotateTimeline.ENTRIES]); | ||
| } else if (timelineName === "translate" || timelineName === "scale" || timelineName === "shear") { | ||
| let timeline: TranslateTimeline = null; | ||
| let timelineScale = 1; | ||
| if (timelineName === "scale") | ||
| timeline = new ScaleTimeline(timelineMap.length); | ||
| else if (timelineName === "shear") | ||
| timeline = new ShearTimeline(timelineMap.length); | ||
| else { | ||
| timeline = new TranslateTimeline(timelineMap.length); | ||
| timelineScale = scale; | ||
| } | ||
| timeline.boneIndex = boneIndex; | ||
| let frameIndex = 0; | ||
| for (let i = 0; i < timelineMap.length; i++) { | ||
| let valueMap = timelineMap[i]; | ||
| let x = this.getValue(valueMap, "x", 0), y = this.getValue(valueMap, "y", 0); | ||
| timeline.setFrame(frameIndex, valueMap.time, x * timelineScale, y * timelineScale); | ||
| this.readCurve(valueMap, timeline, frameIndex); | ||
| frameIndex++; | ||
| } | ||
| timelines.push(timeline); | ||
| duration = Math.max(duration, timeline.frames[(timeline.getFrameCount() - 1) * TranslateTimeline.ENTRIES]); | ||
| } else | ||
| throw new Error("Invalid timeline type for a bone: " + timelineName + " (" + boneName + ")"); | ||
| } | ||
| } | ||
| } | ||
| // IK constraint timelines. | ||
| if (map.ik) { | ||
| for (let constraintName in map.ik) { | ||
| let constraintMap = map.ik[constraintName]; | ||
| let constraint = skeletonData.findIkConstraint(constraintName); | ||
| let timeline = new IkConstraintTimeline(constraintMap.length); | ||
| timeline.ikConstraintIndex = skeletonData.ikConstraints.indexOf(constraint); | ||
| let frameIndex = 0; | ||
| for (let i = 0; i < constraintMap.length; i++) { | ||
| let valueMap = constraintMap[i]; | ||
| timeline.setFrame(frameIndex, valueMap.time, this.getValue(valueMap, "mix", 1), | ||
| this.getValue(valueMap, "bendPositive", true) ? 1 : -1); | ||
| this.readCurve(valueMap, timeline, frameIndex); | ||
| frameIndex++; | ||
| } | ||
| timelines.push(timeline); | ||
| duration = Math.max(duration, timeline.frames[(timeline.getFrameCount() - 1) * IkConstraintTimeline.ENTRIES]); | ||
| } | ||
| } | ||
| // Transform constraint timelines. | ||
| if (map.transform) { | ||
| for (let constraintName in map.transform) { | ||
| let constraintMap = map.transform[constraintName]; | ||
| let constraint = skeletonData.findTransformConstraint(constraintName); | ||
| let timeline = new TransformConstraintTimeline(constraintMap.length); | ||
| timeline.transformConstraintIndex = skeletonData.transformConstraints.indexOf(constraint); | ||
| let frameIndex = 0; | ||
| for (let i = 0; i < constraintMap.length; i++) { | ||
| let valueMap = constraintMap[i]; | ||
| timeline.setFrame(frameIndex, valueMap.time, this.getValue(valueMap, "rotateMix", 1), | ||
| this.getValue(valueMap, "translateMix", 1), this.getValue(valueMap, "scaleMix", 1), this.getValue(valueMap, "shearMix", 1)); | ||
| this.readCurve(valueMap, timeline, frameIndex); | ||
| frameIndex++; | ||
| } | ||
| timelines.push(timeline); | ||
| duration = Math.max(duration, | ||
| timeline.frames[(timeline.getFrameCount() - 1) * TransformConstraintTimeline.ENTRIES]); | ||
| } | ||
| } | ||
| // Path constraint timelines. | ||
| if (map.paths) { | ||
| for (let constraintName in map.paths) { | ||
| let constraintMap = map.paths[constraintName]; | ||
| let index = skeletonData.findPathConstraintIndex(constraintName); | ||
| if (index == -1) throw new Error("Path constraint not found: " + constraintName); | ||
| let data = skeletonData.pathConstraints[index]; | ||
| for (let timelineName in constraintMap) { | ||
| let timelineMap = constraintMap[timelineName]; | ||
| if (timelineName === "position" || timelineName === "spacing") { | ||
| let timeline: PathConstraintPositionTimeline = null; | ||
| let timelineScale = 1; | ||
| if (timelineName === "spacing") { | ||
| timeline = new PathConstraintSpacingTimeline(timelineMap.length); | ||
| if (data.spacingMode == SpacingMode.Length || data.spacingMode == SpacingMode.Fixed) timelineScale = scale; | ||
| } else { | ||
| timeline = new PathConstraintPositionTimeline(timelineMap.length); | ||
| if (data.positionMode == PositionMode.Fixed) timelineScale = scale; | ||
| } | ||
| timeline.pathConstraintIndex = index; | ||
| let frameIndex = 0; | ||
| for (let i = 0; i < timelineMap.length; i++) { | ||
| let valueMap = timelineMap[i]; | ||
| timeline.setFrame(frameIndex, valueMap.time, this.getValue(valueMap, timelineName, 0) * timelineScale); | ||
| this.readCurve(valueMap, timeline, frameIndex); | ||
| frameIndex++; | ||
| } | ||
| timelines.push(timeline); | ||
| duration = Math.max(duration, | ||
| timeline.frames[(timeline.getFrameCount() - 1) * PathConstraintPositionTimeline.ENTRIES]); | ||
| } else if (timelineName === "mix") { | ||
| let timeline = new PathConstraintMixTimeline(timelineMap.length); | ||
| timeline.pathConstraintIndex = index; | ||
| let frameIndex = 0; | ||
| for (let i = 0; i < timelineMap.length; i++) { | ||
| let valueMap = timelineMap[i]; | ||
| timeline.setFrame(frameIndex, valueMap.time, this.getValue(valueMap, "rotateMix", 1), | ||
| this.getValue(valueMap, "translateMix", 1)); | ||
| this.readCurve(valueMap, timeline, frameIndex); | ||
| frameIndex++; | ||
| } | ||
| timelines.push(timeline); | ||
| duration = Math.max(duration, | ||
| timeline.frames[(timeline.getFrameCount() - 1) * PathConstraintMixTimeline.ENTRIES]); | ||
| } | ||
| } | ||
| } | ||
| } | ||
| // Deform timelines. | ||
| if (map.deform) { | ||
| for (let deformName in map.deform) { | ||
| let deformMap = map.deform[deformName]; | ||
| let skin = skeletonData.findSkin(deformName); | ||
| if (skin == null) throw new Error("Skin not found: " + deformName); | ||
| for (let slotName in deformMap) { | ||
| let slotMap = deformMap[slotName]; | ||
| let slotIndex = skeletonData.findSlotIndex(slotName); | ||
| if (slotIndex == -1) throw new Error("Slot not found: " + slotMap.name); | ||
| for (let timelineName in slotMap) { | ||
| let timelineMap = slotMap[timelineName]; | ||
| let attachment = <VertexAttachment>skin.getAttachment(slotIndex, timelineName); | ||
| if (attachment == null) throw new Error("Deform attachment not found: " + timelineMap.name); | ||
| let weighted = attachment.bones != null; | ||
| let vertices = attachment.vertices; | ||
| let deformLength = weighted ? vertices.length / 3 * 2 : vertices.length; | ||
| let timeline = new DeformTimeline(timelineMap.length); | ||
| timeline.slotIndex = slotIndex; | ||
| timeline.attachment = attachment; | ||
| let frameIndex = 0; | ||
| for (let j = 0; j < timelineMap.length; j++) { | ||
| let valueMap = timelineMap[j]; | ||
| let deform: ArrayLike<number>; | ||
| let verticesValue: Array<Number> = this.getValue(valueMap, "vertices", null); | ||
| if (verticesValue == null) | ||
| deform = weighted ? Utils.newFloatArray(deformLength) : vertices; | ||
| else { | ||
| deform = Utils.newFloatArray(deformLength); | ||
| let start = <number>this.getValue(valueMap, "offset", 0); | ||
| Utils.arrayCopy(verticesValue, 0, deform, start, verticesValue.length); | ||
| if (scale != 1) { | ||
| for (let i = start, n = i + verticesValue.length; i < n; i++) | ||
| deform[i] *= scale; | ||
| } | ||
| if (!weighted) { | ||
| for (let i = 0; i < deformLength; i++) | ||
| deform[i] += vertices[i]; | ||
| } | ||
| } | ||
| timeline.setFrame(frameIndex, valueMap.time, deform); | ||
| this.readCurve(valueMap, timeline, frameIndex); | ||
| frameIndex++; | ||
| } | ||
| timelines.push(timeline); | ||
| duration = Math.max(duration, timeline.frames[timeline.getFrameCount() - 1]); | ||
| } | ||
| } | ||
| } | ||
| } | ||
| // Draw order timeline. | ||
| let drawOrderNode = map.drawOrder; | ||
| if (drawOrderNode == null) drawOrderNode = map.draworder; | ||
| if (drawOrderNode != null) { | ||
| let timeline = new DrawOrderTimeline(drawOrderNode.length); | ||
| let slotCount = skeletonData.slots.length; | ||
| let frameIndex = 0; | ||
| for (let j = 0; j < drawOrderNode.length; j++) { | ||
| let drawOrderMap = drawOrderNode[j]; | ||
| let drawOrder: Array<number> = null; | ||
| let offsets = this.getValue(drawOrderMap, "offsets", null); | ||
| if (offsets != null) { | ||
| drawOrder = Utils.newArray<number>(slotCount, -1); | ||
| let unchanged = Utils.newArray<number>(slotCount - offsets.length, 0); | ||
| let originalIndex = 0, unchangedIndex = 0; | ||
| for (let i = 0; i < offsets.length; i++) { | ||
| let offsetMap = offsets[i]; | ||
| let slotIndex = skeletonData.findSlotIndex(offsetMap.slot); | ||
| if (slotIndex == -1) throw new Error("Slot not found: " + offsetMap.slot); | ||
| // Collect unchanged items. | ||
| while (originalIndex != slotIndex) | ||
| unchanged[unchangedIndex++] = originalIndex++; | ||
| // Set changed items. | ||
| drawOrder[originalIndex + offsetMap.offset] = originalIndex++; | ||
| } | ||
| // Collect remaining unchanged items. | ||
| while (originalIndex < slotCount) | ||
| unchanged[unchangedIndex++] = originalIndex++; | ||
| // Fill in unchanged items. | ||
| for (let i = slotCount - 1; i >= 0; i--) | ||
| if (drawOrder[i] == -1) drawOrder[i] = unchanged[--unchangedIndex]; | ||
| } | ||
| timeline.setFrame(frameIndex++, drawOrderMap.time, drawOrder); | ||
| } | ||
| timelines.push(timeline); | ||
| duration = Math.max(duration, timeline.frames[timeline.getFrameCount() - 1]); | ||
| } | ||
| // Event timeline. | ||
| if (map.events) { | ||
| let timeline = new EventTimeline(map.events.length); | ||
| let frameIndex = 0; | ||
| for (let i = 0; i < map.events.length; i++) { | ||
| let eventMap = map.events[i]; | ||
| let eventData = skeletonData.findEvent(eventMap.name); | ||
| if (eventData == null) throw new Error("Event not found: " + eventMap.name); | ||
| let event = new Event(Utils.toSinglePrecision(eventMap.time), eventData); | ||
| event.intValue = this.getValue(eventMap, "int", eventData.intValue); | ||
| event.floatValue = this.getValue(eventMap, "float", eventData.floatValue); | ||
| event.stringValue = this.getValue(eventMap, "string", eventData.stringValue); | ||
| timeline.setFrame(frameIndex++, event); | ||
| } | ||
| timelines.push(timeline); | ||
| duration = Math.max(duration, timeline.frames[timeline.getFrameCount() - 1]); | ||
| } | ||
| if (isNaN(duration)) { | ||
| throw new Error("Error while parsing animation, duration is NaN"); | ||
| } | ||
| skeletonData.animations.push(new Animation(name, timelines, duration)); | ||
| } | ||
| readCurve (map: any, timeline: CurveTimeline, frameIndex: number) { | ||
| if (!map.curve) return; | ||
| if (map.curve === "stepped") | ||
| timeline.setStepped(frameIndex); | ||
| else if (Object.prototype.toString.call(map.curve) === '[object Array]') { | ||
| let curve: Array<number> = map.curve; | ||
| timeline.setCurve(frameIndex, curve[0], curve[1], curve[2], curve[3]); | ||
| } | ||
| } | ||
| getValue (map: any, prop: string, defaultValue: any) { | ||
| return map[prop] !== undefined ? map[prop] : defaultValue; | ||
| } | ||
| 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) { | ||
| str = str.toLowerCase(); | ||
| if (str == "fixed") return PositionMode.Fixed; | ||
| if (str == "percent") return PositionMode.Percent; | ||
| throw new Error(`Unknown position mode: ${str}`); | ||
| } | ||
| static spacingModeFromString (str: string) { | ||
| str = str.toLowerCase(); | ||
| if (str == "length") return SpacingMode.Length; | ||
| if (str == "fixed") return SpacingMode.Fixed; | ||
| if (str == "percent") return SpacingMode.Percent; | ||
| throw new Error(`Unknown position mode: ${str}`); | ||
| } | ||
| static rotateModeFromString (str: string) { | ||
| str = str.toLowerCase(); | ||
| if (str == "tangent") return RotateMode.Tangent; | ||
| if (str == "chain") return RotateMode.Chain; | ||
| if (str == "chainscale") return RotateMode.ChainScale; | ||
| throw new Error(`Unknown rotate mode: ${str}`); | ||
| } | ||
| static transformModeFromString(str: string) { | ||
| str = str.toLowerCase(); | ||
| if (str == "normal") return TransformMode.Normal; | ||
| if (str == "onlytranslation") return TransformMode.OnlyTranslation; | ||
| if (str == "norotationorreflection") return TransformMode.NoRotationOrReflection; | ||
| if (str == "noscale") return TransformMode.NoScale; | ||
| if (str == "noscaleorreflection") return TransformMode.NoScaleOrReflection; | ||
| throw new Error(`Unknown transform mode: ${str}`); | ||
| } | ||
| } | ||
| class LinkedMesh { | ||
| parent: string; skin: string; | ||
| slotIndex: number; | ||
| mesh: MeshAttachment; | ||
| constructor (mesh: MeshAttachment, skin: string, slotIndex: number, parent: string) { | ||
| this.mesh = mesh; | ||
| this.skin = skin; | ||
| this.slotIndex = slotIndex; | ||
| this.parent = parent; | ||
| } | ||
| } | ||
| } |
| /****************************************************************************** | ||
| * Spine Runtimes Software License | ||
| * Version 2.5 | ||
| * | ||
| * Copyright (c) 2013-2016, Esoteric Software | ||
| * All rights reserved. | ||
| * | ||
| * You are granted a perpetual, non-exclusive, non-sublicensable, and | ||
| * non-transferable license to use, install, execute, and perform the Spine | ||
| * Runtimes software and derivative works solely for personal or internal | ||
| * use. Without the written permission of Esoteric Software (see Section 2 of | ||
| * the Spine Software License Agreement), you may not (a) modify, translate, | ||
| * adapt, or develop new applications using the Spine Runtimes or otherwise | ||
| * create derivative works or improvements of the Spine Runtimes or (b) remove, | ||
| * delete, alter, or obscure any trademarks or any copyright, trademark, patent, | ||
| * or other intellectual property or proprietary rights notices on or in the | ||
| * Software, including any copy thereof. Redistributions in binary or source | ||
| * form must include this license and terms. | ||
| * | ||
| * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR | ||
| * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF | ||
| * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO | ||
| * EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | ||
| * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, | ||
| * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF | ||
| * USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER | ||
| * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) | ||
| * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE | ||
| * POSSIBILITY OF SUCH DAMAGE. | ||
| *****************************************************************************/ | ||
| namespace pixi_spine.core { | ||
| export class Skin { | ||
| name: string; | ||
| attachments = new Array<Map<Attachment>>(); | ||
| constructor(name: string) { | ||
| if (name == null) throw new Error("name cannot be null."); | ||
| this.name = name; | ||
| } | ||
| addAttachment(slotIndex: number, name: string, attachment: Attachment) { | ||
| if (attachment == null) throw new Error("attachment cannot be null."); | ||
| let attachments = this.attachments; | ||
| if (slotIndex >= attachments.length) attachments.length = slotIndex + 1; | ||
| if (!attachments[slotIndex]) attachments[slotIndex] = {}; | ||
| attachments[slotIndex][name] = attachment; | ||
| } | ||
| /** @return May be null. */ | ||
| getAttachment(slotIndex: number, name: string): Attachment { | ||
| let dictionary = this.attachments[slotIndex]; | ||
| return dictionary ? dictionary[name] : null; | ||
| } | ||
| /** Attach each attachment in this skin if the corresponding attachment in the old skin is currently attached. */ | ||
| attachAll(skeleton: Skeleton, oldSkin: Skin) { | ||
| let slotIndex = 0; | ||
| for (let i = 0; i < skeleton.slots.length; i++) { | ||
| let slot = skeleton.slots[i]; | ||
| let slotAttachment = slot.getAttachment(); | ||
| if (slotAttachment && slotIndex < oldSkin.attachments.length) { | ||
| let dictionary = oldSkin.attachments[slotIndex]; | ||
| for (let key in dictionary) { | ||
| let skinAttachment: Attachment = dictionary[key]; | ||
| if (slotAttachment == skinAttachment) { | ||
| let attachment = this.getAttachment(slotIndex, name); | ||
| if (attachment != null) slot.setAttachment(attachment); | ||
| break; | ||
| } | ||
| } | ||
| } | ||
| slotIndex++; | ||
| } | ||
| } | ||
| } | ||
| } |
-105
| /****************************************************************************** | ||
| * Spine Runtimes Software License | ||
| * Version 2.5 | ||
| * | ||
| * Copyright (c) 2013-2016, Esoteric Software | ||
| * All rights reserved. | ||
| * | ||
| * You are granted a perpetual, non-exclusive, non-sublicensable, and | ||
| * non-transferable license to use, install, execute, and perform the Spine | ||
| * Runtimes software and derivative works solely for personal or internal | ||
| * use. Without the written permission of Esoteric Software (see Section 2 of | ||
| * the Spine Software License Agreement), you may not (a) modify, translate, | ||
| * adapt, or develop new applications using the Spine Runtimes or otherwise | ||
| * create derivative works or improvements of the Spine Runtimes or (b) remove, | ||
| * delete, alter, or obscure any trademarks or any copyright, trademark, patent, | ||
| * or other intellectual property or proprietary rights notices on or in the | ||
| * Software, including any copy thereof. Redistributions in binary or source | ||
| * form must include this license and terms. | ||
| * | ||
| * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR | ||
| * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF | ||
| * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO | ||
| * EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | ||
| * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, | ||
| * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF | ||
| * USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER | ||
| * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) | ||
| * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE | ||
| * POSSIBILITY OF SUCH DAMAGE. | ||
| *****************************************************************************/ | ||
| namespace pixi_spine.core { | ||
| export class Slot { | ||
| //this is for PIXI | ||
| currentMesh: any; | ||
| currentSprite: any; | ||
| currentGraphics: any; | ||
| clippingContainer: any; | ||
| meshes: any; | ||
| currentMeshName: string; | ||
| sprites: any; | ||
| currentSpriteName: string; | ||
| blendMode: number; | ||
| //assign hack region a bit later | ||
| tempRegion: TextureRegion; | ||
| tempAttachment: Attachment; | ||
| //this is canon | ||
| data: SlotData; | ||
| bone: Bone; | ||
| color: Color; | ||
| darkColor: Color; | ||
| attachment: Attachment; | ||
| private attachmentTime: number; | ||
| attachmentVertices = new Array<number>(); | ||
| constructor (data: SlotData, bone: Bone) { | ||
| if (data == null) throw new Error("data cannot be null."); | ||
| if (bone == null) throw new Error("bone cannot be null."); | ||
| this.data = data; | ||
| this.bone = bone; | ||
| this.color = new Color(); | ||
| this.darkColor = data.darkColor == null ? null : new Color(); | ||
| this.setToSetupPose(); | ||
| this.blendMode = this.data.blendMode; | ||
| } | ||
| /** @return May be null. */ | ||
| getAttachment (): Attachment { | ||
| return this.attachment; | ||
| } | ||
| /** Sets the attachment and if it changed, resets {@link #getAttachmentTime()} and clears {@link #getAttachmentVertices()}. | ||
| * @param attachment May be null. */ | ||
| setAttachment (attachment: Attachment) { | ||
| if (this.attachment == attachment) return; | ||
| this.attachment = attachment; | ||
| this.attachmentTime = this.bone.skeleton.time; | ||
| this.attachmentVertices.length = 0; | ||
| } | ||
| setAttachmentTime (time: number) { | ||
| this.attachmentTime = this.bone.skeleton.time - time; | ||
| } | ||
| /** Returns the time since the attachment was set. */ | ||
| getAttachmentTime (): number { | ||
| return this.bone.skeleton.time - this.attachmentTime; | ||
| } | ||
| setToSetupPose () { | ||
| this.color.setFromColor(this.data.color); | ||
| if (this.darkColor != null) this.darkColor.setFromColor(this.data.darkColor); | ||
| if (this.data.attachmentName == null) | ||
| this.attachment = null; | ||
| else { | ||
| this.attachment = null; | ||
| this.setAttachment(this.bone.skeleton.getAttachment(this.data.index, this.data.attachmentName)); | ||
| } | ||
| } | ||
| } | ||
| } |
| /****************************************************************************** | ||
| * Spine Runtimes Software License | ||
| * Version 2.5 | ||
| * | ||
| * Copyright (c) 2013-2016, Esoteric Software | ||
| * All rights reserved. | ||
| * | ||
| * You are granted a perpetual, non-exclusive, non-sublicensable, and | ||
| * non-transferable license to use, install, execute, and perform the Spine | ||
| * Runtimes software and derivative works solely for personal or internal | ||
| * use. Without the written permission of Esoteric Software (see Section 2 of | ||
| * the Spine Software License Agreement), you may not (a) modify, translate, | ||
| * adapt, or develop new applications using the Spine Runtimes or otherwise | ||
| * create derivative works or improvements of the Spine Runtimes or (b) remove, | ||
| * delete, alter, or obscure any trademarks or any copyright, trademark, patent, | ||
| * or other intellectual property or proprietary rights notices on or in the | ||
| * Software, including any copy thereof. Redistributions in binary or source | ||
| * form must include this license and terms. | ||
| * | ||
| * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR | ||
| * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF | ||
| * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO | ||
| * EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | ||
| * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, | ||
| * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF | ||
| * USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER | ||
| * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) | ||
| * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE | ||
| * POSSIBILITY OF SUCH DAMAGE. | ||
| *****************************************************************************/ | ||
| namespace pixi_spine.core { | ||
| export class SlotData { | ||
| index: number; | ||
| name: string; | ||
| boneData: BoneData; | ||
| color = new Color(1, 1, 1, 1); | ||
| darkColor: Color; | ||
| attachmentName: string; | ||
| blendMode: BlendMode; | ||
| constructor (index: number, name: string, boneData: BoneData) { | ||
| if (index < 0) throw new Error("index must be >= 0."); | ||
| if (name == null) throw new Error("name cannot be null."); | ||
| if (boneData == null) throw new Error("boneData cannot be null."); | ||
| this.index = index; | ||
| this.name = name; | ||
| this.boneData = boneData; | ||
| } | ||
| } | ||
| } |
| /****************************************************************************** | ||
| * Spine Runtimes Software License | ||
| * Version 2.5 | ||
| * | ||
| * Copyright (c) 2013-2016, Esoteric Software | ||
| * All rights reserved. | ||
| * | ||
| * You are granted a perpetual, non-exclusive, non-sublicensable, and | ||
| * non-transferable license to use, install, execute, and perform the Spine | ||
| * Runtimes software and derivative works solely for personal or internal | ||
| * use. Without the written permission of Esoteric Software (see Section 2 of | ||
| * the Spine Software License Agreement), you may not (a) modify, translate, | ||
| * adapt, or develop new applications using the Spine Runtimes or otherwise | ||
| * create derivative works or improvements of the Spine Runtimes or (b) remove, | ||
| * delete, alter, or obscure any trademarks or any copyright, trademark, patent, | ||
| * or other intellectual property or proprietary rights notices on or in the | ||
| * Software, including any copy thereof. Redistributions in binary or source | ||
| * form must include this license and terms. | ||
| * | ||
| * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR | ||
| * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF | ||
| * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO | ||
| * EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | ||
| * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, | ||
| * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF | ||
| * USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER | ||
| * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) | ||
| * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE | ||
| * POSSIBILITY OF SUCH DAMAGE. | ||
| *****************************************************************************/ | ||
| namespace pixi_spine.core { | ||
| export abstract class Texture { | ||
| protected _image: HTMLImageElement; | ||
| constructor (image: HTMLImageElement) { | ||
| this._image = image; | ||
| } | ||
| getImage (): HTMLImageElement { | ||
| return this._image; | ||
| } | ||
| abstract setFilters (minFilter: TextureFilter, magFilter: TextureFilter): void; | ||
| abstract setWraps (uWrap: TextureWrap, vWrap: TextureWrap): void; | ||
| abstract dispose (): void; | ||
| public static filterFromString (text: string): TextureFilter { | ||
| switch (text.toLowerCase()) { | ||
| case "nearest": return TextureFilter.Nearest; | ||
| case "linear": return TextureFilter.Linear; | ||
| case "mipmap": return TextureFilter.MipMap; | ||
| case "mipmapnearestnearest": return TextureFilter.MipMapNearestNearest; | ||
| case "mipmaplinearnearest": return TextureFilter.MipMapLinearNearest; | ||
| case "mipmapnearestlinear": return TextureFilter.MipMapNearestLinear; | ||
| case "mipmaplinearlinear": return TextureFilter.MipMapLinearLinear; | ||
| default: throw new Error(`Unknown texture filter ${text}`); | ||
| } | ||
| } | ||
| public static wrapFromString (text: string): TextureWrap { | ||
| switch (text.toLowerCase()) { | ||
| case "mirroredtepeat": return TextureWrap.MirroredRepeat; | ||
| case "clamptoedge": return TextureWrap.ClampToEdge; | ||
| case "repeat": return TextureWrap.Repeat; | ||
| default: throw new Error(`Unknown texture wrap ${text}`); | ||
| } | ||
| } | ||
| } | ||
| export enum TextureFilter { | ||
| Nearest = 9728, // WebGLRenderingContext.NEAREST | ||
| Linear = 9729, // WebGLRenderingContext.LINEAR | ||
| MipMap = 9987, // WebGLRenderingContext.LINEAR_MIPMAP_LINEAR | ||
| MipMapNearestNearest = 9984, // WebGLRenderingContext.NEAREST_MIPMAP_NEAREST | ||
| MipMapLinearNearest = 9985, // WebGLRenderingContext.LINEAR_MIPMAP_NEAREST | ||
| MipMapNearestLinear = 9986, // WebGLRenderingContext.NEAREST_MIPMAP_LINEAR | ||
| MipMapLinearLinear = 9987 // WebGLRenderingContext.LINEAR_MIPMAP_LINEAR | ||
| } | ||
| export enum TextureWrap { | ||
| MirroredRepeat = 33648, // WebGLRenderingContext.MIRRORED_REPEAT | ||
| ClampToEdge = 33071, // WebGLRenderingContext.CLAMP_TO_EDGE | ||
| Repeat = 10497 // WebGLRenderingContext.REPEAT | ||
| } | ||
| export class TextureRegion { | ||
| texture: PIXI.Texture; | ||
| //thats for overrides | ||
| size: PIXI.Rectangle = null; | ||
| get width(): number { | ||
| const tex = this.texture; | ||
| if (PIXI.VERSION[0] == '3') { | ||
| return (tex as any).crop.width; | ||
| } | ||
| if (tex.trim) { | ||
| return tex.trim.width; | ||
| } | ||
| return tex.orig.width; | ||
| } | ||
| get height(): number { | ||
| const tex = this.texture; | ||
| if (PIXI.VERSION[0] == '3') { | ||
| return (tex as any).crop.height; | ||
| } | ||
| if (tex.trim) { | ||
| return tex.trim.height; | ||
| } | ||
| return tex.orig.height; | ||
| } | ||
| get u(): number { | ||
| return (this.texture as any)._uvs.x0; | ||
| } | ||
| get v(): number { | ||
| return (this.texture as any)._uvs.y0; | ||
| } | ||
| get u2(): number { | ||
| return (this.texture as any)._uvs.x2; | ||
| } | ||
| get v2(): number { | ||
| return (this.texture as any)._uvs.y2; | ||
| } | ||
| get offsetX(): number { | ||
| const tex = this.texture; | ||
| return tex.trim ? tex.trim.x : 0; | ||
| } | ||
| get offsetY(): number { | ||
| console.warn("Deprecation Warning: @Hackerham: I guess, if you are using PIXI-SPINE ATLAS region.offsetY, you want a texture, right? Use region.texture from now on."); | ||
| return this.spineOffsetY; | ||
| } | ||
| get pixiOffsetY(): number { | ||
| const tex = this.texture; | ||
| return tex.trim ? tex.trim.y : 0; | ||
| } | ||
| get spineOffsetY(): number { | ||
| var tex = this.texture; | ||
| return this.originalHeight - this.height - (tex.trim ? tex.trim.y : 0); | ||
| } | ||
| get originalWidth(): number { | ||
| var tex = this.texture; | ||
| if (PIXI.VERSION[0] == '3') { | ||
| if (tex.trim) { | ||
| return tex.trim.width; | ||
| } | ||
| return (tex as any).crop.width; | ||
| } | ||
| return tex.orig.width; | ||
| } | ||
| get originalHeight(): number { | ||
| const tex = this.texture; | ||
| if (PIXI.VERSION[0] == '3') { | ||
| if (tex.trim) { | ||
| return tex.trim.height; | ||
| } | ||
| return (tex as any).crop.height; | ||
| } | ||
| return tex.orig.height; | ||
| } | ||
| get x(): number { | ||
| return this.texture.frame.x; | ||
| } | ||
| get y(): number { | ||
| return this.texture.frame.y; | ||
| } | ||
| get rotate(): boolean { | ||
| return this.texture.rotate !== 0; | ||
| } | ||
| } | ||
| } |
| /****************************************************************************** | ||
| * Spine Runtimes Software License | ||
| * Version 2.5 | ||
| * | ||
| * Copyright (c) 2013-2016, Esoteric Software | ||
| * All rights reserved. | ||
| * | ||
| * You are granted a perpetual, non-exclusive, non-sublicensable, and | ||
| * non-transferable license to use, install, execute, and perform the Spine | ||
| * Runtimes software and derivative works solely for personal or internal | ||
| * use. Without the written permission of Esoteric Software (see Section 2 of | ||
| * the Spine Software License Agreement), you may not (a) modify, translate, | ||
| * adapt, or develop new applications using the Spine Runtimes or otherwise | ||
| * create derivative works or improvements of the Spine Runtimes or (b) remove, | ||
| * delete, alter, or obscure any trademarks or any copyright, trademark, patent, | ||
| * or other intellectual property or proprietary rights notices on or in the | ||
| * Software, including any copy thereof. Redistributions in binary or source | ||
| * form must include this license and terms. | ||
| * | ||
| * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR | ||
| * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF | ||
| * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO | ||
| * EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | ||
| * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, | ||
| * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF | ||
| * USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER | ||
| * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) | ||
| * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE | ||
| * POSSIBILITY OF SUCH DAMAGE. | ||
| *****************************************************************************/ | ||
| namespace pixi_spine.core { | ||
| export class TextureAtlas implements Disposable { | ||
| pages = new Array<TextureAtlasPage>(); | ||
| regions = new Array<TextureAtlasRegion>(); | ||
| constructor(atlasText?: string, textureLoader?: (path: string, loaderFunction: (tex: PIXI.BaseTexture) => any) => any, callback?: (obj: TextureAtlas) => any) { | ||
| if (atlasText) { | ||
| this.addSpineAtlas(atlasText, textureLoader, callback); | ||
| } | ||
| } | ||
| addTexture(name: string, texture: PIXI.Texture) { | ||
| let pages = this.pages; | ||
| let page: TextureAtlasPage = null; | ||
| for (var i = 0; i < pages.length; i++) { | ||
| if (pages[i].baseTexture === texture.baseTexture) { | ||
| page = pages[i]; | ||
| break; | ||
| } | ||
| } | ||
| if (page === null) { | ||
| page = new TextureAtlasPage(); | ||
| page.name = 'texturePage'; | ||
| var baseTexture = texture.baseTexture; | ||
| page.width = baseTexture.realWidth; | ||
| page.height = baseTexture.realHeight; | ||
| page.baseTexture = baseTexture; | ||
| //those fields are not relevant in Pixi | ||
| page.minFilter = page.magFilter = TextureFilter.Nearest; | ||
| page.uWrap = TextureWrap.ClampToEdge; | ||
| page.vWrap = TextureWrap.ClampToEdge; | ||
| pages.push(page); | ||
| } | ||
| var region = new TextureAtlasRegion(); | ||
| region.name = name; | ||
| region.page = page; | ||
| region.texture = texture; | ||
| region.index = -1; | ||
| this.regions.push(region); | ||
| return region; | ||
| } | ||
| addTextureHash(textures: Map<PIXI.Texture>, stripExtension: boolean) { | ||
| for (var key in textures) { | ||
| if (textures.hasOwnProperty(key)) { | ||
| this.addTexture(stripExtension && key.indexOf('.') !== -1 ? key.substr(0, key.lastIndexOf('.')) : key, textures[key]); | ||
| } | ||
| } | ||
| } | ||
| public addSpineAtlas(atlasText: string, textureLoader: (path: string, loaderFunction: (tex: PIXI.BaseTexture) => any) => any, callback: (obj: TextureAtlas) => any) { | ||
| return this.load(atlasText, textureLoader, callback); | ||
| } | ||
| private load(atlasText: string, textureLoader: (path: string, loaderFunction: (tex: PIXI.BaseTexture) => any) => any, callback: (obj: TextureAtlas) => any) { | ||
| if (textureLoader == null) | ||
| throw new Error("textureLoader cannot be null."); | ||
| let reader = new TextureAtlasReader(atlasText); | ||
| let tuple = new Array<string>(4); | ||
| let page: TextureAtlasPage = null; | ||
| let iterateParser = () => { | ||
| while (true) { | ||
| let line = reader.readLine(); | ||
| if (line == null) { | ||
| return callback && callback(this); | ||
| } | ||
| line = line.trim(); | ||
| if (line.length == 0) | ||
| page = null; | ||
| else if (!page) { | ||
| page = new TextureAtlasPage(); | ||
| page.name = line; | ||
| if (reader.readTuple(tuple) == 2) { // size is only optional for an atlas packed with an old TexturePacker. | ||
| page.width = parseInt(tuple[0]); | ||
| page.height = parseInt(tuple[1]); | ||
| reader.readTuple(tuple); | ||
| } | ||
| // page.format = Format[tuple[0]]; we don't need format in WebGL | ||
| reader.readTuple(tuple); | ||
| page.minFilter = Texture.filterFromString(tuple[0]); | ||
| page.magFilter = Texture.filterFromString(tuple[1]); | ||
| let direction = reader.readValue(); | ||
| page.uWrap = TextureWrap.ClampToEdge; | ||
| page.vWrap = TextureWrap.ClampToEdge; | ||
| if (direction == "x") | ||
| page.uWrap = TextureWrap.Repeat; | ||
| else if (direction == "y") | ||
| page.vWrap = TextureWrap.Repeat; | ||
| else if (direction == "xy") | ||
| page.uWrap = page.vWrap = TextureWrap.Repeat; | ||
| textureLoader(line, (texture: PIXI.BaseTexture) => { | ||
| page.baseTexture = texture; | ||
| if (!texture.valid) { | ||
| texture.width = page.width; | ||
| texture.height = page.height; | ||
| } | ||
| this.pages.push(page); | ||
| page.setFilters(); | ||
| if (!page.width || !page.height) { | ||
| page.width = texture.realWidth; | ||
| page.height = texture.realHeight; | ||
| if (!page.width || !page.height) { | ||
| console.log("ERROR spine atlas page " + page.name + ": meshes wont work if you dont specify size in atlas (http://www.html5gamedevs.com/topic/18888-pixi-spines-and-meshes/?p=107121)"); | ||
| } | ||
| } | ||
| iterateParser(); | ||
| }); | ||
| this.pages.push(page); | ||
| break; | ||
| } else { | ||
| let region: TextureAtlasRegion = new TextureAtlasRegion(); | ||
| region.name = line; | ||
| region.page = page; | ||
| let rotate: number = reader.readValue() == "true" ? 6 : 0; | ||
| reader.readTuple(tuple); | ||
| let x = parseInt(tuple[0]); | ||
| let y = parseInt(tuple[1]); | ||
| reader.readTuple(tuple); | ||
| let width = parseInt(tuple[0]); | ||
| let height = parseInt(tuple[1]); | ||
| let resolution = page.baseTexture.resolution; | ||
| x /= resolution; | ||
| y /= resolution; | ||
| width /= resolution; | ||
| height /= resolution; | ||
| let frame = new PIXI.Rectangle(x, y, rotate ? height : width, rotate ? width : height); | ||
| if (reader.readTuple(tuple) == 4) { // split is optional | ||
| // region.splits = new Vector.<int>(parseInt(tuple[0]), parseInt(tuple[1]), parseInt(tuple[2]), parseInt(tuple[3])); | ||
| if (reader.readTuple(tuple) == 4) { // pad is optional, but only present with splits | ||
| //region.pads = Vector.<int>(parseInt(tuple[0]), parseInt(tuple[1]), parseInt(tuple[2]), parseInt(tuple[3])); | ||
| reader.readTuple(tuple); | ||
| } | ||
| } | ||
| let originalWidth = parseInt(tuple[0]) / resolution; | ||
| let originalHeight = parseInt(tuple[1]) / resolution; | ||
| reader.readTuple(tuple); | ||
| let offsetX = parseInt(tuple[0]) / resolution; | ||
| let offsetY = parseInt(tuple[1]) / resolution; | ||
| let orig = new PIXI.Rectangle(0, 0, originalWidth, originalHeight); | ||
| let trim = new PIXI.Rectangle(offsetX, originalHeight - height - offsetY, width, height); | ||
| //TODO: pixiv3 uses different frame/crop/trim | ||
| if (PIXI.VERSION[0] != '3') { | ||
| // pixi v4 or v5 | ||
| region.texture = new PIXI.Texture(region.page.baseTexture, frame, orig, trim, rotate); | ||
| } else { | ||
| // pixi v3.0.11 | ||
| var frame2 = new PIXI.Rectangle(x, y, width, height); | ||
| var crop = frame2.clone(); | ||
| trim.width = originalWidth; | ||
| trim.height = originalHeight; | ||
| region.texture = new PIXI.Texture(region.page.baseTexture, frame2, crop, trim, rotate); | ||
| } | ||
| region.index = parseInt(reader.readValue()); | ||
| region.texture.updateUvs(); | ||
| this.regions.push(region); | ||
| } | ||
| } | ||
| } | ||
| iterateParser(); | ||
| } | ||
| findRegion(name: string): TextureAtlasRegion { | ||
| for (let i = 0; i < this.regions.length; i++) { | ||
| if (this.regions[i].name == name) { | ||
| return this.regions[i]; | ||
| } | ||
| } | ||
| return null; | ||
| } | ||
| dispose() { | ||
| for (let i = 0; i < this.pages.length; i++) { | ||
| this.pages[i].baseTexture.dispose(); | ||
| } | ||
| } | ||
| } | ||
| class TextureAtlasReader { | ||
| lines: Array<string>; | ||
| index: number = 0; | ||
| constructor(text: string) { | ||
| this.lines = text.split(/\r\n|\r|\n/); | ||
| } | ||
| readLine(): string { | ||
| if (this.index >= this.lines.length) | ||
| return null; | ||
| return this.lines[this.index++]; | ||
| } | ||
| readValue(): string { | ||
| let line = this.readLine(); | ||
| let colon = line.indexOf(":"); | ||
| if (colon == -1) | ||
| throw new Error("Invalid line: " + line); | ||
| return line.substring(colon + 1).trim(); | ||
| } | ||
| readTuple(tuple: Array<string>): number { | ||
| let line = this.readLine(); | ||
| let colon = line.indexOf(":"); | ||
| if (colon == -1) | ||
| throw new Error("Invalid line: " + line); | ||
| let i = 0, lastMatch = colon + 1; | ||
| for (; i < 3; i++) { | ||
| let comma = line.indexOf(",", lastMatch); | ||
| if (comma == -1) break; | ||
| tuple[i] = line.substr(lastMatch, comma - lastMatch).trim(); | ||
| lastMatch = comma + 1; | ||
| } | ||
| tuple[i] = line.substring(lastMatch).trim(); | ||
| return i + 1; | ||
| } | ||
| } | ||
| export class TextureAtlasPage { | ||
| name: string; | ||
| minFilter: TextureFilter; | ||
| magFilter: TextureFilter; | ||
| uWrap: TextureWrap; | ||
| vWrap: TextureWrap; | ||
| baseTexture: PIXI.BaseTexture; | ||
| width: number; | ||
| height: number; | ||
| public setFilters() { | ||
| let tex = this.baseTexture; | ||
| let filter = this.minFilter; | ||
| if (filter == TextureFilter.Linear) { | ||
| tex.scaleMode = PIXI.SCALE_MODES.LINEAR; | ||
| } else if (this.minFilter == TextureFilter.Nearest) { | ||
| tex.scaleMode = PIXI.SCALE_MODES.NEAREST; | ||
| } else { | ||
| tex.mipmap = true; | ||
| if (filter == TextureFilter.MipMapNearestNearest) { | ||
| tex.scaleMode = PIXI.SCALE_MODES.NEAREST; | ||
| } else { | ||
| tex.scaleMode = PIXI.SCALE_MODES.LINEAR; | ||
| } | ||
| } | ||
| } | ||
| } | ||
| export class TextureAtlasRegion extends TextureRegion { | ||
| page: TextureAtlasPage; | ||
| name: string; | ||
| index: number; | ||
| } | ||
| } |
| /****************************************************************************** | ||
| * Spine Runtimes Software License | ||
| * Version 2.5 | ||
| * | ||
| * Copyright (c) 2013-2016, Esoteric Software | ||
| * All rights reserved. | ||
| * | ||
| * You are granted a perpetual, non-exclusive, non-sublicensable, and | ||
| * non-transferable license to use, install, execute, and perform the Spine | ||
| * Runtimes software and derivative works solely for personal or internal | ||
| * use. Without the written permission of Esoteric Software (see Section 2 of | ||
| * the Spine Software License Agreement), you may not (a) modify, translate, | ||
| * adapt, or develop new applications using the Spine Runtimes or otherwise | ||
| * create derivative works or improvements of the Spine Runtimes or (b) remove, | ||
| * delete, alter, or obscure any trademarks or any copyright, trademark, patent, | ||
| * or other intellectual property or proprietary rights notices on or in the | ||
| * Software, including any copy thereof. Redistributions in binary or source | ||
| * form must include this license and terms. | ||
| * | ||
| * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR | ||
| * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF | ||
| * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO | ||
| * EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | ||
| * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, | ||
| * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF | ||
| * USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER | ||
| * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) | ||
| * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE | ||
| * POSSIBILITY OF SUCH DAMAGE. | ||
| *****************************************************************************/ | ||
| namespace pixi_spine.core { | ||
| export class TransformConstraint implements Constraint { | ||
| data: TransformConstraintData; | ||
| bones: Array<Bone>; | ||
| target: Bone; | ||
| rotateMix = 0; | ||
| translateMix = 0; | ||
| scaleMix = 0; | ||
| shearMix = 0; | ||
| temp = new Vector2(); | ||
| constructor(data: TransformConstraintData, skeleton: Skeleton) { | ||
| if (data == null) throw new Error("data cannot be null."); | ||
| if (skeleton == null) throw new Error("skeleton cannot be null."); | ||
| this.data = data; | ||
| this.rotateMix = data.rotateMix; | ||
| this.translateMix = data.translateMix; | ||
| this.scaleMix = data.scaleMix; | ||
| this.shearMix = data.shearMix; | ||
| this.bones = new Array<Bone>(); | ||
| for (let i = 0; i < data.bones.length; i++) | ||
| this.bones.push(skeleton.findBone(data.bones[i].name)); | ||
| this.target = skeleton.findBone(data.target.name); | ||
| } | ||
| apply() { | ||
| this.update(); | ||
| } | ||
| update() { | ||
| 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 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 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) - Math.atan2(c, a) + 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 - mat.tx) * translateMix; | ||
| mat.ty += (temp.y - mat.ty) * translateMix; | ||
| modified = true; | ||
| } | ||
| if (scaleMix > 0) { | ||
| 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; | ||
| 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; | ||
| mat.c *= s; | ||
| mat.d *= s; | ||
| modified = true; | ||
| } | ||
| if (shearMix > 0) { | ||
| 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(mat.b, mat.a)); | ||
| if (r > MathUtils.PI) | ||
| r -= MathUtils.PI2; | ||
| else if (r < -MathUtils.PI) | ||
| r += MathUtils.PI2; | ||
| r = by + (r + 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; | ||
| } | ||
| } | ||
| 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() { | ||
| return this.data.order; | ||
| } | ||
| } | ||
| } |
| /****************************************************************************** | ||
| * Spine Runtimes Software License | ||
| * Version 2.5 | ||
| * | ||
| * Copyright (c) 2013-2016, Esoteric Software | ||
| * All rights reserved. | ||
| * | ||
| * You are granted a perpetual, non-exclusive, non-sublicensable, and | ||
| * non-transferable license to use, install, execute, and perform the Spine | ||
| * Runtimes software and derivative works solely for personal or internal | ||
| * use. Without the written permission of Esoteric Software (see Section 2 of | ||
| * the Spine Software License Agreement), you may not (a) modify, translate, | ||
| * adapt, or develop new applications using the Spine Runtimes or otherwise | ||
| * create derivative works or improvements of the Spine Runtimes or (b) remove, | ||
| * delete, alter, or obscure any trademarks or any copyright, trademark, patent, | ||
| * or other intellectual property or proprietary rights notices on or in the | ||
| * Software, including any copy thereof. Redistributions in binary or source | ||
| * form must include this license and terms. | ||
| * | ||
| * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR | ||
| * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF | ||
| * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO | ||
| * EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | ||
| * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, | ||
| * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF | ||
| * USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER | ||
| * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) | ||
| * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE | ||
| * POSSIBILITY OF SUCH DAMAGE. | ||
| *****************************************************************************/ | ||
| namespace pixi_spine.core { | ||
| export class TransformConstraintData { | ||
| name: string; | ||
| order = 0; | ||
| bones = new Array<BoneData>(); | ||
| target: BoneData; | ||
| 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) { | ||
| if (name == null) throw new Error("name cannot be null."); | ||
| this.name = name; | ||
| } | ||
| } | ||
| } |
| /****************************************************************************** | ||
| * Spine Runtimes Software License v2.5 | ||
| * | ||
| * Copyright (c) 2013-2016, Esoteric Software | ||
| * All rights reserved. | ||
| * | ||
| * You are granted a perpetual, non-exclusive, non-sublicensable, and | ||
| * non-transferable license to use, install, execute, and perform the Spine | ||
| * Runtimes software and derivative works solely for personal or internal | ||
| * use. Without the written permission of Esoteric Software (see Section 2 of | ||
| * the Spine Software License Agreement), you may not (a) modify, translate, | ||
| * adapt, or develop new applications using the Spine Runtimes or otherwise | ||
| * create derivative works or improvements of the Spine Runtimes or (b) remove, | ||
| * delete, alter, or obscure any trademarks or any copyright, trademark, patent, | ||
| * or other intellectual property or proprietary rights notices on or in the | ||
| * Software, including any copy thereof. Redistributions in binary or source | ||
| * form must include this license and terms. | ||
| * | ||
| * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR | ||
| * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF | ||
| * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO | ||
| * EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | ||
| * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, | ||
| * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF | ||
| * USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER | ||
| * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) | ||
| * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE | ||
| * POSSIBILITY OF SUCH DAMAGE. | ||
| *****************************************************************************/ | ||
| namespace pixi_spine.core { | ||
| export class Triangulator { | ||
| private convexPolygons = new Array<Array<number>>(); | ||
| private convexPolygonsIndices = new Array<Array<number>>(); | ||
| private indicesArray = new Array<number>(); | ||
| private isConcaveArray = new Array<boolean>(); | ||
| private triangles = new Array<number>(); | ||
| private polygonPool = new Pool<Array<number>>(() => { | ||
| return new Array<number>(); | ||
| }); | ||
| private polygonIndicesPool = new Pool<Array<number>>(() => { | ||
| return new Array<number>(); | ||
| }); | ||
| public triangulate (verticesArray: ArrayLike<number>): Array<number> { | ||
| let vertices = verticesArray; | ||
| let vertexCount = verticesArray.length >> 1; | ||
| let indices = this.indicesArray; | ||
| indices.length = 0; | ||
| for (let i = 0; i < vertexCount; i++) | ||
| indices[i] = i; | ||
| let isConcave = this.isConcaveArray; | ||
| isConcave.length = 0; | ||
| for (let i = 0, n = vertexCount; i < n; ++i) | ||
| isConcave[i] = Triangulator.isConcave(i, vertexCount, vertices, indices); | ||
| let triangles = this.triangles; | ||
| triangles.length = 0; | ||
| while (vertexCount > 3) { | ||
| // Find ear tip. | ||
| let previous = vertexCount - 1, i = 0, next = 1; | ||
| while (true) { | ||
| outer: | ||
| if (!isConcave[i]) { | ||
| let p1 = indices[previous] << 1, p2 = indices[i] << 1, p3 = indices[next] << 1; | ||
| let p1x = vertices[p1], p1y = vertices[p1 + 1]; | ||
| let p2x = vertices[p2], p2y = vertices[p2 + 1]; | ||
| let p3x = vertices[p3], p3y = vertices[p3 + 1]; | ||
| for (let ii = (next + 1) % vertexCount; ii != previous; ii = (ii + 1) % vertexCount) { | ||
| if (!isConcave[ii]) continue; | ||
| let v = indices[ii] << 1; | ||
| let vx = vertices[v], vy = vertices[v + 1]; | ||
| if (Triangulator.positiveArea(p3x, p3y, p1x, p1y, vx, vy)) { | ||
| if (Triangulator.positiveArea(p1x, p1y, p2x, p2y, vx, vy)) { | ||
| if (Triangulator.positiveArea(p2x, p2y, p3x, p3y, vx, vy)) break outer; | ||
| } | ||
| } | ||
| } | ||
| break; | ||
| } | ||
| if (next == 0) { | ||
| do { | ||
| if (!isConcave[i]) break; | ||
| i--; | ||
| } while (i > 0); | ||
| break; | ||
| } | ||
| previous = i; | ||
| i = next; | ||
| next = (next + 1) % vertexCount; | ||
| } | ||
| // Cut ear tip. | ||
| triangles.push(indices[(vertexCount + i - 1) % vertexCount]); | ||
| triangles.push(indices[i]); | ||
| triangles.push(indices[(i + 1) % vertexCount]); | ||
| indices.splice(i, 1); | ||
| isConcave.splice(i, 1); | ||
| vertexCount--; | ||
| let previousIndex = (vertexCount + i - 1) % vertexCount; | ||
| let nextIndex = i == vertexCount ? 0 : i; | ||
| isConcave[previousIndex] = Triangulator.isConcave(previousIndex, vertexCount, vertices, indices); | ||
| isConcave[nextIndex] = Triangulator.isConcave(nextIndex, vertexCount, vertices, indices); | ||
| } | ||
| if (vertexCount == 3) { | ||
| triangles.push(indices[2]); | ||
| triangles.push(indices[0]); | ||
| triangles.push(indices[1]); | ||
| } | ||
| return triangles; | ||
| } | ||
| decompose (verticesArray: Array<number>, triangles: Array<number>) : Array<Array<number>> { | ||
| let vertices = verticesArray; | ||
| let convexPolygons = this.convexPolygons; | ||
| this.polygonPool.freeAll(convexPolygons); | ||
| convexPolygons.length = 0; | ||
| let convexPolygonsIndices = this.convexPolygonsIndices; | ||
| this.polygonIndicesPool.freeAll(convexPolygonsIndices); | ||
| convexPolygonsIndices.length = 0; | ||
| let polygonIndices = this.polygonIndicesPool.obtain(); | ||
| polygonIndices.length = 0; | ||
| let polygon = this.polygonPool.obtain(); | ||
| polygon.length = 0; | ||
| // Merge subsequent triangles if they form a triangle fan. | ||
| let fanBaseIndex = -1, lastWinding = 0; | ||
| for (let i = 0, n = triangles.length; i < n; i += 3) { | ||
| let t1 = triangles[i] << 1, t2 = triangles[i + 1] << 1, t3 = triangles[i + 2] << 1; | ||
| let x1 = vertices[t1], y1 = vertices[t1 + 1]; | ||
| let x2 = vertices[t2], y2 = vertices[t2 + 1]; | ||
| let x3 = vertices[t3], y3 = vertices[t3 + 1]; | ||
| // If the base of the last triangle is the same as this triangle, check if they form a convex polygon (triangle fan). | ||
| let merged = false; | ||
| if (fanBaseIndex == t1) { | ||
| let o = polygon.length - 4; | ||
| let winding1 = Triangulator.winding(polygon[o], polygon[o + 1], polygon[o + 2], polygon[o + 3], x3, y3); | ||
| let winding2 = Triangulator.winding(x3, y3, polygon[0], polygon[1], polygon[2], polygon[3]); | ||
| if (winding1 == lastWinding && winding2 == lastWinding) { | ||
| polygon.push(x3); | ||
| polygon.push(y3); | ||
| polygonIndices.push(t3); | ||
| merged = true; | ||
| } | ||
| } | ||
| // Otherwise make this triangle the new base. | ||
| if (!merged) { | ||
| if (polygon.length > 0) { | ||
| convexPolygons.push(polygon); | ||
| convexPolygonsIndices.push(polygonIndices); | ||
| } else { | ||
| this.polygonPool.free(polygon) | ||
| this.polygonIndicesPool.free(polygonIndices); | ||
| } | ||
| polygon = this.polygonPool.obtain(); | ||
| polygon.length = 0; | ||
| polygon.push(x1); | ||
| polygon.push(y1); | ||
| polygon.push(x2); | ||
| polygon.push(y2); | ||
| polygon.push(x3); | ||
| polygon.push(y3); | ||
| polygonIndices = this.polygonIndicesPool.obtain(); | ||
| polygonIndices.length = 0; | ||
| polygonIndices.push(t1); | ||
| polygonIndices.push(t2); | ||
| polygonIndices.push(t3); | ||
| lastWinding = Triangulator.winding(x1, y1, x2, y2, x3, y3); | ||
| fanBaseIndex = t1; | ||
| } | ||
| } | ||
| if (polygon.length > 0) { | ||
| convexPolygons.push(polygon); | ||
| convexPolygonsIndices.push(polygonIndices); | ||
| } | ||
| // Go through the list of polygons and try to merge the remaining triangles with the found triangle fans. | ||
| for (let i = 0, n = convexPolygons.length; i < n; i++) { | ||
| polygonIndices = convexPolygonsIndices[i]; | ||
| if (polygonIndices.length == 0) continue; | ||
| let firstIndex = polygonIndices[0]; | ||
| let lastIndex = polygonIndices[polygonIndices.length - 1]; | ||
| polygon = convexPolygons[i]; | ||
| let o = polygon.length - 4; | ||
| let prevPrevX = polygon[o], prevPrevY = polygon[o + 1]; | ||
| let prevX = polygon[o + 2], prevY = polygon[o + 3]; | ||
| let firstX = polygon[0], firstY = polygon[1]; | ||
| let secondX = polygon[2], secondY = polygon[3]; | ||
| let winding = Triangulator.winding(prevPrevX, prevPrevY, prevX, prevY, firstX, firstY); | ||
| for (let ii = 0; ii < n; ii++) { | ||
| if (ii == i) continue; | ||
| let otherIndices = convexPolygonsIndices[ii]; | ||
| if (otherIndices.length != 3) continue; | ||
| let otherFirstIndex = otherIndices[0]; | ||
| let otherSecondIndex = otherIndices[1]; | ||
| let otherLastIndex = otherIndices[2]; | ||
| let otherPoly = convexPolygons[ii]; | ||
| let x3 = otherPoly[otherPoly.length - 2], y3 = otherPoly[otherPoly.length - 1]; | ||
| if (otherFirstIndex != firstIndex || otherSecondIndex != lastIndex) continue; | ||
| let winding1 = Triangulator.winding(prevPrevX, prevPrevY, prevX, prevY, x3, y3); | ||
| let winding2 = Triangulator.winding(x3, y3, firstX, firstY, secondX, secondY); | ||
| if (winding1 == winding && winding2 == winding) { | ||
| otherPoly.length = 0; | ||
| otherIndices.length = 0; | ||
| polygon.push(x3); | ||
| polygon.push(y3); | ||
| polygonIndices.push(otherLastIndex); | ||
| prevPrevX = prevX; | ||
| prevPrevY = prevY; | ||
| prevX = x3; | ||
| prevY = y3; | ||
| ii = 0; | ||
| } | ||
| } | ||
| } | ||
| // Remove empty polygons that resulted from the merge step above. | ||
| for (let i = convexPolygons.length - 1; i >= 0; i--) { | ||
| polygon = convexPolygons[i]; | ||
| if (polygon.length == 0) { | ||
| convexPolygons.splice(i, 1); | ||
| this.polygonPool.free(polygon); | ||
| polygonIndices = convexPolygonsIndices[i] | ||
| convexPolygonsIndices.splice(i, 1) | ||
| this.polygonIndicesPool.free(polygonIndices); | ||
| } | ||
| } | ||
| return convexPolygons; | ||
| } | ||
| private static isConcave (index: number, vertexCount: number, vertices: ArrayLike<number>, indices: ArrayLike<number>): boolean { | ||
| let previous = indices[(vertexCount + index - 1) % vertexCount] << 1; | ||
| let current = indices[index] << 1; | ||
| let next = indices[(index + 1) % vertexCount] << 1; | ||
| return !this.positiveArea(vertices[previous], vertices[previous + 1], vertices[current], vertices[current + 1], vertices[next], | ||
| vertices[next + 1]); | ||
| } | ||
| private static positiveArea (p1x: number, p1y: number, p2x: number, p2y: number, p3x: number, p3y: number): boolean { | ||
| return p1x * (p3y - p2y) + p2x * (p1y - p3y) + p3x * (p2y - p1y) >= 0; | ||
| } | ||
| private static winding (p1x: number, p1y: number, p2x: number, p2y: number, p3x: number, p3y: number): number { | ||
| let px = p2x - p1x, py = p2y - p1y; | ||
| return p3x * py - p3y * px + px * p1y - p1x * py >= 0 ? 1 : -1; | ||
| } | ||
| } | ||
| } |
| /****************************************************************************** | ||
| * Spine Runtimes Software License | ||
| * Version 2.5 | ||
| * | ||
| * Copyright (c) 2013-2016, Esoteric Software | ||
| * All rights reserved. | ||
| * | ||
| * You are granted a perpetual, non-exclusive, non-sublicensable, and | ||
| * non-transferable license to use, install, execute, and perform the Spine | ||
| * Runtimes software and derivative works solely for personal or internal | ||
| * use. Without the written permission of Esoteric Software (see Section 2 of | ||
| * the Spine Software License Agreement), you may not (a) modify, translate, | ||
| * adapt, or develop new applications using the Spine Runtimes or otherwise | ||
| * create derivative works or improvements of the Spine Runtimes or (b) remove, | ||
| * delete, alter, or obscure any trademarks or any copyright, trademark, patent, | ||
| * or other intellectual property or proprietary rights notices on or in the | ||
| * Software, including any copy thereof. Redistributions in binary or source | ||
| * form must include this license and terms. | ||
| * | ||
| * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR | ||
| * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF | ||
| * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO | ||
| * EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | ||
| * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, | ||
| * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF | ||
| * USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER | ||
| * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) | ||
| * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE | ||
| * POSSIBILITY OF SUCH DAMAGE. | ||
| *****************************************************************************/ | ||
| namespace pixi_spine.core { | ||
| export interface Updatable { | ||
| update(): void; | ||
| } | ||
| } |
| /****************************************************************************** | ||
| * Spine Runtimes Software License v2.5 | ||
| * | ||
| * Copyright (c) 2013-2016, Esoteric Software | ||
| * All rights reserved. | ||
| * | ||
| * You are granted a perpetual, non-exclusive, non-sublicensable, and | ||
| * non-transferable license to use, install, execute, and perform the Spine | ||
| * Runtimes software and derivative works solely for personal or internal | ||
| * use. Without the written permission of Esoteric Software (see Section 2 of | ||
| * the Spine Software License Agreement), you may not (a) modify, translate, | ||
| * adapt, or develop new applications using the Spine Runtimes or otherwise | ||
| * create derivative works or improvements of the Spine Runtimes or (b) remove, | ||
| * delete, alter, or obscure any trademarks or any copyright, trademark, patent, | ||
| * or other intellectual property or proprietary rights notices on or in the | ||
| * Software, including any copy thereof. Redistributions in binary or source | ||
| * form must include this license and terms. | ||
| * | ||
| * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR | ||
| * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF | ||
| * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO | ||
| * EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | ||
| * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, | ||
| * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF | ||
| * USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER | ||
| * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) | ||
| * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE | ||
| * POSSIBILITY OF SUCH DAMAGE. | ||
| *****************************************************************************/ | ||
| namespace pixi_spine.core { | ||
| export interface Map<T> { | ||
| [key: string]: T; | ||
| } | ||
| export class IntSet { | ||
| array = new Array<number>(); | ||
| 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; | ||
| } | ||
| remove (value: number) { | ||
| this.array[value | 0] = undefined; | ||
| } | ||
| clear () { | ||
| this.array.length = 0; | ||
| } | ||
| } | ||
| export interface Disposable { | ||
| dispose (): void; | ||
| } | ||
| export interface Restorable { | ||
| restore (): 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); | ||
| constructor (public r: number = 0, public g: number = 0, public b: number = 0, public a: number = 0) { | ||
| } | ||
| 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; | ||
| } | ||
| setFromColor (c: Color) { | ||
| this.r = c.r; | ||
| this.g = c.g; | ||
| this.b = c.b; | ||
| this.a = c.a; | ||
| 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; | ||
| } | ||
| 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; | ||
| } | ||
| clamp () { | ||
| if (this.r < 0) this.r = 0; | ||
| else if (this.r > 1) this.r = 1; | ||
| if (this.g < 0) this.g = 0; | ||
| else if (this.g > 1) this.g = 1; | ||
| if (this.b < 0) this.b = 0; | ||
| else if (this.b > 1) this.b = 1; | ||
| if (this.a < 0) this.a = 0; | ||
| else if (this.a > 1) this.a = 1; | ||
| return this; | ||
| } | ||
| } | ||
| 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 clamp (value: number, min: number, max: number) { | ||
| if (value < min) return min; | ||
| if (value > max) return max; | ||
| return value; | ||
| } | ||
| static cosDeg (degrees: number) { | ||
| return Math.cos(degrees * MathUtils.degRad); | ||
| } | ||
| static sinDeg (degrees: number) { | ||
| return Math.sin(degrees * MathUtils.degRad); | ||
| } | ||
| static signum (value: number): number { | ||
| return value > 0 ? 1 : value < 0 ? -1 : 0; | ||
| } | ||
| static toInt (x: number) { | ||
| return x > 0 ? Math.floor(x) : Math.ceil(x); | ||
| } | ||
| static cbrt (x: number) { | ||
| let y = Math.pow(Math.abs(x), 1/3); | ||
| return x < 0 ? -y : y; | ||
| } | ||
| static randomTriangular (min: number, max: number): number { | ||
| return MathUtils.randomTriangularWith(min, max, (min + max) * 0.5); | ||
| } | ||
| 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)); | ||
| } | ||
| } | ||
| 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); | ||
| } | ||
| } | ||
| export class Pow extends Interpolation { | ||
| protected power = 2; | ||
| constructor (power: number) { | ||
| super(); | ||
| this.power = power; | ||
| } | ||
| 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 PowOut extends Pow { | ||
| constructor (power: number) { | ||
| super(power); | ||
| } | ||
| applyInternal (a: number) : number { | ||
| return Math.pow(a - 1, this.power) * (this.power % 2 == 0 ? -1 : 1) + 1; | ||
| } | ||
| } | ||
| export class Utils { | ||
| static SUPPORTS_TYPED_ARRAYS = typeof(Float32Array) !== "undefined"; | ||
| 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 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 ensureArrayCapacity<T> (array: Array<T>, size: number, value: any = 0): Array<T> { | ||
| if (array.length >= size) return array; | ||
| return Utils.setArraySize(array, size, value); | ||
| } | ||
| 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; | ||
| } | ||
| 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; | ||
| } | ||
| } | ||
| 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; | ||
| } | ||
| } | ||
| static toFloatArray (array: Array<number>) { | ||
| return Utils.SUPPORTS_TYPED_ARRAYS ? new Float32Array(array) : array; | ||
| } | ||
| static toSinglePrecision (value: number) { | ||
| return Utils.SUPPORTS_TYPED_ARRAYS ? Math.fround(value) : value; | ||
| } | ||
| } | ||
| 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 Pool<T> { | ||
| private items = new Array<T>(); | ||
| private instantiator: () => T; | ||
| constructor (instantiator: () => T) { | ||
| this.instantiator = instantiator; | ||
| } | ||
| obtain () { | ||
| return this.items.length > 0 ? this.items.pop() : this.instantiator(); | ||
| } | ||
| 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; | ||
| } | ||
| } | ||
| } | ||
| } |
| /****************************************************************************** | ||
| * Spine Runtimes Software License v2.5 | ||
| * | ||
| * Copyright (c) 2013-2016, Esoteric Software | ||
| * All rights reserved. | ||
| * | ||
| * You are granted a perpetual, non-exclusive, non-sublicensable, and | ||
| * non-transferable license to use, install, execute, and perform the Spine | ||
| * Runtimes software and derivative works solely for personal or internal | ||
| * use. Without the written permission of Esoteric Software (see Section 2 of | ||
| * the Spine Software License Agreement), you may not (a) modify, translate, | ||
| * adapt, or develop new applications using the Spine Runtimes or otherwise | ||
| * create derivative works or improvements of the Spine Runtimes or (b) remove, | ||
| * delete, alter, or obscure any trademarks or any copyright, trademark, patent, | ||
| * or other intellectual property or proprietary rights notices on or in the | ||
| * Software, including any copy thereof. Redistributions in binary or source | ||
| * form must include this license and terms. | ||
| * | ||
| * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR | ||
| * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF | ||
| * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO | ||
| * EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | ||
| * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, | ||
| * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF | ||
| * USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER | ||
| * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) | ||
| * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE | ||
| * POSSIBILITY OF SUCH DAMAGE. | ||
| *****************************************************************************/ | ||
| namespace pixi_spine.core { | ||
| export interface VertexEffect { | ||
| begin(skeleton: Skeleton): void; | ||
| transform(position: Vector2, uv: Vector2, light: Color, dark: Color): void; | ||
| end(): void; | ||
| } | ||
| } |
| /****************************************************************************** | ||
| * Spine Runtimes Software License v2.5 | ||
| * | ||
| * Copyright (c) 2013-2016, Esoteric Software | ||
| * All rights reserved. | ||
| * | ||
| * You are granted a perpetual, non-exclusive, non-sublicensable, and | ||
| * non-transferable license to use, install, execute, and perform the Spine | ||
| * Runtimes software and derivative works solely for personal or internal | ||
| * use. Without the written permission of Esoteric Software (see Section 2 of | ||
| * the Spine Software License Agreement), you may not (a) modify, translate, | ||
| * adapt, or develop new applications using the Spine Runtimes or otherwise | ||
| * create derivative works or improvements of the Spine Runtimes or (b) remove, | ||
| * delete, alter, or obscure any trademarks or any copyright, trademark, patent, | ||
| * or other intellectual property or proprietary rights notices on or in the | ||
| * Software, including any copy thereof. Redistributions in binary or source | ||
| * form must include this license and terms. | ||
| * | ||
| * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR | ||
| * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF | ||
| * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO | ||
| * EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | ||
| * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, | ||
| * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF | ||
| * USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER | ||
| * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) | ||
| * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE | ||
| * POSSIBILITY OF SUCH DAMAGE. | ||
| *****************************************************************************/ | ||
| namespace pixi_spine.core { | ||
| export class JitterEffect implements VertexEffect { | ||
| jitterX = 0; | ||
| jitterY = 0; | ||
| constructor (jitterX: number, jitterY: number) { | ||
| this.jitterX = jitterX; | ||
| this.jitterY = jitterY; | ||
| } | ||
| begin(skeleton: Skeleton): void { | ||
| } | ||
| transform(position: Vector2, uv: Vector2, light: Color, dark: Color): void { | ||
| position.x += MathUtils.randomTriangular(-this.jitterX, this.jitterY); | ||
| position.y += MathUtils.randomTriangular(-this.jitterX, this.jitterY); | ||
| } | ||
| end(): void { | ||
| } | ||
| } | ||
| } |
| /****************************************************************************** | ||
| * Spine Runtimes Software License v2.5 | ||
| * | ||
| * Copyright (c) 2013-2016, Esoteric Software | ||
| * All rights reserved. | ||
| * | ||
| * You are granted a perpetual, non-exclusive, non-sublicensable, and | ||
| * non-transferable license to use, install, execute, and perform the Spine | ||
| * Runtimes software and derivative works solely for personal or internal | ||
| * use. Without the written permission of Esoteric Software (see Section 2 of | ||
| * the Spine Software License Agreement), you may not (a) modify, translate, | ||
| * adapt, or develop new applications using the Spine Runtimes or otherwise | ||
| * create derivative works or improvements of the Spine Runtimes or (b) remove, | ||
| * delete, alter, or obscure any trademarks or any copyright, trademark, patent, | ||
| * or other intellectual property or proprietary rights notices on or in the | ||
| * Software, including any copy thereof. Redistributions in binary or source | ||
| * form must include this license and terms. | ||
| * | ||
| * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR | ||
| * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF | ||
| * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO | ||
| * EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | ||
| * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, | ||
| * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF | ||
| * USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER | ||
| * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) | ||
| * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE | ||
| * POSSIBILITY OF SUCH DAMAGE. | ||
| *****************************************************************************/ | ||
| namespace pixi_spine.core { | ||
| export class SwirlEffect implements VertexEffect { | ||
| static interpolation = new PowOut(2); | ||
| centerX = 0; | ||
| centerY = 0; | ||
| radius = 0; | ||
| angle = 0; | ||
| private worldX = 0; | ||
| private worldY = 0; | ||
| constructor (radius: number) { | ||
| this.radius = radius; | ||
| } | ||
| begin(skeleton: Skeleton): void { | ||
| this.worldX = skeleton.x + this.centerX; | ||
| this.worldY = skeleton.y + this.centerY; | ||
| } | ||
| transform(position: Vector2, uv: Vector2, light: Color, dark: Color): void { | ||
| let radAngle = this.angle * MathUtils.degreesToRadians; | ||
| let x = position.x - this.worldX; | ||
| let y = position.y - this.worldY; | ||
| let dist = Math.sqrt(x * x + y * y); | ||
| if (dist < this.radius) { | ||
| let theta = SwirlEffect.interpolation.apply(0, radAngle, (this.radius - dist) / this.radius); | ||
| let cos = Math.cos(theta); | ||
| let sin = Math.sin(theta); | ||
| position.x = cos * x - sin * y + this.worldX; | ||
| position.y = sin * x + cos * y + this.worldY; | ||
| } | ||
| } | ||
| end(): void { | ||
| } | ||
| } | ||
| } |
| namespace pixi_spine { | ||
| (PIXI as any).spine = pixi_spine; | ||
| } |
-124
| namespace pixi_spine { | ||
| function isJson(resource: PIXI.LoaderResource) { | ||
| return resource.type === PIXI.LoaderResource.TYPE.JSON; | ||
| } | ||
| export class AtlasParser { | ||
| static use(this: PIXI.Loader, resource: PIXI.LoaderResource, next: () => any) { | ||
| // skip if no data, its not json, or it isn't atlas data | ||
| if (!resource.data || | ||
| !isJson(resource) || | ||
| !resource.data.bones) { | ||
| return next(); | ||
| } | ||
| const metadata = resource.metadata || {}; | ||
| const metadataSkeletonScale = metadata ? resource.metadata.spineSkeletonScale : null; | ||
| const metadataAtlas = metadata ? resource.metadata.spineAtlas : null; | ||
| if (metadataAtlas === false) { | ||
| return next(); | ||
| } | ||
| if (metadataAtlas && metadataAtlas.pages) { | ||
| //its an atlas! | ||
| const spineJsonParser = new core.SkeletonJson(new core.AtlasAttachmentLoader(metadataAtlas)); | ||
| const skeletonData = spineJsonParser.readSkeletonData(resource.data); | ||
| resource.spineData = skeletonData; | ||
| resource.spineAtlas = metadataAtlas; | ||
| return next(); | ||
| } | ||
| const metadataAtlasSuffix = metadata.spineAtlasSuffix || '.atlas'; | ||
| /** | ||
| * use a bit of hackery to load the atlas file, here we assume that the .json, .atlas and .png files | ||
| * that correspond to the spine file are in the same base URL and that the .json and .atlas files | ||
| * have the same name | ||
| */ | ||
| let atlasPath = resource.url.substr(0, resource.url.lastIndexOf('.')) + metadataAtlasSuffix; | ||
| // use atlas path as a params. (no need to use same atlas file name with json file name) | ||
| if (resource.metadata && resource.metadata.spineAtlasFile) { | ||
| atlasPath = resource.metadata.spineAtlasFile; | ||
| } | ||
| //remove the baseUrl | ||
| atlasPath = atlasPath.replace(this.baseUrl, ''); | ||
| const atlasOptions = { | ||
| crossOrigin: resource.crossOrigin, | ||
| xhrType: PIXI.LoaderResource.XHR_RESPONSE_TYPE.TEXT, | ||
| metadata: metadata.spineMetadata || null, | ||
| parentResource: resource | ||
| }; | ||
| const imageOptions = { | ||
| crossOrigin: resource.crossOrigin, | ||
| metadata: metadata.imageMetadata || null, | ||
| parentResource: resource | ||
| }; | ||
| let baseUrl = resource.url.substr(0, resource.url.lastIndexOf('/') + 1); | ||
| //remove the baseUrl | ||
| baseUrl = baseUrl.replace(this.baseUrl, ''); | ||
| 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); | ||
| const createSkeletonWithRawAtlas = function (rawData: string) { | ||
| new core.TextureAtlas(rawData, adapter, function (spineAtlas) { | ||
| const spineJsonParser = new pixi_spine.core.SkeletonJson(new pixi_spine.core.AtlasAttachmentLoader(spineAtlas)); | ||
| if (metadataSkeletonScale) { | ||
| spineJsonParser.scale = metadataSkeletonScale; | ||
| } | ||
| resource.spineData = spineJsonParser.readSkeletonData(resource.data); | ||
| resource.spineAtlas = spineAtlas; | ||
| next(); | ||
| }); | ||
| }; | ||
| if (resource.metadata && resource.metadata.atlasRawData) { | ||
| createSkeletonWithRawAtlas(resource.metadata.atlasRawData) | ||
| } else { | ||
| this.add(resource.name + '_atlas', atlasPath, atlasOptions, function (atlasResource: any) { | ||
| createSkeletonWithRawAtlas(atlasResource.xhr.responseText); | ||
| }); | ||
| } | ||
| } | ||
| } | ||
| export function imageLoaderAdapter(loader: any, namePrefix: any, baseUrl: any, imageOptions: any) { | ||
| if (baseUrl && baseUrl.lastIndexOf('/') !== (baseUrl.length - 1)) { | ||
| baseUrl += '/'; | ||
| } | ||
| return function (line: string, callback: (baseTexture: PIXI.BaseTexture) => any) { | ||
| const name = namePrefix + line; | ||
| const url = baseUrl + line; | ||
| loader.add(name, url, imageOptions, (resource: PIXI.LoaderResource) => { | ||
| callback(resource.texture.baseTexture); | ||
| }); | ||
| } | ||
| } | ||
| export function syncImageLoaderAdapter(baseUrl: any, crossOrigin: any) { | ||
| if (baseUrl && baseUrl.lastIndexOf('/') !== (baseUrl.length - 1)) { | ||
| baseUrl += '/'; | ||
| } | ||
| return function (line: any, callback: any) { | ||
| callback(PIXI.BaseTexture.from(line, { resourceOptions: { crossOrigin } })); | ||
| } | ||
| } | ||
| 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.Loader.registerPlugin(AtlasParser); | ||
| } |
| interface Math { | ||
| fround(n: number): number; | ||
| } | ||
| (() => { | ||
| if (!Math.fround) { | ||
| Math.fround = Math.fround = (function(array) { | ||
| return function(x: number) { | ||
| return array[0] = x, array[0]; | ||
| }; | ||
| })(new Float32Array(1)); | ||
| } | ||
| })(); |
-621
| /// <reference types="pixi.js" /> | ||
| /// <reference path="polyfills.ts" /> | ||
| /// <reference path="core/Bone.ts" /> | ||
| namespace pixi_spine { | ||
| /* Esoteric Software SPINE wrapper for pixi.js */ | ||
| core.Bone.yDown = true; | ||
| let tempRgb = [0, 0, 0]; | ||
| export class SpineSprite extends PIXI.Sprite { | ||
| region: core.TextureRegion = null; | ||
| } | ||
| export class SpineMesh extends PIXI.Mesh { | ||
| region: core.TextureRegion; | ||
| constructor(texture: PIXI.Texture, vertices?: Float32Array, uvs?: Float32Array, indices?: Uint16Array, drawMode?: number) { | ||
| super(texture, vertices, uvs, indices, drawMode); | ||
| } | ||
| } | ||
| /** | ||
| * A class that enables the you to import and run your spine animations in pixi. | ||
| * The Spine animation data needs to be loaded using either the Loader or a SpineLoader before it can be used by this class | ||
| * See example 12 (http://www.goodboydigital.com/pixijs/examples/12/) to see a working example and check out the source | ||
| * | ||
| * ```js | ||
| * let spineAnimation = new spine(spineData); | ||
| * ``` | ||
| * | ||
| * @class | ||
| * @extends Container | ||
| * @memberof spine | ||
| * @param spineData {object} The spine data loaded from a spine atlas. | ||
| */ | ||
| export class Spine extends PIXI.Container { | ||
| static globalAutoUpdate: boolean = true; | ||
| tintRgb: ArrayLike<number>; | ||
| spineData: core.SkeletonData; | ||
| skeleton: core.Skeleton; | ||
| stateData: core.AnimationStateData; | ||
| state: core.AnimationState; | ||
| slotContainers: Array<PIXI.Container>; | ||
| tempClipContainers: Array<PIXI.Container>; | ||
| constructor(spineData: core.SkeletonData) { | ||
| super(); | ||
| if (!spineData) { | ||
| throw new Error('The spineData param is required.'); | ||
| } | ||
| if ((typeof spineData) === "string") { | ||
| throw new Error('spineData param cant be string. Please use spine.Spine.fromAtlas("YOUR_RESOURCE_NAME") from now on.'); | ||
| } | ||
| /** | ||
| * The spineData object | ||
| * | ||
| * @member {object} | ||
| */ | ||
| this.spineData = spineData; | ||
| /** | ||
| * A spine Skeleton object | ||
| * | ||
| * @member {object} | ||
| */ | ||
| this.skeleton = new core.Skeleton(spineData); | ||
| this.skeleton.updateWorldTransform(); | ||
| /** | ||
| * A spine AnimationStateData object created from the spine data passed in the constructor | ||
| * | ||
| * @member {object} | ||
| */ | ||
| this.stateData = new core.AnimationStateData(spineData); | ||
| /** | ||
| * A spine AnimationState object created from the spine AnimationStateData object | ||
| * | ||
| * @member {object} | ||
| */ | ||
| this.state = new core.AnimationState(this.stateData); | ||
| /** | ||
| * An array of containers | ||
| * | ||
| * @member {Container[]} | ||
| */ | ||
| this.slotContainers = []; | ||
| 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 = this.newContainer(); | ||
| this.slotContainers.push(slotContainer); | ||
| this.addChild(slotContainer); | ||
| this.tempClipContainers.push(null); | ||
| if (attachment instanceof core.RegionAttachment) { | ||
| let spriteName = (attachment.region as core.TextureAtlasRegion).name; | ||
| let sprite = this.createSprite(slot, attachment, spriteName); | ||
| slot.currentSprite = sprite; | ||
| slot.currentSpriteName = spriteName; | ||
| slotContainer.addChild(sprite); | ||
| } | ||
| else if (attachment instanceof core.MeshAttachment) { | ||
| let mesh = this.createMesh(slot, attachment); | ||
| slot.currentMesh = mesh; | ||
| slot.currentMeshName = attachment.name; | ||
| slotContainer.addChild(mesh); | ||
| } | ||
| else if (attachment instanceof core.ClippingAttachment) { | ||
| this.createGraphics(slot, attachment); | ||
| slotContainer.addChild(slot.clippingContainer); | ||
| slotContainer.addChild(slot.currentGraphics); | ||
| } | ||
| else { | ||
| continue; | ||
| } | ||
| } | ||
| /** | ||
| * Should the Spine object update its transforms | ||
| * | ||
| * @member {boolean} | ||
| */ | ||
| this.autoUpdate = true; | ||
| /** | ||
| * The tint applied to all spine slots. This is a [r,g,b] value. A value of [1,1,1] will remove any tint effect. | ||
| * | ||
| * @member {number} | ||
| * @memberof spine.Spine# | ||
| */ | ||
| this.tintRgb = new Float32Array([1, 1, 1]); | ||
| } | ||
| /** | ||
| * If this flag is set to true, the spine animation will be autoupdated every time | ||
| * the object id drawn. The down side of this approach is that the delta time is | ||
| * automatically calculated and you could miss out on cool effects like slow motion, | ||
| * pause, skip ahead and the sorts. Most of these effects can be achieved even with | ||
| * autoupdate enabled but are harder to achieve. | ||
| * | ||
| * @member {boolean} | ||
| * @memberof spine.Spine# | ||
| * @default true | ||
| */ | ||
| get autoUpdate(): boolean { | ||
| return (this.updateTransform === Spine.prototype.autoUpdateTransform); | ||
| } | ||
| set autoUpdate(value: boolean) { | ||
| this.updateTransform = value ? Spine.prototype.autoUpdateTransform : PIXI.Container.prototype.updateTransform; | ||
| } | ||
| /** | ||
| * The tint applied to the spine object. This is a hex value. A value of 0xFFFFFF will remove any tint effect. | ||
| * | ||
| * @member {number} | ||
| * @memberof spine.Spine# | ||
| * @default 0xFFFFFF | ||
| */ | ||
| get tint(): number { | ||
| return PIXI.utils.rgb2hex(this.tintRgb as any); | ||
| } | ||
| set tint(value: number) { | ||
| this.tintRgb = PIXI.utils.hex2rgb(value, this.tintRgb as any); | ||
| } | ||
| /** | ||
| * Update the spine skeleton and its animations by delta time (dt) | ||
| * | ||
| * @param dt {number} Delta time. Time by which the animation should be updated | ||
| */ | ||
| update(dt: number) { | ||
| this.state.update(dt); | ||
| this.state.apply(this.skeleton); | ||
| this.skeleton.updateWorldTransform(); | ||
| let slots = this.skeleton.slots; | ||
| // in case pixi has double tint | ||
| let globalClr = (this as any).color; | ||
| let light: ArrayLike<number> = null, dark: ArrayLike<number> = null; | ||
| if (globalClr) { | ||
| light = globalClr.light; | ||
| dark = globalClr.dark; | ||
| } else { | ||
| light = this.tintRgb; | ||
| } | ||
| let thack = false; | ||
| for (let i = 0, n = slots.length; i < n; i++) { | ||
| let slot = slots[i]; | ||
| let attachment = slot.attachment; | ||
| let slotContainer = this.slotContainers[i]; | ||
| if (!attachment) { | ||
| slotContainer.visible = false; | ||
| continue; | ||
| } | ||
| let spriteColor: any = null; | ||
| let attColor = (attachment as any).color; | ||
| if (attachment instanceof core.RegionAttachment) { | ||
| let region = (attachment as core.RegionAttachment).region; | ||
| if (region) { | ||
| if (slot.currentMesh) { | ||
| slot.currentMesh.visible = false; | ||
| slot.currentMesh = null; | ||
| slot.currentMeshName = undefined; | ||
| } | ||
| let ar = region as core.TextureAtlasRegion; | ||
| if (!slot.currentSpriteName || slot.currentSpriteName !== ar.name) { | ||
| let spriteName = ar.name; | ||
| if (slot.currentSprite) { | ||
| slot.currentSprite.visible = false; | ||
| } | ||
| slot.sprites = slot.sprites || {}; | ||
| if (slot.sprites[spriteName] !== undefined) { | ||
| slot.sprites[spriteName].visible = true; | ||
| } | ||
| else { | ||
| let sprite = this.createSprite(slot, attachment, spriteName); | ||
| slotContainer.addChild(sprite); | ||
| } | ||
| slot.currentSprite = slot.sprites[spriteName]; | ||
| slot.currentSpriteName = spriteName; | ||
| } | ||
| } | ||
| if (slotContainer.transform) { | ||
| //TODO: refactor this thing, switch it on and off for container | ||
| let transform = slotContainer.transform; | ||
| transform.setFromMatrix(slot.bone.matrix); | ||
| } else { | ||
| //PIXI v3 | ||
| let lt = slotContainer.localTransform || new PIXI.Matrix(); | ||
| slot.bone.matrix.copyTo(lt); | ||
| slotContainer.localTransform = lt; | ||
| (slotContainer as any).displayObjectUpdateTransform = SlotContainerUpdateTransformV3; | ||
| } | ||
| if (slot.currentSprite.color) { | ||
| //YAY! double - tint! | ||
| spriteColor = slot.currentSprite.color; | ||
| } else { | ||
| tempRgb[0] = light[0] * slot.color.r * attColor.r; | ||
| tempRgb[1] = light[1] * slot.color.g * attColor.g; | ||
| tempRgb[2] = light[2] * slot.color.b * attColor.b; | ||
| slot.currentSprite.tint = PIXI.utils.rgb2hex(tempRgb); | ||
| } | ||
| slot.currentSprite.blendMode = slot.blendMode; | ||
| } | ||
| else if (attachment instanceof core.MeshAttachment) { | ||
| if (slot.currentSprite) { | ||
| //TODO: refactor this thing, switch it on and off for container | ||
| slot.currentSprite.visible = false; | ||
| slot.currentSprite = null; | ||
| slot.currentSpriteName = undefined; | ||
| } | ||
| if (!slot.currentMeshName || slot.currentMeshName !== attachment.name) { | ||
| let meshName = attachment.name; | ||
| if (slot.currentMesh) { | ||
| slot.currentMesh.visible = false; | ||
| } | ||
| slot.meshes = slot.meshes || {}; | ||
| if (slot.meshes[meshName] !== undefined) { | ||
| slot.meshes[meshName].visible = true; | ||
| } | ||
| else { | ||
| let mesh = this.createMesh(slot, attachment); | ||
| slotContainer.addChild(mesh); | ||
| } | ||
| slot.currentMesh = slot.meshes[meshName]; | ||
| slot.currentMeshName = meshName; | ||
| } | ||
| (attachment as core.VertexAttachment).computeWorldVerticesOld(slot, slot.currentMesh.vertices); | ||
| if (slot.currentMesh.color) { | ||
| spriteColor = slot.currentMesh.color; | ||
| } else if (PIXI.VERSION[0] !== '3') { | ||
| // PIXI version 4 | ||
| // slot.currentMesh.dirty++; | ||
| //only for PIXI v4 | ||
| let tintRgb = slot.currentMesh.tintRgb; | ||
| tintRgb[0] = light[0] * slot.color.r * attColor.r; | ||
| tintRgb[1] = light[1] * slot.color.g * attColor.g; | ||
| tintRgb[2] = light[2] * slot.color.b * attColor.b; | ||
| } | ||
| slot.currentMesh.blendMode = slot.blendMode; | ||
| } | ||
| 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 { | ||
| slotContainer.visible = false; | ||
| continue; | ||
| } | ||
| slotContainer.visible = true; | ||
| // pixi has double tint | ||
| if (spriteColor) { | ||
| let r0 = slot.color.r * attColor.r; | ||
| let g0 = slot.color.g * attColor.g; | ||
| let b0 = slot.color.b * attColor.b; | ||
| //YAY! double-tint! | ||
| spriteColor.setLight( | ||
| light[0] * r0 + dark[0] * (1.0 - r0), | ||
| light[1] * g0 + dark[1] * (1.0 - g0), | ||
| light[2] * b0 + dark[2] * (1.0 - b0), | ||
| ); | ||
| if (slot.darkColor) { | ||
| r0 = slot.darkColor.r; | ||
| g0 = slot.darkColor.g; | ||
| b0 = slot.darkColor.b; | ||
| } else { | ||
| r0 = 0.0; | ||
| g0 = 0.0; | ||
| b0 = 0.0; | ||
| } | ||
| spriteColor.setDark( | ||
| light[0] * r0 + dark[0] * (1 - r0), | ||
| light[1] * g0 + dark[1] * (1 - g0), | ||
| light[2] * b0 + dark[2] * (1 - b0), | ||
| ); | ||
| } | ||
| slotContainer.alpha = slot.color.a; | ||
| } | ||
| //== 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 && slot.attachment) { | ||
| clippingContainer = slot.clippingContainer; | ||
| clippingAttachment = slot.attachment as core.ClippingAttachment; | ||
| clippingContainer.children.length = 0; | ||
| this.children[i] = slotContainer; | ||
| if (clippingAttachment.endSlot == slot.data) { | ||
| clippingAttachment.endSlot = null; | ||
| } | ||
| } else { | ||
| if (clippingContainer) { | ||
| let c = this.tempClipContainers[i]; | ||
| if (!c) { | ||
| c = this.tempClipContainers[i] = this.newContainer(); | ||
| 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; | ||
| } | ||
| } | ||
| } | ||
| }; | ||
| private setSpriteRegion(attachment: core.RegionAttachment, sprite: SpineSprite, region: core.TextureRegion) { | ||
| sprite.region = region; | ||
| sprite.texture = region.texture; | ||
| if (!region.size) { | ||
| sprite.scale.x = attachment.scaleX * attachment.width / region.originalWidth; | ||
| sprite.scale.y = -attachment.scaleY * attachment.height / region.originalHeight; | ||
| } else { | ||
| //hacked! | ||
| sprite.scale.x = region.size.width / region.originalWidth; | ||
| sprite.scale.y = -region.size.height / region.originalHeight; | ||
| } | ||
| } | ||
| private setMeshRegion(attachment: core.MeshAttachment, mesh: SpineMesh, region: core.TextureRegion) { | ||
| mesh.region = region; | ||
| mesh.texture = region.texture; | ||
| region.texture.updateUvs(); | ||
| attachment.updateUVs(region, mesh.uvs); | ||
| // if (PIXI.VERSION[0] !== '3') { | ||
| // PIXI version 4 | ||
| // mesh.indexDirty++; | ||
| // } else { | ||
| // PIXI version 3 | ||
| mesh.dirty++; | ||
| // } | ||
| } | ||
| protected lastTime: number; | ||
| /** | ||
| * When autoupdate is set to yes this function is used as pixi's updateTransform function | ||
| * | ||
| * @private | ||
| */ | ||
| autoUpdateTransform() { | ||
| if (Spine.globalAutoUpdate) { | ||
| this.lastTime = this.lastTime || Date.now(); | ||
| let timeDelta = (Date.now() - this.lastTime) * 0.001; | ||
| this.lastTime = Date.now(); | ||
| this.update(timeDelta); | ||
| } else { | ||
| this.lastTime = 0; | ||
| } | ||
| PIXI.Container.prototype.updateTransform.call(this); | ||
| }; | ||
| /** | ||
| * Create a new sprite to be used with core.RegionAttachment | ||
| * | ||
| * @param slot {spine.Slot} The slot to which the attachment is parented | ||
| * @param attachment {spine.RegionAttachment} The attachment that the sprite will represent | ||
| * @private | ||
| */ | ||
| createSprite(slot: core.Slot, attachment: core.RegionAttachment, defName: string) { | ||
| let region = attachment.region; | ||
| if (slot.tempAttachment === attachment) { | ||
| region = slot.tempRegion; | ||
| slot.tempAttachment = null; | ||
| slot.tempRegion = null; | ||
| } | ||
| let texture = region.texture; | ||
| let sprite = this.newSprite(texture); | ||
| sprite.rotation = attachment.rotation * core.MathUtils.degRad; | ||
| sprite.anchor.x = 0.5; | ||
| sprite.anchor.y = 0.5; | ||
| sprite.position.x = attachment.x; | ||
| sprite.position.y = attachment.y; | ||
| sprite.alpha = attachment.color.a; | ||
| sprite.region = attachment.region; | ||
| this.setSpriteRegion(attachment, sprite, attachment.region); | ||
| slot.sprites = slot.sprites || {}; | ||
| slot.sprites[defName] = sprite; | ||
| return sprite; | ||
| }; | ||
| /** | ||
| * Creates a Strip from the spine data | ||
| * @param slot {spine.Slot} The slot to which the attachment is parented | ||
| * @param attachment {spine.RegionAttachment} The attachment that the sprite will represent | ||
| * @private | ||
| */ | ||
| createMesh(slot: core.Slot, attachment: core.MeshAttachment) { | ||
| let region = attachment.region; | ||
| if (slot.tempAttachment === attachment) { | ||
| region = slot.tempRegion; | ||
| slot.tempAttachment = null; | ||
| slot.tempRegion = null; | ||
| } | ||
| let strip = this.newMesh( | ||
| region.texture, | ||
| new Float32Array(attachment.regionUVs.length), | ||
| new Float32Array(attachment.regionUVs.length), | ||
| new Uint16Array(attachment.triangles), | ||
| PIXI.Mesh.DRAW_MODES.TRIANGLES); | ||
| strip.alpha = attachment.color.a; | ||
| strip.region = attachment.region; | ||
| this.setMeshRegion(attachment, strip, region); | ||
| slot.meshes = slot.meshes || {}; | ||
| slot.meshes[attachment.name] = strip; | ||
| return strip; | ||
| }; | ||
| static clippingPolygon: Array<number> = []; | ||
| createGraphics(slot: core.Slot, clip: core.ClippingAttachment) { | ||
| let graphics = this.newGraphics(); | ||
| let poly = new PIXI.Polygon([]); | ||
| graphics.clear(); | ||
| graphics.beginFill(0xffffff, 1); | ||
| graphics.drawPolygon(poly as any); | ||
| graphics.renderable = false; | ||
| slot.currentGraphics = graphics; | ||
| slot.clippingContainer = this.newContainer(); | ||
| 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++; | ||
| slot.currentGraphics.clearDirty++; | ||
| } | ||
| /** | ||
| * Changes texture in attachment in specific slot. | ||
| * | ||
| * PIXI runtime feature, it was made to satisfy our users. | ||
| * | ||
| * @param slotIndex {number} | ||
| * @param [texture = null] {PIXI.Texture} If null, take default (original) texture | ||
| * @param [size = null] {PIXI.Point} sometimes we need new size for region attachment, you can pass 'texture.orig' there | ||
| * @returns {boolean} Success flag | ||
| */ | ||
| hackTextureBySlotIndex(slotIndex: number, texture: PIXI.Texture = null, size: PIXI.Rectangle = null) { | ||
| let slot = this.skeleton.slots[slotIndex]; | ||
| if (!slot) { | ||
| return false; | ||
| } | ||
| let attachment: any = slot.attachment; | ||
| let region: core.TextureRegion = attachment.region; | ||
| if (texture) { | ||
| region = new core.TextureRegion(); | ||
| region.texture = texture; | ||
| region.size = size; | ||
| } | ||
| if (slot.currentSprite && slot.currentSprite.region != region) { | ||
| this.setSpriteRegion(attachment, slot.currentSprite, region); | ||
| slot.currentSprite.region = region; | ||
| } else if (slot.currentMesh && slot.currentMesh.region != region) { | ||
| this.setMeshRegion(attachment, slot.currentMesh, region); | ||
| } else { | ||
| slot.tempRegion = region; | ||
| slot.tempAttachment = attachment; | ||
| } | ||
| return true; | ||
| } | ||
| /** | ||
| * Changes texture in attachment in specific slot. | ||
| * | ||
| * PIXI runtime feature, it was made to satisfy our users. | ||
| * | ||
| * @param slotName {string} | ||
| * @param [texture = null] {PIXI.Texture} If null, take default (original) texture | ||
| * @param [size = null] {PIXI.Point} sometimes we need new size for region attachment, you can pass 'texture.orig' there | ||
| * @returns {boolean} Success flag | ||
| */ | ||
| hackTextureBySlotName(slotName: string, texture: PIXI.Texture = null, size: PIXI.Rectangle = null) { | ||
| let index = this.skeleton.findSlotIndex(slotName); | ||
| if (index == -1) { | ||
| return false; | ||
| } | ||
| return this.hackTextureBySlotIndex(index, texture, size); | ||
| } | ||
| //those methods can be overriden to spawn different classes | ||
| newContainer() { | ||
| return new PIXI.Container(); | ||
| } | ||
| newSprite(tex: PIXI.Texture) { | ||
| return new SpineSprite(tex); | ||
| } | ||
| newGraphics() { | ||
| return new PIXI.Graphics(); | ||
| } | ||
| newMesh(texture: PIXI.Texture, vertices?: Float32Array, uvs?: Float32Array, indices?: Uint16Array, drawMode?: number) { | ||
| return new SpineMesh(texture, vertices, uvs, indices, drawMode); | ||
| } | ||
| transformHack() { | ||
| return 1; | ||
| } | ||
| } | ||
| function SlotContainerUpdateTransformV3() { | ||
| let pt = this.parent.worldTransform; | ||
| let wt = this.worldTransform; | ||
| let lt = this.localTransform; | ||
| wt.a = lt.a * pt.a + lt.b * pt.c; | ||
| wt.b = lt.a * pt.b + lt.b * pt.d; | ||
| wt.c = lt.c * pt.a + lt.d * pt.c; | ||
| wt.d = lt.c * pt.b + lt.d * pt.d; | ||
| wt.tx = lt.tx * pt.a + lt.ty * pt.c + pt.tx; | ||
| wt.ty = lt.tx * pt.b + lt.ty * pt.d + pt.ty; | ||
| this.worldAlpha = this.alpha * this.parent.worldAlpha; | ||
| this._currentBounds = null; | ||
| } | ||
| } |
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is not supported yet
Unidentified License
LicenseSomething that seems like a license was found, but its contents could not be matched with a known license.
Found 1 instance in 1 package
Long strings
Supply chain riskContains long string literals, which may be a sign of obfuscated or packed code.
Found 1 instance in 1 package
No v1
QualityPackage is not semver >=1. This means it is not stable and does not support ^ ranges.
Found 1 instance in 1 package
Unidentified License
LicenseSomething that seems like a license was found, but its contents could not be matched with a known license.
Found 1 instance in 1 package
0
-100%88
2.33%1
-80%1208941
-23.58%12
9.09%6
-88.24%8821
-46.4%