Browse Source

ES2015 modulification of spine-ts core.

Mario Zechner 4 years ago
parent
commit
474dae82a0
100 changed files with 20428 additions and 11489 deletions
  1. 1 0
      .gitignore
  2. 0 2145
      spine-ts/core/src/Animation.ts
  3. 0 1171
      spine-ts/core/src/AnimationState.ts
  4. 0 76
      spine-ts/core/src/AnimationStateData.ts
  5. 0 284
      spine-ts/core/src/AssetManager.ts
  6. 0 76
      spine-ts/core/src/AtlasAttachmentLoader.ts
  7. 0 378
      spine-ts/core/src/Bone.ts
  8. 0 90
      spine-ts/core/src/BoneData.ts
  9. 0 294
      spine-ts/core/src/IkConstraint.ts
  10. 0 483
      spine-ts/core/src/PathConstraint.ts
  11. 0 84
      spine-ts/core/src/PathConstraintData.ts
  12. 0 638
      spine-ts/core/src/Skeleton.ts
  13. 0 1132
      spine-ts/core/src/SkeletonBinary.ts
  14. 0 231
      spine-ts/core/src/SkeletonBounds.ts
  15. 0 347
      spine-ts/core/src/SkeletonClipping.ts
  16. 0 216
      spine-ts/core/src/SkeletonData.ts
  17. 0 965
      spine-ts/core/src/SkeletonJson.ts
  18. 0 202
      spine-ts/core/src/Skin.ts
  19. 0 118
      spine-ts/core/src/Slot.ts
  20. 0 78
      spine-ts/core/src/Texture.ts
  21. 0 264
      spine-ts/core/src/TextureAtlas.ts
  22. 0 279
      spine-ts/core/src/TransformConstraint.ts
  23. 0 269
      spine-ts/core/src/Triangulator.ts
  24. 0 462
      spine-ts/core/src/Utils.ts
  25. 0 159
      spine-ts/core/src/attachments/Attachment.ts
  26. 0 198
      spine-ts/core/src/attachments/MeshAttachment.ts
  27. 0 69
      spine-ts/core/src/attachments/PointAttachment.ts
  28. 0 226
      spine-ts/core/src/attachments/RegionAttachment.ts
  29. 9023 0
      spine-ts/package-lock.json
  30. 37 0
      spine-ts/package.json
  31. 0 0
      spine-ts/spine-canvas/example/assets/spineboy-ess.json
  32. 0 0
      spine-ts/spine-canvas/example/assets/spineboy.atlas
  33. 0 0
      spine-ts/spine-canvas/example/assets/spineboy.png
  34. 0 0
      spine-ts/spine-canvas/example/index.html
  35. 0 0
      spine-ts/spine-canvas/src/AssetManager.ts
  36. 0 0
      spine-ts/spine-canvas/src/CanvasTexture.ts
  37. 7 7
      spine-ts/spine-canvas/src/SkeletonRenderer.ts
  38. 25 0
      spine-ts/spine-core/package.json
  39. 2151 0
      spine-ts/spine-core/src/Animation.ts
  40. 1176 0
      spine-ts/spine-core/src/AnimationState.ts
  41. 78 0
      spine-ts/spine-core/src/AnimationStateData.ts
  42. 286 0
      spine-ts/spine-core/src/AssetManager.ts
  43. 84 0
      spine-ts/spine-core/src/AtlasAttachmentLoader.ts
  44. 380 0
      spine-ts/spine-core/src/Bone.ts
  45. 89 0
      spine-ts/spine-core/src/BoneData.ts
  46. 3 5
      spine-ts/spine-core/src/ConstraintData.ts
  47. 18 19
      spine-ts/spine-core/src/Event.ts
  48. 13 15
      spine-ts/spine-core/src/EventData.ts
  49. 298 0
      spine-ts/spine-core/src/IkConstraint.ts
  50. 27 26
      spine-ts/spine-core/src/IkConstraintData.ts
  51. 489 0
      spine-ts/spine-core/src/PathConstraint.ts
  52. 86 0
      spine-ts/spine-core/src/PathConstraintData.ts
  53. 649 0
      spine-ts/spine-core/src/Skeleton.ts
  54. 1145 0
      spine-ts/spine-core/src/SkeletonBinary.ts
  55. 232 0
      spine-ts/spine-core/src/SkeletonBounds.ts
  56. 350 0
      spine-ts/spine-core/src/SkeletonClipping.ts
  57. 223 0
      spine-ts/spine-core/src/SkeletonData.ts
  58. 977 0
      spine-ts/spine-core/src/SkeletonJson.ts
  59. 207 0
      spine-ts/spine-core/src/Skin.ts
  60. 121 0
      spine-ts/spine-core/src/Slot.ts
  61. 69 0
      spine-ts/spine-core/src/SlotData.ts
  62. 40 19
      spine-ts/spine-core/src/Texture.ts
  63. 266 0
      spine-ts/spine-core/src/TextureAtlas.ts
  64. 283 0
      spine-ts/spine-core/src/TransformConstraint.ts
  65. 32 32
      spine-ts/spine-core/src/TransformConstraintData.ts
  66. 269 0
      spine-ts/spine-core/src/Triangulator.ts
  67. 8 11
      spine-ts/spine-core/src/Updatable.ts
  68. 468 0
      spine-ts/spine-core/src/Utils.ts
  69. 7 6
      spine-ts/spine-core/src/VertexEffect.ts
  70. 160 0
      spine-ts/spine-core/src/attachments/Attachment.ts
  71. 25 34
      spine-ts/spine-core/src/attachments/AttachmentLoader.ts
  72. 18 17
      spine-ts/spine-core/src/attachments/BoundingBoxAttachment.ts
  73. 22 20
      spine-ts/spine-core/src/attachments/ClippingAttachment.ts
  74. 200 0
      spine-ts/spine-core/src/attachments/MeshAttachment.ts
  75. 29 29
      spine-ts/spine-core/src/attachments/PathAttachment.ts
  76. 71 0
      spine-ts/spine-core/src/attachments/PointAttachment.ts
  77. 228 0
      spine-ts/spine-core/src/attachments/RegionAttachment.ts
  78. 1 1
      spine-ts/spine-core/src/polyfills.ts
  79. 18 16
      spine-ts/spine-core/src/vertexeffects/JitterEffect.ts
  80. 32 30
      spine-ts/spine-core/src/vertexeffects/SwirlEffect.ts
  81. 7 0
      spine-ts/spine-core/tsconfig.json
  82. 0 0
      spine-ts/spine-core/tsconfig.tsbuildinfo
  83. 0 268
      spine-ts/spine-player/css/spine-player.css
  84. 0 0
      spine-ts/spine-player/example/assets/raptor-pma.atlas
  85. 0 0
      spine-ts/spine-player/example/assets/raptor-pma.png
  86. 0 0
      spine-ts/spine-player/example/assets/raptor-pro.json
  87. 0 0
      spine-ts/spine-player/example/assets/spineboy-pma.atlas
  88. 0 0
      spine-ts/spine-player/example/assets/spineboy-pma.png
  89. 0 0
      spine-ts/spine-player/example/assets/spineboy-pro.skel
  90. 0 0
      spine-ts/spine-player/example/editor.html
  91. 0 0
      spine-ts/spine-player/example/embedding-binary-example.html
  92. 0 0
      spine-ts/spine-player/example/embedding-json-example.html
  93. 0 0
      spine-ts/spine-player/example/example.html
  94. 0 0
      spine-ts/spine-player/example/external/codemirror.css
  95. 0 0
      spine-ts/spine-player/example/external/codemirror.js
  96. 0 0
      spine-ts/spine-player/example/generator/embedding-generator.html
  97. 0 0
      spine-ts/spine-player/example/generator/embedding-generator.js
  98. 0 0
      spine-ts/spine-player/example/generator/generator.html
  99. 0 0
      spine-ts/spine-player/example/generator/jscolor.js
  100. 0 0
      spine-ts/spine-player/example/generator/loader.js

+ 1 - 0
.gitignore

@@ -18,6 +18,7 @@ build/
 cmake-build-debug/
 .vscode/settings.json
 spine-runtimes.code-workspace
+node_modules
 
 spine-as3/spine-as3/.settings/com.powerflasher.fdt.core.metaDataTags.prefs
 

+ 0 - 2145
spine-ts/core/src/Animation.ts

@@ -1,2145 +0,0 @@
-/******************************************************************************
- * Spine Runtimes License Agreement
- * Last updated January 1, 2020. Replaces all prior versions.
- *
- * Copyright (c) 2013-2020, Esoteric Software LLC
- *
- * Integration of the Spine Runtimes into software or otherwise creating
- * derivative works of the Spine Runtimes is permitted under the terms and
- * conditions of Section 2 of the Spine Editor License Agreement:
- * http://esotericsoftware.com/spine-editor-license
- *
- * Otherwise, it is permitted to integrate the Spine Runtimes into software
- * or otherwise create derivative works of the Spine Runtimes (collectively,
- * "Products"), provided that each user of the Products must obtain their own
- * Spine Editor license and redistribution of the Products in any form must
- * include this license and copyright notice.
- *
- * THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "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 LLC 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
- * THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *****************************************************************************/
-
-module spine {
-
-	/** A simple container for a list of timelines and a name. */
-	export class Animation {
-		/** The animation's name, which is unique across all animations in the skeleton. */
-		name: string;
-		timelines: Array<Timeline>;
-		timelineIds: StringSet;
-
-		/** The duration of the animation in seconds, which is the highest time of all keys in the timeline. */
-		duration: number;
-
-		constructor (name: string, timelines: Array<Timeline>, duration: number) {
-			if (!name) throw new Error("name cannot be null.");
-			this.name = name;
-			this.setTimelines(timelines);
-			this.duration = duration;
-		}
-
-		setTimelines (timelines: Array<Timeline>) {
-			if (!timelines) throw new Error("timelines cannot be null.");
-			this.timelines = timelines;
-			this.timelineIds = new StringSet();
-			for (var i = 0; i < timelines.length; i++)
-				this.timelineIds.addAll(timelines[i].getPropertyIds());
-		}
-
-		hasTimeline (ids: string[]): boolean {
-			for (let i = 0; i < ids.length; i++)
-				if (this.timelineIds.contains(ids[i])) return true;
-			return false;
-		}
-
-		/** Applies all the animation's timelines to the specified skeleton.
-		 *
-		 * See Timeline {@link Timeline#apply(Skeleton, float, float, Array, float, MixBlend, MixDirection)}.
-		 * @param loop If true, the animation repeats after {@link #getDuration()}.
-		 * @param events May be null to ignore fired events. */
-		apply (skeleton: Skeleton, lastTime: number, time: number, loop: boolean, events: Array<Event>, alpha: number, blend: MixBlend, direction: MixDirection) {
-			if (!skeleton) 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, blend, direction);
-		}
-	}
-
-	/** Controls how a timeline value is mixed with the setup pose value or current pose value when a timeline's `alpha`
-	 * < 1.
-	 *
-	 * See Timeline {@link Timeline#apply(Skeleton, float, float, Array, float, MixBlend, MixDirection)}. */
-	export enum MixBlend {
-		/** Transitions from the setup value to the timeline value (the current value is not used). Before the first key, the setup
-		 * value is set. */
-		setup,
-		/** Transitions from the current value to the timeline value. Before the first key, transitions from the current value to
-		 * the setup value. Timelines which perform instant transitions, such as {@link DrawOrderTimeline} or
-		 * {@link AttachmentTimeline}, use the setup value before the first key.
-		 *
-		 * `first` is intended for the first animations applied, not for animations layered on top of those. */
-		first,
-		/** Transitions from the current value to the timeline value. No change is made before the first key (the current value is
-		 * kept until the first key).
-		 *
-		 * `replace` is intended for animations layered on top of others, not for the first animations applied. */
-		replace,
-		/** Transitions from the current value to the current value plus the timeline value. No change is made before the first key
-		 * (the current value is kept until the first key).
-		 *
-		 * `add` is intended for animations layered on top of others, not for the first animations applied. Properties
-		 * keyed by additive animations must be set manually or by another animation before applying the additive animations, else
-		 * the property values will increase continually. */
-		add
-	}
-
-	/** Indicates whether a timeline's `alpha` is mixing out over time toward 0 (the setup or current pose value) or
-	 * mixing in toward 1 (the timeline's value).
-	 *
-	 * See Timeline {@link Timeline#apply(Skeleton, float, float, Array, float, MixBlend, MixDirection)}. */
-	export enum MixDirection {
-		mixIn, mixOut
-	}
-
-	const Property = {
-		rotate: 0,
-		x: 1,
-		y: 2,
-		scaleX: 3,
-		scaleY: 4,
-		shearX: 5,
-		shearY: 6,
-
-		rgb: 7,
-		alpha: 8,
-		rgb2: 9,
-
-		attachment: 10,
-		deform: 11,
-
-		event: 12,
-		drawOrder: 13,
-
-		ikConstraint: 14,
-		transformConstraint: 15,
-
-		pathConstraintPosition: 16,
-		pathConstraintSpacing: 17,
-		pathConstraintMix: 18
-	}
-
-	/** The interface for all timelines. */
-	export abstract class Timeline {
-		propertyIds: string[];
-		frames: ArrayLike<number>;
-
-		constructor (frameCount: number, propertyIds: string[]) {
-			this.propertyIds = propertyIds;
-			this.frames = Utils.newFloatArray(frameCount * this.getFrameEntries());
-		}
-
-		getPropertyIds () {
-			return this.propertyIds;
-		}
-
-		getFrameEntries (): number {
-			return 1;
-		}
-
-		getFrameCount () {
-			return this.frames.length / this.getFrameEntries();
-		}
-
-		getDuration (): number {
-			return this.frames[this.frames.length - this.getFrameEntries()];
-		}
-
-		abstract apply (skeleton: Skeleton, lastTime: number, time: number, events: Array<Event>, alpha: number, blend: MixBlend, direction: MixDirection): void;
-
-		static search1 (frames: ArrayLike<number>, time: number) {
-			let n = frames.length;
-			for (let i = 1; i < n; i++)
-				if (frames[i] > time) return i - 1;
-			return n - 1;
-		}
-
-		static search (frames: ArrayLike<number>, time: number, step: number) {
-			let n = frames.length;
-			for (let i = step; i < n; i += step)
-				if (frames[i] > time) return i - step;
-			return n - step;
-		}
-	}
-
-	export interface BoneTimeline {
-		/** The index of the bone in {@link Skeleton#bones} that will be changed. */
-		boneIndex: number;
-	}
-
-	export interface SlotTimeline {
-		/** The index of the slot in {@link Skeleton#slots} that will be changed. */
-		slotIndex: number;
-	}
-
-	/** The base class for timelines that use interpolation between key frame values. */
-	export abstract class CurveTimeline extends Timeline {
-		protected curves: ArrayLike<number>; // type, x, y, ...
-
-		constructor (frameCount: number, bezierCount: number, propertyIds: string[]) {
-			super(frameCount, propertyIds);
-			this.curves = Utils.newFloatArray(frameCount + bezierCount * 18/*BEZIER_SIZE*/);
-			this.curves[frameCount - 1] = 1/*STEPPED*/;
-		}
-
-		/** Sets the specified key frame to linear interpolation. */
-		setLinear (frame: number) {
-			this.curves[frame] = 0/*LINEAR*/;
-		}
-
-		/** Sets the specified key frame to stepped interpolation. */
-		setStepped (frame: number) {
-			this.curves[frame] = 1/*STEPPED*/;
-		}
-
-		/** Shrinks the storage for Bezier curves, for use when <code>bezierCount</code> (specified in the constructor) was larger
-		 * than the actual number of Bezier curves. */
-		shrink (bezierCount: number) {
-			let size = this.getFrameCount() + bezierCount * 18/*BEZIER_SIZE*/;
-			if (this.curves.length > size) {
-				let newCurves = Utils.newFloatArray(size);
-				Utils.arrayCopy(this.curves, 0, newCurves, 0, size);
-				this.curves = newCurves;
-			}
-		}
-
-		/** Stores the segments for the specified Bezier curve. For timelines that modify multiple values, there may be more than
-		 * one curve per frame.
-		 * @param bezier The ordinal of this Bezier curve for this timeline, between 0 and <code>bezierCount - 1</code> (specified
-		 *           in the constructor), inclusive.
-		 * @param frame Between 0 and <code>frameCount - 1</code>, inclusive.
-		 * @param value The index of the value for this frame that this curve is used for.
-		 * @param time1 The time for the first key.
-		 * @param value1 The value for the first key.
-		 * @param cx1 The time for the first Bezier handle.
-		 * @param cy1 The value for the first Bezier handle.
-		 * @param cx2 The time of the second Bezier handle.
-		 * @param cy2 The value for the second Bezier handle.
-		 * @param time2 The time for the second key.
-		 * @param value2 The value for the second key. */
-		setBezier (bezier: number, frame: number, value: number, time1: number, value1: number, cx1: number, cy1: number, cx2: number,
-			cy2: number, time2: number, value2: number) {
-			let curves = this.curves;
-			let i = this.getFrameCount() + bezier * 18/*BEZIER_SIZE*/;
-			if (value == 0) curves[frame] = 2/*BEZIER*/ + i;
-			let tmpx = (time1 - cx1 * 2 + cx2) * 0.03, tmpy = (value1 - cy1 * 2 + cy2) * 0.03;
-			let dddx = ((cx1 - cx2) * 3 - time1 + time2) * 0.006, dddy = ((cy1 - cy2) * 3 - value1 + value2) * 0.006;
-			let ddx = tmpx * 2 + dddx, ddy = tmpy * 2 + dddy;
-			let dx = (cx1 - time1) * 0.3 + tmpx + dddx * 0.16666667, dy = (cy1 - value1) * 0.3 + tmpy + dddy * 0.16666667;
-			let x = time1 + dx, y = value1 + dy;
-			for (let n = i + 18/*BEZIER_SIZE*/; i < n; i += 2) {
-				curves[i] = x;
-				curves[i + 1] = y;
-				dx += ddx;
-				dy += ddy;
-				ddx += dddx;
-				ddy += dddy;
-				x += dx;
-				y += dy;
-			}
-		}
-
-		/** Returns the Bezier interpolated value for the specified time.
-		 * @param frameIndex The index into {@link #getFrames()} for the values of the frame before <code>time</code>.
-		 * @param valueOffset The offset from <code>frameIndex</code> to the value this curve is used for.
-		 * @param i The index of the Bezier segments. See {@link #getCurveType(int)}. */
-		getBezierValue (time: number, frameIndex: number, valueOffset: number, i: number) {
-			let curves = this.curves;
-			if (curves[i] > time) {
-				let x = this.frames[frameIndex], y = this.frames[frameIndex + valueOffset];
-				return y + (time - x) / (curves[i] - x) * (curves[i + 1] - y);
-			}
-			let n = i + 18/*BEZIER_SIZE*/;
-			for (i += 2; i < n; i += 2) {
-				if (curves[i] >= time) {
-					let x = curves[i - 2], y = curves[i - 1];
-					return y + (time - x) / (curves[i] - x) * (curves[i + 1] - y);
-				}
-			}
-			frameIndex += this.getFrameEntries();
-			let x = curves[n - 2], y = curves[n - 1];
-			return y + (time - x) / (this.frames[frameIndex] - x) * (this.frames[frameIndex + valueOffset] - y);
-		}
-	}
-
-	export abstract class CurveTimeline1 extends CurveTimeline {
-		constructor (frameCount: number, bezierCount: number, propertyId: string) {
-			super(frameCount, bezierCount, [propertyId]);
-		}
-
-		getFrameEntries () {
-			return 2/*ENTRIES*/;
-		}
-
-		/** Sets the time and value for the specified frame.
-		 * @param frame Between 0 and <code>frameCount</code>, inclusive.
-		 * @param time The frame time in seconds. */
-		setFrame (frame: number, time: number, value: number) {
-			frame <<= 1;
-			this.frames[frame] = time;
-			this.frames[frame + 1/*VALUE*/] = value;
-		}
-
-		/** Returns the interpolated value for the specified time. */
-		getCurveValue (time: number) {
-			let frames = this.frames;
-			let i = frames.length - 2;
-			for (let ii = 2; ii <= i; ii += 2) {
-				if (frames[ii] > time) {
-					i = ii - 2;
-					break;
-				}
-			}
-
-			let curveType = this.curves[i >> 1];
-			switch (curveType) {
-				case 0/*LINEAR*/:
-					let before = frames[i], value = frames[i + 1/*VALUE*/];
-					return value + (time - before) / (frames[i + 2/*ENTRIES*/] - before) * (frames[i + 2/*ENTRIES*/ + 1/*VALUE*/] - value);
-				case 1/*STEPPED*/:
-					return frames[i + 1/*VALUE*/];
-			}
-			return this.getBezierValue(time, i, 1/*VALUE*/, curveType - 2/*BEZIER*/);
-		}
-	}
-
-	/** The base class for a {@link CurveTimeline} which sets two properties. */
-	export abstract class CurveTimeline2 extends CurveTimeline {
-		/** @param bezierCount The maximum number of Bezier curves. See {@link #shrink(int)}.
-		 * @param propertyIds Unique identifiers for the properties the timeline modifies. */
-		constructor (frameCount: number, bezierCount: number, propertyId1: string, propertyId2: string) {
-			super(frameCount, bezierCount, [propertyId1, propertyId2]);
-		}
-
-		getFrameEntries () {
-			return 3/*ENTRIES*/;
-		}
-
-		/** Sets the time and values for the specified frame.
-		 * @param frame Between 0 and <code>frameCount</code>, inclusive.
-		 * @param time The frame time in seconds. */
-		setFrame (frame: number, time: number, value1: number, value2: number) {
-			frame *= 3/*ENTRIES*/;
-			this.frames[frame] = time;
-			this.frames[frame + 1/*VALUE1*/] = value1;
-			this.frames[frame + 2/*VALUE2*/] = value2;
-		}
-	}
-
-	/** Changes a bone's local {@link Bone#rotation}. */
-	export class RotateTimeline extends CurveTimeline1 implements BoneTimeline {
-		boneIndex = 0;
-
-		constructor (frameCount: number, bezierCount: number, boneIndex: number) {
-			super(frameCount, bezierCount, Property.rotate + "|" + boneIndex);
-			this.boneIndex = boneIndex;
-		}
-
-		apply (skeleton: Skeleton, lastTime: number, time: number, events: Array<Event>, alpha: number, blend: MixBlend, direction: MixDirection) {
-			let bone = skeleton.bones[this.boneIndex];
-			if (!bone.active) return;
-
-			let frames = this.frames;
-			if (time < frames[0]) {
-				switch (blend) {
-					case MixBlend.setup:
-						bone.rotation = bone.data.rotation;
-						return;
-					case MixBlend.first:
-						bone.rotation += (bone.data.rotation - bone.rotation) * alpha;
-				}
-				return;
-			}
-
-			let r = this.getCurveValue(time);
-			switch (blend) {
-				case MixBlend.setup:
-					bone.rotation = bone.data.rotation + r * alpha;
-					break;
-				case MixBlend.first:
-				case MixBlend.replace:
-					r += bone.data.rotation - bone.rotation;
-				case MixBlend.add:
-					bone.rotation += r * alpha;
-			}
-		}
-	}
-
-	/** Changes a bone's local {@link Bone#x} and {@link Bone#y}. */
-	export class TranslateTimeline extends CurveTimeline2 implements BoneTimeline {
-		boneIndex = 0;
-
-		constructor (frameCount: number, bezierCount: number, boneIndex: number) {
-			super(frameCount, bezierCount,
-				Property.x + "|" + boneIndex,
-				Property.y + "|" + boneIndex,
-			);
-			this.boneIndex = boneIndex;
-		}
-
-		apply (skeleton: Skeleton, lastTime: number, time: number, events: Array<Event>, alpha: number, blend: MixBlend, direction: MixDirection) {
-			let bone = skeleton.bones[this.boneIndex];
-			if (!bone.active) return;
-
-			let frames = this.frames;
-			if (time < frames[0]) {
-				switch (blend) {
-					case MixBlend.setup:
-						bone.x = bone.data.x;
-						bone.y = bone.data.y;
-						return;
-					case MixBlend.first:
-						bone.x += (bone.data.x - bone.x) * alpha;
-						bone.y += (bone.data.y - bone.y) * alpha;
-				}
-				return;
-			}
-
-			let x = 0, y = 0;
-			let i = Timeline.search(frames, time, 3/*ENTRIES*/);
-			let curveType = this.curves[i / 3/*ENTRIES*/];
-			switch (curveType) {
-				case 0/*LINEAR*/:
-					let before = frames[i];
-					x = frames[i + 1/*VALUE1*/];
-					y = frames[i + 2/*VALUE2*/];
-					let t = (time - before) / (frames[i + 3/*ENTRIES*/] - before);
-					x += (frames[i + 3/*ENTRIES*/ + 1/*VALUE1*/] - x) * t;
-					y += (frames[i + 3/*ENTRIES*/ + 2/*VALUE2*/] - y) * t;
-					break;
-				case 1/*STEPPED*/:
-					x = frames[i + 1/*VALUE1*/];
-					y = frames[i + 2/*VALUE2*/];
-					break;
-				default:
-					x = this.getBezierValue(time, i, 1/*VALUE1*/, curveType - 2/*BEZIER*/);
-					y = this.getBezierValue(time, i, 2/*VALUE2*/, curveType + 18/*BEZIER_SIZE*/ - 2/*BEZIER*/);
-			}
-
-			switch (blend) {
-				case MixBlend.setup:
-					bone.x = bone.data.x + x * alpha;
-					bone.y = bone.data.y + y * alpha;
-					break;
-				case MixBlend.first:
-				case MixBlend.replace:
-					bone.x += (bone.data.x + x - bone.x) * alpha;
-					bone.y += (bone.data.y + y - bone.y) * alpha;
-					break;
-				case MixBlend.add:
-					bone.x += x * alpha;
-					bone.y += y * alpha;
-			}
-		}
-	}
-
-	/** Changes a bone's local {@link Bone#x}. */
-	export class TranslateXTimeline extends CurveTimeline1 implements BoneTimeline {
-		boneIndex = 0;
-
-		constructor (frameCount: number, bezierCount: number, boneIndex: number) {
-			super(frameCount, bezierCount, Property.x + "|" + boneIndex);
-			this.boneIndex = boneIndex;
-		}
-
-		apply (skeleton: Skeleton, lastTime: number, time: number, events: Array<Event>, alpha: number, blend: MixBlend, direction: MixDirection) {
-			let bone = skeleton.bones[this.boneIndex];
-			if (!bone.active) return;
-
-			let frames = this.frames;
-			if (time < frames[0]) {
-				switch (blend) {
-					case MixBlend.setup:
-						bone.x = bone.data.x;
-						return;
-					case MixBlend.first:
-						bone.x += (bone.data.x - bone.x) * alpha;
-				}
-				return;
-			}
-
-			let x = this.getCurveValue(time);
-			switch (blend) {
-				case MixBlend.setup:
-					bone.x = bone.data.x + x * alpha;
-					break;
-				case MixBlend.first:
-				case MixBlend.replace:
-					bone.x += (bone.data.x + x - bone.x) * alpha;
-					break;
-				case MixBlend.add:
-					bone.x += x * alpha;
-			}
-		}
-	}
-
-	/** Changes a bone's local {@link Bone#x}. */
-	export class TranslateYTimeline extends CurveTimeline1 implements BoneTimeline {
-		boneIndex = 0;
-
-		constructor (frameCount: number, bezierCount: number, boneIndex: number) {
-			super(frameCount, bezierCount, Property.y + "|" + boneIndex);
-			this.boneIndex = boneIndex;
-		}
-
-		apply (skeleton: Skeleton, lastTime: number, time: number, events: Array<Event>, alpha: number, blend: MixBlend, direction: MixDirection) {
-			let bone = skeleton.bones[this.boneIndex];
-			if (!bone.active) return;
-
-			let frames = this.frames;
-			if (time < frames[0]) {
-				switch (blend) {
-					case MixBlend.setup:
-						bone.y = bone.data.y;
-						return;
-					case MixBlend.first:
-						bone.y += (bone.data.y - bone.y) * alpha;
-				}
-				return;
-			}
-
-			let y = this.getCurveValue(time);
-			switch (blend) {
-				case MixBlend.setup:
-					bone.y = bone.data.y + y * alpha;
-					break;
-				case MixBlend.first:
-				case MixBlend.replace:
-					bone.y += (bone.data.y + y - bone.y) * alpha;
-					break;
-				case MixBlend.add:
-					bone.y += y * alpha;
-			}
-		}
-	}
-
-	/** Changes a bone's local {@link Bone#scaleX)} and {@link Bone#scaleY}. */
-	export class ScaleTimeline extends CurveTimeline2 implements BoneTimeline {
-		boneIndex = 0;
-
-		constructor (frameCount: number, bezierCount: number, boneIndex: number) {
-			super(frameCount, bezierCount,
-				Property.scaleX + "|" + boneIndex,
-				Property.scaleY + "|" + boneIndex
-			);
-			this.boneIndex = boneIndex;
-		}
-
-		apply (skeleton: Skeleton, lastTime: number, time: number, events: Array<Event>, alpha: number, blend: MixBlend, direction: MixDirection) {
-			let bone = skeleton.bones[this.boneIndex];
-			if (!bone.active) return;
-
-			let frames = this.frames;
-			if (time < frames[0]) {
-				switch (blend) {
-					case MixBlend.setup:
-						bone.scaleX = bone.data.scaleX;
-						bone.scaleY = bone.data.scaleY;
-						return;
-					case MixBlend.first:
-						bone.scaleX += (bone.data.scaleX - bone.scaleX) * alpha;
-						bone.scaleY += (bone.data.scaleY - bone.scaleY) * alpha;
-				}
-				return;
-			}
-
-			let x, y;
-			let i = Timeline.search(frames, time, 3/*ENTRIES*/);
-			let curveType = this.curves[i / 3/*ENTRIES*/];
-			switch (curveType) {
-				case 0/*LINEAR*/:
-					let before = frames[i];
-					x = frames[i + 1/*VALUE1*/];
-					y = frames[i + 2/*VALUE2*/];
-					let t = (time - before) / (frames[i + 3/*ENTRIES*/] - before);
-					x += (frames[i + 3/*ENTRIES*/ + 1/*VALUE1*/] - x) * t;
-					y += (frames[i + 3/*ENTRIES*/ + 2/*VALUE2*/] - y) * t;
-					break;
-				case 1/*STEPPED*/:
-					x = frames[i + 1/*VALUE1*/];
-					y = frames[i + 2/*VALUE2*/];
-					break;
-				default:
-					x = this.getBezierValue(time, i, 1/*VALUE1*/, curveType - 2/*BEZIER*/);
-					y = this.getBezierValue(time, i, 2/*VALUE2*/, curveType + 18/*BEZIER_SIZE*/ - 2/*BEZIER*/);
-			}
-			x *= bone.data.scaleX;
-			y *= bone.data.scaleY;
-
-			if (alpha == 1) {
-				if (blend == MixBlend.add) {
-					bone.scaleX += x - bone.data.scaleX;
-					bone.scaleY += y - bone.data.scaleY;
-				} else {
-					bone.scaleX = x;
-					bone.scaleY = y;
-				}
-			} else {
-				let bx = 0, by = 0;
-				if (direction == MixDirection.mixOut) {
-					switch (blend) {
-						case MixBlend.setup:
-							bx = bone.data.scaleX;
-							by = bone.data.scaleY;
-							bone.scaleX = bx + (Math.abs(x) * MathUtils.signum(bx) - bx) * alpha;
-							bone.scaleY = by + (Math.abs(y) * MathUtils.signum(by) - by) * alpha;
-							break;
-						case MixBlend.first:
-						case MixBlend.replace:
-							bx = bone.scaleX;
-							by = bone.scaleY;
-							bone.scaleX = bx + (Math.abs(x) * MathUtils.signum(bx) - bx) * alpha;
-							bone.scaleY = by + (Math.abs(y) * MathUtils.signum(by) - by) * alpha;
-							break;
-						case MixBlend.add:
-							bx = bone.scaleX;
-							by = bone.scaleY;
-							bone.scaleX = bx + (Math.abs(x) * MathUtils.signum(bx) - bone.data.scaleX) * alpha;
-							bone.scaleY = by + (Math.abs(y) * MathUtils.signum(by) - bone.data.scaleY) * alpha;
-					}
-				} else {
-					switch (blend) {
-						case MixBlend.setup:
-							bx = Math.abs(bone.data.scaleX) * MathUtils.signum(x);
-							by = Math.abs(bone.data.scaleY) * MathUtils.signum(y);
-							bone.scaleX = bx + (x - bx) * alpha;
-							bone.scaleY = by + (y - by) * alpha;
-							break;
-						case MixBlend.first:
-						case MixBlend.replace:
-							bx = Math.abs(bone.scaleX) * MathUtils.signum(x);
-							by = Math.abs(bone.scaleY) * MathUtils.signum(y);
-							bone.scaleX = bx + (x - bx) * alpha;
-							bone.scaleY = by + (y - by) * alpha;
-							break;
-						case MixBlend.add:
-							bx = MathUtils.signum(x);
-							by = MathUtils.signum(y);
-							bone.scaleX = Math.abs(bone.scaleX) * bx + (x - Math.abs(bone.data.scaleX) * bx) * alpha;
-							bone.scaleY = Math.abs(bone.scaleY) * by + (y - Math.abs(bone.data.scaleY) * by) * alpha;
-					}
-				}
-			}
-		}
-	}
-
-	/** Changes a bone's local {@link Bone#scaleX)} and {@link Bone#scaleY}. */
-	export class ScaleXTimeline extends CurveTimeline1 implements BoneTimeline {
-		boneIndex = 0;
-
-		constructor (frameCount: number, bezierCount: number, boneIndex: number) {
-			super(frameCount, bezierCount, Property.scaleX + "|" + boneIndex);
-			this.boneIndex = boneIndex;
-		}
-
-		apply (skeleton: Skeleton, lastTime: number, time: number, events: Array<Event>, alpha: number, blend: MixBlend, direction: MixDirection) {
-			let bone = skeleton.bones[this.boneIndex];
-			if (!bone.active) return;
-
-			let frames = this.frames;
-			if (time < frames[0]) {
-				switch (blend) {
-					case MixBlend.setup:
-						bone.scaleX = bone.data.scaleX;
-						return;
-					case MixBlend.first:
-						bone.scaleX += (bone.data.scaleX - bone.scaleX) * alpha;
-				}
-				return;
-			}
-
-			let x = this.getCurveValue(time) * bone.data.scaleX;
-			if (alpha == 1) {
-				if (blend == MixBlend.add)
-					bone.scaleX += x - bone.data.scaleX;
-				else
-					bone.scaleX = x;
-			} else {
-				// Mixing out uses sign of setup or current pose, else use sign of key.
-				let bx = 0;
-				if (direction == MixDirection.mixOut) {
-					switch (blend) {
-						case MixBlend.setup:
-							bx = bone.data.scaleX;
-							bone.scaleX = bx + (Math.abs(x) * MathUtils.signum(bx) - bx) * alpha;
-							break;
-						case MixBlend.first:
-						case MixBlend.replace:
-							bx = bone.scaleX;
-							bone.scaleX = bx + (Math.abs(x) * MathUtils.signum(bx) - bx) * alpha;
-							break;
-						case MixBlend.add:
-							bx = bone.scaleX;
-							bone.scaleX = bx + (Math.abs(x) * MathUtils.signum(bx) - bone.data.scaleX) * alpha;
-					}
-				} else {
-					switch (blend) {
-						case MixBlend.setup:
-							bx = Math.abs(bone.data.scaleX) * MathUtils.signum(x);
-							bone.scaleX = bx + (x - bx) * alpha;
-							break;
-						case MixBlend.first:
-						case MixBlend.replace:
-							bx = Math.abs(bone.scaleX) * MathUtils.signum(x);
-							bone.scaleX = bx + (x - bx) * alpha;
-							break;
-						case MixBlend.add:
-							bx = MathUtils.signum(x);
-							bone.scaleX = Math.abs(bone.scaleX) * bx + (x - Math.abs(bone.data.scaleX) * bx) * alpha;
-					}
-				}
-			}
-		}
-	}
-
-	/** Changes a bone's local {@link Bone#scaleX)} and {@link Bone#scaleY}. */
-	export class ScaleYTimeline extends CurveTimeline1 implements BoneTimeline {
-		boneIndex = 0;
-
-		constructor (frameCount: number, bezierCount: number, boneIndex: number) {
-			super(frameCount, bezierCount, Property.scaleY + "|" + boneIndex);
-			this.boneIndex = boneIndex;
-		}
-
-		apply (skeleton: Skeleton, lastTime: number, time: number, events: Array<Event>, alpha: number, blend: MixBlend, direction: MixDirection) {
-			let bone = skeleton.bones[this.boneIndex];
-			if (!bone.active) return;
-
-			let frames = this.frames;
-			if (time < frames[0]) {
-				switch (blend) {
-					case MixBlend.setup:
-						bone.scaleY = bone.data.scaleY;
-						return;
-					case MixBlend.first:
-						bone.scaleY += (bone.data.scaleY - bone.scaleY) * alpha;
-				}
-				return;
-			}
-
-			let y = this.getCurveValue(time) * bone.data.scaleY;
-			if (alpha == 1) {
-				if (blend == MixBlend.add)
-					bone.scaleY += y - bone.data.scaleY;
-				else
-					bone.scaleY = y;
-			} else {
-				// Mixing out uses sign of setup or current pose, else use sign of key.
-				let by = 0;
-				if (direction == MixDirection.mixOut) {
-					switch (blend) {
-						case MixBlend.setup:
-							by = bone.data.scaleY;
-							bone.scaleY = by + (Math.abs(y) * MathUtils.signum(by) - by) * alpha;
-							break;
-						case MixBlend.first:
-						case MixBlend.replace:
-							by = bone.scaleY;
-							bone.scaleY = by + (Math.abs(y) * MathUtils.signum(by) - by) * alpha;
-							break;
-						case MixBlend.add:
-							by = bone.scaleY;
-							bone.scaleY = by + (Math.abs(y) * MathUtils.signum(by) - bone.data.scaleY) * alpha;
-					}
-				} else {
-					switch (blend) {
-						case MixBlend.setup:
-							by = Math.abs(bone.data.scaleY) * MathUtils.signum(y);
-							bone.scaleY = by + (y - by) * alpha;
-							break;
-						case MixBlend.first:
-						case MixBlend.replace:
-							by = Math.abs(bone.scaleY) * MathUtils.signum(y);
-							bone.scaleY = by + (y - by) * alpha;
-							break;
-						case MixBlend.add:
-							by = MathUtils.signum(y);
-							bone.scaleY = Math.abs(bone.scaleY) * by + (y - Math.abs(bone.data.scaleY) * by) * alpha;
-					}
-				}
-			}
-		}
-	}
-
-	/** Changes a bone's local {@link Bone#shearX} and {@link Bone#shearY}. */
-	export class ShearTimeline extends CurveTimeline2 implements BoneTimeline {
-		boneIndex = 0;
-
-		constructor (frameCount: number, bezierCount: number, boneIndex: number) {
-			super(frameCount, bezierCount,
-				Property.shearX + "|" + boneIndex,
-				Property.shearY + "|" + boneIndex
-			);
-			this.boneIndex = boneIndex;
-		}
-
-		apply (skeleton: Skeleton, lastTime: number, time: number, events: Array<Event>, alpha: number, blend: MixBlend, direction: MixDirection) {
-			let bone = skeleton.bones[this.boneIndex];
-			if (!bone.active) return;
-
-			let frames = this.frames;
-			if (time < frames[0]) {
-				switch (blend) {
-					case MixBlend.setup:
-						bone.shearX = bone.data.shearX;
-						bone.shearY = bone.data.shearY;
-						return;
-					case MixBlend.first:
-						bone.shearX += (bone.data.shearX - bone.shearX) * alpha;
-						bone.shearY += (bone.data.shearY - bone.shearY) * alpha;
-				}
-				return;
-			}
-
-			let x = 0, y = 0;
-			let i = Timeline.search(frames, time, 3/*ENTRIES*/);
-			let curveType = this.curves[i / 3/*ENTRIES*/];
-			switch (curveType) {
-				case 0/*LINEAR*/:
-					let before = frames[i];
-					x = frames[i + 1/*VALUE1*/];
-					y = frames[i + 2/*VALUE2*/];
-					let t = (time - before) / (frames[i + 3/*ENTRIES*/] - before);
-					x += (frames[i + 3/*ENTRIES*/ + 1/*VALUE1*/] - x) * t;
-					y += (frames[i + 3/*ENTRIES*/ + 2/*VALUE2*/] - y) * t;
-					break;
-				case 1/*STEPPED*/:
-					x = frames[i + 1/*VALUE1*/];
-					y = frames[i + 2/*VALUE2*/];
-					break;
-				default:
-					x = this.getBezierValue(time, i, 1/*VALUE1*/, curveType - 2/*BEZIER*/);
-					y = this.getBezierValue(time, i, 2/*VALUE2*/, curveType + 18/*BEZIER_SIZE*/ - 2/*BEZIER*/);
-			}
-
-			switch (blend) {
-				case MixBlend.setup:
-					bone.shearX = bone.data.shearX + x * alpha;
-					bone.shearY = bone.data.shearY + y * alpha;
-					break;
-				case MixBlend.first:
-				case MixBlend.replace:
-					bone.shearX += (bone.data.shearX + x - bone.shearX) * alpha;
-					bone.shearY += (bone.data.shearY + y - bone.shearY) * alpha;
-					break;
-				case MixBlend.add:
-					bone.shearX += x * alpha;
-					bone.shearY += y * alpha;
-			}
-		}
-	}
-
-	/** Changes a bone's local {@link Bone#shearX} and {@link Bone#shearY}. */
-	export class ShearXTimeline extends CurveTimeline1 implements BoneTimeline {
-		boneIndex = 0;
-
-		constructor (frameCount: number, bezierCount: number, boneIndex: number) {
-			super(frameCount, bezierCount, Property.shearX + "|" + boneIndex);
-			this.boneIndex = boneIndex;
-		}
-
-		apply (skeleton: Skeleton, lastTime: number, time: number, events: Array<Event>, alpha: number, blend: MixBlend, direction: MixDirection) {
-			let bone = skeleton.bones[this.boneIndex];
-			if (!bone.active) return;
-
-			let frames = this.frames;
-			if (time < frames[0]) {
-				switch (blend) {
-					case MixBlend.setup:
-						bone.shearX = bone.data.shearX;
-						return;
-					case MixBlend.first:
-						bone.shearX += (bone.data.shearX - bone.shearX) * alpha;
-				}
-				return;
-			}
-
-			let x = this.getCurveValue(time);
-			switch (blend) {
-				case MixBlend.setup:
-					bone.shearX = bone.data.shearX + x * alpha;
-					break;
-				case MixBlend.first:
-				case MixBlend.replace:
-					bone.shearX += (bone.data.shearX + x - bone.shearX) * alpha;
-					break;
-				case MixBlend.add:
-					bone.shearX += x * alpha;
-			}
-		}
-	}
-
-	/** Changes a bone's local {@link Bone#shearX} and {@link Bone#shearY}. */
-	export class ShearYTimeline extends CurveTimeline1 implements BoneTimeline {
-		boneIndex = 0;
-
-		constructor (frameCount: number, bezierCount: number, boneIndex: number) {
-			super(frameCount, bezierCount, Property.shearY + "|" + boneIndex);
-			this.boneIndex = boneIndex;
-		}
-
-		apply (skeleton: Skeleton, lastTime: number, time: number, events: Array<Event>, alpha: number, blend: MixBlend, direction: MixDirection) {
-			let bone = skeleton.bones[this.boneIndex];
-			if (!bone.active) return;
-
-			let frames = this.frames;
-			if (time < frames[0]) {
-				switch (blend) {
-					case MixBlend.setup:
-						bone.shearY = bone.data.shearY;
-						return;
-					case MixBlend.first:
-						bone.shearY += (bone.data.shearY - bone.shearY) * alpha;
-				}
-				return;
-			}
-
-			let y = this.getCurveValue(time);
-			switch (blend) {
-				case MixBlend.setup:
-					bone.shearY = bone.data.shearY + y * alpha;
-					break;
-				case MixBlend.first:
-				case MixBlend.replace:
-					bone.shearY += (bone.data.shearY + y - bone.shearY) * alpha;
-					break;
-				case MixBlend.add:
-					bone.shearY += y * alpha;
-			}
-		}
-	}
-
-	/** Changes a slot's {@link Slot#color}. */
-	export class RGBATimeline extends CurveTimeline implements SlotTimeline {
-		slotIndex = 0;
-
-		constructor (frameCount: number, bezierCount: number, slotIndex: number) {
-			super(frameCount, bezierCount, [
-				Property.rgb + "|" + slotIndex,
-				Property.alpha + "|" + slotIndex
-			]);
-			this.slotIndex = slotIndex;
-		}
-
-		getFrameEntries () {
-			return 5/*ENTRIES*/;
-		}
-
-		/** Sets the time in seconds, red, green, blue, and alpha for the specified key frame. */
-		setFrame (frame: number, time: number, r: number, g: number, b: number, a: number) {
-			frame *= 5/*ENTRIES*/;
-			this.frames[frame] = time;
-			this.frames[frame + 1/*R*/] = r;
-			this.frames[frame + 2/*G*/] = g;
-			this.frames[frame + 3/*B*/] = b;
-			this.frames[frame + 4/*A*/] = a;
-		}
-
-		apply (skeleton: Skeleton, lastTime: number, time: number, events: Array<Event>, alpha: number, blend: MixBlend, direction: MixDirection) {
-			let slot = skeleton.slots[this.slotIndex];
-			if (!slot.bone.active) return;
-
-			let frames = this.frames;
-			let color = slot.color;
-			if (time < frames[0]) {
-				let setup = slot.data.color;
-				switch (blend) {
-					case MixBlend.setup:
-						color.setFromColor(setup);
-						return;
-					case MixBlend.first:
-						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;
-			let i = Timeline.search(frames, time, 5/*ENTRIES*/);
-			let curveType = this.curves[i / 5/*ENTRIES*/];
-			switch (curveType) {
-				case 0/*LINEAR*/:
-					let before = frames[i];
-					r = frames[i + 1/*R*/];
-					g = frames[i + 2/*G*/];
-					b = frames[i + 3/*B*/];
-					a = frames[i + 4/*A*/];
-					let t = (time - before) / (frames[i + 5/*ENTRIES*/] - before);
-					r += (frames[i + 5/*ENTRIES*/ + 1/*R*/] - r) * t;
-					g += (frames[i + 5/*ENTRIES*/ + 2/*G*/] - g) * t;
-					b += (frames[i + 5/*ENTRIES*/ + 3/*B*/] - b) * t;
-					a += (frames[i + 5/*ENTRIES*/ + 4/*A*/] - a) * t;
-					break;
-				case 1/*STEPPED*/:
-					r = frames[i + 1/*R*/];
-					g = frames[i + 2/*G*/];
-					b = frames[i + 3/*B*/];
-					a = frames[i + 4/*A*/];
-					break;
-				default:
-					r = this.getBezierValue(time, i, 1/*R*/, curveType - 2/*BEZIER*/);
-					g = this.getBezierValue(time, i, 2/*G*/, curveType + 18/*BEZIER_SIZE*/ - 2/*BEZIER*/);
-					b = this.getBezierValue(time, i, 3/*B*/, curveType + 18/*BEZIER_SIZE*/ * 2 - 2/*BEZIER*/);
-					a = this.getBezierValue(time, i, 4/*A*/, curveType + 18/*BEZIER_SIZE*/ * 3 - 2/*BEZIER*/);
-			}
-			if (alpha == 1)
-				color.set(r, g, b, a);
-			else {
-				if (blend == MixBlend.setup) color.setFromColor(slot.data.color);
-				color.add((r - color.r) * alpha, (g - color.g) * alpha, (b - color.b) * alpha, (a - color.a) * alpha);
-			}
-		}
-	}
-
-	/** Changes a slot's {@link Slot#color}. */
-	export class RGBTimeline extends CurveTimeline implements SlotTimeline {
-		slotIndex = 0;
-
-		constructor (frameCount: number, bezierCount: number, slotIndex: number) {
-			super(frameCount, bezierCount, [
-				Property.rgb + "|" + slotIndex
-			]);
-			this.slotIndex = slotIndex;
-		}
-
-		getFrameEntries () {
-			return 4/*ENTRIES*/;
-		}
-
-		/** Sets the time in seconds, red, green, blue, and alpha for the specified key frame. */
-		setFrame (frame: number, time: number, r: number, g: number, b: number) {
-			frame <<= 2;
-			this.frames[frame] = time;
-			this.frames[frame + 1/*R*/] = r;
-			this.frames[frame + 2/*G*/] = g;
-			this.frames[frame + 3/*B*/] = b;
-		}
-
-		apply (skeleton: Skeleton, lastTime: number, time: number, events: Array<Event>, alpha: number, blend: MixBlend, direction: MixDirection) {
-			let slot = skeleton.slots[this.slotIndex];
-			if (!slot.bone.active) return;
-
-			let frames = this.frames;
-			let color = slot.color;
-			if (time < frames[0]) {
-				let setup = slot.data.color;
-				switch (blend) {
-					case MixBlend.setup:
-						color.r = setup.r;
-						color.g = setup.g;
-						color.b = setup.b;
-						return;
-					case MixBlend.first:
-						color.r += (setup.r - color.r) * alpha;
-						color.g += (setup.g - color.g) * alpha;
-						color.b += (setup.b - color.b) * alpha;
-				}
-				return;
-			}
-
-			let r = 0, g = 0, b = 0;
-			let i = Timeline.search(frames, time, 4/*ENTRIES*/);
-			let curveType = this.curves[i >> 2];
-			switch (curveType) {
-				case 0/*LINEAR*/:
-					let before = frames[i];
-					r = frames[i + 1/*R*/];
-					g = frames[i + 2/*G*/];
-					b = frames[i + 3/*B*/];
-					let t = (time - before) / (frames[i + 4/*ENTRIES*/] - before);
-					r += (frames[i + 4/*ENTRIES*/ + 1/*R*/] - r) * t;
-					g += (frames[i + 4/*ENTRIES*/ + 2/*G*/] - g) * t;
-					b += (frames[i + 4/*ENTRIES*/ + 3/*B*/] - b) * t;
-					break;
-				case 1/*STEPPED*/:
-					r = frames[i + 1/*R*/];
-					g = frames[i + 2/*G*/];
-					b = frames[i + 3/*B*/];
-					break;
-				default:
-					r = this.getBezierValue(time, i, 1/*R*/, curveType - 2/*BEZIER*/);
-					g = this.getBezierValue(time, i, 2/*G*/, curveType + 18/*BEZIER_SIZE*/ - 2/*BEZIER*/);
-					b = this.getBezierValue(time, i, 3/*B*/, curveType + 18/*BEZIER_SIZE*/ * 2 - 2/*BEZIER*/);
-			}
-			if (alpha == 1) {
-				color.r = r;
-				color.g = g;
-				color.b = b;
-			} else {
-				if (blend == MixBlend.setup) {
-					let setup = slot.data.color;
-					color.r = setup.r;
-					color.g = setup.g;
-					color.b = setup.b;
-				}
-				color.r += (r - color.r) * alpha;
-				color.g += (g - color.g) * alpha;
-				color.b += (b - color.b) * alpha;
-			}
-		}
-	}
-
-	/** Changes a bone's local {@link Bone#shearX} and {@link Bone#shearY}. */
-	export class AlphaTimeline extends CurveTimeline1 implements SlotTimeline {
-		slotIndex = 0;
-
-		constructor (frameCount: number, bezierCount: number, slotIndex: number) {
-			super(frameCount, bezierCount, Property.alpha + "|" + slotIndex);
-			this.slotIndex = slotIndex;
-		}
-
-		apply (skeleton: Skeleton, lastTime: number, time: number, events: Array<Event>, alpha: number, blend: MixBlend, direction: MixDirection) {
-			let slot = skeleton.slots[this.slotIndex];
-			if (!slot.bone.active) return;
-
-			let color = slot.color;
-			if (time < this.frames[0]) { // Time is before first frame.
-				let setup = slot.data.color;
-				switch (blend) {
-					case MixBlend.setup:
-						color.a = setup.a;
-						return;
-					case MixBlend.first:
-						color.a += (setup.a - color.a) * alpha;
-				}
-				return;
-			}
-
-			let a = this.getCurveValue(time);
-			if (alpha == 1)
-				color.a = a;
-			else {
-				if (blend == MixBlend.setup) color.a = slot.data.color.a;
-				color.a += (a - color.a) * alpha;
-			}
-		}
-	}
-
-	/** Changes a slot's {@link Slot#color} and {@link Slot#darkColor} for two color tinting. */
-	export class RGBA2Timeline extends CurveTimeline implements SlotTimeline {
-		slotIndex = 0;
-
-		constructor (frameCount: number, bezierCount: number, slotIndex: number) {
-			super(frameCount, bezierCount, [
-				Property.rgb + "|" + slotIndex,
-				Property.alpha + "|" + slotIndex,
-				Property.rgb2 + "|" + slotIndex
-			]);
-			this.slotIndex = slotIndex;
-		}
-
-		getFrameEntries () {
-			return 8/*ENTRIES*/;
-		}
-
-		/** Sets the time in seconds, light, and dark colors for the specified key frame. */
-		setFrame (frame: number, time: number, r: number, g: number, b: number, a: number, r2: number, g2: number, b2: number) {
-			frame <<= 3;
-			this.frames[frame] = time;
-			this.frames[frame + 1/*R*/] = r;
-			this.frames[frame + 2/*G*/] = g;
-			this.frames[frame + 3/*B*/] = b;
-			this.frames[frame + 4/*A*/] = a;
-			this.frames[frame + 5/*R2*/] = r2;
-			this.frames[frame + 6/*G2*/] = g2;
-			this.frames[frame + 7/*B2*/] = b2;
-		}
-
-		apply (skeleton: Skeleton, lastTime: number, time: number, events: Array<Event>, alpha: number, blend: MixBlend, direction: MixDirection) {
-			let slot = skeleton.slots[this.slotIndex];
-			if (!slot.bone.active) return;
-
-			let frames = this.frames;
-			let light = slot.color, dark = slot.darkColor;
-			if (time < frames[0]) {
-				let setupLight = slot.data.color, setupDark = slot.data.darkColor;
-				switch (blend) {
-					case MixBlend.setup:
-						light.setFromColor(setupLight);
-						dark.r = setupDark.r;
-						dark.g = setupDark.g;
-						dark.b = setupDark.b;
-						return;
-					case MixBlend.first:
-						light.add((setupLight.r - light.r) * alpha, (setupLight.g - light.g) * alpha, (setupLight.b - light.b) * alpha,
-							(setupLight.a - light.a) * alpha);
-						dark.r += (setupDark.r - dark.r) * alpha;
-						dark.g += (setupDark.g - dark.g) * alpha;
-						dark.b += (setupDark.b - dark.b) * alpha;
-				}
-				return;
-			}
-
-			let r = 0, g = 0, b = 0, a = 0, r2 = 0, g2 = 0, b2 = 0;
-			let i = Timeline.search(frames, time, 8/*ENTRIES*/);
-			let curveType = this.curves[i >> 3];
-			switch (curveType) {
-				case 0/*LINEAR*/:
-					let before = frames[i];
-					r = frames[i + 1/*R*/];
-					g = frames[i + 2/*G*/];
-					b = frames[i + 3/*B*/];
-					a = frames[i + 4/*A*/];
-					r2 = frames[i + 5/*R2*/];
-					g2 = frames[i + 6/*G2*/];
-					b2 = frames[i + 7/*B2*/];
-					let t = (time - before) / (frames[i + 8/*ENTRIES*/] - before);
-					r += (frames[i + 8/*ENTRIES*/ + 1/*R*/] - r) * t;
-					g += (frames[i + 8/*ENTRIES*/ + 2/*G*/] - g) * t;
-					b += (frames[i + 8/*ENTRIES*/ + 3/*B*/] - b) * t;
-					a += (frames[i + 8/*ENTRIES*/ + 4/*A*/] - a) * t;
-					r2 += (frames[i + 8/*ENTRIES*/ + 5/*R2*/] - r2) * t;
-					g2 += (frames[i + 8/*ENTRIES*/ + 6/*G2*/] - g2) * t;
-					b2 += (frames[i + 8/*ENTRIES*/ + 7/*B2*/] - b2) * t;
-					break;
-				case 1/*STEPPED*/:
-					r = frames[i + 1/*R*/];
-					g = frames[i + 2/*G*/];
-					b = frames[i + 3/*B*/];
-					a = frames[i + 4/*A*/];
-					r2 = frames[i + 5/*R2*/];
-					g2 = frames[i + 6/*G2*/];
-					b2 = frames[i + 7/*B2*/];
-					break;
-				default:
-					r = this.getBezierValue(time, i, 1/*R*/, curveType - 2/*BEZIER*/);
-					g = this.getBezierValue(time, i, 2/*G*/, curveType + 18/*BEZIER_SIZE*/ - 2/*BEZIER*/);
-					b = this.getBezierValue(time, i, 3/*B*/, curveType + 18/*BEZIER_SIZE*/ * 2 - 2/*BEZIER*/);
-					a = this.getBezierValue(time, i, 4/*A*/, curveType + 18/*BEZIER_SIZE*/ * 3 - 2/*BEZIER*/);
-					r2 = this.getBezierValue(time, i, 5/*R2*/, curveType + 18/*BEZIER_SIZE*/ * 4 - 2/*BEZIER*/);
-					g2 = this.getBezierValue(time, i, 6/*G2*/, curveType + 18/*BEZIER_SIZE*/ * 5 - 2/*BEZIER*/);
-					b2 = this.getBezierValue(time, i, 7/*B2*/, curveType + 18/*BEZIER_SIZE*/ * 6 - 2/*BEZIER*/);
-			}
-
-			if (alpha == 1) {
-				light.set(r, g, b, a);
-				dark.r = r2;
-				dark.g = g2;
-				dark.b = b2;
-			} else {
-				if (blend == MixBlend.setup) {
-					light.setFromColor(slot.data.color);
-					let setupDark = slot.data.darkColor;
-					dark.r = setupDark.r;
-					dark.g = setupDark.g;
-					dark.b = setupDark.b;
-				}
-				light.add((r - light.r) * alpha, (g - light.g) * alpha, (b - light.b) * alpha, (a - light.a) * alpha);
-				dark.r += (r2 - dark.r) * alpha;
-				dark.g += (g2 - dark.g) * alpha;
-				dark.b += (b2 - dark.b) * alpha;
-			}
-		}
-	}
-
-	/** Changes a slot's {@link Slot#color} and {@link Slot#darkColor} for two color tinting. */
-	export class RGB2Timeline extends CurveTimeline implements SlotTimeline {
-		slotIndex = 0;
-
-		constructor (frameCount: number, bezierCount: number, slotIndex: number) {
-			super(frameCount, bezierCount, [
-				Property.rgb + "|" + slotIndex,
-				Property.rgb2 + "|" + slotIndex
-			]);
-			this.slotIndex = slotIndex;
-		}
-
-		getFrameEntries () {
-			return 7/*ENTRIES*/;
-		}
-
-		/** Sets the time in seconds, light, and dark colors for the specified key frame. */
-		setFrame (frame: number, time: number, r: number, g: number, b: number, r2: number, g2: number, b2: number) {
-			frame *= 7/*ENTRIES*/;
-			this.frames[frame] = time;
-			this.frames[frame + 1/*R*/] = r;
-			this.frames[frame + 2/*G*/] = g;
-			this.frames[frame + 3/*B*/] = b;
-			this.frames[frame + 4/*R2*/] = r2;
-			this.frames[frame + 5/*G2*/] = g2;
-			this.frames[frame + 6/*B2*/] = b2;
-		}
-
-		apply (skeleton: Skeleton, lastTime: number, time: number, events: Array<Event>, alpha: number, blend: MixBlend, direction: MixDirection) {
-			let slot = skeleton.slots[this.slotIndex];
-			if (!slot.bone.active) return;
-
-			let frames = this.frames;
-			let light = slot.color, dark = slot.darkColor;
-			if (time < frames[0]) {
-				let setupLight = slot.data.color, setupDark = slot.data.darkColor;
-				switch (blend) {
-					case MixBlend.setup:
-						light.r = setupLight.r;
-						light.g = setupLight.g;
-						light.b = setupLight.b;
-						dark.r = setupDark.r;
-						dark.g = setupDark.g;
-						dark.b = setupDark.b;
-						return;
-					case MixBlend.first:
-						light.r += (setupLight.r - light.r) * alpha;
-						light.g += (setupLight.g - light.g) * alpha;
-						light.b += (setupLight.b - light.b) * alpha;
-						dark.r += (setupDark.r - dark.r) * alpha;
-						dark.g += (setupDark.g - dark.g) * alpha;
-						dark.b += (setupDark.b - dark.b) * alpha;
-				}
-				return;
-			}
-
-			let r = 0, g = 0, b = 0, a = 0, r2 = 0, g2 = 0, b2 = 0;
-			let i = Timeline.search(frames, time, 7/*ENTRIES*/);
-			let curveType = this.curves[i / 7/*ENTRIES*/];
-			switch (curveType) {
-				case 0/*LINEAR*/:
-					let before = frames[i];
-					r = frames[i + 1/*R*/];
-					g = frames[i + 2/*G*/];
-					b = frames[i + 3/*B*/];
-					r2 = frames[i + 4/*R2*/];
-					g2 = frames[i + 5/*G2*/];
-					b2 = frames[i + 6/*B2*/];
-					let t = (time - before) / (frames[i + 7/*ENTRIES*/] - before);
-					r += (frames[i + 7/*ENTRIES*/ + 1/*R*/] - r) * t;
-					g += (frames[i + 7/*ENTRIES*/ + 2/*G*/] - g) * t;
-					b += (frames[i + 7/*ENTRIES*/ + 3/*B*/] - b) * t;
-					r2 += (frames[i + 7/*ENTRIES*/ + 4/*R2*/] - r2) * t;
-					g2 += (frames[i + 7/*ENTRIES*/ + 5/*G2*/] - g2) * t;
-					b2 += (frames[i + 7/*ENTRIES*/ + 6/*B2*/] - b2) * t;
-					break;
-				case 1/*STEPPED*/:
-					r = frames[i + 1/*R*/];
-					g = frames[i + 2/*G*/];
-					b = frames[i + 3/*B*/];
-					r2 = frames[i + 4/*R2*/];
-					g2 = frames[i + 5/*G2*/];
-					b2 = frames[i + 6/*B2*/];
-					break;
-				default:
-					r = this.getBezierValue(time, i, 1/*R*/, curveType - 2/*BEZIER*/);
-					g = this.getBezierValue(time, i, 2/*G*/, curveType + 18/*BEZIER_SIZE*/ - 2/*BEZIER*/);
-					b = this.getBezierValue(time, i, 3/*B*/, curveType + 18/*BEZIER_SIZE*/ * 2 - 2/*BEZIER*/);
-					r2 = this.getBezierValue(time, i, 4/*R2*/, curveType + 18/*BEZIER_SIZE*/ * 3 - 2/*BEZIER*/);
-					g2 = this.getBezierValue(time, i, 5/*G2*/, curveType + 18/*BEZIER_SIZE*/ * 4 - 2/*BEZIER*/);
-					b2 = this.getBezierValue(time, i, 6/*B2*/, curveType + 18/*BEZIER_SIZE*/ * 5 - 2/*BEZIER*/);
-			}
-
-			if (alpha == 1) {
-				light.r = r;
-				light.g = g;
-				light.b = b;
-				dark.r = r2;
-				dark.g = g2;
-				dark.b = b2;
-			} else {
-				if (blend == MixBlend.setup) {
-					let setupLight = slot.data.color, setupDark = slot.data.darkColor;
-					light.r = setupLight.r;
-					light.g = setupLight.g;
-					light.b = setupLight.b;
-					dark.r = setupDark.r;
-					dark.g = setupDark.g;
-					dark.b = setupDark.b;
-				}
-				light.r += (r - light.r) * alpha;
-				light.g += (g - light.g) * alpha;
-				light.b += (b - light.b) * alpha;
-				dark.r += (r2 - dark.r) * alpha;
-				dark.g += (g2 - dark.g) * alpha;
-				dark.b += (b2 - dark.b) * alpha;
-			}
-		}
-	}
-
-	/** Changes a slot's {@link Slot#attachment}. */
-	export class AttachmentTimeline extends Timeline implements SlotTimeline {
-		slotIndex = 0;
-
-		/** The attachment name for each key frame. May contain null values to clear the attachment. */
-		attachmentNames: Array<string>;
-
-		constructor (frameCount: number, slotIndex: number) {
-			super(frameCount, [
-				Property.attachment + "|" + slotIndex
-			]);
-			this.slotIndex = slotIndex;
-			this.attachmentNames = new Array<string>(frameCount);
-		}
-
-		getFrameCount () {
-			return this.frames.length;
-		}
-
-		/** Sets the time in seconds and the attachment name for the specified key frame. */
-		setFrame (frame: number, time: number, attachmentName: string) {
-			this.frames[frame] = time;
-			this.attachmentNames[frame] = attachmentName;
-		}
-
-		apply (skeleton: Skeleton, lastTime: number, time: number, events: Array<Event>, alpha: number, blend: MixBlend, direction: MixDirection) {
-			let slot = skeleton.slots[this.slotIndex];
-			if (!slot.bone.active) return;
-
-			if (direction == MixDirection.mixOut) {
-				if (blend == MixBlend.setup) this.setAttachment(skeleton, slot, slot.data.attachmentName);
-				return;
-			}
-
-			if (time < this.frames[0]) {
-				if (blend == MixBlend.setup || blend == MixBlend.first) this.setAttachment(skeleton, slot, slot.data.attachmentName);
-				return;
-			}
-
-			this.setAttachment(skeleton, slot, this.attachmentNames[Timeline.search1(this.frames, time)]);
-		}
-
-		setAttachment (skeleton: Skeleton, slot: Slot, attachmentName: string) {
-			slot.setAttachment(!attachmentName ? null : skeleton.getAttachment(this.slotIndex, attachmentName));
-		}
-	}
-
-	/** Changes a slot's {@link Slot#deform} to deform a {@link VertexAttachment}. */
-	export class DeformTimeline extends CurveTimeline implements SlotTimeline {
-		slotIndex = 0;
-
-		/** The attachment that will be deformed. */
-		attachment: VertexAttachment;
-
-		/** The vertices for each key frame. */
-		vertices: Array<ArrayLike<number>>;
-
-		constructor (frameCount: number, bezierCount: number, slotIndex: number, attachment: VertexAttachment) {
-			super(frameCount, bezierCount, [
-				Property.deform + "|" + slotIndex + "|" + attachment.id
-			]);
-			this.slotIndex = slotIndex;
-			this.attachment = attachment;
-			this.vertices = new Array<ArrayLike<number>>(frameCount);
-		}
-
-		getFrameCount () {
-			return this.frames.length;
-		}
-
-		/** Sets the time in seconds and the vertices for the specified key frame.
-		 * @param vertices Vertex positions for an unweighted VertexAttachment, or deform offsets if it has weights. */
-		setFrame (frame: number, time: number, vertices: ArrayLike<number>) {
-			this.frames[frame] = time;
-			this.vertices[frame] = vertices;
-		}
-
-		/** @param value1 Ignored (0 is used for a deform timeline).
-		 * @param value2 Ignored (1 is used for a deform timeline). */
-		setBezier (bezier: number, frame: number, value: number, time1: number, value1: number, cx1: number, cy1: number, cx2: number,
-			cy2: number, time2: number, value2: number) {
-			let curves = this.curves;
-			let i = this.getFrameCount() + bezier * 18/*BEZIER_SIZE*/;
-			if (value == 0) curves[frame] = 2/*BEZIER*/ + i;
-			let tmpx = (time1 - cx1 * 2 + cx2) * 0.03, tmpy = cy2 * 0.03 - cy1 * 0.06;
-			let dddx = ((cx1 - cx2) * 3 - time1 + time2) * 0.006, dddy = (cy1 - cy2 + 0.33333333) * 0.018;
-			let ddx = tmpx * 2 + dddx, ddy = tmpy * 2 + dddy;
-			let dx = (cx1 - time1) * 0.3 + tmpx + dddx * 0.16666667, dy = cy1 * 0.3 + tmpy + dddy * 0.16666667;
-			let x = time1 + dx, y = dy;
-			for (let n = i + 18/*BEZIER_SIZE*/; i < n; i += 2) {
-				curves[i] = x;
-				curves[i + 1] = y;
-				dx += ddx;
-				dy += ddy;
-				ddx += dddx;
-				ddy += dddy;
-				x += dx;
-				y += dy;
-			}
-		}
-
-		getCurvePercent (time: number, frame: number) {
-			let curves = this.curves;
-			let i = curves[frame];
-			switch (i) {
-				case 0/*LINEAR*/:
-					let x = this.frames[frame];
-					return (time - x) / (this.frames[frame + this.getFrameEntries()] - x);
-				case 1/*STEPPED*/:
-					return 0;
-			}
-			i -= 2/*BEZIER*/;
-			if (curves[i] > time) {
-				let x = this.frames[frame];
-				return curves[i + 1] * (time - x) / (curves[i] - x);
-			}
-			let n = i + 18/*BEZIER_SIZE*/;
-			for (i += 2; i < n; i += 2) {
-				if (curves[i] >= time) {
-					let x = curves[i - 2], y = curves[i - 1];
-					return y + (time - x) / (curves[i] - x) * (curves[i + 1] - y);
-				}
-			}
-			let x = curves[n - 2], y = curves[n - 1];
-			return y + (1 - y) * (time - x) / (this.frames[frame + this.getFrameEntries()] - x);
-		}
-
-		apply (skeleton: Skeleton, lastTime: number, time: number, firedEvents: Array<Event>, alpha: number, blend: MixBlend, direction: MixDirection) {
-			let slot: Slot = skeleton.slots[this.slotIndex];
-			if (!slot.bone.active) return;
-			let slotAttachment: Attachment = slot.getAttachment();
-			if (!(slotAttachment instanceof VertexAttachment) || (<VertexAttachment>slotAttachment).deformAttachment != this.attachment) return;
-
-			let deform: Array<number> = slot.deform;
-			if (deform.length == 0) blend = MixBlend.setup;
-
-			let vertices = this.vertices;
-			let vertexCount = vertices[0].length;
-
-			let frames = this.frames;
-			if (time < frames[0]) {
-				let vertexAttachment = <VertexAttachment>slotAttachment;
-				switch (blend) {
-					case MixBlend.setup:
-						deform.length = 0;
-						return;
-					case MixBlend.first:
-						if (alpha == 1) {
-							deform.length = 0;
-							return;
-						}
-						deform.length = vertexCount;
-						if (!vertexAttachment.bones) {
-							// Unweighted vertex positions.
-							let setupVertices = vertexAttachment.vertices;
-							for (var i = 0; i < vertexCount; i++)
-								deform[i] += (setupVertices[i] - deform[i]) * alpha;
-						} else {
-							// Weighted deform offsets.
-							alpha = 1 - alpha;
-							for (var i = 0; i < vertexCount; i++)
-								deform[i] *= alpha;
-						}
-				}
-				return;
-			}
-
-			deform.length = vertexCount;
-			if (time >= frames[frames.length - 1]) { // Time is after last frame.
-				let lastVertices = vertices[frames.length - 1];
-				if (alpha == 1) {
-					if (blend == MixBlend.add) {
-						let vertexAttachment = slotAttachment as VertexAttachment;
-						if (!vertexAttachment.bones) {
-							// Unweighted vertex positions, with alpha.
-							let setupVertices = vertexAttachment.vertices;
-							for (let i = 0; i < vertexCount; i++)
-								deform[i] += lastVertices[i] - setupVertices[i];
-						} else {
-							// Weighted deform offsets, with alpha.
-							for (let i = 0; i < vertexCount; i++)
-								deform[i] += lastVertices[i];
-						}
-					} else
-						Utils.arrayCopy(lastVertices, 0, deform, 0, vertexCount);
-				} else {
-					switch (blend) {
-						case MixBlend.setup: {
-							let vertexAttachment = slotAttachment as VertexAttachment;
-							if (!vertexAttachment.bones) {
-								// Unweighted vertex positions, with alpha.
-								let setupVertices = vertexAttachment.vertices;
-								for (let i = 0; i < vertexCount; i++) {
-									let setup = setupVertices[i];
-									deform[i] = setup + (lastVertices[i] - setup) * alpha;
-								}
-							} else {
-								// Weighted deform offsets, with alpha.
-								for (let i = 0; i < vertexCount; i++)
-									deform[i] = lastVertices[i] * alpha;
-							}
-							break;
-						}
-						case MixBlend.first:
-						case MixBlend.replace:
-							for (let i = 0; i < vertexCount; i++)
-								deform[i] += (lastVertices[i] - deform[i]) * alpha;
-							break;
-						case MixBlend.add:
-							let vertexAttachment = slotAttachment as VertexAttachment;
-							if (!vertexAttachment.bones) {
-								// Unweighted vertex positions, with alpha.
-								let setupVertices = vertexAttachment.vertices;
-								for (let i = 0; i < vertexCount; i++)
-									deform[i] += (lastVertices[i] - setupVertices[i]) * alpha;
-							} else {
-								// Weighted deform offsets, with alpha.
-								for (let i = 0; i < vertexCount; i++)
-									deform[i] += lastVertices[i] * alpha;
-							}
-					}
-				}
-				return;
-			}
-
-			// Interpolate between the previous frame and the current frame.
-			let frame = Timeline.search1(frames, time);
-			let percent = this.getCurvePercent(time, frame);
-			let prevVertices = vertices[frame];
-			let nextVertices = vertices[frame + 1];
-
-			if (alpha == 1) {
-				if (blend == MixBlend.add) {
-					let vertexAttachment = slotAttachment as VertexAttachment;
-					if (!vertexAttachment.bones) {
-						// Unweighted vertex positions, with alpha.
-						let setupVertices = vertexAttachment.vertices;
-						for (let i = 0; i < vertexCount; i++) {
-							let prev = prevVertices[i];
-							deform[i] += prev + (nextVertices[i] - prev) * percent - setupVertices[i];
-						}
-					} else {
-						// Weighted deform offsets, with alpha.
-						for (let i = 0; i < vertexCount; i++) {
-							let prev = prevVertices[i];
-							deform[i] += prev + (nextVertices[i] - prev) * percent;
-						}
-					}
-				} else {
-					for (let i = 0; i < vertexCount; i++) {
-						let prev = prevVertices[i];
-						deform[i] = prev + (nextVertices[i] - prev) * percent;
-					}
-				}
-			} else {
-				switch (blend) {
-					case MixBlend.setup: {
-						let vertexAttachment = slotAttachment as VertexAttachment;
-						if (!vertexAttachment.bones) {
-							// Unweighted vertex positions, with alpha.
-							let setupVertices = vertexAttachment.vertices;
-							for (let i = 0; i < vertexCount; i++) {
-								let prev = prevVertices[i], setup = setupVertices[i];
-								deform[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];
-								deform[i] = (prev + (nextVertices[i] - prev) * percent) * alpha;
-							}
-						}
-						break;
-					}
-					case MixBlend.first:
-					case MixBlend.replace:
-						for (let i = 0; i < vertexCount; i++) {
-							let prev = prevVertices[i];
-							deform[i] += (prev + (nextVertices[i] - prev) * percent - deform[i]) * alpha;
-						}
-						break;
-					case MixBlend.add:
-						let vertexAttachment = slotAttachment as VertexAttachment;
-						if (!vertexAttachment.bones) {
-							// Unweighted vertex positions, with alpha.
-							let setupVertices = vertexAttachment.vertices;
-							for (let i = 0; i < vertexCount; i++) {
-								let prev = prevVertices[i];
-								deform[i] += (prev + (nextVertices[i] - prev) * percent - setupVertices[i]) * alpha;
-							}
-						} else {
-							// Weighted deform offsets, with alpha.
-							for (let i = 0; i < vertexCount; i++) {
-								let prev = prevVertices[i];
-								deform[i] += (prev + (nextVertices[i] - prev) * percent) * alpha;
-							}
-						}
-				}
-			}
-		}
-	}
-
-	/** Fires an {@link Event} when specific animation times are reached. */
-	export class EventTimeline extends Timeline {
-		static propertyIds = ["" + Property.event];
-
-		/** The event for each key frame. */
-		events: Array<Event>;
-
-		constructor (frameCount: number) {
-			super(frameCount, EventTimeline.propertyIds);
-
-			this.events = new Array<Event>(frameCount);
-		}
-
-		getFrameCount () {
-			return this.frames.length;
-		}
-
-		/** Sets the time in seconds and the event for the specified key frame. */
-		setFrame (frame: number, event: Event) {
-			this.frames[frame] = event.time;
-			this.events[frame] = event;
-		}
-
-		/** Fires events for frames > `lastTime` and <= `time`. */
-		apply (skeleton: Skeleton, lastTime: number, time: number, firedEvents: Array<Event>, alpha: number, blend: MixBlend, direction: MixDirection) {
-			if (!firedEvents) 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, blend, 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 i = 0;
-			if (lastTime < frames[0])
-				i = 0;
-			else {
-				i = Timeline.search1(frames, lastTime) + 1;
-				let frameTime = frames[i];
-				while (i > 0) { // Fire multiple events with the same frame.
-					if (frames[i - 1] != frameTime) break;
-					i--;
-				}
-			}
-			for (; i < frameCount && time >= frames[i]; i++)
-				firedEvents.push(this.events[i]);
-		}
-	}
-
-	/** Changes a skeleton's {@link Skeleton#drawOrder}. */
-	export class DrawOrderTimeline extends Timeline {
-		static propertyIds = ["" + Property.drawOrder];
-
-		/** The draw order for each key frame. See {@link #setFrame(int, float, int[])}. */
-		drawOrders: Array<Array<number>>;
-
-		constructor (frameCount: number) {
-			super(frameCount, DrawOrderTimeline.propertyIds);
-			this.drawOrders = new Array<Array<number>>(frameCount);
-		}
-
-		getFrameCount () {
-			return this.frames.length;
-		}
-
-		/** Sets the time in seconds and the draw order for the specified key frame.
-		 * @param drawOrder For each slot in {@link Skeleton#slots}, the index of the new draw order. May be null to use setup pose
-		 *           draw order. */
-		setFrame (frame: number, time: number, drawOrder: Array<number>) {
-			this.frames[frame] = time;
-			this.drawOrders[frame] = drawOrder;
-		}
-
-		apply (skeleton: Skeleton, lastTime: number, time: number, firedEvents: Array<Event>, alpha: number, blend: MixBlend, direction: MixDirection) {
-			if (direction == MixDirection.mixOut) {
-				if (blend == MixBlend.setup) Utils.arrayCopy(skeleton.slots, 0, skeleton.drawOrder, 0, skeleton.slots.length);
-				return;
-			}
-
-			if (time < this.frames[0]) {
-				if (blend == MixBlend.setup || blend == MixBlend.first) Utils.arrayCopy(skeleton.slots, 0, skeleton.drawOrder, 0, skeleton.slots.length);
-				return;
-			}
-
-			let drawOrderToSetupIndex = this.drawOrders[Timeline.search1(this.frames, time)];
-			if (!drawOrderToSetupIndex)
-				Utils.arrayCopy(skeleton.slots, 0, skeleton.drawOrder, 0, skeleton.slots.length);
-			else {
-				let drawOrder: Array<Slot> = skeleton.drawOrder;
-				let slots: Array<Slot> = skeleton.slots;
-				for (let i = 0, n = drawOrderToSetupIndex.length; i < n; i++)
-					drawOrder[i] = slots[drawOrderToSetupIndex[i]];
-			}
-		}
-	}
-
-	/** Changes an IK constraint's {@link IkConstraint#mix}, {@link IkConstraint#softness},
-	 * {@link IkConstraint#bendDirection}, {@link IkConstraint#stretch}, and {@link IkConstraint#compress}. */
-	export class IkConstraintTimeline extends CurveTimeline {
-		/** The index of the IK constraint slot in {@link Skeleton#ikConstraints} that will be changed. */
-		ikConstraintIndex: number;
-
-		constructor (frameCount: number, bezierCount: number, ikConstraintIndex: number) {
-			super(frameCount, bezierCount, [
-				Property.ikConstraint + "|" + ikConstraintIndex
-			]);
-			this.ikConstraintIndex = ikConstraintIndex;
-		}
-
-		getFrameEntries () {
-			return 6/*ENTRIES*/;
-		}
-
-		/** Sets the time in seconds, mix, softness, bend direction, compress, and stretch for the specified key frame. */
-		setFrame (frame: number, time: number, mix: number, softness: number, bendDirection: number, compress: boolean, stretch: boolean) {
-			frame *= 6/*ENTRIES*/;
-			this.frames[frame] = time;
-			this.frames[frame + 1/*MIX*/] = mix;
-			this.frames[frame + 2/*SOFTNESS*/] = softness;
-			this.frames[frame + 3/*BEND_DIRECTION*/] = bendDirection;
-			this.frames[frame + 4/*COMPRESS*/] = compress ? 1 : 0;
-			this.frames[frame + 5/*STRETCH*/] = stretch ? 1 : 0;
-		}
-
-		apply (skeleton: Skeleton, lastTime: number, time: number, firedEvents: Array<Event>, alpha: number, blend: MixBlend, direction: MixDirection) {
-			let constraint: IkConstraint = skeleton.ikConstraints[this.ikConstraintIndex];
-			if (!constraint.active) return;
-
-			let frames = this.frames;
-			if (time < frames[0]) {
-				switch (blend) {
-					case MixBlend.setup:
-						constraint.mix = constraint.data.mix;
-						constraint.softness = constraint.data.softness;
-						constraint.bendDirection = constraint.data.bendDirection;
-						constraint.compress = constraint.data.compress;
-						constraint.stretch = constraint.data.stretch;
-						return;
-					case MixBlend.first:
-						constraint.mix += (constraint.data.mix - constraint.mix) * alpha;
-						constraint.softness += (constraint.data.softness - constraint.softness) * alpha;
-						constraint.bendDirection = constraint.data.bendDirection;
-						constraint.compress = constraint.data.compress;
-						constraint.stretch = constraint.data.stretch;
-				}
-				return;
-			}
-
-			let mix = 0, softness = 0;
-			let i = Timeline.search(frames, time, 6/*ENTRIES*/)
-			let curveType = this.curves[i / 6/*ENTRIES*/];
-			switch (curveType) {
-				case 0/*LINEAR*/:
-					let before = frames[i];
-					mix = frames[i + 1/*MIX*/];
-					softness = frames[i + 2/*SOFTNESS*/];
-					let t = (time - before) / (frames[i + 6/*ENTRIES*/] - before);
-					mix += (frames[i + 6/*ENTRIES*/ + 1/*MIX*/] - mix) * t;
-					softness += (frames[i + 6/*ENTRIES*/ + 2/*SOFTNESS*/] - softness) * t;
-					break;
-				case 1/*STEPPED*/:
-					mix = frames[i + 1/*MIX*/];
-					softness = frames[i + 2/*SOFTNESS*/];
-					break;
-				default:
-					mix = this.getBezierValue(time, i, 1/*MIX*/, curveType - 2/*BEZIER*/);
-					softness = this.getBezierValue(time, i, 2/*SOFTNESS*/, curveType + 18/*BEZIER_SIZE*/ - 2/*BEZIER*/);
-			}
-
-			if (blend == MixBlend.setup) {
-				constraint.mix = constraint.data.mix + (mix - constraint.data.mix) * alpha;
-				constraint.softness = constraint.data.softness + (softness - constraint.data.softness) * alpha;
-
-				if (direction == MixDirection.mixOut) {
-					constraint.bendDirection = constraint.data.bendDirection;
-					constraint.compress = constraint.data.compress;
-					constraint.stretch = constraint.data.stretch;
-				} else {
-					constraint.bendDirection = frames[i + 3/*BEND_DIRECTION*/];
-					constraint.compress = frames[i + 4/*COMPRESS*/] != 0;
-					constraint.stretch = frames[i + 5/*STRETCH*/] != 0;
-				}
-			} else {
-				constraint.mix += (mix - constraint.mix) * alpha;
-				constraint.softness += (softness - constraint.softness) * alpha;
-				if (direction == MixDirection.mixIn) {
-					constraint.bendDirection = frames[i + 3/*BEND_DIRECTION*/];
-					constraint.compress = frames[i + 4/*COMPRESS*/] != 0;
-					constraint.stretch = frames[i + 5/*STRETCH*/] != 0;
-				}
-			}
-		}
-	}
-
-	/** Changes a transform constraint's {@link TransformConstraint#rotateMix}, {@link TransformConstraint#translateMix},
-	 * {@link TransformConstraint#scaleMix}, and {@link TransformConstraint#shearMix}. */
-	export class TransformConstraintTimeline extends CurveTimeline {
-		/** The index of the transform constraint slot in {@link Skeleton#transformConstraints} that will be changed. */
-		transformConstraintIndex: number;
-
-		constructor (frameCount: number, bezierCount: number, transformConstraintIndex: number) {
-			super(frameCount, bezierCount, [
-				Property.transformConstraint + "|" + transformConstraintIndex
-			]);
-			this.transformConstraintIndex = transformConstraintIndex;
-		}
-
-		getFrameEntries () {
-			return 7/*ENTRIES*/;
-		}
-
-		/** The time in seconds, rotate mix, translate mix, scale mix, and shear mix for the specified key frame. */
-		setFrame (frame: number, time: number, mixRotate: number, mixX: number, mixY: number, mixScaleX: number, mixScaleY: number,
-			mixShearY: number) {
-			let frames = this.frames;
-			frame *= 7/*ENTRIES*/;
-			frames[frame] = time;
-			frames[frame + 1/*ROTATE*/] = mixRotate;
-			frames[frame + 2/*X*/] = mixX;
-			frames[frame + 3/*Y*/] = mixY;
-			frames[frame + 4/*SCALEX*/] = mixScaleX;
-			frames[frame + 5/*SCALEY*/] = mixScaleY;
-			frames[frame + 6/*SHEARY*/] = mixShearY;
-		}
-
-		apply (skeleton: Skeleton, lastTime: number, time: number, firedEvents: Array<Event>, alpha: number, blend: MixBlend, direction: MixDirection) {
-			let constraint: TransformConstraint = skeleton.transformConstraints[this.transformConstraintIndex];
-			if (!constraint.active) return;
-
-			let frames = this.frames;
-			if (time < frames[0]) {
-				let data = constraint.data;
-				switch (blend) {
-					case MixBlend.setup:
-						constraint.mixRotate = data.mixRotate;
-						constraint.mixX = data.mixX;
-						constraint.mixY = data.mixY;
-						constraint.mixScaleX = data.mixScaleX;
-						constraint.mixScaleY = data.mixScaleY;
-						constraint.mixShearY = data.mixShearY;
-						return;
-					case MixBlend.first:
-						constraint.mixRotate += (data.mixRotate - constraint.mixRotate) * alpha;
-						constraint.mixX += (data.mixX - constraint.mixX) * alpha;
-						constraint.mixY += (data.mixY - constraint.mixY) * alpha;
-						constraint.mixScaleX += (data.mixScaleX - constraint.mixScaleX) * alpha;
-						constraint.mixScaleY += (data.mixScaleY - constraint.mixScaleY) * alpha;
-						constraint.mixShearY += (data.mixShearY - constraint.mixShearY) * alpha;
-				}
-				return;
-			}
-
-			let rotate, x, y, scaleX, scaleY, shearY;
-			let i = Timeline.search(frames, time, 7/*ENTRIES*/);
-			let curveType = this.curves[i / 7/*ENTRIES*/];
-			switch (curveType) {
-				case 0/*LINEAR*/:
-					let before = frames[i];
-					rotate = frames[i + 1/*ROTATE*/];
-					x = frames[i + 2/*X*/];
-					y = frames[i + 3/*Y*/];
-					scaleX = frames[i + 4/*SCALEX*/];
-					scaleY = frames[i + 5/*SCALEY*/];
-					shearY = frames[i + 6/*SHEARY*/];
-					let t = (time - before) / (frames[i + 7/*ENTRIES*/] - before);
-					rotate += (frames[i + 7/*ENTRIES*/ + 1/*ROTATE*/] - rotate) * t;
-					x += (frames[i + 7/*ENTRIES*/ + 2/*X*/] - x) * t;
-					y += (frames[i + 7/*ENTRIES*/ + 3/*Y*/] - y) * t;
-					scaleX += (frames[i + 7/*ENTRIES*/ + 4/*SCALEX*/] - scaleX) * t;
-					scaleY += (frames[i + 7/*ENTRIES*/ + 5/*SCALEY*/] - scaleY) * t;
-					shearY += (frames[i + 7/*ENTRIES*/ + 6/*SHEARY*/] - shearY) * t;
-					break;
-				case 1/*STEPPED*/:
-					rotate = frames[i + 1/*ROTATE*/];
-					x = frames[i + 2/*X*/];
-					y = frames[i + 3/*Y*/];
-					scaleX = frames[i + 4/*SCALEX*/];
-					scaleY = frames[i + 5/*SCALEY*/];
-					shearY = frames[i + 6/*SHEARY*/];
-					break;
-				default:
-					rotate = this.getBezierValue(time, i, 1/*ROTATE*/, curveType - 2/*BEZIER*/);
-					x = this.getBezierValue(time, i, 2/*X*/, curveType + 18/*BEZIER_SIZE*/ - 2/*BEZIER*/);
-					y = this.getBezierValue(time, i, 3/*Y*/, curveType + 18/*BEZIER_SIZE*/ * 2 - 2/*BEZIER*/);
-					scaleX = this.getBezierValue(time, i, 4/*SCALEX*/, curveType + 18/*BEZIER_SIZE*/ * 3 - 2/*BEZIER*/);
-					scaleY = this.getBezierValue(time, i, 5/*SCALEY*/, curveType + 18/*BEZIER_SIZE*/ * 4 - 2/*BEZIER*/);
-					shearY = this.getBezierValue(time, i, 6/*SHEARY*/, curveType + 18/*BEZIER_SIZE*/ * 5 - 2/*BEZIER*/);
-			}
-
-			if (blend == MixBlend.setup) {
-				let data = constraint.data;
-				constraint.mixRotate = data.mixRotate + (rotate - data.mixRotate) * alpha;
-				constraint.mixX = data.mixX + (x - data.mixX) * alpha;
-				constraint.mixY = data.mixY + (y - data.mixY) * alpha;
-				constraint.mixScaleX = data.mixScaleX + (scaleX - data.mixScaleX) * alpha;
-				constraint.mixScaleY = data.mixScaleY + (scaleY - data.mixScaleY) * alpha;
-				constraint.mixShearY = data.mixShearY + (shearY - data.mixShearY) * alpha;
-			} else {
-				constraint.mixRotate += (rotate - constraint.mixRotate) * alpha;
-				constraint.mixX += (x - constraint.mixX) * alpha;
-				constraint.mixY += (y - constraint.mixY) * alpha;
-				constraint.mixScaleX += (scaleX - constraint.mixScaleX) * alpha;
-				constraint.mixScaleY += (scaleY - constraint.mixScaleY) * alpha;
-				constraint.mixShearY += (shearY - constraint.mixShearY) * alpha;
-			}
-		}
-	}
-
-	/** Changes a path constraint's {@link PathConstraint#position}. */
-	export class PathConstraintPositionTimeline extends CurveTimeline1 {
-		/** The index of the path constraint slot in {@link Skeleton#pathConstraints} that will be changed. */
-		pathConstraintIndex: number;
-
-		constructor (frameCount: number, bezierCount: number, pathConstraintIndex: number) {
-			super(frameCount, bezierCount, Property.pathConstraintPosition + "|" + pathConstraintIndex);
-			this.pathConstraintIndex = pathConstraintIndex;
-		}
-
-		apply (skeleton: Skeleton, lastTime: number, time: number, firedEvents: Array<Event>, alpha: number, blend: MixBlend, direction: MixDirection) {
-			let constraint: PathConstraint = skeleton.pathConstraints[this.pathConstraintIndex];
-			if (!constraint.active) return;
-
-			let frames = this.frames;
-			if (time < frames[0]) {
-				switch (blend) {
-					case MixBlend.setup:
-						constraint.position = constraint.data.position;
-						return;
-					case MixBlend.first:
-						constraint.position += (constraint.data.position - constraint.position) * alpha;
-				}
-				return;
-			}
-
-			let position = this.getCurveValue(time);
-
-			if (blend == MixBlend.setup)
-				constraint.position = constraint.data.position + (position - constraint.data.position) * alpha;
-			else
-				constraint.position += (position - constraint.position) * alpha;
-		}
-	}
-
-	/** Changes a path constraint's {@link PathConstraint#spacing}. */
-	export class PathConstraintSpacingTimeline extends CurveTimeline1 {
-		/** The index of the path constraint slot in {@link Skeleton#getPathConstraints()} that will be changed. */
-		pathConstraintIndex = 0;
-
-		constructor (frameCount: number, bezierCount: number, pathConstraintIndex: number) {
-			super(frameCount, bezierCount, Property.pathConstraintSpacing + "|" + pathConstraintIndex);
-			this.pathConstraintIndex = pathConstraintIndex;
-		}
-
-		apply (skeleton: Skeleton, lastTime: number, time: number, firedEvents: Array<Event>, alpha: number, blend: MixBlend, direction: MixDirection) {
-			let constraint: PathConstraint = skeleton.pathConstraints[this.pathConstraintIndex];
-			if (!constraint.active) return;
-
-			let frames = this.frames;
-			if (time < frames[0]) {
-				switch (blend) {
-					case MixBlend.setup:
-						constraint.spacing = constraint.data.spacing;
-						return;
-					case MixBlend.first:
-						constraint.spacing += (constraint.data.spacing - constraint.spacing) * alpha;
-				}
-				return;
-			}
-
-			let spacing = this.getCurveValue(time);
-
-			if (blend == MixBlend.setup)
-				constraint.spacing = constraint.data.spacing + (spacing - constraint.data.spacing) * alpha;
-			else
-				constraint.spacing += (spacing - constraint.spacing) * alpha;
-		}
-	}
-
-	/** Changes a transform constraint's {@link PathConstraint#getMixRotate()}, {@link PathConstraint#getMixX()}, and
-	 * {@link PathConstraint#getMixY()}. */
-	export class PathConstraintMixTimeline extends CurveTimeline {
-		/** The index of the path constraint slot in {@link Skeleton#getPathConstraints()} that will be changed. */
-		pathConstraintIndex = 0;
-
-		constructor (frameCount: number, bezierCount: number, pathConstraintIndex: number) {
-			super(frameCount, bezierCount, [
-				Property.pathConstraintMix + "|" + pathConstraintIndex
-			]);
-			this.pathConstraintIndex = pathConstraintIndex;
-		}
-
-		getFrameEntries () {
-			return 4/*ENTRIES*/;
-		}
-
-		setFrame (frame: number, time: number, mixRotate: number, mixX: number, mixY: number) {
-			let frames = this.frames;
-			frame <<= 2;
-			frames[frame] = time;
-			frames[frame + 1/*ROTATE*/] = mixRotate;
-			frames[frame + 2/*X*/] = mixX;
-			frames[frame + 3/*Y*/] = mixY;
-		}
-
-		apply (skeleton: Skeleton, lastTime: number, time: number, firedEvents: Array<Event>, alpha: number, blend: MixBlend, direction: MixDirection) {
-			let constraint: PathConstraint = skeleton.pathConstraints[this.pathConstraintIndex];
-			if (!constraint.active) return;
-
-			let frames = this.frames;
-			if (time < frames[0]) {
-				switch (blend) {
-					case MixBlend.setup:
-						constraint.mixRotate = constraint.data.mixRotate;
-						constraint.mixX = constraint.data.mixX;
-						constraint.mixY = constraint.data.mixY;
-						return;
-					case MixBlend.first:
-						constraint.mixRotate += (constraint.data.mixRotate - constraint.mixRotate) * alpha;
-						constraint.mixX += (constraint.data.mixX - constraint.mixX) * alpha;
-						constraint.mixY += (constraint.data.mixY - constraint.mixY) * alpha;
-				}
-				return;
-			}
-
-			let rotate, x, y;
-			let i = Timeline.search(frames, time, 4/*ENTRIES*/);
-			let curveType = this.curves[i >> 2];
-			switch (curveType) {
-				case 0/*LINEAR*/:
-					let before = frames[i];
-					rotate = frames[i + 1/*ROTATE*/];
-					x = frames[i + 2/*X*/];
-					y = frames[i + 3/*Y*/];
-					let t = (time - before) / (frames[i + 4/*ENTRIES*/] - before);
-					rotate += (frames[i + 4/*ENTRIES*/ + 1/*ROTATE*/] - rotate) * t;
-					x += (frames[i + 4/*ENTRIES*/ + 2/*X*/] - x) * t;
-					y += (frames[i + 4/*ENTRIES*/ + 3/*Y*/] - y) * t;
-					break;
-				case 1/*STEPPED*/:
-					rotate = frames[i + 1/*ROTATE*/];
-					x = frames[i + 2/*X*/];
-					y = frames[i + 3/*Y*/];
-					break;
-				default:
-					rotate = this.getBezierValue(time, i, 1/*ROTATE*/, curveType - 2/*BEZIER*/);
-					x = this.getBezierValue(time, i, 2/*X*/, curveType + 18/*BEZIER_SIZE*/ - 2/*BEZIER*/);
-					y = this.getBezierValue(time, i, 3/*Y*/, curveType + 18/*BEZIER_SIZE*/ * 2 - 2/*BEZIER*/);
-			}
-
-			if (blend == MixBlend.setup) {
-				let data = constraint.data;
-				constraint.mixRotate = data.mixRotate + (rotate - data.mixRotate) * alpha;
-				constraint.mixX = data.mixX + (x - data.mixX) * alpha;
-				constraint.mixY = data.mixY + (y - data.mixY) * alpha;
-			} else {
-				constraint.mixRotate += (rotate - constraint.mixRotate) * alpha;
-				constraint.mixX += (x - constraint.mixX) * alpha;
-				constraint.mixY += (y - constraint.mixY) * alpha;
-			}
-		}
-	}
-}

+ 0 - 1171
spine-ts/core/src/AnimationState.ts

@@ -1,1171 +0,0 @@
-/******************************************************************************
- * Spine Runtimes License Agreement
- * Last updated January 1, 2020. Replaces all prior versions.
- *
- * Copyright (c) 2013-2020, Esoteric Software LLC
- *
- * Integration of the Spine Runtimes into software or otherwise creating
- * derivative works of the Spine Runtimes is permitted under the terms and
- * conditions of Section 2 of the Spine Editor License Agreement:
- * http://esotericsoftware.com/spine-editor-license
- *
- * Otherwise, it is permitted to integrate the Spine Runtimes into software
- * or otherwise create derivative works of the Spine Runtimes (collectively,
- * "Products"), provided that each user of the Products must obtain their own
- * Spine Editor license and redistribution of the Products in any form must
- * include this license and copyright notice.
- *
- * THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "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 LLC 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
- * THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *****************************************************************************/
-
-module spine {
-
-	/** Applies animations over time, queues animations for later playback, mixes (crossfading) between animations, and applies
-	 * multiple animations on top of each other (layering).
-	 *
-	 * See [Applying Animations](http://esotericsoftware.com/spine-applying-animations/) in the Spine Runtimes Guide. */
-	export class AnimationState {
-		private static emptyAnimation (): Animation {
-			if (!_emptyAnimation) _emptyAnimation = new Animation("<empty>", [], 0);
-			return _emptyAnimation;
-		}
-
-		/** The AnimationStateData to look up mix durations. */
-		data: AnimationStateData;
-
-		/** The list of tracks that currently have animations, which may contain null entries. */
-		tracks = new Array<TrackEntry>();
-
-		/** Multiplier for the delta time when the animation state is updated, causing time for all animations and mixes to play slower
-		 * or faster. Defaults to 1.
-		 *
-		 * See TrackEntry {@link TrackEntry#timeScale} for affecting a single animation. */
-		timeScale = 1;
-		unkeyedState = 0;
-
-		events = new Array<Event>();
-		listeners = new Array<AnimationStateListener>();
-		queue = new EventQueue(this);
-		propertyIDs = new StringSet();
-		animationsChanged = false;
-
-		trackEntryPool = new Pool<TrackEntry>(() => new TrackEntry());
-
-		constructor (data: AnimationStateData) {
-			this.data = data;
-		}
-
-		/** Increments each track entry {@link TrackEntry#trackTime()}, setting queued animations as current if needed. */
-		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) 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) {
-					// 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 += current.timeScale == 0 ? 0 : (nextTime / current.timeScale + delta) * next.timeScale;
-						current.trackTime += currentDelta;
-						this.setCurrent(i, next, true);
-						while (next.mixingFrom) {
-							next.mixTime += delta;
-							next = next.mixingFrom;
-						}
-						continue;
-					}
-				} else if (current.trackLast >= current.trackEnd && !current.mixingFrom) {
-					tracks[i] = null;
-					this.queue.end(current);
-					this.clearNext(current);
-					continue;
-				}
-				if (current.mixingFrom && this.updateMixingFrom(current, delta)) {
-					// End mixing from entries once all have completed.
-					let from = current.mixingFrom;
-					current.mixingFrom = null;
-					if (from) from.mixingTo = null;
-					while (from) {
-						this.queue.end(from);
-						from = from.mixingFrom;
-					}
-				}
-
-				current.trackTime += currentDelta;
-			}
-
-			this.queue.drain();
-		}
-
-		/** Returns true when all mixing from entries are complete. */
-		updateMixingFrom (to: TrackEntry, delta: number): boolean {
-			let from = to.mixingFrom;
-			if (!from) return true;
-
-			let finished = this.updateMixingFrom(from, delta);
-
-			from.animationLast = from.nextAnimationLast;
-			from.trackLast = from.nextTrackLast;
-
-			// Require mixTime > 0 to ensure the mixing from entry was applied at least once.
-			if (to.mixTime > 0 && to.mixTime >= to.mixDuration) {
-				// Require totalAlpha == 0 to ensure mixing is complete, unless mixDuration == 0 (the transition is a single frame).
-				if (from.totalAlpha == 0 || to.mixDuration == 0) {
-					to.mixingFrom = from.mixingFrom;
-					if (from.mixingFrom) from.mixingFrom.mixingTo = to;
-					to.interruptAlpha = from.interruptAlpha;
-					this.queue.end(from);
-				}
-				return finished;
-			}
-
-			from.trackTime += delta * from.timeScale;
-			to.mixTime += delta;
-			return false;
-		}
-
-		/** Poses the skeleton using the track entry animations. There are no side effects other than invoking listeners, so the
-		 * animation state can be applied to multiple skeletons to pose them identically.
-		 * @returns True if any animations were applied. */
-		apply (skeleton: Skeleton): boolean {
-			if (!skeleton) 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 || current.delay > 0) continue;
-				applied = true;
-				let blend: MixBlend = i == 0 ? MixBlend.first : current.mixBlend;
-
-				// Apply mixing from entries first.
-				let mix = current.alpha;
-				if (current.mixingFrom)
-					mix *= this.applyMixingFrom(current, skeleton, blend);
-				else if (current.trackTime >= current.trackEnd && !current.next)
-					mix = 0;
-
-				// Apply current entry.
-				let animationLast = current.animationLast, animationTime = current.getAnimationTime(), applyTime = animationTime;
-				let applyEvents = events;
-				if (current.reverse) {
-					applyTime = current.animation.duration - applyTime;
-					applyEvents = null;
-				}
-				let timelines = current.animation.timelines;
-				let timelineCount = timelines.length;
-				if ((i == 0 && mix == 1) || blend == MixBlend.add) {
-					for (let ii = 0; ii < timelineCount; ii++) {
-						// Fixes issue #302 on IOS9 where mix, blend sometimes became undefined and caused assets
-						// to sometimes stop rendering when using color correction, as their RGBA values become NaN.
-						// (https://github.com/pixijs/pixi-spine/issues/302)
-						Utils.webkit602BugfixHelper(mix, blend);
-						var timeline = timelines[ii];
-						if (timeline instanceof AttachmentTimeline)
-							this.applyAttachmentTimeline(timeline, skeleton, applyTime, blend, true);
-						else
-							timeline.apply(skeleton, animationLast, applyTime, applyEvents, mix, blend, MixDirection.mixIn);
-					}
-				} else {
-					let timelineMode = current.timelineMode;
-
-					let firstFrame = current.timelinesRotation.length != timelineCount << 1;
-					if (firstFrame) current.timelinesRotation.length = timelineCount << 1;
-
-					for (let ii = 0; ii < timelineCount; ii++) {
-						let timeline = timelines[ii];
-						let timelineBlend = timelineMode[ii] == SUBSEQUENT ? blend : MixBlend.setup;
-						if (timeline instanceof RotateTimeline) {
-							this.applyRotateTimeline(timeline, skeleton, applyTime, mix, timelineBlend, current.timelinesRotation, ii << 1, firstFrame);
-						} else if (timeline instanceof AttachmentTimeline) {
-							this.applyAttachmentTimeline(timeline, skeleton, applyTime, blend, true);
-						} else {
-							// This fixes the WebKit 602 specific issue described at http://esotericsoftware.com/forum/iOS-10-disappearing-graphics-10109
-							Utils.webkit602BugfixHelper(mix, blend);
-							timeline.apply(skeleton, animationLast, applyTime, applyEvents, mix, timelineBlend, MixDirection.mixIn);
-						}
-					}
-				}
-				this.queueEvents(current, animationTime);
-				events.length = 0;
-				current.nextAnimationLast = animationTime;
-				current.nextTrackLast = current.trackTime;
-			}
-
-			// Set slots attachments to the setup pose, if needed. This occurs if an animation that is mixing out sets attachments so
-			// subsequent timelines see any deform, but the subsequent timelines don't set an attachment (eg they are also mixing out or
-			// the time is before the first key).
-			var setupState = this.unkeyedState + SETUP;
-			var slots = skeleton.slots;
-			for (var i = 0, n = skeleton.slots.length; i < n; i++) {
-				var slot = slots[i];
-				if (slot.attachmentState == setupState) {
-					var attachmentName = slot.data.attachmentName;
-					slot.setAttachment(!attachmentName ? null : skeleton.getAttachment(slot.data.index, attachmentName));
-				}
-			}
-			this.unkeyedState += 2; // Increasing after each use avoids the need to reset attachmentState for every slot.
-
-			this.queue.drain();
-			return applied;
-		}
-
-		applyMixingFrom (to: TrackEntry, skeleton: Skeleton, blend: MixBlend) {
-			let from = to.mixingFrom;
-			if (from.mixingFrom) this.applyMixingFrom(from, skeleton, blend);
-
-			let mix = 0;
-			if (to.mixDuration == 0) { // Single frame mix to undo mixingFrom changes.
-				mix = 1;
-				if (blend == MixBlend.first) blend = MixBlend.setup;
-			} else {
-				mix = to.mixTime / to.mixDuration;
-				if (mix > 1) mix = 1;
-				if (blend != MixBlend.first) blend = from.mixBlend;
-			}
-
-			let attachments = mix < from.attachmentThreshold, drawOrder = mix < from.drawOrderThreshold;
-			let timelines = from.animation.timelines;
-			let timelineCount = timelines.length;
-			let alphaHold = from.alpha * to.interruptAlpha, alphaMix = alphaHold * (1 - mix);
-			let animationLast = from.animationLast, animationTime = from.getAnimationTime(), applyTime = animationTime;
-			let events = null;
-			if (from.reverse)
-				applyTime = from.animation.duration - applyTime;
-			else if (mix < from.eventThreshold)
-				events = this.events;
-
-			if (blend == MixBlend.add) {
-				for (let i = 0; i < timelineCount; i++)
-					timelines[i].apply(skeleton, animationLast, applyTime, events, alphaMix, blend, MixDirection.mixOut);
-			} else {
-				let timelineMode = from.timelineMode;
-				let timelineHoldMix = from.timelineHoldMix;
-
-				let firstFrame = from.timelinesRotation.length != timelineCount << 1;
-				if (firstFrame) from.timelinesRotation.length = timelineCount << 1;
-
-				from.totalAlpha = 0;
-				for (let i = 0; i < timelineCount; i++) {
-					let timeline = timelines[i];
-					let direction = MixDirection.mixOut;
-					let timelineBlend: MixBlend;
-					let alpha = 0;
-					switch (timelineMode[i]) {
-						case SUBSEQUENT:
-							if (!drawOrder && timeline instanceof DrawOrderTimeline) continue;
-							timelineBlend = blend;
-							alpha = alphaMix;
-							break;
-						case FIRST:
-							timelineBlend = MixBlend.setup;
-							alpha = alphaMix;
-							break;
-						case HOLD_SUBSEQUENT:
-							timelineBlend = blend;
-							alpha = alphaHold;
-							break;
-						case HOLD_FIRST:
-							timelineBlend = MixBlend.setup;
-							alpha = alphaHold;
-							break;
-						default:
-							timelineBlend = MixBlend.setup;
-							let holdMix = timelineHoldMix[i];
-							alpha = alphaHold * Math.max(0, 1 - holdMix.mixTime / holdMix.mixDuration);
-							break;
-					}
-					from.totalAlpha += alpha;
-
-					if (timeline instanceof RotateTimeline)
-						this.applyRotateTimeline(timeline, skeleton, applyTime, alpha, timelineBlend, from.timelinesRotation, i << 1, firstFrame);
-					else if (timeline instanceof AttachmentTimeline)
-						this.applyAttachmentTimeline(timeline, skeleton, applyTime, timelineBlend, attachments);
-					else {
-						// This fixes the WebKit 602 specific issue described at http://esotericsoftware.com/forum/iOS-10-disappearing-graphics-10109
-						Utils.webkit602BugfixHelper(alpha, blend);
-						if (drawOrder && timeline instanceof DrawOrderTimeline && timelineBlend == MixBlend.setup)
-							direction = MixDirection.mixIn;
-						timeline.apply(skeleton, animationLast, applyTime, events, alpha, timelineBlend, direction);
-					}
-				}
-			}
-
-			if (to.mixDuration > 0) this.queueEvents(from, animationTime);
-			this.events.length = 0;
-			from.nextAnimationLast = animationTime;
-			from.nextTrackLast = from.trackTime;
-
-			return mix;
-		}
-
-		applyAttachmentTimeline (timeline: AttachmentTimeline, skeleton: Skeleton, time: number, blend: MixBlend, attachments: boolean) {
-			var slot = skeleton.slots[timeline.slotIndex];
-			if (!slot.bone.active) return;
-
-			if (time < timeline.frames[0]) { // Time is before first frame.
-				if (blend == MixBlend.setup || blend == MixBlend.first)
-					this.setAttachment(skeleton, slot, slot.data.attachmentName, attachments);
-			} else
-				this.setAttachment(skeleton, slot, timeline.attachmentNames[Timeline.search1(timeline.frames, time)], attachments);
-
-			// If an attachment wasn't set (ie before the first frame or attachments is false), set the setup attachment later.
-			if (slot.attachmentState <= this.unkeyedState) slot.attachmentState = this.unkeyedState + SETUP;
-		}
-
-		setAttachment (skeleton: Skeleton, slot: Slot, attachmentName: string, attachments: boolean) {
-			slot.setAttachment(!attachmentName ? null : skeleton.getAttachment(slot.data.index, attachmentName));
-			if (attachments) slot.attachmentState = this.unkeyedState + CURRENT;
-		}
-
-		applyRotateTimeline (timeline: RotateTimeline, skeleton: Skeleton, time: number, alpha: number, blend: MixBlend,
-			timelinesRotation: Array<number>, i: number, firstFrame: boolean) {
-
-			if (firstFrame) timelinesRotation[i] = 0;
-
-			if (alpha == 1) {
-				timeline.apply(skeleton, 0, time, null, 1, blend, MixDirection.mixIn);
-				return;
-			}
-
-			let bone = skeleton.bones[timeline.boneIndex];
-			if (!bone.active) return;
-			let frames = timeline.frames;
-			let r1 = 0, r2 = 0;
-			if (time < frames[0]) {
-				switch (blend) {
-					case MixBlend.setup:
-						bone.rotation = bone.data.rotation;
-					default:
-						return;
-					case MixBlend.first:
-						r1 = bone.rotation;
-						r2 = bone.data.rotation;
-				}
-			} else {
-				r1 = blend == MixBlend.setup ? bone.data.rotation : bone.rotation;
-				r2 = bone.data.rotation + timeline.getCurveValue(time);
-			}
-
-			// Mix between rotations using the direction of the shortest route on the first frame while detecting crosses.
-			let total = 0, diff = r2 - r1;
-			diff -= (16384 - ((16384.499999999996 - diff / 360) | 0)) * 360;
-			if (diff == 0) {
-				total = timelinesRotation[i];
-			} else {
-				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;
-			bone.rotation = r1 + total * alpha;
-		}
-
-		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.
-			let complete = false;
-			if (entry.loop)
-				complete = duration == 0 || trackLastWrapped > entry.trackTime % duration;
-			else
-				complete = animationTime >= animationEnd && entry.animationLast < animationEnd;
-			if (complete) 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, event);
-			}
-		}
-
-		/** Removes all animations from all tracks, leaving skeletons in their current pose.
-		 *
-		 * It may be desired to use {@link AnimationState#setEmptyAnimation()} to mix the skeletons back to the setup pose,
-		 * rather than leaving them in their current pose. */
-		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();
-		}
-
-		/** Removes all animations from the track, leaving skeletons in their current pose.
-		 *
-		 * It may be desired to use {@link AnimationState#setEmptyAnimation()} to mix the skeletons back to the setup pose,
-		 * rather than leaving them in their current pose. */
-		clearTrack (trackIndex: number) {
-			if (trackIndex >= this.tracks.length) return;
-			let current = this.tracks[trackIndex];
-			if (!current) return;
-
-			this.queue.end(current);
-
-			this.clearNext(current);
-
-			let entry = current;
-			while (true) {
-				let from = entry.mixingFrom;
-				if (!from) break;
-				this.queue.end(from);
-				entry.mixingFrom = null;
-				entry.mixingTo = 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;
-			current.previous = null;
-
-			if (from) {
-				if (interrupt) this.queue.interrupt(from);
-				current.mixingFrom = from;
-				from.mixingTo = current;
-				current.mixTime = 0;
-
-				// Store the interrupted mix percentage.
-				if (from.mixingFrom && 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);
-		}
-
-		/** Sets an animation by name.
-	 	*
-	 	* See {@link #setAnimationWith()}. */
-		setAnimation (trackIndex: number, animationName: string, loop: boolean = false) {
-			let animation = this.data.skeletonData.findAnimation(animationName);
-			if (!animation) throw new Error("Animation not found: " + animationName);
-			return this.setAnimationWith(trackIndex, animation, loop);
-		}
-
-		/** Sets the current animation for a track, discarding any queued animations. If the formerly current track entry was never
-		 * applied to a skeleton, it is replaced (not mixed from).
-		 * @param loop If true, the animation will repeat. If false it will not, instead its last frame is applied if played beyond its
-		 *           duration. In either case {@link TrackEntry#trackEnd} determines when the track is cleared.
-		 * @returns A track entry to allow further customization of animation playback. References to the track entry must not be kept
-		 *         after the {@link AnimationStateListener#dispose()} event occurs. */
-		setAnimationWith (trackIndex: number, animation: Animation, loop: boolean = false) {
-			if (!animation) throw new Error("animation cannot be null.");
-			let interrupt = true;
-			let current = this.expandToIndex(trackIndex);
-			if (current) {
-				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.clearNext(current);
-					current = current.mixingFrom;
-					interrupt = false;
-				} else
-					this.clearNext(current);
-			}
-			let entry = this.trackEntry(trackIndex, animation, loop, current);
-			this.setCurrent(trackIndex, entry, interrupt);
-			this.queue.drain();
-			return entry;
-		}
-
-		/** Queues an animation by name.
-		 *
-		 * See {@link #addAnimationWith()}. */
-		addAnimation (trackIndex: number, animationName: string, loop: boolean = false, delay: number = 0) {
-			let animation = this.data.skeletonData.findAnimation(animationName);
-			if (!animation) throw new Error("Animation not found: " + animationName);
-			return this.addAnimationWith(trackIndex, animation, loop, delay);
-		}
-
-		/** Adds an animation to be played after the current or last queued animation for a track. If the track is empty, it is
-		 * equivalent to calling {@link #setAnimationWith()}.
-		 * @param delay If > 0, sets {@link TrackEntry#delay}. If <= 0, the delay set is the duration of the previous track entry
-		 *           minus any mix duration (from the {@link AnimationStateData}) plus the specified `delay` (ie the mix
-		 *           ends at (`delay` = 0) or before (`delay` < 0) the previous track entry duration). If the
-		 *           previous entry is looping, its next loop completion is used instead of its duration.
-		 * @returns A track entry to allow further customization of animation playback. References to the track entry must not be kept
-		 *         after the {@link AnimationStateListener#dispose()} event occurs. */
-		addAnimationWith (trackIndex: number, animation: Animation, loop: boolean = false, delay: number = 0) {
-			if (!animation) throw new Error("animation cannot be null.");
-
-			let last = this.expandToIndex(trackIndex);
-			if (last) {
-				while (last.next)
-					last = last.next;
-			}
-
-			let entry = this.trackEntry(trackIndex, animation, loop, last);
-
-			if (!last) {
-				this.setCurrent(trackIndex, entry, true);
-				this.queue.drain();
-			} else {
-				last.next = entry;
-				entry.previous = last;
-				if (delay <= 0) delay += last.getTrackComplete() - entry.mixDuration;
-			}
-
-			entry.delay = delay;
-			return entry;
-		}
-
-		/** Sets an empty animation for a track, discarding any queued animations, and sets the track entry's
-		 * {@link TrackEntry#mixduration}. An empty animation has no timelines and serves as a placeholder for mixing in or out.
-		 *
-		 * Mixing out is done by setting an empty animation with a mix duration using either {@link #setEmptyAnimation()},
-		 * {@link #setEmptyAnimations()}, or {@link #addEmptyAnimation()}. Mixing to an empty animation causes
-		 * the previous animation to be applied less and less over the mix duration. Properties keyed in the previous animation
-		 * transition to the value from lower tracks or to the setup pose value if no lower tracks key the property. A mix duration of
-		 * 0 still mixes out over one frame.
-		 *
-		 * Mixing in is done by first setting an empty animation, then adding an animation using
-		 * {@link #addAnimation()} and on the returned track entry, set the
-		 * {@link TrackEntry#setMixDuration()}. Mixing from an empty animation causes the new animation to be applied more and
-		 * more over the mix duration. Properties keyed in the new animation transition from the value from lower tracks or from the
-		 * setup pose value if no lower tracks key the property to the value keyed in the new animation. */
-		setEmptyAnimation (trackIndex: number, mixDuration: number = 0) {
-			let entry = this.setAnimationWith(trackIndex, AnimationState.emptyAnimation(), false);
-			entry.mixDuration = mixDuration;
-			entry.trackEnd = mixDuration;
-			return entry;
-		}
-
-		/** Adds an empty animation to be played after the current or last queued animation for a track, and sets the track entry's
-		 * {@link TrackEntry#mixDuration}. If the track is empty, it is equivalent to calling
-		 * {@link #setEmptyAnimation()}.
-		 *
-		 * See {@link #setEmptyAnimation()}.
-		 * @param delay If > 0, sets {@link TrackEntry#delay}. If <= 0, the delay set is the duration of the previous track entry
-		 *           minus any mix duration plus the specified `delay` (ie the mix ends at (`delay` = 0) or
-		 *           before (`delay` < 0) the previous track entry duration). If the previous entry is looping, its next
-		 *           loop completion is used instead of its duration.
-		 * @return A track entry to allow further customization of animation playback. References to the track entry must not be kept
-		 *         after the {@link AnimationStateListener#dispose()} event occurs. */
-		addEmptyAnimation (trackIndex: number, mixDuration: number = 0, delay: number = 0) {
-			let entry = this.addAnimationWith(trackIndex, AnimationState.emptyAnimation(), false, delay);
-			if (delay <= 0) entry.delay += entry.mixDuration - mixDuration;
-			entry.mixDuration = mixDuration;
-			entry.trackEnd = mixDuration;
-			return entry;
-		}
-
-		/** Sets an empty animation for every track, discarding any queued animations, and mixes to it over the specified mix
-	 	* duration. */
-		setEmptyAnimations (mixDuration: number = 0) {
-			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) 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 + 1, null);
-			this.tracks.length = index + 1;
-			return null;
-		}
-
-		/** @param last May be 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.holdPrevious = false;
-
-			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 ? 0 : this.data.getMix(last.animation, animation);
-			entry.mixBlend = MixBlend.replace;
-			return entry;
-		}
-
-		/** Removes the {@link TrackEntry#getNext() next entry} and all entries after it for the specified entry. */
-		clearNext (entry: TrackEntry) {
-			let next = entry.next;
-			while (next) {
-				this.queue.dispose(next);
-				next = next.next;
-			}
-			entry.next = null;
-		}
-
-		_animationsChanged () {
-			this.animationsChanged = false;
-
-			this.propertyIDs.clear();
-			let tracks = this.tracks;
-			for (let i = 0, n = tracks.length; i < n; i++) {
-				let entry = tracks[i];
-				if (!entry) continue;
-				while (entry.mixingFrom)
-					entry = entry.mixingFrom;
-				do {
-					if (!entry.mixingTo || entry.mixBlend != MixBlend.add) this.computeHold(entry);
-					entry = entry.mixingTo;
-				} while (entry);
-			}
-		}
-
-		computeHold (entry: TrackEntry) {
-			let to = entry.mixingTo;
-			let timelines = entry.animation.timelines;
-			let timelinesCount = entry.animation.timelines.length;
-			let timelineMode = entry.timelineMode;
-			timelineMode.length = timelinesCount;
-			let timelineHoldMix = entry.timelineHoldMix;
-			timelineHoldMix.length = 0;
-			let propertyIDs = this.propertyIDs;
-
-			if (to && to.holdPrevious) {
-				for (let i = 0; i < timelinesCount; i++)
-					timelineMode[i] = propertyIDs.addAll(timelines[i].getPropertyIds()) ? HOLD_FIRST : HOLD_SUBSEQUENT;
-				return;
-			}
-
-			outer:
-			for (let i = 0; i < timelinesCount; i++) {
-				let timeline = timelines[i];
-				let ids = timeline.getPropertyIds();
-				if (!propertyIDs.addAll(ids))
-					timelineMode[i] = SUBSEQUENT;
-				else if (!to || timeline instanceof AttachmentTimeline || timeline instanceof DrawOrderTimeline
-					|| timeline instanceof EventTimeline || !to.animation.hasTimeline(ids)) {
-					timelineMode[i] = FIRST;
-				} else {
-					for (let next = to.mixingTo; next; next = next.mixingTo) {
-						if (next.animation.hasTimeline(ids)) continue;
-						if (entry.mixDuration > 0) {
-							timelineMode[i] = HOLD_MIX;
-							timelineHoldMix[i] = next;
-							continue outer;
-						}
-						break;
-					}
-					timelineMode[i] = HOLD_FIRST;
-				}
-			}
-		}
-
-		/** Returns the track entry for the animation currently playing on the track, or null if no animation is currently playing. */
-		getCurrent (trackIndex: number) {
-			if (trackIndex >= this.tracks.length) return null;
-			return this.tracks[trackIndex];
-		}
-
-		/** Adds a listener to receive events for all track entries. */
-		addListener (listener: AnimationStateListener) {
-			if (!listener) throw new Error("listener cannot be null.");
-			this.listeners.push(listener);
-		}
-
-		/** Removes the listener added with {@link #addListener()}. */
-		removeListener (listener: AnimationStateListener) {
-			let index = this.listeners.indexOf(listener);
-			if (index >= 0) this.listeners.splice(index, 1);
-		}
-
-		/** Removes all listeners added with {@link #addListener()}. */
-		clearListeners () {
-			this.listeners.length = 0;
-		}
-
-		/** Discards all listener notifications that have not yet been delivered. This can be useful to call from an
-		 * {@link AnimationStateListener} when it is known that further notifications that may have been already queued for delivery
-		 * are not wanted because new animations are being set. */
-		clearListenerNotifications () {
-			this.queue.clear();
-		}
-	}
-
-	/** Stores settings and other state for the playback of an animation on an {@link AnimationState} track.
-	 *
-	 * References to a track entry must not be kept after the {@link AnimationStateListener#dispose()} event occurs. */
-	export class TrackEntry {
-		/** The animation to apply for this track entry. */
-		animation: Animation;
-
-		previous: TrackEntry;
-
-		/** The animation queued to start after this animation, or null. `next` makes up a linked list. */
-		next: TrackEntry;
-
-		/** The track entry for the previous animation when mixing from the previous animation to this animation, or null if no
-		 * mixing is currently occuring. When mixing from multiple animations, `mixingFrom` makes up a linked list. */
-		mixingFrom: TrackEntry;
-
-		/** The track entry for the next animation when mixing from this animation to the next animation, or null if no mixing is
-		 * currently occuring. When mixing to multiple animations, `mixingTo` makes up a linked list. */
-		mixingTo: TrackEntry;
-
-		/** The listener for events generated by this track entry, or null.
-		 *
-		 * A track entry returned from {@link AnimationState#setAnimation()} is already the current animation
-		 * for the track, so the track entry listener {@link AnimationStateListener#start()} will not be called. */
-		listener: AnimationStateListener;
-
-		/** The index of the track where this track entry is either current or queued.
-		 *
-		 * See {@link AnimationState#getCurrent()}. */
-		trackIndex: number;
-
-		/** If true, the animation will repeat. If false it will not, instead its last frame is applied if played beyond its
-		 * duration. */
-		loop: boolean;
-
-		/** If true, when mixing from the previous animation to this animation, the previous animation is applied as normal instead
-		 * of being mixed out.
-		 *
-		 * When mixing between animations that key the same property, if a lower track also keys that property then the value will
-		 * briefly dip toward the lower track value during the mix. This happens because the first animation mixes from 100% to 0%
-		 * while the second animation mixes from 0% to 100%. Setting `holdPrevious` to true applies the first animation
-		 * at 100% during the mix so the lower track value is overwritten. Such dipping does not occur on the lowest track which
-		 * keys the property, only when a higher track also keys the property.
-		 *
-		 * Snapping will occur if `holdPrevious` is true and this animation does not key all the same properties as the
-		 * previous animation. */
-		holdPrevious: boolean;
-
-		reverse: boolean;
-
-		/** When the mix percentage ({@link #mixTime} / {@link #mixDuration}) is less than the
-		 * `eventThreshold`, event timelines are applied while this animation is being mixed out. Defaults to 0, so event
-		 * timelines are not applied while this animation is being mixed out. */
-		eventThreshold: number;
-
-		/** When the mix percentage ({@link #mixtime} / {@link #mixDuration}) is less than the
-		 * `attachmentThreshold`, attachment timelines are applied while this animation is being mixed out. Defaults to
-		 * 0, so attachment timelines are not applied while this animation is being mixed out. */
-		attachmentThreshold: number;
-
-		/** When the mix percentage ({@link #mixTime} / {@link #mixDuration}) is less than the
-		 * `drawOrderThreshold`, draw order timelines are applied while this animation is being mixed out. Defaults to 0,
-		 * so draw order timelines are not applied while this animation is being mixed out. */
-		drawOrderThreshold: number;
-
-		/** Seconds when this animation starts, both initially and after looping. Defaults to 0.
-		 *
-		 * When changing the `animationStart` time, it often makes sense to set {@link #animationLast} to the same
-		 * value to prevent timeline keys before the start time from triggering. */
-		animationStart: number;
-
-		/** Seconds for the last frame of this animation. Non-looping animations won't play past this time. Looping animations will
-		 * loop back to {@link #animationStart} at this time. Defaults to the animation {@link Animation#duration}. */
-		animationEnd: number;
-
-
-		/** The time in seconds this animation was last applied. Some timelines use this for one-time triggers. Eg, when this
-		 * animation is applied, event timelines will fire all events between the `animationLast` time (exclusive) and
-		 * `animationTime` (inclusive). Defaults to -1 to ensure triggers on frame 0 happen the first time this animation
-		 * is applied. */
-		animationLast: number;
-
-		nextAnimationLast: number;
-
-		/** Seconds to postpone playing the animation. When this track entry is the current track entry, `delay`
-		 * postpones incrementing the {@link #trackTime}. When this track entry is queued, `delay` is the time from
-		 * the start of the previous animation to when this track entry will become the current track entry (ie when the previous
-		 * track entry {@link TrackEntry#trackTime} >= this track entry's `delay`).
-		 *
-		 * {@link #timeScale} affects the delay. */
-		delay: number;
-
-		/** Current time in seconds this track entry has been the current track entry. The track time determines
-		 * {@link #animationTime}. The track time can be set to start the animation at a time other than 0, without affecting
-		 * looping. */
-		trackTime: number;
-
-		trackLast: number; nextTrackLast: number;
-
-		/** The track time in seconds when this animation will be removed from the track. Defaults to the highest possible float
-		 * value, meaning the animation will be applied until a new animation is set or the track is cleared. If the track end time
-		 * is reached, no other animations are queued for playback, and mixing from any previous animations is complete, then the
-		 * properties keyed by the animation are set to the setup pose and the track is cleared.
-		 *
-		 * It may be desired to use {@link AnimationState#addEmptyAnimation()} rather than have the animation
-		 * abruptly cease being applied. */
-		trackEnd: number;
-
-		/** Multiplier for the delta time when this track entry is updated, causing time for this animation to pass slower or
-		 * faster. Defaults to 1.
-		 *
-		 * {@link #mixTime} is not affected by track entry time scale, so {@link #mixDuration} may need to be adjusted to
-		 * match the animation speed.
-		 *
-		 * When using {@link AnimationState#addAnimation()} with a `delay` <= 0, note the
-		 * {@link #delay} is set using the mix duration from the {@link AnimationStateData}, assuming time scale to be 1. If
-		 * the time scale is not 1, the delay may need to be adjusted.
-		 *
-		 * See AnimationState {@link AnimationState#timeScale} for affecting all animations. */
-		timeScale: number;
-
-		/** Values < 1 mix this animation with the skeleton's current pose (usually the pose resulting from lower tracks). Defaults
-		 * to 1, which overwrites the skeleton's current pose with this animation.
-		 *
-		 * Typically track 0 is used to completely pose the skeleton, then alpha is used on higher tracks. It doesn't make sense to
-		 * use alpha on track 0 if the skeleton pose is from the last frame render. */
-		alpha: number;
-
-		/** Seconds from 0 to the {@link #getMixDuration()} when mixing from the previous animation to this animation. May be
-		 * slightly more than `mixDuration` when the mix is complete. */
-		mixTime: number;
-
-		/** Seconds for mixing from the previous animation to this animation. Defaults to the value provided by AnimationStateData
-		 * {@link AnimationStateData#getMix()} based on the animation before this animation (if any).
-		 *
-		 * A mix duration of 0 still mixes out over one frame to provide the track entry being mixed out a chance to revert the
-		 * properties it was animating.
-		 *
-		 * The `mixDuration` can be set manually rather than use the value from
-		 * {@link AnimationStateData#getMix()}. In that case, the `mixDuration` can be set for a new
-		 * track entry only before {@link AnimationState#update(float)} is first called.
-		 *
-		 * When using {@link AnimationState#addAnimation()} with a `delay` <= 0, note the
-		 * {@link #delay} is set using the mix duration from the {@link AnimationStateData}, not a mix duration set
-		 * afterward. */
-		mixDuration: number; interruptAlpha: number; totalAlpha: number;
-
-		/** Controls how properties keyed in the animation are mixed with lower tracks. Defaults to {@link MixBlend#replace}, which
-		 * replaces the values from the lower tracks with the animation values. {@link MixBlend#add} adds the animation values to
-		 * the values from the lower tracks.
-		 *
-		 * The `mixBlend` can be set for a new track entry only before {@link AnimationState#apply()} is first
-		 * called. */
-		mixBlend = MixBlend.replace;
-		timelineMode = new Array<number>();
-		timelineHoldMix = new Array<TrackEntry>();
-		timelinesRotation = new Array<number>();
-
-		reset () {
-			this.next = null;
-			this.previous = null;
-			this.mixingFrom = null;
-			this.mixingTo = null;
-			this.animation = null;
-			this.listener = null;
-			this.timelineMode.length = 0;
-			this.timelineHoldMix.length = 0;
-			this.timelinesRotation.length = 0;
-		}
-
-		/** Uses {@link #trackTime} to compute the `animationTime`, which is between {@link #animationStart}
-		 * and {@link #animationEnd}. When the `trackTime` is 0, the `animationTime` is equal to the
-		 * `animationStart` time. */
-		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;
-		}
-
-		/** Returns true if at least one loop has been completed.
-		 *
-		 * See {@link AnimationStateListener#complete()}. */
-		isComplete () {
-			return this.trackTime >= this.animationEnd - this.animationStart;
-		}
-
-		/** Resets the rotation directions for mixing this entry's rotate timelines. This can be useful to avoid bones rotating the
-		 * long way around when using {@link #alpha} and starting animations on other tracks.
-		 *
-		 * Mixing with {@link MixBlend#replace} involves finding a rotation between two others, which has two possible solutions:
-		 * the short way or the long way around. The two rotations likely change over time, so which direction is the short or long
-		 * way also changes. If the short way was always chosen, bones would flip to the other side when that direction became the
-		 * long way. TrackEntry chooses the short way the first time it is applied and remembers that direction. */
-		resetRotationDirections () {
-			this.timelinesRotation.length = 0;
-		}
-
-		getTrackComplete () {
-			let duration = this.animationEnd - this.animationStart;
-			if (duration != 0) {
-				if (this.loop) return duration * (1 + ((this.trackTime / duration) | 0)); // Completion of next loop.
-				if (this.trackTime < duration) return duration; // Before duration.
-			}
-			return this.trackTime; // Next update.
-		}
-	}
-
-	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);
-		}
-
-		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 && entry.listener.start) entry.listener.start(entry);
-						for (let ii = 0; ii < listeners.length; ii++)
-							if (listeners[ii].start) listeners[ii].start(entry);
-						break;
-					case EventType.interrupt:
-						if (entry.listener && 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 && entry.listener.end) entry.listener.end(entry);
-						for (let ii = 0; ii < listeners.length; ii++)
-							if (listeners[ii].end) listeners[ii].end(entry);
-					// Fall through.
-					case EventType.dispose:
-						if (entry.listener && 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 && entry.listener.complete) entry.listener.complete(entry);
-						for (let ii = 0; ii < listeners.length; ii++)
-							if (listeners[ii].complete) listeners[ii].complete(entry);
-						break;
-					case EventType.event:
-						let event = objects[i++ + 2] as Event;
-						if (entry.listener && 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);
-						break;
-				}
-			}
-			this.clear();
-
-			this.drainDisabled = false;
-		}
-
-		clear () {
-			this.objects.length = 0;
-		}
-	}
-
-	export enum EventType {
-		start, interrupt, end, dispose, complete, event
-	}
-
-	/** The interface to implement for receiving TrackEntry events. It is always safe to call AnimationState methods when receiving
-	 * events.
-	 *
-	 * See TrackEntry {@link TrackEntry#listener} and AnimationState
-	 * {@link AnimationState#addListener()}. */
-	export interface AnimationStateListener {
-		/** 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 AnimationStateAdapter implements AnimationStateListener {
-		start (entry: TrackEntry) {
-		}
-
-		interrupt (entry: TrackEntry) {
-		}
-
-		end (entry: TrackEntry) {
-		}
-
-		dispose (entry: TrackEntry) {
-		}
-
-		complete (entry: TrackEntry) {
-		}
-
-		event (entry: TrackEntry, event: Event) {
-		}
-	}
-
-	/** 1. A previously applied timeline has set this property.
-	 *
-	 * Result: Mix from the current pose to the timeline pose. */
-	const SUBSEQUENT = 0;
-	/** 1. This is the first timeline to set this property.
-	 * 2. The next track entry applied after this one does not have a timeline to set this property.
-	 *
-	 * Result: Mix from the setup pose to the timeline pose. */
-	const FIRST = 1;
-	/** 1) A previously applied timeline has set this property.<br>
-	 * 2) The next track entry to be applied does have a timeline to set this property.<br>
-	 * 3) The next track entry after that one does not have a timeline to set this property.<br>
-	 * Result: Mix from the current pose to the timeline pose, but do not mix out. This avoids "dipping" when crossfading
-	 * animations that key the same property. A subsequent timeline will set this property using a mix. */
-	const HOLD_SUBSEQUENT = 2;
-	/** 1) This is the first timeline to set this property.<br>
-	 * 2) The next track entry to be applied does have a timeline to set this property.<br>
-	 * 3) The next track entry after that one does not have a timeline to set this property.<br>
-	 * Result: Mix from the setup pose to the timeline pose, but do not mix out. This avoids "dipping" when crossfading animations
-	 * that key the same property. A subsequent timeline will set this property using a mix. */
-	const HOLD_FIRST = 3;
-	/** 1. This is the first timeline to set this property.
-	 * 2. The next track entry to be applied does have a timeline to set this property.
-	 * 3. The next track entry after that one does have a timeline to set this property.
-	 * 4. timelineHoldMix stores the first subsequent track entry that does not have a timeline to set this property.
-	 *
-	 * Result: The same as HOLD except the mix percentage from the timelineHoldMix track entry is used. This handles when more than
-	 * 2 track entries in a row have a timeline that sets the same property.
-	 *
-	 * Eg, A -> B -> C -> D where A, B, and C have a timeline setting same property, but D does not. When A is applied, to avoid
-	 * "dipping" A is not mixed out, however D (the first entry that doesn't set the property) mixing in is used to mix out A
-	 * (which affects B and C). Without using D to mix out, A would be applied fully until mixing completes, then snap into
-	 * place. */
-	const HOLD_MIX = 4;
-
-	const SETUP = 1;
-	const CURRENT = 2;
-
-	let _emptyAnimation: Animation = null;
-}

+ 0 - 76
spine-ts/core/src/AnimationStateData.ts

@@ -1,76 +0,0 @@
-/******************************************************************************
- * Spine Runtimes License Agreement
- * Last updated January 1, 2020. Replaces all prior versions.
- *
- * Copyright (c) 2013-2020, Esoteric Software LLC
- *
- * Integration of the Spine Runtimes into software or otherwise creating
- * derivative works of the Spine Runtimes is permitted under the terms and
- * conditions of Section 2 of the Spine Editor License Agreement:
- * http://esotericsoftware.com/spine-editor-license
- *
- * Otherwise, it is permitted to integrate the Spine Runtimes into software
- * or otherwise create derivative works of the Spine Runtimes (collectively,
- * "Products"), provided that each user of the Products must obtain their own
- * Spine Editor license and redistribution of the Products in any form must
- * include this license and copyright notice.
- *
- * THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "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 LLC 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
- * THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *****************************************************************************/
-
-module spine {
-
-	/** Stores mix (crossfade) durations to be applied when {@link AnimationState} animations are changed. */
-	export class AnimationStateData {
-		/** The SkeletonData to look up animations when they are specified by name. */
-		skeletonData: SkeletonData;
-
-		animationToMixTime: Map<number> = {};
-
-		/** The mix duration to use when no mix duration has been defined between two animations. */
-		defaultMix = 0;
-
-		constructor (skeletonData: SkeletonData) {
-			if (!skeletonData) throw new Error("skeletonData cannot be null.");
-			this.skeletonData = skeletonData;
-		}
-
-		/** Sets a mix duration by animation name.
-		 *
-		 * See {@link #setMixWith()}. */
-		setMix (fromName: string, toName: string, duration: number) {
-			let from = this.skeletonData.findAnimation(fromName);
-			if (!from) throw new Error("Animation not found: " + fromName);
-			let to = this.skeletonData.findAnimation(toName);
-			if (!to) throw new Error("Animation not found: " + toName);
-			this.setMixWith(from, to, duration);
-		}
-
-		/** Sets the mix duration when changing from the specified animation to the other.
-		 *
-		 * See {@link TrackEntry#mixDuration}. */
-		setMixWith (from: Animation, to: Animation, duration: number) {
-			if (!from) throw new Error("from cannot be null.");
-			if (!to) throw new Error("to cannot be null.");
-			let key = from.name + "." + to.name;
-			this.animationToMixTime[key] = duration;
-		}
-
-		/** Returns the mix duration to use when changing from the specified animation to the other, or the {@link #defaultMix} if
-	 	* no mix duration has been set. */
-		getMix (from: Animation, to: Animation) {
-			let key = from.name + "." + to.name;
-			let value = this.animationToMixTime[key];
-			return value === undefined ? this.defaultMix : value;
-		}
-	}
-}

+ 0 - 284
spine-ts/core/src/AssetManager.ts

@@ -1,284 +0,0 @@
-/******************************************************************************
- * Spine Runtimes License Agreement
- * Last updated January 1, 2020. Replaces all prior versions.
- *
- * Copyright (c) 2013-2020, Esoteric Software LLC
- *
- * Integration of the Spine Runtimes into software or otherwise creating
- * derivative works of the Spine Runtimes is permitted under the terms and
- * conditions of Section 2 of the Spine Editor License Agreement:
- * http://esotericsoftware.com/spine-editor-license
- *
- * Otherwise, it is permitted to integrate the Spine Runtimes into software
- * or otherwise create derivative works of the Spine Runtimes (collectively,
- * "Products"), provided that each user of the Products must obtain their own
- * Spine Editor license and redistribution of the Products in any form must
- * include this license and copyright notice.
- *
- * THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "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 LLC 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
- * THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *****************************************************************************/
-
-module spine {
-	export class AssetManager implements Disposable {
-		private pathPrefix: string;
-		private textureLoader: (image: HTMLImageElement | ImageBitmap) => Texture;
-		private downloader: Downloader;
-		private assets: Map<any> = {};
-		private errors: Map<string> = {};
-		private toLoad = 0;
-		private loaded = 0;
-
-		constructor (textureLoader: (image: HTMLImageElement | ImageBitmap) => Texture, pathPrefix: string = "", downloader: Downloader = null) {
-			this.textureLoader = textureLoader;
-			this.pathPrefix = pathPrefix;
-			this.downloader = downloader || new Downloader();
-		}
-
-		private start (path: string): string {
-			this.toLoad++;
-			return this.pathPrefix + path;
-		}
-
-		private success (callback: (path: string, data: any) => void, path: string, asset: any) {
-			this.toLoad--;
-			this.loaded++;
-			this.assets[path] = asset;
-			if (callback) callback(path, asset);
-		}
-
-		private error (callback: (path: string, message: string) => void, path: string, message: string) {
-			this.toLoad--;
-			this.loaded++;
-			this.errors[path] = message;
-			if (callback) callback(path, message);
-		}
-
-		setRawDataURI (path: string, data: string) {
-			this.downloader.rawDataUris[this.pathPrefix + path] = data;
-		}
-
-		loadBinary (path: string,
-			success: (path: string, binary: Uint8Array) => void = null,
-			error: (path: string, message: string) => void = null) {
-			path = this.start(path);
-
-			this.downloader.downloadBinary(path, (data: Uint8Array): void => {
-				this.success(success, path, data);
-			}, (status: number, responseText: string): void => {
-				this.error(error, path, `Couldn't load binary ${path}: status ${status}, ${responseText}`);
-			});
-		}
-
-		loadText (path: string,
-			success: (path: string, text: string) => void = null,
-			error: (path: string, message: string) => void = null) {
-			path = this.start(path);
-
-			this.downloader.downloadText(path, (data: string): void => {
-				this.success(success, path, data);
-			}, (status: number, responseText: string): void => {
-				this.error(error, path, `Couldn't load text ${path}: status ${status}, ${responseText}`);
-			});
-		}
-
-		loadJson (path: string,
-			success: (path: string, object: object) => void = null,
-			error: (path: string, message: string) => void = null) {
-			path = this.start(path);
-
-			this.downloader.downloadJson(path, (data: object): void => {
-				this.success(success, path, data);
-			}, (status: number, responseText: string): void => {
-				this.error(error, path, `Couldn't load JSON ${path}: status ${status}, ${responseText}`);
-			});
-		}
-
-		loadTexture (path: string,
-			success: (path: string, texture: Texture) => void = null,
-			error: (path: string, message: string) => void = null) {
-			path = this.start(path);
-
-			let isBrowser = !!(typeof window !== 'undefined' && typeof navigator !== 'undefined' && window.document);
-			let isWebWorker = !isBrowser && typeof importScripts !== 'undefined';
-			if (isWebWorker) {
-				fetch(path, { mode: <RequestMode>"cors" }).then((response) => {
-					if (response.ok) return response.blob();
-					this.error(error, path, `Couldn't load image: ${path}`);
-					return null;
-				}).then((blob) => {
-					return blob ? createImageBitmap(blob, { premultiplyAlpha: "none", colorSpaceConversion: "none" }) : null;
-				}).then((bitmap) => {
-					if (bitmap) this.success(success, path, this.textureLoader(bitmap));
-				});
-			} else {
-				let image = new Image();
-				image.crossOrigin = "anonymous";
-				image.onload = () => {
-					this.success(success, path, this.textureLoader(image));
-				};
-				image.onerror = () => {
-					this.error(error, path, `Couldn't load image: ${path}`);
-				};
-				if (this.downloader.rawDataUris[path]) path = this.downloader.rawDataUris[path];
-				image.src = path;
-			}
-		}
-
-		loadTextureAtlas (path: string,
-			success: (path: string, atlas: TextureAtlas) => void = null,
-			error: (path: string, message: string) => void = null
-		) {
-			let index = path.lastIndexOf("/");
-			let parent = index >= 0 ? path.substring(0, index + 1) : "";
-			path = this.start(path);
-
-			this.downloader.downloadText(path, (atlasText: string): void => {
-				try {
-					let atlas = new TextureAtlas(atlasText);
-					let toLoad = atlas.pages.length, abort = false;
-					for (let page of atlas.pages) {
-						this.loadTexture(parent + page.name,
-							(imagePath: string, texture: Texture) => {
-								if (!abort) {
-									page.setTexture(texture);
-									if (--toLoad == 0) this.success(success, path, atlas);
-								}
-							},
-							(imagePath: string, message: string) => {
-								if (!abort) this.error(error, path, `Couldn't load texture atlas ${path} page image: ${imagePath}`);
-								abort = true;
-							}
-						);
-					}
-				} catch (e) {
-					this.error(error, path, `Couldn't parse texture atlas ${path}: ${e.message}`);
-				}
-			}, (status: number, responseText: string): void => {
-				this.error(error, path, `Couldn't load texture atlas ${path}: status ${status}, ${responseText}`);
-			});
-		}
-
-		get (path: string) {
-			return this.assets[this.pathPrefix + path];
-		}
-
-		require (path: string) {
-			path = this.pathPrefix + path;
-			let asset = this.assets[path];
-			if (asset) return asset;
-			let error = this.errors[path];
-			throw Error("Asset not found: " + path + (error ? "\n" + error : ""));
-		}
-
-		remove (path: string) {
-			path = this.pathPrefix + path;
-			let asset = this.assets[path];
-			if ((<any>asset).dispose) (<any>asset).dispose();
-			delete this.assets[path];
-			return asset;
-		}
-
-		removeAll () {
-			for (let key in this.assets) {
-				let asset = this.assets[key];
-				if ((<any>asset).dispose) (<any>asset).dispose();
-			}
-			this.assets = {};
-		}
-
-		isLoadingComplete (): boolean {
-			return this.toLoad == 0;
-		}
-
-		getToLoad (): number {
-			return this.toLoad;
-		}
-
-		getLoaded (): number {
-			return this.loaded;
-		}
-
-		dispose () {
-			this.removeAll();
-		}
-
-		hasErrors () {
-			return Object.keys(this.errors).length > 0;
-		}
-
-		getErrors () {
-			return this.errors;
-		}
-	}
-
-	export class Downloader {
-		private callbacks: Map<Array<Function>> = {};
-		rawDataUris: Map<string> = {};
-
-		downloadText (url: string, success: (data: string) => void, error: (status: number, responseText: string) => void) {
-			if (this.rawDataUris[url]) url = this.rawDataUris[url];
-			if (this.start(url, success, error)) return;
-			let request = new XMLHttpRequest();
-			request.overrideMimeType("text/html");
-			request.open("GET", url, true);
-			let done = () => {
-				this.finish(url, request.status, request.responseText);
-			};
-			request.onload = done;
-			request.onerror = done;
-			request.send();
-		}
-
-		downloadJson (url: string, success: (data: object) => void, error: (status: number, responseText: string) => void) {
-			this.downloadText(url, (data: string): void => {
-				success(JSON.parse(data));
-			}, error);
-		}
-
-		downloadBinary (url: string, success: (data: Uint8Array) => void, error: (status: number, responseText: string) => void) {
-			if (this.rawDataUris[url]) url = this.rawDataUris[url];
-			if (this.start(url, success, error)) return;
-			let request = new XMLHttpRequest();
-			request.open("GET", url, true);
-			request.responseType = "arraybuffer";
-			let onerror = () => {
-				this.finish(url, request.status, request.responseText);
-			};
-			request.onload = () => {
-				if (request.status == 200)
-					this.finish(url, 200, new Uint8Array(request.response as ArrayBuffer));
-				else
-					onerror();
-			};
-			request.onerror = onerror;
-			request.send();
-		}
-
-		private start (url: string, success: any, error: any) {
-			let callbacks = this.callbacks[url];
-			try {
-				if (callbacks) return true;
-				this.callbacks[url] = callbacks = [];
-			} finally {
-				callbacks.push(success, error);
-			}
-		}
-
-		private finish (url: string, status: number, data: any) {
-			let callbacks = this.callbacks[url];
-			delete this.callbacks[url];
-			let args = status == 200 ? [data] : [status, data];
-			for (let i = args.length - 1, n = callbacks.length; i < n; i += 2)
-				callbacks[i].apply(null, args);
-		}
-	}
-}

+ 0 - 76
spine-ts/core/src/AtlasAttachmentLoader.ts

@@ -1,76 +0,0 @@
-/******************************************************************************
- * Spine Runtimes License Agreement
- * Last updated January 1, 2020. Replaces all prior versions.
- *
- * Copyright (c) 2013-2020, Esoteric Software LLC
- *
- * Integration of the Spine Runtimes into software or otherwise creating
- * derivative works of the Spine Runtimes is permitted under the terms and
- * conditions of Section 2 of the Spine Editor License Agreement:
- * http://esotericsoftware.com/spine-editor-license
- *
- * Otherwise, it is permitted to integrate the Spine Runtimes into software
- * or otherwise create derivative works of the Spine Runtimes (collectively,
- * "Products"), provided that each user of the Products must obtain their own
- * Spine Editor license and redistribution of the Products in any form must
- * include this license and copyright notice.
- *
- * THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "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 LLC 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
- * THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *****************************************************************************/
-
-module spine {
-	/** An {@link AttachmentLoader} that configures attachments using texture regions from an {@link TextureAtlas}.
-	 *
-	 * See [Loading skeleton data](http://esotericsoftware.com/spine-loading-skeleton-data#JSON-and-binary-data) in the
-	 * Spine Runtimes Guide. */
-	export class AtlasAttachmentLoader implements AttachmentLoader {
-		atlas: TextureAtlas;
-
-		constructor (atlas: TextureAtlas) {
-			this.atlas = atlas;
-		}
-
-		newRegionAttachment (skin: Skin, name: string, path: string): RegionAttachment {
-			let region = this.atlas.findRegion(path);
-			if (!region) throw new Error("Region not found in atlas: " + path + " (region attachment: " + name + ")");
-			region.renderObject = region;
-			let attachment = new RegionAttachment(name);
-			attachment.setRegion(region);
-			return attachment;
-		}
-
-		newMeshAttachment (skin: Skin, name: string, path: string): MeshAttachment {
-			let region = this.atlas.findRegion(path);
-			if (!region) throw new Error("Region not found in atlas: " + path + " (mesh attachment: " + name + ")");
-			region.renderObject = region;
-			let attachment = new MeshAttachment(name);
-			attachment.region = region;
-			return attachment;
-		}
-
-		newBoundingBoxAttachment (skin: Skin, name: string): BoundingBoxAttachment {
-			return new BoundingBoxAttachment(name);
-		}
-
-		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);
-		}
-	}
-}

+ 0 - 378
spine-ts/core/src/Bone.ts

@@ -1,378 +0,0 @@
-/******************************************************************************
- * Spine Runtimes License Agreement
- * Last updated January 1, 2020. Replaces all prior versions.
- *
- * Copyright (c) 2013-2020, Esoteric Software LLC
- *
- * Integration of the Spine Runtimes into software or otherwise creating
- * derivative works of the Spine Runtimes is permitted under the terms and
- * conditions of Section 2 of the Spine Editor License Agreement:
- * http://esotericsoftware.com/spine-editor-license
- *
- * Otherwise, it is permitted to integrate the Spine Runtimes into software
- * or otherwise create derivative works of the Spine Runtimes (collectively,
- * "Products"), provided that each user of the Products must obtain their own
- * Spine Editor license and redistribution of the Products in any form must
- * include this license and copyright notice.
- *
- * THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "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 LLC 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
- * THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *****************************************************************************/
-
-module spine {
-
-	/** Stores a bone's current pose.
-	 *
-	 * A bone has a local transform which is used to compute its world transform. A bone also has an applied transform, which is a
-	 * local transform that can be applied to compute the world transform. The local transform and applied transform may differ if a
-	 * constraint or application code modifies the world transform after it was computed from the local transform. */
-	export class Bone implements Updatable {
-		/** The bone's setup pose data. */
-		data: BoneData;
-
-		/** The skeleton this bone belongs to. */
-		skeleton: Skeleton;
-
-		/** The parent bone, or null if this is the root bone. */
-		parent: Bone;
-
-		/** The immediate children of this bone. */
-		children = new Array<Bone>();
-
-		/** The local x translation. */
-		x = 0;
-
-		/** The local y translation. */
-		y = 0;
-
-		/** The local rotation in degrees, counter clockwise. */
-		rotation = 0;
-
-		/** The local scaleX. */
-		scaleX = 0;
-
-		/** The local scaleY. */
-		scaleY = 0;
-
-		/** The local shearX. */
-		shearX = 0;
-
-		/** The local shearY. */
-		shearY = 0;
-
-		/** The applied local x translation. */
-		ax = 0;
-
-		/** The applied local y translation. */
-		ay = 0;
-
-		/** The applied local rotation in degrees, counter clockwise. */
-		arotation = 0;
-
-		/** The applied local scaleX. */
-		ascaleX = 0;
-
-		/** The applied local scaleY. */
-		ascaleY = 0;
-
-		/** The applied local shearX. */
-		ashearX = 0;
-
-		/** The applied local shearY. */
-		ashearY = 0;
-
-		/** Part of the world transform matrix for the X axis. If changed, {@link #updateAppliedTransform()} should be called. */
-		a = 0;
-
-		/** Part of the world transform matrix for the Y axis. If changed, {@link #updateAppliedTransform()} should be called. */
-		b = 0;
-
-		/** Part of the world transform matrix for the X axis. If changed, {@link #updateAppliedTransform()} should be called. */
-		c = 0;
-
-		/** Part of the world transform matrix for the Y axis. If changed, {@link #updateAppliedTransform()} should be called. */
-		d = 0;
-
-		/** The world X position. If changed, {@link #updateAppliedTransform()} should be called. */
-		worldY = 0;
-
-		/** The world Y position. If changed, {@link #updateAppliedTransform()} should be called. */
-		worldX = 0;
-
-		sorted = false;
-		active = false;
-
-		/** @param parent May be null. */
-		constructor (data: BoneData, skeleton: Skeleton, parent: Bone) {
-			if (!data) throw new Error("data cannot be null.");
-			if (!skeleton) throw new Error("skeleton cannot be null.");
-			this.data = data;
-			this.skeleton = skeleton;
-			this.parent = parent;
-			this.setToSetupPose();
-		}
-
-		/** Returns false when the bone has not been computed because {@link BoneData#skinRequired} is true and the
-	 	* {@link Skeleton#skin active skin} does not {@link Skin#bones contain} this bone. */
-		isActive () {
-			return this.active;
-		}
-
-		/** Computes the world transform using the parent bone and this bone's local applied transform. */
-		update () {
-			this.updateWorldTransformWith(this.ax, this.ay, this.arotation, this.ascaleX, this.ascaleY, this.ashearX, this.ashearY);
-		}
-
-		/** Computes the world transform using the parent bone and this bone's local transform.
-		 *
-		 * See {@link #updateWorldTransformWith()}. */
-		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. The applied transform is set to the
-		 * specified local transform. Child bones are not updated.
-		 *
-		 * See [World transforms](http://esotericsoftware.com/spine-runtime-skeletons#World-transforms) in the Spine
-		 * Runtimes Guide. */
-		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;
-
-			let parent = this.parent;
-			if (!parent) { // Root bone.
-				let skeleton = this.skeleton;
-				let rotationY = rotation + 90 + shearY;
-				let sx = skeleton.scaleX;
-				let sy = skeleton.scaleY;
-				this.a = MathUtils.cosDeg(rotation + shearX) * scaleX * sx;
-				this.b = MathUtils.cosDeg(rotationY) * scaleY * sx;
-				this.c = MathUtils.sinDeg(rotation + shearX) * scaleX * sy;
-				this.d = MathUtils.sinDeg(rotationY) * scaleY * sy;
-				this.worldX = x * sx + skeleton.x;
-				this.worldY = y * sy + skeleton.y;
-				return;
-			}
-
-			let pa = parent.a, pb = parent.b, pc = parent.c, pd = parent.d;
-			this.worldX = pa * x + pb * y + parent.worldX;
-			this.worldY = pc * x + pd * y + parent.worldY;
-
-			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;
-					this.a = pa * la + pb * lc;
-					this.b = pa * lb + pb * ld;
-					this.c = pc * la + pd * lc;
-					this.d = pc * lb + pd * ld;
-					return;
-				}
-				case TransformMode.OnlyTranslation: {
-					let rotationY = rotation + 90 + shearY;
-					this.a = MathUtils.cosDeg(rotation + shearX) * scaleX;
-					this.b = MathUtils.cosDeg(rotationY) * scaleY;
-					this.c = MathUtils.sinDeg(rotation + shearX) * scaleX;
-					this.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;
-						pa /= this.skeleton.scaleX;
-						pc /= this.skeleton.scaleY;
-						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;
-					this.a = pa * la - pb * lc;
-					this.b = pa * lb - pb * ld;
-					this.c = pc * la + pd * lc;
-					this.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) / this.skeleton.scaleX;
-					let zc = (pc * cos + pd * sin) / this.skeleton.scaleY;
-					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);
-					if (this.data.transformMode == TransformMode.NoScale
-						&& (pa * pd - pb * pc < 0) != (this.skeleton.scaleX < 0 != this.skeleton.scaleY < 0)) s = -s;
-					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;
-					this.a = za * la + zb * lc;
-					this.b = za * lb + zb * ld;
-					this.c = zc * la + zd * lc;
-					this.d = zc * lb + zd * ld;
-					break;
-				}
-			}
-			this.a *= this.skeleton.scaleX;
-			this.b *= this.skeleton.scaleX;
-			this.c *= this.skeleton.scaleY;
-			this.d *= this.skeleton.scaleY;
-		}
-
-		/** Sets this bone's local transform to the setup pose. */
-		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;
-		}
-
-		/** The world rotation for the X axis, calculated using {@link #a} and {@link #c}. */
-		getWorldRotationX () {
-			return Math.atan2(this.c, this.a) * MathUtils.radDeg;
-		}
-
-		/** The world rotation for the Y axis, calculated using {@link #b} and {@link #d}. */
-		getWorldRotationY () {
-			return Math.atan2(this.d, this.b) * MathUtils.radDeg;
-		}
-
-		/** The magnitude (always positive) of the world scale X, calculated using {@link #a} and {@link #c}. */
-		getWorldScaleX () {
-			return Math.sqrt(this.a * this.a + this.c * this.c);
-		}
-
-		/** The magnitude (always positive) of the world scale Y, calculated using {@link #b} and {@link #d}. */
-		getWorldScaleY () {
-			return Math.sqrt(this.b * this.b + this.d * this.d);
-		}
-
-		/** Computes the applied transform values from the world transform.
-		 *
-		 * If the world transform is modified (by a constraint, {@link #rotateWorld(float)}, etc) then this method should be called so
-		 * the applied transform matches the world transform. The applied transform may be needed by other code (eg to apply other
-		 * constraints).
-		 *
-		 * Some information is ambiguous in the world transform, such as -1,-1 scale versus 180 rotation. The applied transform after
-		 * calling this method is equivalent to the local transform used to compute the world transform, but may not be identical. */
-		updateAppliedTransform () {
-			let parent = this.parent;
-			if (!parent) {
-				this.ax = this.worldX;
-				this.ay = this.worldY;
-				this.arotation = Math.atan2(this.c, this.a) * MathUtils.radDeg;
-				this.ascaleX = Math.sqrt(this.a * this.a + this.c * this.c);
-				this.ascaleY = Math.sqrt(this.b * this.b + this.d * this.d);
-				this.ashearX = 0;
-				this.ashearY = Math.atan2(this.a * this.b + this.c * this.d, this.a * this.d - this.b * this.c) * MathUtils.radDeg;
-				return;
-			}
-			let pa = parent.a, pb = parent.b, pc = parent.c, pd = parent.d;
-			let pid = 1 / (pa * pd - pb * pc);
-			let dx = this.worldX - parent.worldX, dy = this.worldY - parent.worldY;
-			this.ax = (dx * pd * pid - dy * pb * pid);
-			this.ay = (dy * pa * pid - dx * pc * pid);
-			let ia = pid * pd;
-			let id = pid * pa;
-			let ib = pid * pb;
-			let ic = pid * pc;
-			let ra = ia * this.a - ib * this.c;
-			let rb = ia * this.b - ib * this.d;
-			let rc = id * this.c - ic * this.a;
-			let rd = id * this.d - ic * this.b;
-			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;
-			}
-		}
-
-		/** Transforms a point from world coordinates to the bone's local coordinates. */
-		worldToLocal (world: Vector2) {
-			let invDet = 1 / (this.a * this.d - this.b * this.c);
-			let x = world.x - this.worldX, y = world.y - this.worldY;
-			world.x = x * this.d * invDet - y * this.b * invDet;
-			world.y = y * this.a * invDet - x * this.c * invDet;
-			return world;
-		}
-
-		/** Transforms a point from the bone's local coordinates to world coordinates. */
-		localToWorld (local: Vector2) {
-			let x = local.x, y = local.y;
-			local.x = x * this.a + y * this.b + this.worldX;
-			local.y = x * this.c + y * this.d + this.worldY;
-			return local;
-		}
-
-		/** Transforms a world rotation to a local rotation. */
-		worldToLocalRotation (worldRotation: number) {
-			let sin = MathUtils.sinDeg(worldRotation), cos = MathUtils.cosDeg(worldRotation);
-			return Math.atan2(this.a * sin - this.c * cos, this.d * cos - this.b * sin) * MathUtils.radDeg + this.rotation - this.shearX;
-		}
-
-		/** Transforms a local rotation to a world rotation. */
-		localToWorldRotation (localRotation: number) {
-			localRotation -= this.rotation - this.shearX;
-			let sin = MathUtils.sinDeg(localRotation), cos = MathUtils.cosDeg(localRotation);
-			return Math.atan2(cos * this.c + sin * this.d, cos * this.a + sin * this.b) * MathUtils.radDeg;
-		}
-
-		/** Rotates the world transform the specified amount.
-		 * <p>
-		 * After changes are made to the world transform, {@link #updateAppliedTransform()} should be called and {@link #update()} will
-		 * need to be called on any child bones, recursively. */
-		rotateWorld (degrees: number) {
-			let a = this.a, b = this.b, c = this.c, d = this.d;
-			let cos = MathUtils.cosDeg(degrees), sin = MathUtils.sinDeg(degrees);
-			this.a = cos * a - sin * c;
-			this.b = cos * b - sin * d;
-			this.c = sin * a + cos * c;
-			this.d = sin * b + cos * d;
-		}
-	}
-}

+ 0 - 90
spine-ts/core/src/BoneData.ts

@@ -1,90 +0,0 @@
-/******************************************************************************
- * Spine Runtimes License Agreement
- * Last updated January 1, 2020. Replaces all prior versions.
- *
- * Copyright (c) 2013-2020, Esoteric Software LLC
- *
- * Integration of the Spine Runtimes into software or otherwise creating
- * derivative works of the Spine Runtimes is permitted under the terms and
- * conditions of Section 2 of the Spine Editor License Agreement:
- * http://esotericsoftware.com/spine-editor-license
- *
- * Otherwise, it is permitted to integrate the Spine Runtimes into software
- * or otherwise create derivative works of the Spine Runtimes (collectively,
- * "Products"), provided that each user of the Products must obtain their own
- * Spine Editor license and redistribution of the Products in any form must
- * include this license and copyright notice.
- *
- * THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "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 LLC 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
- * THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *****************************************************************************/
-
-module spine {
-
-	/** Stores the setup pose for a {@link Bone}. */
-	export class BoneData {
-		/** The index of the bone in {@link Skeleton#getBones()}. */
-		index: number;
-
-		/** The name of the bone, which is unique across all bones in the skeleton. */
-		name: string;
-
-		/** @returns May be null. */
-		parent: BoneData;
-
-		/** The bone's length. */
-		length: number;
-
-		/** The local x translation. */
-		x = 0;
-
-		/** The local y translation. */
-		y = 0;
-
-		/** The local rotation. */
-		rotation = 0;
-
-		/** The local scaleX. */
-		scaleX = 1;
-
-		/** The local scaleY. */
-		scaleY = 1;
-
-		/** The local shearX. */
-		shearX = 0;
-
-		/** The local shearX. */
-		shearY = 0;
-
-		/** The transform mode for how parent world transforms affect this bone. */
-		transformMode = TransformMode.Normal;
-
-		/** When true, {@link Skeleton#updateWorldTransform()} only updates this bone if the {@link Skeleton#skin} contains this
-	 	* bone.
-	 	* @see Skin#bones */
-		skinRequired = false;
-
-		/** The color of the bone as it was in Spine. Available only when nonessential data was exported. Bones are not usually
-		 * rendered at runtime. */
-		color = new Color();
-
-		constructor (index: number, name: string, parent: BoneData) {
-			if (index < 0) throw new Error("index must be >= 0.");
-			if (!name) throw new Error("name cannot be null.");
-			this.index = index;
-			this.name = name;
-			this.parent = parent;
-		}
-	}
-
-	/** Determines how a bone inherits world transforms from parent bones. */
-	export enum TransformMode { Normal, OnlyTranslation, NoRotationOrReflection, NoScale, NoScaleOrReflection }
-}

+ 0 - 294
spine-ts/core/src/IkConstraint.ts

@@ -1,294 +0,0 @@
-/******************************************************************************
- * Spine Runtimes License Agreement
- * Last updated January 1, 2020. Replaces all prior versions.
- *
- * Copyright (c) 2013-2020, Esoteric Software LLC
- *
- * Integration of the Spine Runtimes into software or otherwise creating
- * derivative works of the Spine Runtimes is permitted under the terms and
- * conditions of Section 2 of the Spine Editor License Agreement:
- * http://esotericsoftware.com/spine-editor-license
- *
- * Otherwise, it is permitted to integrate the Spine Runtimes into software
- * or otherwise create derivative works of the Spine Runtimes (collectively,
- * "Products"), provided that each user of the Products must obtain their own
- * Spine Editor license and redistribution of the Products in any form must
- * include this license and copyright notice.
- *
- * THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "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 LLC 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
- * THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *****************************************************************************/
-
-module spine {
-
-	/** Stores the current pose for an IK constraint. An IK constraint adjusts the rotation of 1 or 2 constrained bones so the tip of
-	 * the last bone is as close to the target bone as possible.
-	 *
-	 * See [IK constraints](http://esotericsoftware.com/spine-ik-constraints) in the Spine User Guide. */
-	export class IkConstraint implements Updatable {
-		/** The IK constraint's setup pose data. */
-		data: IkConstraintData;
-
-		/** The bones that will be modified by this IK constraint. */
-		bones: Array<Bone>;
-
-		/** The bone that is the IK target. */
-		target: Bone;
-
-		/** Controls the bend direction of the IK bones, either 1 or -1. */
-		bendDirection = 0;
-
-		/** When true and only a single bone is being constrained, if the target is too close, the bone is scaled to reach it. */
-		compress = false;
-
-		/** When true, if the target is out of range, the parent bone is scaled to reach it. If more than one bone is being constrained
-		 * and the parent bone has local nonuniform scale, stretch is not applied. */
-		stretch = false;
-
-		/** A percentage (0-1) that controls the mix between the constrained and unconstrained rotations. */
-		mix = 1;
-
-		/** For two bone IK, the distance from the maximum reach of the bones that rotation will slow. */
-		softness = 0;
-		active = false;
-
-		constructor (data: IkConstraintData, skeleton: Skeleton) {
-			if (!data) throw new Error("data cannot be null.");
-			if (!skeleton) throw new Error("skeleton cannot be null.");
-			this.data = data;
-			this.mix = data.mix;
-			this.softness = data.softness;
-			this.bendDirection = data.bendDirection;
-			this.compress = data.compress;
-			this.stretch = data.stretch;
-
-			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);
-		}
-
-		isActive () {
-			return this.active;
-		}
-
-		update () {
-			if (this.mix == 0) return;
-			let target = this.target;
-			let bones = this.bones;
-			switch (bones.length) {
-				case 1:
-					this.apply1(bones[0], target.worldX, target.worldY, this.compress, this.stretch, this.data.uniform, this.mix);
-					break;
-				case 2:
-					this.apply2(bones[0], bones[1], target.worldX, target.worldY, this.bendDirection, this.stretch, this.data.uniform, this.softness, this.mix);
-					break;
-			}
-		}
-
-		/** Applies 1 bone IK. The target is specified in the world coordinate system. */
-		apply1 (bone: Bone, targetX: number, targetY: number, compress: boolean, stretch: boolean, uniform: boolean, alpha: number) {
-			let p = bone.parent;
-			let pa = p.a, pb = p.b, pc = p.c, pd = p.d;
-			let rotationIK = -bone.ashearX - bone.arotation, tx = 0, ty = 0;
-
-			switch (bone.data.transformMode) {
-				case TransformMode.OnlyTranslation:
-					tx = targetX - bone.worldX;
-					ty = targetY - bone.worldY;
-					break;
-				case TransformMode.NoRotationOrReflection:
-					let s = Math.abs(pa * pd - pb * pc) / (pa * pa + pc * pc);
-					let sa = pa / bone.skeleton.scaleX;
-					let sc = pc / bone.skeleton.scaleY;
-					pb = -sc * s * bone.skeleton.scaleX;
-					pd = sa * s * bone.skeleton.scaleY;
-					rotationIK += Math.atan2(sc, sa) * MathUtils.radDeg;
-				// Fall through
-				default:
-					let x = targetX - p.worldX, y = targetY - p.worldY;
-					let d = pa * pd - pb * pc;
-					tx = (x * pd - y * pb) / d - bone.ax;
-					ty = (y * pa - x * pc) / d - bone.ay;
-			}
-			rotationIK += Math.atan2(ty, tx) * MathUtils.radDeg;
-			if (bone.ascaleX < 0) rotationIK += 180;
-			if (rotationIK > 180)
-				rotationIK -= 360;
-			else if (rotationIK < -180)
-				rotationIK += 360;
-			let sx = bone.ascaleX, sy = bone.ascaleY;
-			if (compress || stretch) {
-				switch (bone.data.transformMode) {
-					case TransformMode.NoScale:
-					case TransformMode.NoScaleOrReflection:
-						tx = targetX - bone.worldX;
-						ty = targetY - bone.worldY;
-				}
-				let b = bone.data.length * sx, dd = Math.sqrt(tx * tx + ty * ty);
-				if ((compress && dd < b) || (stretch && dd > b) && b > 0.0001) {
-					let s = (dd / b - 1) * alpha + 1;
-					sx *= s;
-					if (uniform) sy *= s;
-				}
-			}
-			bone.updateWorldTransformWith(bone.ax, bone.ay, bone.arotation + rotationIK * alpha, sx, sy, bone.ashearX,
-				bone.ashearY);
-		}
-
-		/** Applies 2 bone IK. 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, stretch: boolean, uniform: boolean, softness: number, alpha: number) {
-			let px = parent.ax, py = parent.ay, psx = parent.ascaleX, psy = parent.ascaleY, sx = psx, sy = psy, csx = child.ascaleX;
-			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 = parent.a, b = parent.b, c = parent.c, d = parent.d;
-			let u = Math.abs(psx - psy) <= 0.0001;
-			if (!u || stretch) {
-				cy = 0;
-				cwx = a * cx + parent.worldX;
-				cwy = c * cx + parent.worldY;
-			} else {
-				cy = child.ay;
-				cwx = a * cx + b * cy + parent.worldX;
-				cwy = c * cx + d * cy + parent.worldY;
-			}
-			let pp = parent.parent;
-			a = pp.a;
-			b = pp.b;
-			c = pp.c;
-			d = pp.d;
-			let id = 1 / (a * d - b * c), x = cwx - pp.worldX, y = cwy - pp.worldY;
-			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, a2;
-			if (l1 < 0.0001) {
-				this.apply1(parent, targetX, targetY, false, stretch, false, alpha);
-				child.updateWorldTransformWith(cx, cy, 0, child.ascaleX, child.ascaleY, child.ashearX, child.ashearY);
-				return;
-			}
-			x = targetX - pp.worldX;
-			y = targetY - pp.worldY;
-			let tx = (x * d - y * b) * id - px, ty = (y * a - x * c) * id - py;
-			let dd = tx * tx + ty * ty;
-			if (softness != 0) {
-				softness *= psx * (csx + 1) * 0.5;
-				let td = Math.sqrt(dd), sd = td - l1 - l2 * psx + softness;
-				if (sd > 0) {
-					let p = Math.min(1, sd / (softness * 2)) - 1;
-					p = (sd - softness * (1 - p * p)) / td;
-					tx -= p * tx;
-					ty -= p * ty;
-					dd = tx * tx + ty * ty;
-				}
-			}
-			outer:
-			if (u) {
-				l2 *= psx;
-				let cos = (dd - l1 * l1 - l2 * l2) / (2 * l1 * l2);
-				if (cos < -1) {
-					cos = -1;
-					a2 = Math.PI * bendDir;
-				} else if (cos > 1) {
-					cos = 1;
-					a2 = 0;
-					if (stretch) {
-						a = (Math.sqrt(dd) / (l1 + l2) - 1) * alpha + 1;
-						sx *= a;
-						if (uniform) sy *= a;
-					}
-				} else
-					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, 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) * 0.5;
-					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) * 0.5) {
-					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, sx, sy, 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);
-		}
-	}
-}

+ 0 - 483
spine-ts/core/src/PathConstraint.ts

@@ -1,483 +0,0 @@
-/******************************************************************************
- * Spine Runtimes License Agreement
- * Last updated January 1, 2020. Replaces all prior versions.
- *
- * Copyright (c) 2013-2020, Esoteric Software LLC
- *
- * Integration of the Spine Runtimes into software or otherwise creating
- * derivative works of the Spine Runtimes is permitted under the terms and
- * conditions of Section 2 of the Spine Editor License Agreement:
- * http://esotericsoftware.com/spine-editor-license
- *
- * Otherwise, it is permitted to integrate the Spine Runtimes into software
- * or otherwise create derivative works of the Spine Runtimes (collectively,
- * "Products"), provided that each user of the Products must obtain their own
- * Spine Editor license and redistribution of the Products in any form must
- * include this license and copyright notice.
- *
- * THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "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 LLC 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
- * THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *****************************************************************************/
-
-module spine {
-
-	/** Stores the current pose for a path constraint. A path constraint adjusts the rotation, translation, and scale of the
-	 * constrained bones so they follow a {@link PathAttachment}.
-	 *
-	 * See [Path constraints](http://esotericsoftware.com/spine-path-constraints) in the Spine User Guide. */
-	export class PathConstraint implements Updatable {
-		static NONE = -1; static BEFORE = -2; static AFTER = -3;
-		static epsilon = 0.00001;
-
-		/** The path constraint's setup pose data. */
-		data: PathConstraintData;
-
-		/** The bones that will be modified by this path constraint. */
-		bones: Array<Bone>;
-
-		/** The slot whose path attachment will be used to constrained the bones. */
-		target: Slot;
-
-		/** The position along the path. */
-		position = 0;
-
-		/** The spacing between bones. */
-		spacing = 0;
-
-		mixRotate = 0;
-
-		mixX = 0;
-
-		mixY = 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>();
-
-		active = false;
-
-		constructor (data: PathConstraintData, skeleton: Skeleton) {
-			if (!data) throw new Error("data cannot be null.");
-			if (!skeleton) 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.mixRotate = data.mixRotate;
-			this.mixX = data.mixX;
-			this.mixY = data.mixY;
-		}
-
-		isActive () {
-			return this.active;
-		}
-
-		update () {
-			let attachment = this.target.getAttachment();
-			if (!(attachment instanceof PathAttachment)) return;
-
-			let mixRotate = this.mixRotate, mixX = this.mixX, mixY = this.mixY;
-			if (mixRotate == 0 && mixX == 0 && mixY == 0) return;
-
-			let data = this.data;
-			let tangents = data.rotateMode == RotateMode.Tangent, scale = data.rotateMode == RotateMode.ChainScale;
-
-			let bones = this.bones;
-			let boneCount = bones.length, spacesCount = tangents ? boneCount : boneCount + 1;
-			let spaces = Utils.setArraySize(this.spaces, spacesCount), lengths: Array<number> = scale ? this.lengths = Utils.setArraySize(this.lengths, boneCount) : null;
-			let spacing = this.spacing;
-
-			switch (data.spacingMode) {
-				case SpacingMode.Percent:
-					if (scale) {
-						for (let i = 0, n = spacesCount - 1; i < n; i++) {
-							let bone = bones[i];
-							let setupLength = bone.data.length;
-							if (setupLength < PathConstraint.epsilon)
-								lengths[i] = 0;
-							else {
-								let x = setupLength * bone.a, y = setupLength * bone.c;
-								lengths[i] = Math.sqrt(x * x + y * y);
-							}
-						}
-					}
-					Utils.arrayFill(spaces, 1, spacesCount, spacing);
-					break;
-				case SpacingMode.Proportional:
-					let sum = 0;
-					for (let i = 0, n = spacesCount - 1; i < n;) {
-						let bone = bones[i];
-						let setupLength = bone.data.length;
-						if (setupLength < PathConstraint.epsilon) {
-							if (scale) lengths[i] = 0;
-							spaces[++i] = spacing;
-						} else {
-							let x = setupLength * bone.a, y = setupLength * bone.c;
-							let length = Math.sqrt(x * x + y * y);
-							if (scale) lengths[i] = length;
-							spaces[++i] = length;
-							sum += length;
-						}
-					}
-					if (sum > 0) {
-						sum = spacesCount / sum * spacing;
-						for (let i = 1; i < spacesCount; i++)
-							spaces[i] *= sum;
-					}
-					break;
-				default:
-					let lengthSpacing = data.spacingMode == SpacingMode.Length;
-					for (let i = 0, n = spacesCount - 1; i < n;) {
-						let bone = bones[i];
-						let setupLength = bone.data.length;
-						if (setupLength < PathConstraint.epsilon) {
-							if (scale) lengths[i] = 0;
-							spaces[++i] = spacing;
-						} else {
-							let x = setupLength * bone.a, y = setupLength * bone.c;
-							let length = Math.sqrt(x * x + y * y);
-							if (scale) lengths[i] = length;
-							spaces[++i] = (lengthSpacing ? setupLength + spacing : spacing) * length / setupLength;
-						}
-					}
-			}
-
-			let positions = this.computeWorldPositions(<PathAttachment>attachment, spacesCount, tangents);
-			let boneX = positions[0], boneY = positions[1], offsetRotation = data.offsetRotation;
-			let tip = false;
-			if (offsetRotation == 0)
-				tip = data.rotateMode == RotateMode.Chain;
-			else {
-				tip = false;
-				let p = this.target.bone;
-				offsetRotation *= p.a * p.d - p.b * p.c > 0 ? MathUtils.degRad : -MathUtils.degRad;
-			}
-			for (let i = 0, p = 3; i < boneCount; i++, p += 3) {
-				let bone = bones[i];
-				bone.worldX += (boneX - bone.worldX) * mixX;
-				bone.worldY += (boneY - bone.worldY) * mixY;
-				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) * mixRotate + 1;
-						bone.a *= s;
-						bone.c *= s;
-					}
-				}
-				boneX = x;
-				boneY = y;
-				if (mixRotate > 0) {
-					let a = bone.a, b = bone.b, c = bone.c, d = bone.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) * mixRotate;
-						boneY += (length * (sin * a + cos * c) - dy) * mixRotate;
-					} else {
-						r += offsetRotation;
-					}
-					if (r > MathUtils.PI)
-						r -= MathUtils.PI2;
-					else if (r < -MathUtils.PI) //
-						r += MathUtils.PI2;
-					r *= mixRotate;
-					cos = Math.cos(r);
-					sin = Math.sin(r);
-					bone.a = cos * a - sin * c;
-					bone.b = cos * b - sin * d;
-					bone.c = sin * a + cos * c;
-					bone.d = sin * b + cos * d;
-				}
-				bone.updateAppliedTransform();
-			}
-		}
-
-		computeWorldPositions (path: PathAttachment, spacesCount: number, tangents: 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 (this.data.positionMode == PositionMode.Percent) position *= pathLength;
-
-				let multiplier;
-				switch (this.data.spacingMode) {
-					case SpacingMode.Percent:
-						multiplier = pathLength;
-						break;
-					case SpacingMode.Proportional:
-						multiplier = pathLength / spacesCount;
-						break;
-					default:
-						multiplier = 1;
-				}
-				world = Utils.setArraySize(this.world, 8);
-				for (let i = 0, o = 0, curve = 0; i < spacesCount; i++, o += 3) {
-					let space = spaces[i] * multiplier;
-					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 (this.data.positionMode == PositionMode.Percent) position *= pathLength;
-
-			let multiplier;
-			switch (this.data.spacingMode) {
-				case SpacingMode.Percent:
-					multiplier = pathLength;
-					break;
-				case SpacingMode.Proportional:
-					multiplier = pathLength / spacesCount;
-					break;
-				default:
-					multiplier = 1;
-			}
-
-			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] * multiplier;
-				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)) {
-				out[o] = x1;
-				out[o + 1] = y1;
-				out[o + 2] = Math.atan2(cy1 - y1, cx1 - x1);
-				return;
-			}
-			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) {
-				if (p < 0.001)
-					out[o + 2] = Math.atan2(cy1 - y1, cx1 - x1);
-				else
-					out[o + 2] = Math.atan2(y - (y1 * uu + cy1 * ut * 2 + cy2 * tt), x - (x1 * uu + cx1 * ut * 2 + cx2 * tt));
-			}
-		}
-	}
-}

+ 0 - 84
spine-ts/core/src/PathConstraintData.ts

@@ -1,84 +0,0 @@
-/******************************************************************************
- * Spine Runtimes License Agreement
- * Last updated January 1, 2020. Replaces all prior versions.
- *
- * Copyright (c) 2013-2020, Esoteric Software LLC
- *
- * Integration of the Spine Runtimes into software or otherwise creating
- * derivative works of the Spine Runtimes is permitted under the terms and
- * conditions of Section 2 of the Spine Editor License Agreement:
- * http://esotericsoftware.com/spine-editor-license
- *
- * Otherwise, it is permitted to integrate the Spine Runtimes into software
- * or otherwise create derivative works of the Spine Runtimes (collectively,
- * "Products"), provided that each user of the Products must obtain their own
- * Spine Editor license and redistribution of the Products in any form must
- * include this license and copyright notice.
- *
- * THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "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 LLC 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
- * THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *****************************************************************************/
-
-module spine {
-
-	/** Stores the setup pose for a {@link PathConstraint}.
-	 *
-	 * See [path constraints](http://esotericsoftware.com/spine-path-constraints) in the Spine User Guide. */
-	export class PathConstraintData extends ConstraintData {
-
-		/** The bones that will be modified by this path constraint. */
-		bones = new Array<BoneData>();
-
-		/** The slot whose path attachment will be used to constrained the bones. */
-		target: SlotData;
-
-		/** The mode for positioning the first bone on the path. */
-		positionMode: PositionMode;
-
-		/** The mode for positioning the bones after the first bone on the path. */
-		spacingMode: SpacingMode;
-
-		/** The mode for adjusting the rotation of the bones. */
-		rotateMode: RotateMode;
-
-		/** An offset added to the constrained bone rotation. */
-		offsetRotation: number;
-
-		/** The position along the path. */
-		position: number;
-
-		/** The spacing between bones. */
-		spacing: number;
-
-		mixRotate = 0;
-		mixX = 0;
-		mixY = 0;
-
-		constructor (name: string) {
-			super(name, 0, false);
-		}
-	}
-
-	/** Controls how the first bone is positioned along the path.
-	 *
-	 * See [position](http://esotericsoftware.com/spine-path-constraints#Position) in the Spine User Guide. */
-	export enum PositionMode { Fixed, Percent }
-
-	/** Controls how bones after the first bone are positioned along the path.
-	 *
-	 * See [spacing](http://esotericsoftware.com/spine-path-constraints#Spacing) in the Spine User Guide. */
-	export enum SpacingMode { Length, Fixed, Percent, Proportional }
-
-	/** Controls how bones are rotated, translated, and scaled to match the path.
-	 *
-	 * See [rotate mix](http://esotericsoftware.com/spine-path-constraints#Rotate-mix) in the Spine User Guide. */
-	export enum RotateMode { Tangent, Chain, ChainScale }
-}

+ 0 - 638
spine-ts/core/src/Skeleton.ts

@@ -1,638 +0,0 @@
-/******************************************************************************
- * Spine Runtimes License Agreement
- * Last updated January 1, 2020. Replaces all prior versions.
- *
- * Copyright (c) 2013-2020, Esoteric Software LLC
- *
- * Integration of the Spine Runtimes into software or otherwise creating
- * derivative works of the Spine Runtimes is permitted under the terms and
- * conditions of Section 2 of the Spine Editor License Agreement:
- * http://esotericsoftware.com/spine-editor-license
- *
- * Otherwise, it is permitted to integrate the Spine Runtimes into software
- * or otherwise create derivative works of the Spine Runtimes (collectively,
- * "Products"), provided that each user of the Products must obtain their own
- * Spine Editor license and redistribution of the Products in any form must
- * include this license and copyright notice.
- *
- * THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "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 LLC 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
- * THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *****************************************************************************/
-
-module spine {
-
-	/** Stores the current pose for a skeleton.
-	 *
-	 * See [Instance objects](http://esotericsoftware.com/spine-runtime-architecture#Instance-objects) in the Spine Runtimes Guide. */
-	export class Skeleton {
-		/** The skeleton's setup pose data. */
-		data: SkeletonData;
-
-		/** The skeleton's bones, sorted parent first. The root bone is always the first bone. */
-		bones: Array<Bone>;
-
-		/** The skeleton's slots. */
-		slots: Array<Slot>;
-
-		/** The skeleton's slots in the order they should be drawn. The returned array may be modified to change the draw order. */
-		drawOrder: Array<Slot>;
-
-		/** The skeleton's IK constraints. */
-		ikConstraints: Array<IkConstraint>;
-
-		/** The skeleton's transform constraints. */
-		transformConstraints: Array<TransformConstraint>;
-
-		/** The skeleton's path constraints. */
-		pathConstraints: Array<PathConstraint>;
-
-		/** The list of bones and constraints, sorted in the order they should be updated, as computed by {@link #updateCache()}. */
-		_updateCache = new Array<Updatable>();
-
-		/** The skeleton's current skin. May be null. */
-		skin: Skin;
-
-		/** The color to tint all the skeleton's attachments. */
-		color: Color;
-
-		/** Returns the skeleton's time. This can be used for tracking, such as with Slot {@link Slot#attachmentTime}.
-		 * <p>
-		 * See {@link #update()}. */
-		time = 0;
-
-		/** Scales the entire skeleton on the X axis. This affects all bones, even if the bone's transform mode disallows scale
-	 	* inheritance. */
-		scaleX = 1;
-
-		/** Scales the entire skeleton on the Y axis. This affects all bones, even if the bone's transform mode disallows scale
-	 	* inheritance. */
-		scaleY = 1;
-
-		/** Sets the skeleton X position, which is added to the root bone worldX position. */
-		x = 0;
-
-		/** Sets the skeleton Y position, which is added to the root bone worldY position. */
-		y = 0;
-
-		constructor (data: SkeletonData) {
-			if (!data) 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)
-					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();
-		}
-
-		/** Caches information about bones and constraints. Must be called if the {@link #getSkin()} is modified or if bones,
-		 * constraints, or weighted path attachments are added or removed. */
-		updateCache () {
-			let updateCache = this._updateCache;
-			updateCache.length = 0;
-
-			let bones = this.bones;
-			for (let i = 0, n = bones.length; i < n; i++) {
-				let bone = bones[i];
-				bone.sorted = bone.data.skinRequired;
-				bone.active = !bone.sorted;
-			}
-
-			if (this.skin) {
-				let skinBones = this.skin.bones;
-				for (let i = 0, n = this.skin.bones.length; i < n; i++) {
-					let bone = this.bones[skinBones[i].index];
-					do {
-						bone.sorted = false;
-						bone.active = true;
-						bone = bone.parent;
-					} while (bone);
-				}
-			}
-
-			// 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) {
-			constraint.active = constraint.target.isActive() && (!constraint.data.skinRequired || (this.skin && Utils.contains(this.skin.constraints, constraint.data, true)));
-			if (!constraint.active) return;
-
-			let target = constraint.target;
-			this.sortBone(target);
-
-			let constrained = constraint.bones;
-			let parent = constrained[0];
-			this.sortBone(parent);
-
-			if (constrained.length == 1) {
-				this._updateCache.push(constraint);
-				this.sortReset(parent.children);
-			} else {
-				let child = constrained[constrained.length - 1];
-				this.sortBone(child);
-
-				this._updateCache.push(constraint);
-
-				this.sortReset(parent.children);
-				child.sorted = true;
-			}
-		}
-
-		sortPathConstraint (constraint: PathConstraint) {
-			constraint.active = constraint.target.bone.isActive() && (!constraint.data.skinRequired || (this.skin && Utils.contains(this.skin.constraints, constraint.data, true)));
-			if (!constraint.active) return;
-
-			let slot = constraint.target;
-			let slotIndex = slot.data.index;
-			let slotBone = slot.bone;
-			if (this.skin) this.sortPathConstraintAttachment(this.skin, slotIndex, slotBone);
-			if (this.data.defaultSkin && 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) {
-			constraint.active = constraint.target.isActive() && (!constraint.data.skinRequired || (this.skin && Utils.contains(this.skin.constraints, constraint.data, true)));
-			if (!constraint.active) return;
-
-			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);
-					this.sortBone(child);
-				}
-			} else {
-				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;
-		}
-
-		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)
-				this.sortBone(slotBone);
-			else {
-				let bones = this.bones;
-				for (let i = 0, n = pathBones.length; i < n;) {
-					let nn = pathBones[i++];
-					nn += i;
-					while (i < nn)
-						this.sortBone(bones[pathBones[i++]]);
-				}
-			}
-		}
-
-		sortBone (bone: Bone) {
-			if (bone.sorted) return;
-			let parent = bone.parent;
-			if (parent) 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.active) continue;
-				if (bone.sorted) this.sortReset(bone.children);
-				bone.sorted = false;
-			}
-		}
-
-		/** Updates the world transform for each bone and applies all constraints.
-		 *
-		 * See [World transforms](http://esotericsoftware.com/spine-runtime-skeletons#World-transforms) in the Spine
-		 * Runtimes Guide. */
-		updateWorldTransform () {
-			let bones = this.bones;
-			for (let i = 0, n = bones.length; i < n; i++) {
-				let bone = bones[i];
-				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;
-			}
-
-			let updateCache = this._updateCache;
-			for (let i = 0, n = updateCache.length; i < n; i++)
-				updateCache[i].update();
-		}
-
-		updateWorldTransformWith (parent: Bone) {
-			// Apply the parent bone transform to the root bone. The root bone always inherits scale, rotation and reflection.
-			let rootBone = this.getRootBone();
-			let pa = parent.a, pb = parent.b, pc = parent.c, pd = parent.d;
-			rootBone.worldX = pa * this.x + pb * this.y + parent.worldX;
-			rootBone.worldY = pc * this.x + pd * this.y + parent.worldY;
-
-			let rotationY = rootBone.rotation + 90 + rootBone.shearY;
-			let la = MathUtils.cosDeg(rootBone.rotation + rootBone.shearX) * rootBone.scaleX;
-			let lb = MathUtils.cosDeg(rotationY) * rootBone.scaleY;
-			let lc = MathUtils.sinDeg(rootBone.rotation + rootBone.shearX) * rootBone.scaleX;
-			let ld = MathUtils.sinDeg(rotationY) * rootBone.scaleY;
-			rootBone.a = (pa * la + pb * lc) * this.scaleX;
-			rootBone.b = (pa * lb + pb * ld) * this.scaleX;
-			rootBone.c = (pc * la + pd * lc) * this.scaleY;
-			rootBone.d = (pc * lb + pd * ld) * this.scaleY;
-
-			// Update everything except root bone.
-			let updateCache = this._updateCache;
-			for (let i = 0, n = updateCache.length; i < n; i++) {
-				let updatable = updateCache[i];
-				if (updatable != rootBone) updatable.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.mix = constraint.data.mix;
-				constraint.softness = constraint.data.softness;
-				constraint.bendDirection = constraint.data.bendDirection;
-				constraint.compress = constraint.data.compress;
-				constraint.stretch = constraint.data.stretch;
-			}
-
-			let transformConstraints = this.transformConstraints;
-			for (let i = 0, n = transformConstraints.length; i < n; i++) {
-				let constraint = transformConstraints[i];
-				let data = constraint.data;
-				constraint.mixRotate = data.mixRotate;
-				constraint.mixX = data.mixX;
-				constraint.mixY = data.mixY;
-				constraint.mixScaleX = data.mixScaleX;
-				constraint.mixScaleY = data.mixScaleY;
-				constraint.mixShearY = data.mixShearY;
-			}
-
-			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.mixRotate = data.mixRotate;
-				constraint.mixX = data.mixX;
-				constraint.mixY = data.mixY;
-			}
-		}
-
-		/** Sets the slots and draw order to their setup pose values. */
-		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();
-		}
-
-		/** @returns May return null. */
-		getRootBone () {
-			if (this.bones.length == 0) return null;
-			return this.bones[0];
-		}
-
-		/** @returns May be null. */
-		findBone (boneName: string) {
-			if (!boneName) 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;
-		}
-
-		/** @returns -1 if the bone was not found. */
-		findBoneIndex (boneName: string) {
-			if (!boneName) 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;
-		}
-
-		/** Finds a slot by comparing each slot's name. It is more efficient to cache the results of this method than to call it
-		 * repeatedly.
-		 * @returns May be null. */
-		findSlot (slotName: string) {
-			if (!slotName) 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;
-		}
-
-		/** @returns -1 if the bone was not found. */
-		findSlotIndex (slotName: string) {
-			if (!slotName) 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 {@link #setSkin()}. */
-		setSkinByName (skinName: string) {
-			let skin = this.data.findSkin(skinName);
-			if (!skin) throw new Error("Skin not found: " + skinName);
-			this.setSkin(skin);
-		}
-
-		/** Sets the skin used to look up attachments before looking in the {@link SkeletonData#defaultSkin default skin}. If the
-		 * skin is changed, {@link #updateCache()} is called.
-		 *
-		 * 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.
-		 *
-		 * After changing the skin, the visible attachments can be reset to those attached in the setup pose by calling
-		 * {@link #setSlotsToSetupPose()}. Also, often {@link AnimationState#apply()} is called before the next time the
-		 * skeleton is rendered to allow any attachment keys in the current animation(s) to hide or show attachments from the new skin.
-		 * @param newSkin May be null. */
-		setSkin (newSkin: Skin) {
-			if (newSkin == this.skin) return;
-			if (newSkin) {
-				if (this.skin)
-					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) {
-							let attachment: Attachment = newSkin.getAttachment(i, name);
-							if (attachment) slot.setAttachment(attachment);
-						}
-					}
-				}
-			}
-			this.skin = newSkin;
-			this.updateCache();
-		}
-
-
-		/** Finds an attachment by looking in the {@link #skin} and {@link SkeletonData#defaultSkin} using the slot name and attachment
-		 * name.
-		 *
-		 * See {@link #getAttachment()}.
-		 * @returns May be null. */
-		getAttachmentByName (slotName: string, attachmentName: string): Attachment {
-			return this.getAttachment(this.data.findSlotIndex(slotName), attachmentName);
-		}
-
-		/** Finds an attachment by looking in the {@link #skin} and {@link SkeletonData#defaultSkin} using the slot index and
-		 * attachment name. First the skin is checked and if the attachment was not found, the default skin is checked.
-		 *
-		 * See [Runtime skins](http://esotericsoftware.com/spine-runtime-skins) in the Spine Runtimes Guide.
-		 * @returns May be null. */
-		getAttachment (slotIndex: number, attachmentName: string): Attachment {
-			if (!attachmentName) throw new Error("attachmentName cannot be null.");
-			if (this.skin) {
-				let attachment: Attachment = this.skin.getAttachment(slotIndex, attachmentName);
-				if (attachment) return attachment;
-			}
-			if (this.data.defaultSkin) return this.data.defaultSkin.getAttachment(slotIndex, attachmentName);
-			return null;
-		}
-
-		/** A convenience method to set an attachment by finding the slot with {@link #findSlot()}, finding the attachment with
-		 * {@link #getAttachment()}, then setting the slot's {@link Slot#attachment}.
-		 * @param attachmentName May be null to clear the slot's attachment. */
-		setAttachment (slotName: string, attachmentName: string) {
-			if (!slotName) 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) {
-						attachment = this.getAttachment(i, attachmentName);
-						if (!attachment) throw new Error("Attachment not found: " + attachmentName + ", for slot: " + slotName);
-					}
-					slot.setAttachment(attachment);
-					return;
-				}
-			}
-			throw new Error("Slot not found: " + slotName);
-		}
-
-
-		/** Finds an IK constraint by comparing each IK constraint's name. It is more efficient to cache the results of this method
-		 * than to call it repeatedly.
-		 * @return May be null. */
-		findIkConstraint (constraintName: string) {
-			if (!constraintName) 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;
-		}
-
-		/** Finds a transform constraint by comparing each transform constraint's name. It is more efficient to cache the results of
-		 * this method than to call it repeatedly.
-		 * @return May be null. */
-		findTransformConstraint (constraintName: string) {
-			if (!constraintName) 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;
-		}
-
-		/** Finds a path constraint by comparing each path constraint's name. It is more efficient to cache the results of this method
-		 * than to call it repeatedly.
-		 * @return May be null. */
-		findPathConstraint (constraintName: string) {
-			if (!constraintName) 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 An output value, the distance from the skeleton origin to the bottom left corner of the AABB.
-		 * @param size An output value, the width and height of the AABB.
-		 * @param temp Working memory to temporarily store attachments' computed world vertices. */
-		getBounds (offset: Vector2, size: Vector2, temp: Array<number> = new Array<number>(2)) {
-			if (!offset) throw new Error("offset cannot be null.");
-			if (!size) 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];
-				if (!slot.bone.active) continue;
-				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) {
-					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);
-		}
-
-		/** Increments the skeleton's {@link #time}. */
-		update (delta: number) {
-			this.time += delta;
-		}
-	}
-}

+ 0 - 1132
spine-ts/core/src/SkeletonBinary.ts

@@ -1,1132 +0,0 @@
-/******************************************************************************
- * Spine Runtimes License Agreement
- * Last updated January 1, 2020. Replaces all prior versions.
- *
- * Copyright (c) 2013-2020, Esoteric Software LLC
- *
- * Integration of the Spine Runtimes into software or otherwise creating
- * derivative works of the Spine Runtimes is permitted under the terms and
- * conditions of Section 2 of the Spine Editor License Agreement:
- * http://esotericsoftware.com/spine-editor-license
- *
- * Otherwise, it is permitted to integrate the Spine Runtimes into software
- * or otherwise create derivative works of the Spine Runtimes (collectively,
- * "Products"), provided that each user of the Products must obtain their own
- * Spine Editor license and redistribution of the Products in any form must
- * include this license and copyright notice.
- *
- * THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "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 LLC 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
- * THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *****************************************************************************/
-
-module spine {
-	/** Loads skeleton data in the Spine binary format.
-	 *
-	 * See [Spine binary format](http://esotericsoftware.com/spine-binary-format) and
-	 * [JSON and binary data](http://esotericsoftware.com/spine-loading-skeleton-data#JSON-and-binary-data) in the Spine
-	 * Runtimes Guide. */
-	export class SkeletonBinary {
-		/** Scales bone positions, image sizes, and translations as they are loaded. This allows different size images to be used at
-		 * runtime than were used in Spine.
-		 *
-		 * See [Scaling](http://esotericsoftware.com/spine-loading-skeleton-data#Scaling) in the Spine Runtimes Guide. */
-		scale = 1;
-
-		attachmentLoader: AttachmentLoader;
-		private linkedMeshes = new Array<LinkedMesh>();
-
-		constructor (attachmentLoader: AttachmentLoader) {
-			this.attachmentLoader = attachmentLoader;
-		}
-
-		readSkeletonData (binary: Uint8Array): SkeletonData {
-			let scale = this.scale;
-
-			let skeletonData = new SkeletonData();
-			skeletonData.name = ""; // BOZO
-
-			let input = new BinaryInput(binary);
-
-			let lowHash = input.readInt32();
-			let highHash = input.readInt32();
-			skeletonData.hash = highHash == 0 && lowHash == 0 ? null : highHash.toString(16) + lowHash.toString(16);
-			skeletonData.version = input.readString();
-			skeletonData.x = input.readFloat();
-			skeletonData.y = input.readFloat();
-			skeletonData.width = input.readFloat();
-			skeletonData.height = input.readFloat();
-
-			let nonessential = input.readBoolean();
-			if (nonessential) {
-				skeletonData.fps = input.readFloat();
-
-				skeletonData.imagesPath = input.readString();
-				skeletonData.audioPath = input.readString();
-			}
-
-			let n = 0;
-			// Strings.
-			n = input.readInt(true)
-			for (let i = 0; i < n; i++)
-				input.strings.push(input.readString());
-
-			// Bones.
-			n = input.readInt(true)
-			for (let i = 0; i < n; i++) {
-				let name = input.readString();
-				let parent = i == 0 ? null : skeletonData.bones[input.readInt(true)];
-				let data = new BoneData(i, name, parent);
-				data.rotation = input.readFloat();
-				data.x = input.readFloat() * scale;
-				data.y = input.readFloat() * scale;
-				data.scaleX = input.readFloat();
-				data.scaleY = input.readFloat();
-				data.shearX = input.readFloat();
-				data.shearY = input.readFloat();
-				data.length = input.readFloat() * scale;
-				data.transformMode = input.readInt(true);
-				data.skinRequired = input.readBoolean();
-				if (nonessential) Color.rgba8888ToColor(data.color, input.readInt32());
-				skeletonData.bones.push(data);
-			}
-
-			// Slots.
-			n = input.readInt(true);
-			for (let i = 0; i < n; i++) {
-				let slotName = input.readString();
-				let boneData = skeletonData.bones[input.readInt(true)];
-				let data = new SlotData(i, slotName, boneData);
-				Color.rgba8888ToColor(data.color, input.readInt32());
-
-				let darkColor = input.readInt32();
-				if (darkColor != -1) Color.rgb888ToColor(data.darkColor = new Color(), darkColor);
-
-				data.attachmentName = input.readStringRef();
-				data.blendMode = input.readInt(true);
-				skeletonData.slots.push(data);
-			}
-
-			// IK constraints.
-			n = input.readInt(true);
-			for (let i = 0, nn; i < n; i++) {
-				let data = new IkConstraintData(input.readString());
-				data.order = input.readInt(true);
-				data.skinRequired = input.readBoolean();
-				nn = input.readInt(true);
-				for (let ii = 0; ii < nn; ii++)
-					data.bones.push(skeletonData.bones[input.readInt(true)]);
-				data.target = skeletonData.bones[input.readInt(true)];
-				data.mix = input.readFloat();
-				data.softness = input.readFloat() * scale;
-				data.bendDirection = input.readByte();
-				data.compress = input.readBoolean();
-				data.stretch = input.readBoolean();
-				data.uniform = input.readBoolean();
-				skeletonData.ikConstraints.push(data);
-			}
-
-			// Transform constraints.
-			n = input.readInt(true);
-			for (let i = 0, nn; i < n; i++) {
-				let data = new TransformConstraintData(input.readString());
-				data.order = input.readInt(true);
-				data.skinRequired = input.readBoolean();
-				nn = input.readInt(true);
-				for (let ii = 0; ii < nn; ii++)
-					data.bones.push(skeletonData.bones[input.readInt(true)]);
-				data.target = skeletonData.bones[input.readInt(true)];
-				data.local = input.readBoolean();
-				data.relative = input.readBoolean();
-				data.offsetRotation = input.readFloat();
-				data.offsetX = input.readFloat() * scale;
-				data.offsetY = input.readFloat() * scale;
-				data.offsetScaleX = input.readFloat();
-				data.offsetScaleY = input.readFloat();
-				data.offsetShearY = input.readFloat();
-				data.mixRotate = input.readFloat();
-				data.mixX = input.readFloat();
-				data.mixY = input.readFloat();
-				data.mixScaleX = input.readFloat();
-				data.mixScaleY = input.readFloat();
-				data.mixShearY = input.readFloat();
-				skeletonData.transformConstraints.push(data);
-			}
-
-			// Path constraints.
-			n = input.readInt(true);
-			for (let i = 0, nn; i < n; i++) {
-				let data = new PathConstraintData(input.readString());
-				data.order = input.readInt(true);
-				data.skinRequired = input.readBoolean();
-				nn = input.readInt(true);
-				for (let ii = 0; ii < nn; ii++)
-					data.bones.push(skeletonData.bones[input.readInt(true)]);
-				data.target = skeletonData.slots[input.readInt(true)];
-				data.positionMode = input.readInt(true);
-				data.spacingMode = input.readInt(true);
-				data.rotateMode = input.readInt(true);
-				data.offsetRotation = input.readFloat();
-				data.position = input.readFloat();
-				if (data.positionMode == PositionMode.Fixed) data.position *= scale;
-				data.spacing = input.readFloat();
-				if (data.spacingMode == SpacingMode.Length || data.spacingMode == SpacingMode.Fixed) data.spacing *= scale;
-				data.mixRotate = input.readFloat();
-				data.mixX = input.readFloat();
-				data.mixY = input.readFloat();
-				skeletonData.pathConstraints.push(data);
-			}
-
-			// Default skin.
-			let defaultSkin = this.readSkin(input, skeletonData, true, nonessential);
-			if (defaultSkin) {
-				skeletonData.defaultSkin = defaultSkin;
-				skeletonData.skins.push(defaultSkin);
-			}
-
-			// Skins.
-			{
-				let i = skeletonData.skins.length;
-				Utils.setArraySize(skeletonData.skins, n = i + input.readInt(true));
-				for (; i < n; i++)
-					skeletonData.skins[i] = this.readSkin(input, skeletonData, false, nonessential);
-			}
-
-			// Linked meshes.
-			n = this.linkedMeshes.length;
-			for (let i = 0; i < n; i++) {
-				let linkedMesh = this.linkedMeshes[i];
-				let skin = !linkedMesh.skin ? skeletonData.defaultSkin : skeletonData.findSkin(linkedMesh.skin);
-				let parent = skin.getAttachment(linkedMesh.slotIndex, linkedMesh.parent);
-				linkedMesh.mesh.deformAttachment = linkedMesh.inheritDeform ? parent as VertexAttachment : linkedMesh.mesh;
-				linkedMesh.mesh.setParentMesh(parent as MeshAttachment);
-				linkedMesh.mesh.updateUVs();
-			}
-			this.linkedMeshes.length = 0;
-
-			// Events.
-			n = input.readInt(true);
-			for (let i = 0; i < n; i++) {
-				let data = new EventData(input.readStringRef());
-				data.intValue = input.readInt(false);
-				data.floatValue = input.readFloat();
-				data.stringValue = input.readString();
-				data.audioPath = input.readString();
-				if (data.audioPath) {
-					data.volume = input.readFloat();
-					data.balance = input.readFloat();
-				}
-				skeletonData.events.push(data);
-			}
-
-			// Animations.
-			n = input.readInt(true);
-			for (let i = 0; i < n; i++)
-				skeletonData.animations.push(this.readAnimation(input, input.readString(), skeletonData));
-			return skeletonData;
-		}
-
-		private readSkin (input: BinaryInput, skeletonData: SkeletonData, defaultSkin: boolean, nonessential: boolean): Skin {
-			let skin = null;
-			let slotCount = 0;
-
-			if (defaultSkin) {
-				slotCount = input.readInt(true)
-				if (slotCount == 0) return null;
-				skin = new Skin("default");
-			} else {
-				skin = new Skin(input.readStringRef());
-				skin.bones.length = input.readInt(true);
-				for (let i = 0, n = skin.bones.length; i < n; i++)
-					skin.bones[i] = skeletonData.bones[input.readInt(true)];
-
-				for (let i = 0, n = input.readInt(true); i < n; i++)
-					skin.constraints.push(skeletonData.ikConstraints[input.readInt(true)]);
-				for (let i = 0, n = input.readInt(true); i < n; i++)
-					skin.constraints.push(skeletonData.transformConstraints[input.readInt(true)]);
-				for (let i = 0, n = input.readInt(true); i < n; i++)
-					skin.constraints.push(skeletonData.pathConstraints[input.readInt(true)]);
-
-				slotCount = input.readInt(true);
-			}
-
-			for (let i = 0; i < slotCount; i++) {
-				let slotIndex = input.readInt(true);
-				for (let ii = 0, nn = input.readInt(true); ii < nn; ii++) {
-					let name = input.readStringRef();
-					let attachment = this.readAttachment(input, skeletonData, skin, slotIndex, name, nonessential);
-					if (attachment) skin.setAttachment(slotIndex, name, attachment);
-				}
-			}
-			return skin;
-		}
-
-		private readAttachment (input: BinaryInput, skeletonData: SkeletonData, skin: Skin, slotIndex: number, attachmentName: string, nonessential: boolean): Attachment {
-			let scale = this.scale;
-
-			let name = input.readStringRef();
-			if (!name) name = attachmentName;
-
-			switch (input.readByte()) {
-				case AttachmentType.Region: {
-					let path = input.readStringRef();
-					let rotation = input.readFloat();
-					let x = input.readFloat();
-					let y = input.readFloat();
-					let scaleX = input.readFloat();
-					let scaleY = input.readFloat();
-					let width = input.readFloat();
-					let height = input.readFloat();
-					let color = input.readInt32();
-
-					if (!path) path = name;
-					let region = this.attachmentLoader.newRegionAttachment(skin, name, path);
-					if (!region) return null;
-					region.path = path;
-					region.x = x * scale;
-					region.y = y * scale;
-					region.scaleX = scaleX;
-					region.scaleY = scaleY;
-					region.rotation = rotation;
-					region.width = width * scale;
-					region.height = height * scale;
-					Color.rgba8888ToColor(region.color, color);
-					region.updateOffset();
-					return region;
-				}
-				case AttachmentType.BoundingBox: {
-					let vertexCount = input.readInt(true);
-					let vertices = this.readVertices(input, vertexCount);
-					let color = nonessential ? input.readInt32() : 0;
-
-					let box = this.attachmentLoader.newBoundingBoxAttachment(skin, name);
-					if (!box) return null;
-					box.worldVerticesLength = vertexCount << 1;
-					box.vertices = vertices.vertices;
-					box.bones = vertices.bones;
-					if (nonessential) Color.rgba8888ToColor(box.color, color);
-					return box;
-				}
-				case AttachmentType.Mesh: {
-					let path = input.readStringRef();
-					let color = input.readInt32();
-					let vertexCount = input.readInt(true);
-					let uvs = this.readFloatArray(input, vertexCount << 1, 1);
-					let triangles = this.readShortArray(input);
-					let vertices = this.readVertices(input, vertexCount);
-					let hullLength = input.readInt(true);
-					let edges = null;
-					let width = 0, height = 0;
-					if (nonessential) {
-						edges = this.readShortArray(input);
-						width = input.readFloat();
-						height = input.readFloat();
-					}
-
-					if (!path) path = name;
-					let mesh = this.attachmentLoader.newMeshAttachment(skin, name, path);
-					if (!mesh) return null;
-					mesh.path = path;
-					Color.rgba8888ToColor(mesh.color, color);
-					mesh.bones = vertices.bones;
-					mesh.vertices = vertices.vertices;
-					mesh.worldVerticesLength = vertexCount << 1;
-					mesh.triangles = triangles;
-					mesh.regionUVs = uvs;
-					mesh.updateUVs();
-					mesh.hullLength = hullLength << 1;
-					if (nonessential) {
-						mesh.edges = edges;
-						mesh.width = width * scale;
-						mesh.height = height * scale;
-					}
-					return mesh;
-				}
-				case AttachmentType.LinkedMesh: {
-					let path = input.readStringRef();
-					let color = input.readInt32();
-					let skinName = input.readStringRef();
-					let parent = input.readStringRef();
-					let inheritDeform = input.readBoolean();
-					let width = 0, height = 0;
-					if (nonessential) {
-						width = input.readFloat();
-						height = input.readFloat();
-					}
-
-					if (!path) path = name;
-					let mesh = this.attachmentLoader.newMeshAttachment(skin, name, path);
-					if (!mesh) return null;
-					mesh.path = path;
-					Color.rgba8888ToColor(mesh.color, color);
-					if (nonessential) {
-						mesh.width = width * scale;
-						mesh.height = height * scale;
-					}
-					this.linkedMeshes.push(new LinkedMesh(mesh, skinName, slotIndex, parent, inheritDeform));
-					return mesh;
-				}
-				case AttachmentType.Path: {
-					let closed = input.readBoolean();
-					let constantSpeed = input.readBoolean();
-					let vertexCount = input.readInt(true);
-					let vertices = this.readVertices(input, vertexCount);
-					let lengths = Utils.newArray(vertexCount / 3, 0);
-					for (let i = 0, n = lengths.length; i < n; i++)
-						lengths[i] = input.readFloat() * scale;
-					let color = nonessential ? input.readInt32() : 0;
-
-					let path = this.attachmentLoader.newPathAttachment(skin, name);
-					if (!path) return null;
-					path.closed = closed;
-					path.constantSpeed = constantSpeed;
-					path.worldVerticesLength = vertexCount << 1;
-					path.vertices = vertices.vertices;
-					path.bones = vertices.bones;
-					path.lengths = lengths;
-					if (nonessential) Color.rgba8888ToColor(path.color, color);
-					return path;
-				}
-				case AttachmentType.Point: {
-					let rotation = input.readFloat();
-					let x = input.readFloat();
-					let y = input.readFloat();
-					let color = nonessential ? input.readInt32() : 0;
-
-					let point = this.attachmentLoader.newPointAttachment(skin, name);
-					if (!point) return null;
-					point.x = x * scale;
-					point.y = y * scale;
-					point.rotation = rotation;
-					if (nonessential) Color.rgba8888ToColor(point.color, color);
-					return point;
-				}
-				case AttachmentType.Clipping: {
-					let endSlotIndex = input.readInt(true);
-					let vertexCount = input.readInt(true);
-					let vertices = this.readVertices(input, vertexCount);
-					let color = nonessential ? input.readInt32() : 0;
-
-					let clip = this.attachmentLoader.newClippingAttachment(skin, name);
-					if (!clip) return null;
-					clip.endSlot = skeletonData.slots[endSlotIndex];
-					clip.worldVerticesLength = vertexCount << 1;
-					clip.vertices = vertices.vertices;
-					clip.bones = vertices.bones;
-					if (nonessential) Color.rgba8888ToColor(clip.color, color);
-					return clip;
-				}
-			}
-			return null;
-		}
-
-		private readVertices (input: BinaryInput, vertexCount: number): Vertices {
-			let scale = this.scale;
-			let verticesLength = vertexCount << 1;
-			let vertices = new Vertices();
-			if (!input.readBoolean()) {
-				vertices.vertices = this.readFloatArray(input, verticesLength, scale);
-				return vertices;
-			}
-			let weights = new Array<number>();
-			let bonesArray = new Array<number>();
-			for (let i = 0; i < vertexCount; i++) {
-				let boneCount = input.readInt(true);
-				bonesArray.push(boneCount);
-				for (let ii = 0; ii < boneCount; ii++) {
-					bonesArray.push(input.readInt(true));
-					weights.push(input.readFloat() * scale);
-					weights.push(input.readFloat() * scale);
-					weights.push(input.readFloat());
-				}
-			}
-			vertices.vertices = Utils.toFloatArray(weights);
-			vertices.bones = bonesArray;
-			return vertices;
-		}
-
-		private readFloatArray (input: BinaryInput, n: number, scale: number): number[] {
-			let array = new Array<number>(n);
-			if (scale == 1) {
-				for (let i = 0; i < n; i++)
-					array[i] = input.readFloat();
-			} else {
-				for (let i = 0; i < n; i++)
-					array[i] = input.readFloat() * scale;
-			}
-			return array;
-		}
-
-		private readShortArray (input: BinaryInput): number[] {
-			let n = input.readInt(true);
-			let array = new Array<number>(n);
-			for (let i = 0; i < n; i++)
-				array[i] = input.readShort();
-			return array;
-		}
-
-		private readAnimation (input: BinaryInput, name: string, skeletonData: SkeletonData): Animation {
-			input.readInt(true); // Number of timelines.
-			let timelines = new Array<Timeline>();
-			let scale = this.scale;
-			let tempColor1 = new Color();
-			let tempColor2 = new Color();
-
-			// Slot timelines.
-			for (let i = 0, n = input.readInt(true); i < n; i++) {
-				let slotIndex = input.readInt(true);
-				for (let ii = 0, nn = input.readInt(true); ii < nn; ii++) {
-					let timelineType = input.readByte();
-					let frameCount = input.readInt(true);
-					let frameLast = frameCount - 1;
-					switch (timelineType) {
-						case SLOT_ATTACHMENT: {
-							let timeline = new AttachmentTimeline(frameCount, slotIndex);
-							for (let frame = 0; frame < frameCount; frame++)
-								timeline.setFrame(frame, input.readFloat(), input.readStringRef());
-							timelines.push(timeline);
-							break;
-						}
-						case SLOT_RGBA: {
-							let bezierCount = input.readInt(true);
-							let timeline = new RGBATimeline(frameCount, bezierCount, slotIndex);
-
-							let time = input.readFloat();
-							let r = input.readUnsignedByte() / 255.0;
-							let g = input.readUnsignedByte() / 255.0;
-							let b = input.readUnsignedByte() / 255.0;
-							let a = input.readUnsignedByte() / 255.0;
-
-							for (let frame = 0, bezier = 0; ; frame++) {
-								timeline.setFrame(frame, time, r, g, b, a);
-								if (frame == frameLast) break;
-
-								let time2 = input.readFloat();
-								let r2 = input.readUnsignedByte() / 255.0;
-								let g2 = input.readUnsignedByte() / 255.0;
-								let b2 = input.readUnsignedByte() / 255.0;
-								let a2 = input.readUnsignedByte() / 255.0;
-
-								switch (input.readByte()) {
-									case CURVE_STEPPED:
-										timeline.setStepped(frame);
-										break;
-									case CURVE_BEZIER:
-										setBezier(input, timeline, bezier++, frame, 0, time, time2, r, r2, 1);
-										setBezier(input, timeline, bezier++, frame, 1, time, time2, g, g2, 1);
-										setBezier(input, timeline, bezier++, frame, 2, time, time2, b, b2, 1);
-										setBezier(input, timeline, bezier++, frame, 3, time, time2, a, a2, 1);
-								}
-								time = time2;
-								r = r2;
-								g = g2;
-								b = b2;
-								a = a2;
-							}
-							timelines.push(timeline);
-							break;
-						}
-						case SLOT_RGB: {
-							let bezierCount = input.readInt(true);
-							let timeline = new RGBTimeline(frameCount, bezierCount, slotIndex);
-
-							let time = input.readFloat();
-							let r = input.readUnsignedByte() / 255.0;
-							let g = input.readUnsignedByte() / 255.0;
-							let b = input.readUnsignedByte() / 255.0;
-
-							for (let frame = 0, bezier = 0; ; frame++) {
-								timeline.setFrame(frame, time, r, g, b);
-								if (frame == frameLast) break;
-
-								let time2 = input.readFloat();
-								let r2 = input.readUnsignedByte() / 255.0;
-								let g2 = input.readUnsignedByte() / 255.0;
-								let b2 = input.readUnsignedByte() / 255.0;
-
-								switch (input.readByte()) {
-									case CURVE_STEPPED:
-										timeline.setStepped(frame);
-										break;
-									case CURVE_BEZIER:
-										setBezier(input, timeline, bezier++, frame, 0, time, time2, r, r2, 1);
-										setBezier(input, timeline, bezier++, frame, 1, time, time2, g, g2, 1);
-										setBezier(input, timeline, bezier++, frame, 2, time, time2, b, b2, 1);
-								}
-								time = time2;
-								r = r2;
-								g = g2;
-								b = b2;
-							}
-							timelines.push(timeline);
-							break;
-						}
-						case SLOT_RGBA2: {
-							let bezierCount = input.readInt(true);
-							let timeline = new RGBA2Timeline(frameCount, bezierCount, slotIndex);
-
-							let time = input.readFloat();
-							let r = input.readUnsignedByte() / 255.0;
-							let g = input.readUnsignedByte() / 255.0;
-							let b = input.readUnsignedByte() / 255.0;
-							let a = input.readUnsignedByte() / 255.0;
-							let r2 = input.readUnsignedByte() / 255.0;
-							let g2 = input.readUnsignedByte() / 255.0;
-							let b2 = input.readUnsignedByte() / 255.0;
-
-							for (let frame = 0, bezier = 0; ; frame++) {
-								timeline.setFrame(frame, time, r, g, b, a, r2, g2, b2);
-								if (frame == frameLast) break;
-								let time2 = input.readFloat();
-								let nr = input.readUnsignedByte() / 255.0;
-								let ng = input.readUnsignedByte() / 255.0;
-								let nb = input.readUnsignedByte() / 255.0;
-								let na = input.readUnsignedByte() / 255.0;
-								let nr2 = input.readUnsignedByte() / 255.0;
-								let ng2 = input.readUnsignedByte() / 255.0;
-								let nb2 = input.readUnsignedByte() / 255.0;
-
-								switch (input.readByte()) {
-									case CURVE_STEPPED:
-										timeline.setStepped(frame);
-										break;
-									case CURVE_BEZIER:
-										setBezier(input, timeline, bezier++, frame, 0, time, time2, r, nr, 1);
-										setBezier(input, timeline, bezier++, frame, 1, time, time2, g, ng, 1);
-										setBezier(input, timeline, bezier++, frame, 2, time, time2, b, nb, 1);
-										setBezier(input, timeline, bezier++, frame, 3, time, time2, a, na, 1);
-										setBezier(input, timeline, bezier++, frame, 4, time, time2, r2, nr2, 1);
-										setBezier(input, timeline, bezier++, frame, 5, time, time2, g2, ng2, 1);
-										setBezier(input, timeline, bezier++, frame, 6, time, time2, b2, nb2, 1);
-								}
-								time = time2;
-								r = nr;
-								g = ng;
-								b = nb;
-								a = na;
-								r2 = nr2;
-								g2 = ng2;
-								b2 = nb2;
-							}
-							timelines.push(timeline);
-							break;
-						}
-						case SLOT_RGB2: {
-							let bezierCount = input.readInt(true);
-							let timeline = new RGB2Timeline(frameCount, bezierCount, slotIndex);
-
-							let time = input.readFloat();
-							let r = input.readUnsignedByte() / 255.0;
-							let g = input.readUnsignedByte() / 255.0;
-							let b = input.readUnsignedByte() / 255.0;
-							let r2 = input.readUnsignedByte() / 255.0;
-							let g2 = input.readUnsignedByte() / 255.0;
-							let b2 = input.readUnsignedByte() / 255.0;
-
-							for (let frame = 0, bezier = 0; ; frame++) {
-								timeline.setFrame(frame, time, r, g, b, r2, g2, b2);
-								if (frame == frameLast) break;
-								let time2 = input.readFloat();
-								let nr = input.readUnsignedByte() / 255.0;
-								let ng = input.readUnsignedByte() / 255.0;
-								let nb = input.readUnsignedByte() / 255.0;
-								let nr2 = input.readUnsignedByte() / 255.0;
-								let ng2 = input.readUnsignedByte() / 255.0;
-								let nb2 = input.readUnsignedByte() / 255.0;
-
-								switch (input.readByte()) {
-									case CURVE_STEPPED:
-										timeline.setStepped(frame);
-										break;
-									case CURVE_BEZIER:
-										setBezier(input, timeline, bezier++, frame, 0, time, time2, r, nr, 1);
-										setBezier(input, timeline, bezier++, frame, 1, time, time2, g, ng, 1);
-										setBezier(input, timeline, bezier++, frame, 2, time, time2, b, nb, 1);
-										setBezier(input, timeline, bezier++, frame, 3, time, time2, r2, nr2, 1);
-										setBezier(input, timeline, bezier++, frame, 4, time, time2, g2, ng2, 1);
-										setBezier(input, timeline, bezier++, frame, 5, time, time2, b2, nb2, 1);
-								}
-								time = time2;
-								r = nr;
-								g = ng;
-								b = nb;
-								r2 = nr2;
-								g2 = ng2;
-								b2 = nb2;
-							}
-							timelines.push(timeline);
-							break;
-						}
-						case SLOT_ALPHA: {
-							let timeline = new AlphaTimeline(frameCount, input.readInt(true), slotIndex);
-							let time = input.readFloat(), a = input.readUnsignedByte() / 255;
-							for (let frame = 0, bezier = 0; ; frame++) {
-								timeline.setFrame(frame, time, a);
-								if (frame == frameLast) break;
-								let time2 = input.readFloat();
-								let a2 = input.readUnsignedByte() / 255;
-								switch (input.readByte()) {
-									case CURVE_STEPPED:
-										timeline.setStepped(frame);
-										break;
-									case CURVE_BEZIER:
-										setBezier(input, timeline, bezier++, frame, 0, time, time2, a, a2, 1);
-								}
-								time = time2;
-								a = a2;
-							}
-							timelines.push(timeline);
-							break;
-						}
-					}
-				}
-			}
-
-			// Bone timelines.
-			for (let i = 0, n = input.readInt(true); i < n; i++) {
-				let boneIndex = input.readInt(true);
-				for (let ii = 0, nn = input.readInt(true); ii < nn; ii++) {
-					let type = input.readByte(), frameCount = input.readInt(true), bezierCount = input.readInt(true);
-					switch (type) {
-						case BONE_ROTATE:
-							timelines.push(readTimeline1(input, new RotateTimeline(frameCount, bezierCount, boneIndex), 1));
-							break;
-						case BONE_TRANSLATE:
-							timelines.push(readTimeline2(input, new TranslateTimeline(frameCount, bezierCount, boneIndex), scale));
-							break;
-						case BONE_TRANSLATEX:
-							timelines.push(readTimeline1(input, new TranslateXTimeline(frameCount, bezierCount, boneIndex), scale));
-							break;
-						case BONE_TRANSLATEY:
-							timelines.push(readTimeline1(input, new TranslateYTimeline(frameCount, bezierCount, boneIndex), scale));
-							break;
-						case BONE_SCALE:
-							timelines.push(readTimeline2(input, new ScaleTimeline(frameCount, bezierCount, boneIndex), 1));
-							break;
-						case BONE_SCALEX:
-							timelines.push(readTimeline1(input, new ScaleXTimeline(frameCount, bezierCount, boneIndex), 1));
-							break;
-						case BONE_SCALEY:
-							timelines.push(readTimeline1(input, new ScaleYTimeline(frameCount, bezierCount, boneIndex), 1));
-							break;
-						case BONE_SHEAR:
-							timelines.push(readTimeline2(input, new ShearTimeline(frameCount, bezierCount, boneIndex), 1));
-							break;
-						case BONE_SHEARX:
-							timelines.push(readTimeline1(input, new ShearXTimeline(frameCount, bezierCount, boneIndex), 1));
-							break;
-						case BONE_SHEARY:
-							timelines.push(readTimeline1(input, new ShearYTimeline(frameCount, bezierCount, boneIndex), 1));
-					}
-				}
-			}
-
-			// IK constraint timelines.
-			for (let i = 0, n = input.readInt(true); i < n; i++) {
-				let index = input.readInt(true), frameCount = input.readInt(true), frameLast = frameCount - 1;
-				let timeline = new IkConstraintTimeline(frameCount, input.readInt(true), index);
-				let time = input.readFloat(), mix = input.readFloat(), softness = input.readFloat() * scale;
-				for (let frame = 0, bezier = 0; ; frame++) {
-					timeline.setFrame(frame, time, mix, softness, input.readByte(), input.readBoolean(), input.readBoolean());
-					if (frame == frameLast) break;
-					let time2 = input.readFloat(), mix2 = input.readFloat(), softness2 = input.readFloat() * scale;
-					switch (input.readByte()) {
-						case CURVE_STEPPED:
-							timeline.setStepped(frame);
-							break;
-						case CURVE_BEZIER:
-							setBezier(input, timeline, bezier++, frame, 0, time, time2, mix, mix2, 1);
-							setBezier(input, timeline, bezier++, frame, 1, time, time2, softness, softness2, scale);
-					}
-					time = time2;
-					mix = mix2;
-					softness = softness2;
-				}
-				timelines.push(timeline);
-			}
-
-			// Transform constraint timelines.
-			for (let i = 0, n = input.readInt(true); i < n; i++) {
-				let index = input.readInt(true), frameCount = input.readInt(true), frameLast = frameCount - 1;
-				let timeline = new TransformConstraintTimeline(frameCount, input.readInt(true), index);
-				let time = input.readFloat(), mixRotate = input.readFloat(), mixX = input.readFloat(), mixY = input.readFloat(),
-					mixScaleX = input.readFloat(), mixScaleY = input.readFloat(), mixShearY = input.readFloat();
-				for (let frame = 0, bezier = 0; ; frame++) {
-					timeline.setFrame(frame, time, mixRotate, mixX, mixY, mixScaleX, mixScaleY, mixShearY);
-					if (frame == frameLast) break;
-					let time2 = input.readFloat(), mixRotate2 = input.readFloat(), mixX2 = input.readFloat(), mixY2 = input.readFloat(),
-						mixScaleX2 = input.readFloat(), mixScaleY2 = input.readFloat(), mixShearY2 = input.readFloat();
-					switch (input.readByte()) {
-						case CURVE_STEPPED:
-							timeline.setStepped(frame);
-							break;
-						case CURVE_BEZIER:
-							setBezier(input, timeline, bezier++, frame, 0, time, time2, mixRotate, mixRotate2, 1);
-							setBezier(input, timeline, bezier++, frame, 1, time, time2, mixX, mixX2, 1);
-							setBezier(input, timeline, bezier++, frame, 2, time, time2, mixY, mixY2, 1);
-							setBezier(input, timeline, bezier++, frame, 3, time, time2, mixScaleX, mixScaleX2, 1);
-							setBezier(input, timeline, bezier++, frame, 4, time, time2, mixScaleY, mixScaleY2, 1);
-							setBezier(input, timeline, bezier++, frame, 5, time, time2, mixShearY, mixShearY2, 1);
-					}
-					time = time2;
-					mixRotate = mixRotate2;
-					mixX = mixX2;
-					mixY = mixY2;
-					mixScaleX = mixScaleX2;
-					mixScaleY = mixScaleY2;
-					mixShearY = mixShearY2;
-				}
-				timelines.push(timeline);
-			}
-
-			// Path constraint timelines.
-			for (let i = 0, n = input.readInt(true); i < n; i++) {
-				let index = input.readInt(true);
-				let data = skeletonData.pathConstraints[index];
-				for (let ii = 0, nn = input.readInt(true); ii < nn; ii++) {
-					switch (input.readByte()) {
-						case PATH_POSITION:
-							timelines
-								.push(readTimeline1(input, new PathConstraintPositionTimeline(input.readInt(true), input.readInt(true), index),
-									data.positionMode == PositionMode.Fixed ? scale : 1));
-							break;
-						case PATH_SPACING:
-							timelines
-								.push(readTimeline1(input, new PathConstraintSpacingTimeline(input.readInt(true), input.readInt(true), index),
-									data.spacingMode == SpacingMode.Length || data.spacingMode == SpacingMode.Fixed ? scale : 1));
-							break;
-						case PATH_MIX:
-							let timeline = new PathConstraintMixTimeline(input.readInt(true), input.readInt(true), index);
-							let time = input.readFloat(), mixRotate = input.readFloat(), mixX = input.readFloat(), mixY = input.readFloat();
-							for (let frame = 0, bezier = 0, frameLast = timeline.getFrameCount() - 1; ; frame++) {
-								timeline.setFrame(frame, time, mixRotate, mixX, mixY);
-								if (frame == frameLast) break;
-								let time2 = input.readFloat(), mixRotate2 = input.readFloat(), mixX2 = input.readFloat(),
-									mixY2 = input.readFloat();
-								switch (input.readByte()) {
-									case CURVE_STEPPED:
-										timeline.setStepped(frame);
-										break;
-									case CURVE_BEZIER:
-										setBezier(input, timeline, bezier++, frame, 0, time, time2, mixRotate, mixRotate2, 1);
-										setBezier(input, timeline, bezier++, frame, 1, time, time2, mixX, mixX2, 1);
-										setBezier(input, timeline, bezier++, frame, 2, time, time2, mixY, mixY2, 1);
-								}
-								time = time2;
-								mixRotate = mixRotate2;
-								mixX = mixX2;
-								mixY = mixY2;
-							}
-							timelines.push(timeline);
-					}
-				}
-			}
-
-			// Deform timelines.
-			for (let i = 0, n = input.readInt(true); i < n; i++) {
-				let skin = skeletonData.skins[input.readInt(true)];
-				for (let ii = 0, nn = input.readInt(true); ii < nn; ii++) {
-					let slotIndex = input.readInt(true);
-					for (let iii = 0, nnn = input.readInt(true); iii < nnn; iii++) {
-						let attachmentName = input.readStringRef();
-						let attachment = skin.getAttachment(slotIndex, attachmentName) as VertexAttachment;
-						let weighted = attachment.bones;
-						let vertices = attachment.vertices;
-						let deformLength = weighted ? vertices.length / 3 * 2 : vertices.length;
-
-						let frameCount = input.readInt(true);
-						let frameLast = frameCount - 1;
-						let bezierCount = input.readInt(true);
-						let timeline = new DeformTimeline(frameCount, bezierCount, slotIndex, attachment);
-
-						let time = input.readFloat();
-						for (let frame = 0, bezier = 0; ; frame++) {
-							let deform;
-							let end = input.readInt(true);
-							if (end == 0)
-								deform = weighted ? Utils.newFloatArray(deformLength) : vertices;
-							else {
-								deform = Utils.newFloatArray(deformLength);
-								let start = input.readInt(true);
-								end += start;
-								if (scale == 1) {
-									for (let v = start; v < end; v++)
-										deform[v] = input.readFloat();
-								} else {
-									for (let v = start; v < end; v++)
-										deform[v] = input.readFloat() * scale;
-								}
-								if (!weighted) {
-									for (let v = 0, vn = deform.length; v < vn; v++)
-										deform[v] += vertices[v];
-								}
-							}
-
-							timeline.setFrame(frame, time, deform);
-							if (frame == frameLast) break;
-							let time2 = input.readFloat();
-							switch (input.readByte()) {
-								case CURVE_STEPPED:
-									timeline.setStepped(frame);
-									break;
-								case CURVE_BEZIER:
-									setBezier(input, timeline, bezier++, frame, 0, time, time2, 0, 1, 1);
-							}
-							time = time2;
-						}
-						timelines.push(timeline);
-					}
-				}
-			}
-
-			// Draw order timeline.
-			let drawOrderCount = input.readInt(true);
-			if (drawOrderCount > 0) {
-				let timeline = new DrawOrderTimeline(drawOrderCount);
-				let slotCount = skeletonData.slots.length;
-				for (let i = 0; i < drawOrderCount; i++) {
-					let time = input.readFloat();
-					let offsetCount = input.readInt(true);
-					let drawOrder = Utils.newArray(slotCount, 0);
-					for (let ii = slotCount - 1; ii >= 0; ii--)
-						drawOrder[ii] = -1;
-					let unchanged = Utils.newArray(slotCount - offsetCount, 0);
-					let originalIndex = 0, unchangedIndex = 0;
-					for (let ii = 0; ii < offsetCount; ii++) {
-						let slotIndex = input.readInt(true);
-						// Collect unchanged items.
-						while (originalIndex != slotIndex)
-							unchanged[unchangedIndex++] = originalIndex++;
-						// Set changed items.
-						drawOrder[originalIndex + input.readInt(true)] = originalIndex++;
-					}
-					// Collect remaining unchanged items.
-					while (originalIndex < slotCount)
-						unchanged[unchangedIndex++] = originalIndex++;
-					// Fill in unchanged items.
-					for (let ii = slotCount - 1; ii >= 0; ii--)
-						if (drawOrder[ii] == -1) drawOrder[ii] = unchanged[--unchangedIndex];
-					timeline.setFrame(i, time, drawOrder);
-				}
-				timelines.push(timeline);
-			}
-
-			// Event timeline.
-			let eventCount = input.readInt(true);
-			if (eventCount > 0) {
-				let timeline = new EventTimeline(eventCount);
-				for (let i = 0; i < eventCount; i++) {
-					let time = input.readFloat();
-					let eventData = skeletonData.events[input.readInt(true)];
-					let event = new Event(time, eventData);
-					event.intValue = input.readInt(false);
-					event.floatValue = input.readFloat();
-					event.stringValue = input.readBoolean() ? input.readString() : eventData.stringValue;
-					if (event.data.audioPath) {
-						event.volume = input.readFloat();
-						event.balance = input.readFloat();
-					}
-					timeline.setFrame(i, event);
-				}
-				timelines.push(timeline);
-			}
-
-			let duration = 0;
-			for (let i = 0, n = timelines.length; i < n; i++)
-				duration = Math.max(duration, timelines[i].getDuration());
-			return new Animation(name, timelines, duration);
-		}
-	}
-
-	class BinaryInput {
-		constructor (data: Uint8Array, public strings = new Array<string>(), private index: number = 0, private buffer = new DataView(data.buffer)) {
-		}
-
-		readByte (): number {
-			return this.buffer.getInt8(this.index++);
-		}
-
-		readUnsignedByte (): number {
-			return this.buffer.getUint8(this.index++);
-		}
-
-		readShort (): number {
-			let value = this.buffer.getInt16(this.index);
-			this.index += 2;
-			return value;
-		}
-
-		readInt32 (): number {
-			let value = this.buffer.getInt32(this.index)
-			this.index += 4;
-			return value;
-		}
-
-		readInt (optimizePositive: boolean) {
-			let b = this.readByte();
-			let result = b & 0x7F;
-			if ((b & 0x80) != 0) {
-				b = this.readByte();
-				result |= (b & 0x7F) << 7;
-				if ((b & 0x80) != 0) {
-					b = this.readByte();
-					result |= (b & 0x7F) << 14;
-					if ((b & 0x80) != 0) {
-						b = this.readByte();
-						result |= (b & 0x7F) << 21;
-						if ((b & 0x80) != 0) {
-							b = this.readByte();
-							result |= (b & 0x7F) << 28;
-						}
-					}
-				}
-			}
-			return optimizePositive ? result : ((result >>> 1) ^ -(result & 1));
-		}
-
-		readStringRef (): string {
-			let index = this.readInt(true);
-			return index == 0 ? null : this.strings[index - 1];
-		}
-
-		readString (): string {
-			let byteCount = this.readInt(true);
-			switch (byteCount) {
-				case 0:
-					return null;
-				case 1:
-					return "";
-			}
-			byteCount--;
-			let chars = "";
-			let charCount = 0;
-			for (let i = 0; i < byteCount;) {
-				let b = this.readByte();
-				switch (b >> 4) {
-					case 12:
-					case 13:
-						chars += String.fromCharCode(((b & 0x1F) << 6 | this.readByte() & 0x3F));
-						i += 2;
-						break;
-					case 14:
-						chars += String.fromCharCode(((b & 0x0F) << 12 | (this.readByte() & 0x3F) << 6 | this.readByte() & 0x3F));
-						i += 3;
-						break;
-					default:
-						chars += String.fromCharCode(b);
-						i++;
-				}
-			}
-			return chars;
-		}
-
-		readFloat (): number {
-			let value = this.buffer.getFloat32(this.index);
-			this.index += 4;
-			return value;
-		}
-
-		readBoolean (): boolean {
-			return this.readByte() != 0;
-		}
-	}
-
-	class LinkedMesh {
-		parent: string; skin: string;
-		slotIndex: number;
-		mesh: MeshAttachment;
-		inheritDeform: boolean;
-
-		constructor (mesh: MeshAttachment, skin: string, slotIndex: number, parent: string, inheritDeform: boolean) {
-			this.mesh = mesh;
-			this.skin = skin;
-			this.slotIndex = slotIndex;
-			this.parent = parent;
-			this.inheritDeform = inheritDeform;
-		}
-	}
-
-	class Vertices {
-		constructor (public bones: Array<number> = null, public vertices: Array<number> | Float32Array = null) { }
-	}
-
-	enum AttachmentType { Region, BoundingBox, Mesh, LinkedMesh, Path, Point, Clipping }
-
-	function readTimeline1 (input: BinaryInput, timeline: CurveTimeline1, scale: number): CurveTimeline1 {
-		let time = input.readFloat(), value = input.readFloat() * scale;
-		for (let frame = 0, bezier = 0, frameLast = timeline.getFrameCount() - 1; ; frame++) {
-			timeline.setFrame(frame, time, value);
-			if (frame == frameLast) break;
-			let time2 = input.readFloat(), value2 = input.readFloat() * scale;
-			switch (input.readByte()) {
-				case CURVE_STEPPED:
-					timeline.setStepped(frame);
-					break;
-				case CURVE_BEZIER:
-					setBezier(input, timeline, bezier++, frame, 0, time, time2, value, value2, scale);
-			}
-			time = time2;
-			value = value2;
-		}
-		return timeline;
-	}
-
-	function readTimeline2 (input: BinaryInput, timeline: CurveTimeline2, scale: number): CurveTimeline2 {
-		let time = input.readFloat(), value1 = input.readFloat() * scale, value2 = input.readFloat() * scale;
-		for (let frame = 0, bezier = 0, frameLast = timeline.getFrameCount() - 1; ; frame++) {
-			timeline.setFrame(frame, time, value1, value2);
-			if (frame == frameLast) break;
-			let time2 = input.readFloat(), nvalue1 = input.readFloat() * scale, nvalue2 = input.readFloat() * scale;
-			switch (input.readByte()) {
-				case CURVE_STEPPED:
-					timeline.setStepped(frame);
-					break;
-				case CURVE_BEZIER:
-					setBezier(input, timeline, bezier++, frame, 0, time, time2, value1, nvalue1, scale);
-					setBezier(input, timeline, bezier++, frame, 1, time, time2, value2, nvalue2, scale);
-			}
-			time = time2;
-			value1 = nvalue1;
-			value2 = nvalue2;
-		}
-		return timeline;
-	}
-
-	function setBezier (input: BinaryInput, timeline: CurveTimeline, bezier: number, frame: number, value: number,
-		time1: number, time2: number, value1: number, value2: number, scale: number) {
-		timeline.setBezier(bezier, frame, value, time1, value1, input.readFloat(), input.readFloat() * scale, input.readFloat(), input.readFloat() * scale, time2, value2);
-	}
-
-	const BONE_ROTATE = 0;
-	const BONE_TRANSLATE = 1;
-	const BONE_TRANSLATEX = 2;
-	const BONE_TRANSLATEY = 3;
-	const BONE_SCALE = 4;
-	const BONE_SCALEX = 5;
-	const BONE_SCALEY = 6;
-	const BONE_SHEAR = 7;
-	const BONE_SHEARX = 8;
-	const BONE_SHEARY = 9;
-
-	const SLOT_ATTACHMENT = 0;
-	const SLOT_RGBA = 1;
-	const SLOT_RGB = 2;
-	const SLOT_RGBA2 = 3;
-	const SLOT_RGB2 = 4;
-	const SLOT_ALPHA = 5;
-
-	const PATH_POSITION = 0;
-	const PATH_SPACING = 1;
-	const PATH_MIX = 2;
-
-	const CURVE_LINEAR = 0;
-	const CURVE_STEPPED = 1;
-	const CURVE_BEZIER = 2;
-}

+ 0 - 231
spine-ts/core/src/SkeletonBounds.ts

@@ -1,231 +0,0 @@
-/******************************************************************************
- * Spine Runtimes License Agreement
- * Last updated January 1, 2020. Replaces all prior versions.
- *
- * Copyright (c) 2013-2020, Esoteric Software LLC
- *
- * Integration of the Spine Runtimes into software or otherwise creating
- * derivative works of the Spine Runtimes is permitted under the terms and
- * conditions of Section 2 of the Spine Editor License Agreement:
- * http://esotericsoftware.com/spine-editor-license
- *
- * Otherwise, it is permitted to integrate the Spine Runtimes into software
- * or otherwise create derivative works of the Spine Runtimes (collectively,
- * "Products"), provided that each user of the Products must obtain their own
- * Spine Editor license and redistribution of the Products in any form must
- * include this license and copyright notice.
- *
- * THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "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 LLC 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
- * THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *****************************************************************************/
-
-module spine {
-	/** Collects each visible {@link BoundingBoxAttachment} and computes the world vertices for its polygon. The polygon vertices are
-	 * provided along with convenience methods for doing hit detection. */
-	export class SkeletonBounds {
-
-		/** The left edge of the axis aligned bounding box. */
-		minX = 0;
-
-		/** The bottom edge of the axis aligned bounding box. */
-		minY = 0;
-
-		/** The right edge of the axis aligned bounding box. */
-		maxX = 0;
-
-		/** The top edge of the axis aligned bounding box. */
-		maxY = 0;
-
-		/** The visible bounding boxes. */
-		boundingBoxes = new Array<BoundingBoxAttachment>();
-
-		/** The world vertices for the bounding box polygons. */
-		polygons = new Array<ArrayLike<number>>();
-
-		private polygonPool = new Pool<ArrayLike<number>>(() => {
-			return Utils.newFloatArray(16);
-		});
-
-		/** Clears any previous polygons, finds all visible bounding box attachments, and computes the world vertices for each bounding
-		 * box's polygon.
-		 * @param updateAabb If true, the axis aligned bounding box containing all the polygons is computed. If false, the
-		 *           SkeletonBounds AABB methods will always return true. */
-		update (skeleton: Skeleton, updateAabb: boolean) {
-			if (!skeleton) 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];
-				if (!slot.bone.active) continue;
-				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()} 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) throw new Error("boundingBox cannot be null.");
-			let index = this.boundingBoxes.indexOf(boundingBox);
-			return index == -1 ? null : this.polygons[index];
-		}
-
-		/** The width of the axis aligned bounding box. */
-		getWidth () {
-			return this.maxX - this.minX;
-		}
-
-		/** The height of the axis aligned bounding box. */
-		getHeight () {
-			return this.maxY - this.minY;
-		}
-	}
-
-}

+ 0 - 347
spine-ts/core/src/SkeletonClipping.ts

@@ -1,347 +0,0 @@
-/******************************************************************************
- * Spine Runtimes License Agreement
- * Last updated January 1, 2020. Replaces all prior versions.
- *
- * Copyright (c) 2013-2020, Esoteric Software LLC
- *
- * Integration of the Spine Runtimes into software or otherwise creating
- * derivative works of the Spine Runtimes is permitted under the terms and
- * conditions of Section 2 of the Spine Editor License Agreement:
- * http://esotericsoftware.com/spine-editor-license
- *
- * Otherwise, it is permitted to integrate the Spine Runtimes into software
- * or otherwise create derivative works of the Spine Runtimes (collectively,
- * "Products"), provided that each user of the Products must obtain their own
- * Spine Editor license and redistribution of the Products in any form must
- * include this license and copyright notice.
- *
- * THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "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 LLC 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
- * THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *****************************************************************************/
-
-module spine {
-	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) 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 && this.clipAttachment.endSlot == slot.data) this.clipEnd();
-		}
-
-		clipEnd () {
-			if (!this.clipAttachment) 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 s = c0 * (edgeX2 - edgeX) - c2 * (edgeY2 - edgeY);
-						if (Math.abs(s) > 0.000001) {
-							let ua = (c2 * (edgeY - inputY) - c0 * (edgeX - inputX)) / s;
-							output.push(edgeX + (edgeX2 - edgeX) * ua);
-							output.push(edgeY + (edgeY2 - edgeY) * ua);
-						} else {
-							output.push(edgeX);
-							output.push(edgeY);
-						}
-					} else if (side2) { // v1 outside, v2 inside
-						let c0 = inputY2 - inputY, c2 = inputX2 - inputX;
-						let s = c0 * (edgeX2 - edgeX) - c2 * (edgeY2 - edgeY);
-						if (Math.abs(s) > 0.000001) {
-							let ua = (c2 * (edgeY - inputY) - c0 * (edgeX - inputX)) / s;
-							output.push(edgeX + (edgeX2 - edgeX) * ua);
-							output.push(edgeY + (edgeY2 - edgeY) * ua);
-						} else {
-							output.push(edgeX);
-							output.push(edgeY);
-						}
-						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;
-			}
-		}
-	}
-}

+ 0 - 216
spine-ts/core/src/SkeletonData.ts

@@ -1,216 +0,0 @@
-/******************************************************************************
- * Spine Runtimes License Agreement
- * Last updated January 1, 2020. Replaces all prior versions.
- *
- * Copyright (c) 2013-2020, Esoteric Software LLC
- *
- * Integration of the Spine Runtimes into software or otherwise creating
- * derivative works of the Spine Runtimes is permitted under the terms and
- * conditions of Section 2 of the Spine Editor License Agreement:
- * http://esotericsoftware.com/spine-editor-license
- *
- * Otherwise, it is permitted to integrate the Spine Runtimes into software
- * or otherwise create derivative works of the Spine Runtimes (collectively,
- * "Products"), provided that each user of the Products must obtain their own
- * Spine Editor license and redistribution of the Products in any form must
- * include this license and copyright notice.
- *
- * THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "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 LLC 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
- * THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *****************************************************************************/
-
-module spine {
-	/** Stores the setup pose and all of the stateless data for a skeleton.
-	 *
-	 * See [Data objects](http://esotericsoftware.com/spine-runtime-architecture#Data-objects) in the Spine Runtimes
-	 * Guide. */
-	export class SkeletonData {
-
-		/** The skeleton's name, which by default is the name of the skeleton data file, if possible. May be null. */
-		name: string;
-
-		/** The skeleton's bones, sorted parent first. The root bone is always the first bone. */
-		bones = new Array<BoneData>(); // Ordered parents first.
-
-		/** The skeleton's slots. */
-		slots = new Array<SlotData>(); // Setup pose draw order.
-		skins = new Array<Skin>();
-
-		/** The skeleton's default skin. By default this skin contains all attachments that were not in a skin in Spine.
-		 *
-		 * See {@link Skeleton#getAttachmentByName()}.
-		 * May be null. */
-		defaultSkin: Skin;
-
-		/** The skeleton's events. */
-		events = new Array<EventData>();
-
-		/** The skeleton's animations. */
-		animations = new Array<Animation>();
-
-		/** The skeleton's IK constraints. */
-		ikConstraints = new Array<IkConstraintData>();
-
-		/** The skeleton's transform constraints. */
-		transformConstraints = new Array<TransformConstraintData>();
-
-		/** The skeleton's path constraints. */
-		pathConstraints = new Array<PathConstraintData>();
-
-		/** The X coordinate of the skeleton's axis aligned bounding box in the setup pose. */
-		x: number;
-
-		/** The Y coordinate of the skeleton's axis aligned bounding box in the setup pose. */
-		y: number;
-
-		/** The width of the skeleton's axis aligned bounding box in the setup pose. */
-		width: number;
-
-		/** The height of the skeleton's axis aligned bounding box in the setup pose. */
-		height: number;
-
-		/** The Spine version used to export the skeleton data, or null. */
-		version: string;
-
-		/** The skeleton data hash. This value will change if any of the skeleton data has changed. May be null. */
-		hash: string;
-
-		// Nonessential
-		/** The dopesheet FPS in Spine. Available only when nonessential data was exported. */
-		fps = 0;
-
-		/** The path to the images directory as defined in Spine. Available only when nonessential data was exported. May be null. */
-		imagesPath: string;
-
-		/** The path to the audio directory as defined in Spine. Available only when nonessential data was exported. May be null. */
-		audioPath: string;
-
-		/** Finds a bone by comparing each bone's name. It is more efficient to cache the results of this method than to call it
-		 * multiple times.
-		 * @returns May be null. */
-		findBone (boneName: string) {
-			if (!boneName) 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) 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;
-		}
-
-		/** Finds a slot by comparing each slot's name. It is more efficient to cache the results of this method than to call it
-		 * multiple times.
-		 * @returns May be null. */
-		findSlot (slotName: string) {
-			if (!slotName) 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) 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;
-		}
-
-		/** Finds a skin by comparing each skin's name. It is more efficient to cache the results of this method than to call it
-		 * multiple times.
-		 * @returns May be null. */
-		findSkin (skinName: string) {
-			if (!skinName) 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;
-		}
-
-		/** Finds an event by comparing each events's name. It is more efficient to cache the results of this method than to call it
-		 * multiple times.
-		 * @returns May be null. */
-		findEvent (eventDataName: string) {
-			if (!eventDataName) 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;
-		}
-
-		/** Finds an animation by comparing each animation's name. It is more efficient to cache the results of this method than to
-		 * call it multiple times.
-		 * @returns May be null. */
-		findAnimation (animationName: string) {
-			if (!animationName) 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;
-		}
-
-		/** Finds an IK constraint by comparing each IK constraint's name. It is more efficient to cache the results of this method
-		 * than to call it multiple times.
-		 * @return May be null. */
-		findIkConstraint (constraintName: string) {
-			if (!constraintName) 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;
-		}
-
-		/** Finds a transform constraint by comparing each transform constraint's name. It is more efficient to cache the results of
-		 * this method than to call it multiple times.
-		 * @return May be null. */
-		findTransformConstraint (constraintName: string) {
-			if (!constraintName) 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;
-		}
-
-		/** Finds a path constraint by comparing each path constraint's name. It is more efficient to cache the results of this method
-		 * than to call it multiple times.
-		 * @return May be null. */
-		findPathConstraint (constraintName: string) {
-			if (!constraintName) 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;
-		}
-	}
-}

+ 0 - 965
spine-ts/core/src/SkeletonJson.ts

@@ -1,965 +0,0 @@
-/******************************************************************************
- * Spine Runtimes License Agreement
- * Last updated January 1, 2020. Replaces all prior versions.
- *
- * Copyright (c) 2013-2020, Esoteric Software LLC
- *
- * Integration of the Spine Runtimes into software or otherwise creating
- * derivative works of the Spine Runtimes is permitted under the terms and
- * conditions of Section 2 of the Spine Editor License Agreement:
- * http://esotericsoftware.com/spine-editor-license
- *
- * Otherwise, it is permitted to integrate the Spine Runtimes into software
- * or otherwise create derivative works of the Spine Runtimes (collectively,
- * "Products"), provided that each user of the Products must obtain their own
- * Spine Editor license and redistribution of the Products in any form must
- * include this license and copyright notice.
- *
- * THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "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 LLC 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
- * THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *****************************************************************************/
-
-module spine {
-
-	/** Loads skeleton data in the Spine JSON format.
-	 *
-	 * See [Spine JSON format](http://esotericsoftware.com/spine-json-format) and
-	 * [JSON and binary data](http://esotericsoftware.com/spine-loading-skeleton-data#JSON-and-binary-data) in the Spine
-	 * Runtimes Guide. */
-	export class SkeletonJson {
-		attachmentLoader: AttachmentLoader;
-
-		/** Scales bone positions, image sizes, and translations as they are loaded. This allows different size images to be used at
-		 * runtime than were used in Spine.
-		 *
-		 * See [Scaling](http://esotericsoftware.com/spine-loading-skeleton-data#Scaling) in the Spine Runtimes Guide. */
-		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) {
-				skeletonData.hash = skeletonMap.hash;
-				skeletonData.version = skeletonMap.spine;
-				skeletonData.x = skeletonMap.x;
-				skeletonData.y = skeletonMap.y;
-				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 = getValue(boneMap, "parent", null);
-					if (parentName) parent = skeletonData.findBone(parentName);
-					let data = new BoneData(skeletonData.bones.length, boneMap.name, parent);
-					data.length = getValue(boneMap, "length", 0) * scale;
-					data.x = getValue(boneMap, "x", 0) * scale;
-					data.y = getValue(boneMap, "y", 0) * scale;
-					data.rotation = getValue(boneMap, "rotation", 0);
-					data.scaleX = getValue(boneMap, "scaleX", 1);
-					data.scaleY = getValue(boneMap, "scaleY", 1);
-					data.shearX = getValue(boneMap, "shearX", 0);
-					data.shearY = getValue(boneMap, "shearY", 0);
-					data.transformMode = Utils.enumValue(TransformMode, getValue(boneMap, "transform", "Normal"));
-					data.skinRequired = getValue(boneMap, "skin", false);
-
-					let color = getValue(boneMap, "color", null);
-					if (color) data.color.setFromString(color);
-
-					skeletonData.bones.push(data);
-				}
-			}
-
-			// Slots.
-			if (root.slots) {
-				for (let i = 0; i < root.slots.length; i++) {
-					let slotMap = root.slots[i];
-					let boneData = skeletonData.findBone(slotMap.bone);
-					let data = new SlotData(skeletonData.slots.length, slotMap.name, boneData);
-
-					let color: string = getValue(slotMap, "color", null);
-					if (color) data.color.setFromString(color);
-
-					let dark: string = getValue(slotMap, "dark", null);
-					if (dark) data.darkColor = Color.fromString(dark);
-
-					data.attachmentName = getValue(slotMap, "attachment", null);
-					data.blendMode = Utils.enumValue(BlendMode, 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 = getValue(constraintMap, "order", 0);
-					data.skinRequired = getValue(constraintMap, "skin", false);
-
-					for (let ii = 0; ii < constraintMap.bones.length; ii++)
-						data.bones.push(skeletonData.findBone(constraintMap.bones[ii]));
-
-					data.target = skeletonData.findBone(constraintMap.target);
-
-					data.mix = getValue(constraintMap, "mix", 1);
-					data.softness = getValue(constraintMap, "softness", 0) * scale;
-					data.bendDirection = getValue(constraintMap, "bendPositive", true) ? 1 : -1;
-					data.compress = getValue(constraintMap, "compress", false);
-					data.stretch = getValue(constraintMap, "stretch", false);
-					data.uniform = getValue(constraintMap, "uniform", false);
-
-					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 = getValue(constraintMap, "order", 0);
-					data.skinRequired = getValue(constraintMap, "skin", false);
-
-					for (let ii = 0; ii < constraintMap.bones.length; ii++)
-						data.bones.push(skeletonData.findBone(constraintMap.bones[ii]));
-
-					let targetName: string = constraintMap.target;
-					data.target = skeletonData.findBone(targetName);
-
-					data.local = getValue(constraintMap, "local", false);
-					data.relative = getValue(constraintMap, "relative", false);
-					data.offsetRotation = getValue(constraintMap, "rotation", 0);
-					data.offsetX = getValue(constraintMap, "x", 0) * scale;
-					data.offsetY = getValue(constraintMap, "y", 0) * scale;
-					data.offsetScaleX = getValue(constraintMap, "scaleX", 0);
-					data.offsetScaleY = getValue(constraintMap, "scaleY", 0);
-					data.offsetShearY = getValue(constraintMap, "shearY", 0);
-
-					data.mixRotate = getValue(constraintMap, "mixRotate", 1);
-					data.mixX = getValue(constraintMap, "mixX", 1);
-					data.mixY = getValue(constraintMap, "mixY", data.mixX);
-					data.mixScaleX = getValue(constraintMap, "mixScaleX", 1);
-					data.mixScaleY = getValue(constraintMap, "mixScaleY", data.mixScaleX);
-					data.mixShearY = getValue(constraintMap, "mixShearY", 1);
-
-					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 = getValue(constraintMap, "order", 0);
-					data.skinRequired = getValue(constraintMap, "skin", false);
-
-					for (let ii = 0; ii < constraintMap.bones.length; ii++)
-						data.bones.push(skeletonData.findBone(constraintMap.bones[ii]));
-
-					let targetName: string = constraintMap.target;
-					data.target = skeletonData.findSlot(targetName);
-
-					data.positionMode = Utils.enumValue(PositionMode, getValue(constraintMap, "positionMode", "Percent"));
-					data.spacingMode = Utils.enumValue(SpacingMode, getValue(constraintMap, "spacingMode", "Length"));
-					data.rotateMode = Utils.enumValue(RotateMode, getValue(constraintMap, "rotateMode", "Tangent"));
-					data.offsetRotation = getValue(constraintMap, "rotation", 0);
-					data.position = getValue(constraintMap, "position", 0);
-					if (data.positionMode == PositionMode.Fixed) data.position *= scale;
-					data.spacing = getValue(constraintMap, "spacing", 0);
-					if (data.spacingMode == SpacingMode.Length || data.spacingMode == SpacingMode.Fixed) data.spacing *= scale;
-					data.mixRotate = getValue(constraintMap, "mixRotate", 1);
-					data.mixX = getValue(constraintMap, "mixX", 1);
-					data.mixY = getValue(constraintMap, "mixY", data.mixX);
-
-					skeletonData.pathConstraints.push(data);
-				}
-			}
-
-			// Skins.
-			if (root.skins) {
-				for (let i = 0; i < root.skins.length; i++) {
-					let skinMap = root.skins[i]
-					let skin = new Skin(skinMap.name);
-
-					if (skinMap.bones) {
-						for (let ii = 0; ii < skinMap.bones.length; ii++)
-							skin.bones.push(skeletonData.findBone(skinMap.bones[ii]));
-					}
-
-					if (skinMap.ik) {
-						for (let ii = 0; ii < skinMap.ik.length; ii++)
-							skin.constraints.push(skeletonData.findIkConstraint(skinMap.ik[ii]));
-					}
-
-					if (skinMap.transform) {
-						for (let ii = 0; ii < skinMap.transform.length; ii++)
-							skin.constraints.push(skeletonData.findTransformConstraint(skinMap.transform[ii]));
-					}
-
-					if (skinMap.path) {
-						for (let ii = 0; ii < skinMap.path.length; ii++)
-							skin.constraints.push(skeletonData.findPathConstraint(skinMap.path[ii]));
-					}
-
-					for (let slotName in skinMap.attachments) {
-						let slot = skeletonData.findSlot(slotName);
-						let slotMap = skinMap.attachments[slotName];
-						for (let entryName in slotMap) {
-							let attachment = this.readAttachment(slotMap[entryName], skin, slot.index, entryName, skeletonData);
-							if (attachment) skin.setAttachment(slot.index, 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 ? skeletonData.defaultSkin : skeletonData.findSkin(linkedMesh.skin);
-				let parent = skin.getAttachment(linkedMesh.slotIndex, linkedMesh.parent);
-				linkedMesh.mesh.deformAttachment = linkedMesh.inheritDeform ? <VertexAttachment>parent : <VertexAttachment>linkedMesh.mesh;
-				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 = getValue(eventMap, "int", 0);
-					data.floatValue = getValue(eventMap, "float", 0);
-					data.stringValue = getValue(eventMap, "string", "");
-					data.audioPath = getValue(eventMap, "audio", null);
-					if (data.audioPath) {
-						data.volume = getValue(eventMap, "volume", 1);
-						data.balance = getValue(eventMap, "balance", 0);
-					}
-					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 = getValue(map, "name", name);
-
-			switch (getValue(map, "type", "region")) {
-				case "region": {
-					let path = getValue(map, "path", name);
-					let region = this.attachmentLoader.newRegionAttachment(skin, name, path);
-					if (!region) return null;
-					region.path = path;
-					region.x = getValue(map, "x", 0) * scale;
-					region.y = getValue(map, "y", 0) * scale;
-					region.scaleX = getValue(map, "scaleX", 1);
-					region.scaleY = getValue(map, "scaleY", 1);
-					region.rotation = getValue(map, "rotation", 0);
-					region.width = map.width * scale;
-					region.height = map.height * scale;
-
-					let color: string = getValue(map, "color", null);
-					if (color) region.color.setFromString(color);
-
-					region.updateOffset();
-					return region;
-				}
-				case "boundingbox": {
-					let box = this.attachmentLoader.newBoundingBoxAttachment(skin, name);
-					if (!box) return null;
-					this.readVertices(map, box, map.vertexCount << 1);
-					let color: string = getValue(map, "color", null);
-					if (color) box.color.setFromString(color);
-					return box;
-				}
-				case "mesh":
-				case "linkedmesh": {
-					let path = getValue(map, "path", name);
-					let mesh = this.attachmentLoader.newMeshAttachment(skin, name, path);
-					if (!mesh) return null;
-					mesh.path = path;
-
-					let color = getValue(map, "color", null);
-					if (color) mesh.color.setFromString(color);
-
-					mesh.width = getValue(map, "width", 0) * scale;
-					mesh.height = getValue(map, "height", 0) * scale;
-
-					let parent: string = getValue(map, "parent", null);
-					if (parent) {
-						this.linkedMeshes.push(new LinkedMesh(mesh, <string>getValue(map, "skin", null), slotIndex, parent, getValue(map, "deform", true)));
-						return mesh;
-					}
-
-					let uvs: Array<number> = map.uvs;
-					this.readVertices(map, mesh, uvs.length);
-					mesh.triangles = map.triangles;
-					mesh.regionUVs = uvs;
-					mesh.updateUVs();
-
-					mesh.edges = getValue(map, "edges", null);
-					mesh.hullLength = getValue(map, "hull", 0) * 2;
-					return mesh;
-				}
-				case "path": {
-					let path = this.attachmentLoader.newPathAttachment(skin, name);
-					if (!path) return null;
-					path.closed = getValue(map, "closed", false);
-					path.constantSpeed = 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 = getValue(map, "color", null);
-					if (color) path.color.setFromString(color);
-					return path;
-				}
-				case "point": {
-					let point = this.attachmentLoader.newPointAttachment(skin, name);
-					if (!point) return null;
-					point.x = getValue(map, "x", 0) * scale;
-					point.y = getValue(map, "y", 0) * scale;
-					point.rotation = getValue(map, "rotation", 0);
-
-					let color = getValue(map, "color", null);
-					if (color) point.color.setFromString(color);
-					return point;
-				}
-				case "clipping": {
-					let clip = this.attachmentLoader.newClippingAttachment(skin, name);
-					if (!clip) return null;
-
-					let end = getValue(map, "end", null);
-					if (end) clip.endSlot = skeletonData.findSlot(end);
-
-					let vertexCount = map.vertexCount;
-					this.readVertices(map, clip, vertexCount << 1);
-
-					let color: string = getValue(map, "color", null);
-					if (color) 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>();
-
-			// Slot timelines.
-			if (map.slots) {
-				for (let slotName in map.slots) {
-					let slotMap = map.slots[slotName];
-					let slotIndex = skeletonData.findSlotIndex(slotName);
-					for (let timelineName in slotMap) {
-						let timelineMap = slotMap[timelineName];
-						if (!timelineMap) continue;
-						if (timelineName == "attachment") {
-							let timeline = new AttachmentTimeline(timelineMap.length, slotIndex);
-							for (let frame = 0; frame < timelineMap.length; frame++) {
-								let keyMap = timelineMap[frame];
-								timeline.setFrame(frame, getValue(keyMap, "time", 0), keyMap.name);
-							}
-							timelines.push(timeline);
-
-						} else if (timelineName == "rgba") {
-							let timeline = new RGBATimeline(timelineMap.length, timelineMap.length << 2, slotIndex);
-							let keyMap = timelineMap[0];
-							let time = getValue(keyMap, "time", 0);
-							let color = Color.fromString(keyMap.color);
-
-							for (let frame = 0, bezier = 0; ; frame++) {
-								timeline.setFrame(frame, time, color.r, color.g, color.b, color.a);
-								let nextMap = timelineMap[frame + 1];
-								if (!nextMap) {
-									timeline.shrink(bezier);
-									break;
-								}
-								let time2 = getValue(nextMap, "time", 0);
-								let newColor = Color.fromString(nextMap.color);
-								let curve = keyMap.curve;
-								if (curve) {
-									bezier = readCurve(curve, timeline, bezier, frame, 0, time, time2, color.r, newColor.r, 1);
-									bezier = readCurve(curve, timeline, bezier, frame, 1, time, time2, color.g, newColor.g, 1);
-									bezier = readCurve(curve, timeline, bezier, frame, 2, time, time2, color.b, newColor.b, 1);
-									bezier = readCurve(curve, timeline, bezier, frame, 3, time, time2, color.a, newColor.a, 1);
-								}
-								time = time2;
-								color = newColor;
-								keyMap = nextMap;
-							}
-
-							timelines.push(timeline);
-
-						} else if (timelineName == "rgb") {
-							let timeline = new RGBTimeline(timelineMap.length, timelineMap.length * 3, slotIndex);
-							let keyMap = timelineMap[0];
-							let time = getValue(keyMap, "time", 0);
-							let color = Color.fromString(keyMap.color);
-
-							for (let frame = 0, bezier = 0; ; frame++) {
-								timeline.setFrame(frame, time, color.r, color.g, color.b);
-								let nextMap = timelineMap[frame + 1];
-								if (!nextMap) {
-									timeline.shrink(bezier);
-									break;
-								}
-								let time2 = getValue(nextMap, "time", 0);
-								let newColor = Color.fromString(nextMap.color);
-								let curve = keyMap.curve;
-								if (curve) {
-									bezier = readCurve(curve, timeline, bezier, frame, 0, time, time2, color.r, newColor.r, 1);
-									bezier = readCurve(curve, timeline, bezier, frame, 1, time, time2, color.g, newColor.g, 1);
-									bezier = readCurve(curve, timeline, bezier, frame, 2, time, time2, color.b, newColor.b, 1);
-								}
-								time = time2;
-								color = newColor;
-								keyMap = nextMap;
-							}
-
-							timelines.push(timeline);
-
-						} else if (timelineName == "alpha") {
-							timelines.push(readTimeline1(timelineMap, new AlphaTimeline(timelineMap.length, timelineMap.length, slotIndex), 0, 1));
-						} else if (timelineName == "rgba2") {
-							let timeline = new RGBA2Timeline(timelineMap.length, timelineMap.length * 7, slotIndex);
-
-							let keyMap = timelineMap[0];
-							let time = getValue(keyMap, "time", 0);
-							let color = Color.fromString(keyMap.light);
-							let color2 = Color.fromString(keyMap.dark);
-
-							for (let frame = 0, bezier = 0; ; frame++) {
-								timeline.setFrame(frame, time, color.r, color.g, color.b, color.a, color2.r, color2.g, color2.b);
-								let nextMap = timelineMap[frame + 1];
-								if (!nextMap) {
-									timeline.shrink(bezier);
-									break;
-								}
-								let time2 = getValue(nextMap, "time", 0);
-								let newColor = Color.fromString(nextMap.light);
-								let newColor2 = Color.fromString(nextMap.dark);
-								let curve = keyMap.curve;
-								if (curve) {
-									bezier = readCurve(curve, timeline, bezier, frame, 0, time, time2, color.r, newColor.r, 1);
-									bezier = readCurve(curve, timeline, bezier, frame, 1, time, time2, color.g, newColor.g, 1);
-									bezier = readCurve(curve, timeline, bezier, frame, 2, time, time2, color.b, newColor.b, 1);
-									bezier = readCurve(curve, timeline, bezier, frame, 3, time, time2, color.a, newColor.a, 1);
-									bezier = readCurve(curve, timeline, bezier, frame, 4, time, time2, color2.r, newColor2.r, 1);
-									bezier = readCurve(curve, timeline, bezier, frame, 5, time, time2, color2.g, newColor2.g, 1);
-									bezier = readCurve(curve, timeline, bezier, frame, 6, time, time2, color2.b, newColor2.b, 1);
-								}
-								time = time2;
-								color = newColor;
-								color2 = newColor2;
-								keyMap = nextMap;
-							}
-
-							timelines.push(timeline);
-
-						} else if (timelineName == "rgb2") {
-							let timeline = new RGB2Timeline(timelineMap.length, timelineMap.length * 6, slotIndex);
-
-							let keyMap = timelineMap[0];
-							let time = getValue(keyMap, "time", 0);
-							let color = Color.fromString(keyMap.light);
-							let color2 = Color.fromString(keyMap.dark);
-
-							for (let frame = 0, bezier = 0; ; frame++) {
-								timeline.setFrame(frame, time, color.r, color.g, color.b, color2.r, color2.g, color2.b);
-								let nextMap = timelineMap[frame + 1];
-								if (!nextMap) {
-									timeline.shrink(bezier);
-									break;
-								}
-								let time2 = getValue(nextMap, "time", 0);
-								let newColor = Color.fromString(nextMap.light);
-								let newColor2 = Color.fromString(nextMap.dark);
-								let curve = keyMap.curve;
-								if (curve) {
-									bezier = readCurve(curve, timeline, bezier, frame, 0, time, time2, color.r, newColor.r, 1);
-									bezier = readCurve(curve, timeline, bezier, frame, 1, time, time2, color.g, newColor.g, 1);
-									bezier = readCurve(curve, timeline, bezier, frame, 2, time, time2, color.b, newColor.b, 1);
-									bezier = readCurve(curve, timeline, bezier, frame, 3, time, time2, color2.r, newColor2.r, 1);
-									bezier = readCurve(curve, timeline, bezier, frame, 4, time, time2, color2.g, newColor2.g, 1);
-									bezier = readCurve(curve, timeline, bezier, frame, 5, time, time2, color2.b, newColor2.b, 1);
-								}
-								time = time2;
-								color = newColor;
-								color2 = newColor2;
-								keyMap = nextMap;
-							}
-
-							timelines.push(timeline);
-						}
-					}
-				}
-			}
-
-			// Bone timelines.
-			if (map.bones) {
-				for (let boneName in map.bones) {
-					let boneMap = map.bones[boneName];
-					let boneIndex = skeletonData.findBoneIndex(boneName);
-					for (let timelineName in boneMap) {
-						let timelineMap = boneMap[timelineName];
-						if (timelineMap.length == 0) continue;
-
-						if (timelineName === "rotate") {
-							timelines.push(readTimeline1(timelineMap, new RotateTimeline(timelineMap.length, timelineMap.length, boneIndex), 0, 1));
-						} else if (timelineName === "translate") {
-							let timeline = new TranslateTimeline(timelineMap.length, timelineMap.length << 1, boneIndex);
-							timelines.push(readTimeline2(timelineMap, timeline, "x", "y", 0, scale));
-						} else if (timelineName === "translatex") {
-							let timeline = new TranslateXTimeline(timelineMap.length, timelineMap.length, boneIndex);
-							timelines.push(readTimeline1(timelineMap, timeline, 0, scale));
-						} else if (timelineName === "translatey") {
-							let timeline = new TranslateYTimeline(timelineMap.length, timelineMap.length, boneIndex);
-							timelines.push(readTimeline1(timelineMap, timeline, 0, scale));
-						} else if (timelineName === "scale") {
-							let timeline = new ScaleTimeline(timelineMap.length, timelineMap.length << 1, boneIndex);
-							timelines.push(readTimeline2(timelineMap, timeline, "x", "y", 1, 1));
-						} else if (timelineName === "scalex") {
-							let timeline = new ScaleXTimeline(timelineMap.length, timelineMap.length, boneIndex);
-							timelines.push(readTimeline1(timelineMap, timeline, 1, 1));
-						} else if (timelineName === "scaley") {
-							let timeline = new ScaleYTimeline(timelineMap.length, timelineMap.length, boneIndex);
-							timelines.push(readTimeline1(timelineMap, timeline, 1, 1));
-						} else if (timelineName === "shear") {
-							let timeline = new ShearTimeline(timelineMap.length, timelineMap.length << 1, boneIndex);
-							timelines.push(readTimeline2(timelineMap, timeline, "x", "y", 0, 1));
-						} else if (timelineName === "shearx") {
-							let timeline = new ShearXTimeline(timelineMap.length, timelineMap.length, boneIndex);
-							timelines.push(readTimeline1(timelineMap, timeline, 0, 1));
-						} else if (timelineName === "sheary") {
-							let timeline = new ShearYTimeline(timelineMap.length, timelineMap.length, boneIndex);
-							timelines.push(readTimeline1(timelineMap, timeline, 0, 1));
-						}
-					}
-				}
-			}
-
-			// IK constraint timelines.
-			if (map.ik) {
-				for (let constraintName in map.ik) {
-					let constraintMap = map.ik[constraintName];
-					let keyMap = constraintMap[0];
-					if (!keyMap) continue;
-
-					let constraint = skeletonData.findIkConstraint(constraintName);
-					let constraintIndex = skeletonData.ikConstraints.indexOf(constraint);
-					let timeline = new IkConstraintTimeline(constraintMap.length, constraintMap.length << 1, constraintIndex);
-
-					let time = getValue(keyMap, "time", 0);
-					let mix = getValue(keyMap, "mix", 1);
-					let softness = getValue(keyMap, "softness", 0) * scale;
-
-					for (let frame = 0, bezier = 0; ; frame++) {
-						timeline.setFrame(frame, time, mix, softness, getValue(keyMap, "bendPositive", true) ? 1 : -1, getValue(keyMap, "compress", false), getValue(keyMap, "stretch", false));
-						let nextMap = constraintMap[frame + 1];
-						if (!nextMap) {
-							timeline.shrink(bezier);
-							break;
-						}
-
-						let time2 = getValue(nextMap, "time", 0);
-						let mix2 = getValue(nextMap, "mix", 1);
-						let softness2 = getValue(nextMap, "softness", 0) * scale;
-						let curve = keyMap.curve;
-						if (curve) {
-							bezier = readCurve(curve, timeline, bezier, frame, 0, time, time2, mix, mix2, 1);
-							bezier = readCurve(curve, timeline, bezier, frame, 1, time, time2, softness, softness2, scale);
-						}
-
-						time = time2;
-						mix = mix2;
-						softness = softness2;
-						keyMap = nextMap;
-					}
-					timelines.push(timeline);
-				}
-			}
-
-			// Transform constraint timelines.
-			if (map.transform) {
-				for (let constraintName in map.transform) {
-					let timelineMap = map.transform[constraintName];
-					let keyMap = timelineMap[0];
-					if (!keyMap) continue;
-
-					let constraint = skeletonData.findTransformConstraint(constraintName);
-					let constraintIndex = skeletonData.transformConstraints.indexOf(constraint);
-					let timeline = new TransformConstraintTimeline(timelineMap.length, timelineMap.length << 2, constraintIndex);
-
-					let time = getValue(keyMap, "time", 0);
-					let mixRotate = getValue(keyMap, "mixRotate", 1);
-					let mixX = getValue(keyMap, "mixX", 1);
-					let mixY = getValue(keyMap, "mixY", mixX);
-					let mixScaleX = getValue(keyMap, "mixScaleX", 1);
-					let mixScaleY = getValue(keyMap, "mixScaleY", mixScaleX);
-					let mixShearY = getValue(keyMap, "mixShearY", 1);
-
-					for (let frame = 0, bezier = 0; ; frame++) {
-						timeline.setFrame(frame, time, mixRotate, mixX, mixY, mixScaleX, mixScaleY, mixShearY);
-						let nextMap = timelineMap[frame + 1];
-						if (!nextMap) {
-							timeline.shrink(bezier);
-							break;
-						}
-
-						let time2 = getValue(nextMap, "time", 0);
-						let mixRotate2 = getValue(nextMap, "mixRotate", 1);
-						let mixX2 = getValue(nextMap, "mixX", 1);
-						let mixY2 = getValue(nextMap, "mixY", mixX2);
-						let mixScaleX2 = getValue(nextMap, "mixScaleX", 1);
-						let mixScaleY2 = getValue(nextMap, "mixScaleY", mixScaleX2);
-						let mixShearY2 = getValue(nextMap, "mixShearY", 1);
-						let curve = keyMap.curve;
-						if (curve) {
-							bezier = readCurve(curve, timeline, bezier, frame, 0, time, time2, mixRotate, mixRotate2, 1);
-							bezier = readCurve(curve, timeline, bezier, frame, 1, time, time2, mixX, mixX2, 1);
-							bezier = readCurve(curve, timeline, bezier, frame, 2, time, time2, mixY, mixY2, 1);
-							bezier = readCurve(curve, timeline, bezier, frame, 3, time, time2, mixScaleX, mixScaleX2, 1);
-							bezier = readCurve(curve, timeline, bezier, frame, 4, time, time2, mixScaleY, mixScaleY2, 1);
-							bezier = readCurve(curve, timeline, bezier, frame, 5, time, time2, mixShearY, mixShearY2, 1);
-						}
-
-						time = time2;
-						mixRotate = mixRotate2;
-						mixX = mixX2;
-						mixY = mixY2;
-						mixScaleX = mixScaleX2;
-						mixScaleY = mixScaleY2;
-						mixScaleX = mixScaleX2;
-						keyMap = nextMap;
-					}
-					timelines.push(timeline);
-				}
-			}
-
-			// Path constraint timelines.
-			if (map.path) {
-				for (let constraintName in map.path) {
-					let constraintMap = map.path[constraintName];
-					let constraint = skeletonData.findPathConstraint(constraintName);
-					let constraintIndex = skeletonData.pathConstraints.indexOf(constraint);
-					for (let timelineName in constraintMap) {
-						let timelineMap = constraintMap[timelineName];
-						let keyMap = timelineMap[0];
-						if (!keyMap) continue;
-
-						if (timelineName === "position") {
-							let timeline = new PathConstraintPositionTimeline(timelineMap.length, timelineMap.length, constraintIndex);
-							timelines.push(readTimeline1(timelineMap, timeline, 0, constraint.positionMode == PositionMode.Fixed ? scale : 1));
-						} else if (timelineName === "spacing") {
-							let timeline = new PathConstraintSpacingTimeline(timelineMap.length, timelineMap.length, constraintIndex);
-							timelines.push(readTimeline1(timelineMap, timeline, 0, constraint.spacingMode == SpacingMode.Length || constraint.spacingMode == SpacingMode.Fixed ? scale : 1));
-						} else if (timelineName === "mix") {
-							let timeline = new PathConstraintMixTimeline(timelineMap.size, timelineMap.size * 3, constraintIndex);
-							let time = getValue(keyMap, "time", 0);
-							let mixRotate = getValue(keyMap, "mixRotate", 1);
-							let mixX = getValue(keyMap, "mixX", 1);
-							let mixY = getValue(keyMap, "mixY", mixX);
-							for (let frame = 0, bezier = 0; ; frame++) {
-								timeline.setFrame(frame, time, mixRotate, mixX, mixY);
-								let nextMap = timelineMap[frame + 1];
-								if (!nextMap) {
-									timeline.shrink(bezier);
-									break;
-								}
-								let time2 = getValue(nextMap, "time", 0);
-								let mixRotate2 = getValue(nextMap, "mixRotate", 1);
-								let mixX2 = getValue(nextMap, "mixX", 1);
-								let mixY2 = getValue(nextMap, "mixY", mixX2);
-								let curve = keyMap.curve;
-								if (curve) {
-									bezier = readCurve(curve, timeline, bezier, frame, 0, time, time2, mixRotate, mixRotate2, 1);
-									bezier = readCurve(curve, timeline, bezier, frame, 1, time, time2, mixX, mixX2, 1);
-									bezier = readCurve(curve, timeline, bezier, frame, 2, time, time2, mixY, mixY2, 1);
-								}
-								time = time2;
-								mixRotate = mixRotate2;
-								mixX = mixX2;
-								mixY = mixY2;
-								keyMap = nextMap;
-							}
-							timelines.push(timeline);
-						}
-					}
-				}
-			}
-
-			// Deform timelines.
-			if (map.deform) {
-				for (let deformName in map.deform) {
-					let deformMap = map.deform[deformName];
-					let skin = skeletonData.findSkin(deformName);
-					for (let slotName in deformMap) {
-						let slotMap = deformMap[slotName];
-						let slotIndex = skeletonData.findSlotIndex(slotName);
-						for (let timelineName in slotMap) {
-							let timelineMap = slotMap[timelineName];
-							let keyMap = timelineMap[0];
-							if (!keyMap) continue;
-
-							let attachment = <VertexAttachment>skin.getAttachment(slotIndex, timelineName);
-							let weighted = attachment.bones;
-							let vertices = attachment.vertices;
-							let deformLength = weighted ? vertices.length / 3 * 2 : vertices.length;
-
-							let timeline = new DeformTimeline(timelineMap.length, timelineMap.length, slotIndex, attachment);
-							let time = getValue(keyMap, "time", 0);
-							for (let frame = 0, bezier = 0; ; frame++) {
-								let deform: ArrayLike<number>;
-								let verticesValue: Array<Number> = getValue(keyMap, "vertices", null);
-								if (!verticesValue)
-									deform = weighted ? Utils.newFloatArray(deformLength) : vertices;
-								else {
-									deform = Utils.newFloatArray(deformLength);
-									let start = <number>getValue(keyMap, "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(frame, time, deform);
-								let nextMap = timelineMap[frame + 1];
-								if (!nextMap) {
-									timeline.shrink(bezier);
-									break;
-								}
-								let time2 = getValue(nextMap, "time", 0);
-								let curve = keyMap.curve;
-								if (curve) bezier = readCurve(curve, timeline, bezier, frame, 0, time, time2, 0, 1, 1);
-								time = time2;
-								keyMap = nextMap;
-							}
-							timelines.push(timeline);
-						}
-					}
-				}
-			}
-
-			// Draw order timelines.
-			if (map.drawOrder) {
-				let timeline = new DrawOrderTimeline(map.drawOrder.length);
-				let slotCount = skeletonData.slots.length;
-				let frame = 0;
-				for (let i = 0; i < map.drawOrder.length; i++, frame++) {
-					let drawOrderMap = map.drawOrder[i];
-					let drawOrder: Array<number> = null;
-					let offsets = getValue(drawOrderMap, "offsets", null);
-					if (offsets) {
-						drawOrder = Utils.newArray<number>(slotCount, -1);
-						let unchanged = Utils.newArray<number>(slotCount - offsets.length, 0);
-						let originalIndex = 0, unchangedIndex = 0;
-						for (let ii = 0; ii < offsets.length; ii++) {
-							let offsetMap = offsets[ii];
-							let slotIndex = skeletonData.findSlotIndex(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 ii = slotCount - 1; ii >= 0; ii--)
-							if (drawOrder[ii] == -1) drawOrder[ii] = unchanged[--unchangedIndex];
-					}
-					timeline.setFrame(frame, getValue(drawOrderMap, "time", 0), drawOrder);
-				}
-				timelines.push(timeline);
-			}
-
-			// Event timelines.
-			if (map.events) {
-				let timeline = new EventTimeline(map.events.length);
-				let frame = 0;
-				for (let i = 0; i < map.events.length; i++, frame++) {
-					let eventMap = map.events[i];
-					let eventData = skeletonData.findEvent(eventMap.name);
-					let event = new Event(Utils.toSinglePrecision(getValue(eventMap, "time", 0)), eventData);
-					event.intValue = getValue(eventMap, "int", eventData.intValue);
-					event.floatValue = getValue(eventMap, "float", eventData.floatValue);
-					event.stringValue = getValue(eventMap, "string", eventData.stringValue);
-					if (event.data.audioPath) {
-						event.volume = getValue(eventMap, "volume", 1);
-						event.balance = getValue(eventMap, "balance", 0);
-					}
-					timeline.setFrame(frame, event);
-				}
-				timelines.push(timeline);
-			}
-
-			let duration = 0;
-			for (let i = 0, n = timelines.length; i < n; i++)
-				duration = Math.max(duration, timelines[i].getDuration());
-			skeletonData.animations.push(new Animation(name, timelines, duration));
-		}
-	}
-
-	class LinkedMesh {
-		parent: string; skin: string;
-		slotIndex: number;
-		mesh: MeshAttachment;
-		inheritDeform: boolean;
-
-		constructor (mesh: MeshAttachment, skin: string, slotIndex: number, parent: string, inheritDeform: boolean) {
-			this.mesh = mesh;
-			this.skin = skin;
-			this.slotIndex = slotIndex;
-			this.parent = parent;
-			this.inheritDeform = inheritDeform;
-		}
-	}
-
-	function readTimeline1 (keys: any[], timeline: CurveTimeline1, defaultValue: number, scale: number) {
-		let keyMap = keys[0];
-		let time = getValue(keyMap, "time", 0);
-		let value = getValue(keyMap, "value", defaultValue) * scale;
-		let bezier = 0;
-		for (let frame = 0; ; frame++) {
-			timeline.setFrame(frame, time, value);
-			let nextMap = keys[frame + 1];
-			if (!nextMap) {
-				timeline.shrink(bezier);
-				return timeline;
-			}
-			let time2 = getValue(nextMap, "time", 0);
-			let value2 = getValue(nextMap, "value", defaultValue) * scale;
-			if (keyMap.curve) bezier = readCurve(keyMap.curve, timeline, bezier, frame, 0, time, time2, value, value2, scale);
-			time = time2;
-			value = value2;
-			keyMap = nextMap;
-		}
-	}
-
-	function readTimeline2 (keys: any[], timeline: CurveTimeline2, name1: string, name2: string, defaultValue: number, scale: number) {
-		let keyMap = keys[0];
-		let time = getValue(keyMap, "time", 0);
-		let value1 = getValue(keyMap, name1, defaultValue) * scale;
-		let value2 = getValue(keyMap, name2, defaultValue) * scale;
-		let bezier = 0;
-		for (let frame = 0; ; frame++) {
-			timeline.setFrame(frame, time, value1, value2);
-			let nextMap = keys[frame + 1];
-			if (!nextMap) {
-				timeline.shrink(bezier);
-				return timeline;
-			}
-			let time2 = getValue(nextMap, "time", 0);
-			let nvalue1 = getValue(nextMap, name1, defaultValue) * scale;
-			let nvalue2 = getValue(nextMap, name2, defaultValue) * scale;
-			let curve = keyMap.curve;
-			if (curve) {
-				bezier = readCurve(curve, timeline, bezier, frame, 0, time, time2, value1, nvalue1, scale);
-				bezier = readCurve(curve, timeline, bezier, frame, 1, time, time2, value2, nvalue2, scale);
-			}
-			time = time2;
-			value1 = nvalue1;
-			value2 = nvalue2;
-			keyMap = nextMap;
-		}
-	}
-
-	function readCurve (curve: any, timeline: CurveTimeline, bezier: number, frame: number, value: number, time1: number, time2: number,
-		value1: number, value2: number, scale: number) {
-		if (curve == "stepped") {
-			timeline.setStepped(frame);
-			return bezier;
-		}
-		let i = value << 2;
-		let cx1 = curve[i];
-		let cy1 = curve[i + 1] * scale;
-		let cx2 = curve[i + 2];
-		let cy2 = curve[i + 3] * scale;
-		timeline.setBezier(bezier, frame, value, time1, value1, cx1, cy1, cx2, cy2, time2, value2);
-		return bezier + 1;
-	}
-
-	function getValue (map: any, property: string, defaultValue: any) {
-		return map[property] !== undefined ? map[property] : defaultValue;
-	}
-}

+ 0 - 202
spine-ts/core/src/Skin.ts

@@ -1,202 +0,0 @@
-/******************************************************************************
- * Spine Runtimes License Agreement
- * Last updated January 1, 2020. Replaces all prior versions.
- *
- * Copyright (c) 2013-2020, Esoteric Software LLC
- *
- * Integration of the Spine Runtimes into software or otherwise creating
- * derivative works of the Spine Runtimes is permitted under the terms and
- * conditions of Section 2 of the Spine Editor License Agreement:
- * http://esotericsoftware.com/spine-editor-license
- *
- * Otherwise, it is permitted to integrate the Spine Runtimes into software
- * or otherwise create derivative works of the Spine Runtimes (collectively,
- * "Products"), provided that each user of the Products must obtain their own
- * Spine Editor license and redistribution of the Products in any form must
- * include this license and copyright notice.
- *
- * THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "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 LLC 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
- * THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *****************************************************************************/
-
-module spine {
-	/** Stores an entry in the skin consisting of the slot index, name, and attachment **/
-	export class SkinEntry {
-		constructor (public slotIndex: number, public name: string, public attachment: Attachment) { }
-	}
-
-	/** Stores attachments by slot index and attachment name.
-	 *
-	 * See SkeletonData {@link SkeletonData#defaultSkin}, Skeleton {@link Skeleton#skin}, and
-	 * [Runtime skins](http://esotericsoftware.com/spine-runtime-skins) in the Spine Runtimes Guide. */
-	export class Skin {
-		/** The skin's name, which is unique across all skins in the skeleton. */
-		name: string;
-
-		attachments = new Array<Map<Attachment>>();
-		bones = Array<BoneData>();
-		constraints = new Array<ConstraintData>();
-
-		constructor (name: string) {
-			if (!name) throw new Error("name cannot be null.");
-			this.name = name;
-		}
-
-		/** Adds an attachment to the skin for the specified slot index and name. */
-		setAttachment (slotIndex: number, name: string, attachment: Attachment) {
-			if (!attachment) 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;
-		}
-
-		/** Adds all attachments, bones, and constraints from the specified skin to this skin. */
-		addSkin (skin: Skin) {
-			for (let i = 0; i < skin.bones.length; i++) {
-				let bone = skin.bones[i];
-				let contained = false;
-				for (let ii = 0; ii < this.bones.length; ii++) {
-					if (this.bones[ii] == bone) {
-						contained = true;
-						break;
-					}
-				}
-				if (!contained) this.bones.push(bone);
-			}
-
-			for (let i = 0; i < skin.constraints.length; i++) {
-				let constraint = skin.constraints[i];
-				let contained = false;
-				for (let ii = 0; ii < this.constraints.length; ii++) {
-					if (this.constraints[ii] == constraint) {
-						contained = true;
-						break;
-					}
-				}
-				if (!contained) this.constraints.push(constraint);
-			}
-
-			let attachments = skin.getAttachments();
-			for (let i = 0; i < attachments.length; i++) {
-				var attachment = attachments[i];
-				this.setAttachment(attachment.slotIndex, attachment.name, attachment.attachment);
-			}
-		}
-
-		/** Adds all bones and constraints and copies of all attachments from the specified skin to this skin. Mesh attachments are not
-		 * copied, instead a new linked mesh is created. The attachment copies can be modified without affecting the originals. */
-		copySkin (skin: Skin) {
-			for (let i = 0; i < skin.bones.length; i++) {
-				let bone = skin.bones[i];
-				let contained = false;
-				for (let ii = 0; ii < this.bones.length; ii++) {
-					if (this.bones[ii] == bone) {
-						contained = true;
-						break;
-					}
-				}
-				if (!contained) this.bones.push(bone);
-			}
-
-			for (let i = 0; i < skin.constraints.length; i++) {
-				let constraint = skin.constraints[i];
-				let contained = false;
-				for (let ii = 0; ii < this.constraints.length; ii++) {
-					if (this.constraints[ii] == constraint) {
-						contained = true;
-						break;
-					}
-				}
-				if (!contained) this.constraints.push(constraint);
-			}
-
-			let attachments = skin.getAttachments();
-			for (let i = 0; i < attachments.length; i++) {
-				var attachment = attachments[i];
-				if (!attachment.attachment) continue;
-				if (attachment.attachment instanceof MeshAttachment) {
-					attachment.attachment = attachment.attachment.newLinkedMesh();
-					this.setAttachment(attachment.slotIndex, attachment.name, attachment.attachment);
-				} else {
-					attachment.attachment = attachment.attachment.copy();
-					this.setAttachment(attachment.slotIndex, attachment.name, attachment.attachment);
-				}
-			}
-		}
-
-		/** Returns the attachment for the specified slot index and name, or null. */
-		getAttachment (slotIndex: number, name: string): Attachment {
-			let dictionary = this.attachments[slotIndex];
-			return dictionary ? dictionary[name] : null;
-		}
-
-		/** Removes the attachment in the skin for the specified slot index and name, if any. */
-		removeAttachment (slotIndex: number, name: string) {
-			let dictionary = this.attachments[slotIndex];
-			if (dictionary) dictionary[name] = null;
-		}
-
-		/** Returns all attachments in this skin. */
-		getAttachments (): Array<SkinEntry> {
-			let entries = new Array<SkinEntry>();
-			for (var i = 0; i < this.attachments.length; i++) {
-				let slotAttachments = this.attachments[i];
-				if (slotAttachments) {
-					for (let name in slotAttachments) {
-						let attachment = slotAttachments[name];
-						if (attachment) entries.push(new SkinEntry(i, name, attachment));
-					}
-				}
-			}
-			return entries;
-		}
-
-		/** Returns all attachments in this skin for the specified slot index. */
-		getAttachmentsForSlot (slotIndex: number, attachments: Array<SkinEntry>) {
-			let slotAttachments = this.attachments[slotIndex];
-			if (slotAttachments) {
-				for (let name in slotAttachments) {
-					let attachment = slotAttachments[name];
-					if (attachment) attachments.push(new SkinEntry(slotIndex, name, attachment));
-				}
-			}
-		}
-
-		/** Clears all attachments, bones, and constraints. */
-		clear () {
-			this.attachments.length = 0;
-			this.bones.length = 0;
-			this.constraints.length = 0;
-		}
-
-		/** 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, key);
-							if (attachment) slot.setAttachment(attachment);
-							break;
-						}
-					}
-				}
-				slotIndex++;
-			}
-		}
-	}
-}

+ 0 - 118
spine-ts/core/src/Slot.ts

@@ -1,118 +0,0 @@
-/******************************************************************************
- * Spine Runtimes License Agreement
- * Last updated January 1, 2020. Replaces all prior versions.
- *
- * Copyright (c) 2013-2020, Esoteric Software LLC
- *
- * Integration of the Spine Runtimes into software or otherwise creating
- * derivative works of the Spine Runtimes is permitted under the terms and
- * conditions of Section 2 of the Spine Editor License Agreement:
- * http://esotericsoftware.com/spine-editor-license
- *
- * Otherwise, it is permitted to integrate the Spine Runtimes into software
- * or otherwise create derivative works of the Spine Runtimes (collectively,
- * "Products"), provided that each user of the Products must obtain their own
- * Spine Editor license and redistribution of the Products in any form must
- * include this license and copyright notice.
- *
- * THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "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 LLC 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
- * THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *****************************************************************************/
-
-module spine {
-
-	/** Stores a slot's current pose. Slots organize attachments for {@link Skeleton#drawOrder} purposes and provide a place to store
-	 * state for an attachment. State cannot be stored in an attachment itself because attachments are stateless and may be shared
-	 * across multiple skeletons. */
-	export class Slot {
-		/** The slot's setup pose data. */
-		data: SlotData;
-
-		/** The bone this slot belongs to. */
-		bone: Bone;
-
-		/** The color used to tint the slot's attachment. If {@link #getDarkColor()} is set, this is used as the light color for two
-		 * color tinting. */
-		color: Color;
-
-		/** The dark color used to tint the slot's attachment for two color tinting, or null if two color tinting is not used. The dark
-		 * color's alpha is not used. */
-		darkColor: Color;
-
-		attachment: Attachment;
-
-		private attachmentTime: number;
-
-		attachmentState: number;
-
-		/** Values to deform the slot's attachment. For an unweighted mesh, the entries are local positions for each vertex. For a
-		 * weighted mesh, the entries are an offset for each vertex which will be added to the mesh's local vertex positions.
-		 *
-		 * See {@link VertexAttachment#computeWorldVertices()} and {@link DeformTimeline}. */
-		deform = new Array<number>();
-
-		constructor (data: SlotData, bone: Bone) {
-			if (!data) throw new Error("data cannot be null.");
-			if (!bone) throw new Error("bone cannot be null.");
-			this.data = data;
-			this.bone = bone;
-			this.color = new Color();
-			this.darkColor = !data.darkColor ? null : new Color();
-			this.setToSetupPose();
-		}
-
-		/** The skeleton this slot belongs to. */
-		getSkeleton (): Skeleton {
-			return this.bone.skeleton;
-		}
-
-		/** The current attachment for the slot, or null if the slot has no attachment. */
-		getAttachment (): Attachment {
-			return this.attachment;
-		}
-
-		/** Sets the slot's attachment and, if the attachment changed, resets {@link #attachmentTime} and clears the {@link #deform}.
-		 * The deform is not cleared if the old attachment has the same {@link VertexAttachment#getDeformAttachment()} as the specified
-		 * attachment.
-		 * @param attachment May be null. */
-		setAttachment (attachment: Attachment) {
-			if (this.attachment == attachment) return;
-			if (!(attachment instanceof VertexAttachment) || !(this.attachment instanceof VertexAttachment)
-				|| (<VertexAttachment>attachment).deformAttachment != (<VertexAttachment>this.attachment).deformAttachment) {
-				this.deform.length = 0;
-			}
-			this.attachment = attachment;
-			this.attachmentTime = this.bone.skeleton.time;
-		}
-
-		setAttachmentTime (time: number) {
-			this.attachmentTime = this.bone.skeleton.time - time;
-		}
-
-		/** The time that has elapsed since the last time the attachment was set or cleared. Relies on Skeleton
-		 * {@link Skeleton#time}. */
-		getAttachmentTime (): number {
-			return this.bone.skeleton.time - this.attachmentTime;
-		}
-
-		/** Sets this slot to the setup pose. */
-		setToSetupPose () {
-			this.color.setFromColor(this.data.color);
-			if (this.darkColor) this.darkColor.setFromColor(this.data.darkColor);
-			if (!this.data.attachmentName)
-				this.attachment = null;
-			else {
-				this.attachment = null;
-				this.setAttachment(this.bone.skeleton.getAttachment(this.data.index, this.data.attachmentName));
-			}
-		}
-	}
-}

+ 0 - 78
spine-ts/core/src/Texture.ts

@@ -1,78 +0,0 @@
-/******************************************************************************
- * Spine Runtimes License Agreement
- * Last updated January 1, 2020. Replaces all prior versions.
- *
- * Copyright (c) 2013-2020, Esoteric Software LLC
- *
- * Integration of the Spine Runtimes into software or otherwise creating
- * derivative works of the Spine Runtimes is permitted under the terms and
- * conditions of Section 2 of the Spine Editor License Agreement:
- * http://esotericsoftware.com/spine-editor-license
- *
- * Otherwise, it is permitted to integrate the Spine Runtimes into software
- * or otherwise create derivative works of the Spine Runtimes (collectively,
- * "Products"), provided that each user of the Products must obtain their own
- * Spine Editor license and redistribution of the Products in any form must
- * include this license and copyright notice.
- *
- * THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "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 LLC 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
- * THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *****************************************************************************/
-
-module spine {
-	export abstract class Texture {
-		protected _image: HTMLImageElement | ImageBitmap;
-
-		constructor (image: HTMLImageElement | ImageBitmap) {
-			this._image = image;
-		}
-
-		getImage (): HTMLImageElement | ImageBitmap {
-			return this._image;
-		}
-
-		abstract setFilters (minFilter: TextureFilter, magFilter: TextureFilter): void;
-		abstract setWraps (uWrap: TextureWrap, vWrap: TextureWrap): void;
-		abstract dispose (): void;
-	}
-
-	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 {
-		renderObject: any;
-		u = 0; v = 0;
-		u2 = 0; v2 = 0;
-		width = 0; height = 0;
-		degrees = 0;
-		offsetX = 0; offsetY = 0;
-		originalWidth = 0; originalHeight = 0;
-	}
-
-	export class FakeTexture extends Texture {
-		setFilters (minFilter: TextureFilter, magFilter: TextureFilter) { }
-		setWraps (uWrap: TextureWrap, vWrap: TextureWrap) { }
-		dispose () { }
-	}
-}

+ 0 - 264
spine-ts/core/src/TextureAtlas.ts

@@ -1,264 +0,0 @@
-/******************************************************************************
- * Spine Runtimes License Agreement
- * Last updated January 1, 2020. Replaces all prior versions.
- *
- * Copyright (c) 2013-2020, Esoteric Software LLC
- *
- * Integration of the Spine Runtimes into software or otherwise creating
- * derivative works of the Spine Runtimes is permitted under the terms and
- * conditions of Section 2 of the Spine Editor License Agreement:
- * http://esotericsoftware.com/spine-editor-license
- *
- * Otherwise, it is permitted to integrate the Spine Runtimes into software
- * or otherwise create derivative works of the Spine Runtimes (collectively,
- * "Products"), provided that each user of the Products must obtain their own
- * Spine Editor license and redistribution of the Products in any form must
- * include this license and copyright notice.
- *
- * THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "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 LLC 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
- * THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *****************************************************************************/
-
-module spine {
-	export class TextureAtlas implements Disposable {
-		pages = new Array<TextureAtlasPage>();
-		regions = new Array<TextureAtlasRegion>();
-
-		constructor (atlasText: string) {
-			let reader = new TextureAtlasReader(atlasText);
-			let entry = new Array<string>(4);
-			let page: TextureAtlasPage = null;
-			let region: TextureAtlasRegion = null;
-
-			let pageFields: Map<Function> = {};
-			pageFields["size"] = () => {
-				page.width = parseInt(entry[1]);
-				page.height = parseInt(entry[2]);
-			};
-			pageFields["format"] = () => {
-				// page.format = Format[tuple[0]]; we don't need format in WebGL
-			};
-			pageFields["filter"] = () => {
-				page.minFilter = Utils.enumValue(TextureFilter, entry[1]);
-				page.magFilter = Utils.enumValue(TextureFilter, entry[2]);
-			};
-			pageFields["repeat"] = () => {
-				if (entry[1].indexOf('x') != -1) page.uWrap = TextureWrap.Repeat;
-				if (entry[1].indexOf('y') != -1) page.vWrap = TextureWrap.Repeat;
-			};
-			pageFields["pma"] = () => {
-				page.pma = entry[1] == "true";
-			};
-
-			var regionFields: Map<Function> = {};
-			regionFields["xy"] = () => { // Deprecated, use bounds.
-				region.x = parseInt(entry[1]);
-				region.y = parseInt(entry[2]);
-			};
-			regionFields["size"] = () => { // Deprecated, use bounds.
-				region.width = parseInt(entry[1]);
-				region.height = parseInt(entry[2]);
-			};
-			regionFields["bounds"] = () => {
-				region.x = parseInt(entry[1]);
-				region.y = parseInt(entry[2]);
-				region.width = parseInt(entry[3]);
-				region.height = parseInt(entry[4]);
-			};
-			regionFields["offset"] = () => { // Deprecated, use offsets.
-				region.offsetX = parseInt(entry[1]);
-				region.offsetY = parseInt(entry[2]);
-			};
-			regionFields["orig"] = () => { // Deprecated, use offsets.
-				region.originalWidth = parseInt(entry[1]);
-				region.originalHeight = parseInt(entry[2]);
-			};
-			regionFields["offsets"] = () => {
-				region.offsetX = parseInt(entry[1]);
-				region.offsetY = parseInt(entry[2]);
-				region.originalWidth = parseInt(entry[3]);
-				region.originalHeight = parseInt(entry[4]);
-			};
-			regionFields["rotate"] = () => {
-				let value = entry[1];
-				if (value == "true")
-					region.degrees = 90;
-				else if (value != "false")
-					region.degrees = parseInt(value);
-			};
-			regionFields["index"] = () => {
-				region.index = parseInt(entry[1]);
-			};
-
-			let line = reader.readLine();
-			// Ignore empty lines before first entry.
-			while (line && line.trim().length == 0)
-				line = reader.readLine();
-			// Header entries.
-			while (true) {
-				if (!line || line.trim().length == 0) break;
-				if (reader.readEntry(entry, line) == 0) break; // Silently ignore all header fields.
-				line = reader.readLine();
-			}
-
-			// Page and region entries.
-			let names: string[] = null;
-			let values: number[][] = null;
-			while (true) {
-				if (line === null) break;
-				if (line.trim().length == 0) {
-					page = null;
-					line = reader.readLine();
-				} else if (!page) {
-					page = new TextureAtlasPage();
-					page.name = line.trim();
-					while (true) {
-						if (reader.readEntry(entry, line = reader.readLine()) == 0) break;
-						let field: Function = pageFields[entry[0]];
-						if (field) field();
-					}
-					this.pages.push(page);
-				} else {
-					region = new TextureAtlasRegion();
-
-					region.page = page;
-					region.name = line;
-					while (true) {
-						let count = reader.readEntry(entry, line = reader.readLine());
-						if (count == 0) break;
-						let field: Function = regionFields[entry[0]];
-						if (field)
-							field();
-						else {
-							if (!names) {
-								names = [];
-								values = [];
-							}
-							names.push(entry[0]);
-							let entryValues: number[] = [];
-							for (let i = 0; i < count; i++)
-								entryValues.push(parseInt(entry[i + 1]));
-							values.push(entryValues);
-						}
-					}
-					if (region.originalWidth == 0 && region.originalHeight == 0) {
-						region.originalWidth = region.width;
-						region.originalHeight = region.height;
-					}
-					if (names && names.length > 0) {
-						region.names = names;
-						region.values = values;
-						names = null;
-						values = null;
-					}
-					region.u = region.x / page.width;
-					region.v = region.y / page.height;
-					if (region.degrees == 90) {
-						region.u2 = (region.x + region.height) / page.width;
-						region.v2 = (region.y + region.width) / page.height;
-					} else {
-						region.u2 = (region.x + region.width) / page.width;
-						region.v2 = (region.y + region.height) / page.height;
-					}
-					this.regions.push(region);
-				}
-			}
-		}
-
-		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;
-		}
-
-		setTextures (assetManager: AssetManager, pathPrefix: string = "") {
-			for (let page of this.pages)
-				page.setTexture(assetManager.get(pathPrefix + page.name));
-		}
-
-		dispose () {
-			for (let i = 0; i < this.pages.length; i++) {
-				this.pages[i].texture.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++];
-		}
-
-		readEntry (entry: string[], line: string): number {
-			if (!line) return 0;
-			line = line.trim();
-			if (line.length == 0) return 0;
-
-			let colon = line.indexOf(':');
-			if (colon == -1) return 0;
-			entry[0] = line.substr(0, colon).trim();
-			for (let i = 1, lastMatch = colon + 1; ; i++) {
-				let comma = line.indexOf(',', lastMatch);
-				if (comma == -1) {
-					entry[i] = line.substr(lastMatch).trim();
-					return i;
-				}
-				entry[i] = line.substr(lastMatch, comma - lastMatch).trim();
-				lastMatch = comma + 1;
-				if (i == 4) return 4;
-			}
-		}
-	}
-
-	export class TextureAtlasPage {
-		name: string;
-		minFilter: TextureFilter = TextureFilter.Nearest;
-		magFilter: TextureFilter = TextureFilter.Nearest;
-		uWrap: TextureWrap = TextureWrap.ClampToEdge;
-		vWrap: TextureWrap = TextureWrap.ClampToEdge;
-		texture: Texture;
-		width: number;
-		height: number;
-		pma: boolean;
-
-		setTexture (texture: Texture) {
-			this.texture = texture;
-			texture.setFilters(this.minFilter, this.magFilter);
-			texture.setWraps(this.uWrap, this.vWrap);
-		}
-	}
-
-	export class TextureAtlasRegion extends TextureRegion {
-		page: TextureAtlasPage;
-		name: string;
-		x: number;
-		y: number;
-		offsetX: number;
-		offsetY: number;
-		originalWidth: number;
-		originalHeight: number;
-		index: number;
-		degrees: number;
-		names: string[];
-		values: number[][];
-	}
-}

+ 0 - 279
spine-ts/core/src/TransformConstraint.ts

@@ -1,279 +0,0 @@
-/******************************************************************************
- * Spine Runtimes License Agreement
- * Last updated January 1, 2020. Replaces all prior versions.
- *
- * Copyright (c) 2013-2020, Esoteric Software LLC
- *
- * Integration of the Spine Runtimes into software or otherwise creating
- * derivative works of the Spine Runtimes is permitted under the terms and
- * conditions of Section 2 of the Spine Editor License Agreement:
- * http://esotericsoftware.com/spine-editor-license
- *
- * Otherwise, it is permitted to integrate the Spine Runtimes into software
- * or otherwise create derivative works of the Spine Runtimes (collectively,
- * "Products"), provided that each user of the Products must obtain their own
- * Spine Editor license and redistribution of the Products in any form must
- * include this license and copyright notice.
- *
- * THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "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 LLC 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
- * THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *****************************************************************************/
-
-module spine {
-
-	/** Stores the current pose for a transform constraint. A transform constraint adjusts the world transform of the constrained
-	 * bones to match that of the target bone.
-	 *
-	 * See [Transform constraints](http://esotericsoftware.com/spine-transform-constraints) in the Spine User Guide. */
-	export class TransformConstraint implements Updatable {
-
-		/** The transform constraint's setup pose data. */
-		data: TransformConstraintData;
-
-		/** The bones that will be modified by this transform constraint. */
-		bones: Array<Bone>;
-
-		/** The target bone whose world transform will be copied to the constrained bones. */
-		target: Bone;
-
-		mixRotate = 0; mixX = 0; mixY = 0; mixScaleX = 0; mixScaleY = 0; mixShearY = 0;
-
-		temp = new Vector2();
-		active = false;
-
-		constructor (data: TransformConstraintData, skeleton: Skeleton) {
-			if (!data) throw new Error("data cannot be null.");
-			if (!skeleton) throw new Error("skeleton cannot be null.");
-			this.data = data;
-			this.mixRotate = data.mixRotate;
-			this.mixX = data.mixX;
-			this.mixY = data.mixY;
-			this.mixScaleX = data.mixScaleX;
-			this.mixScaleY = data.mixScaleY;
-			this.mixShearY = data.mixShearY;
-			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);
-		}
-
-		isActive () {
-			return this.active;
-		}
-
-		update () {
-			if (this.mixRotate == 0 && this.mixX == 0 && this.mixY == 0 && this.mixScaleX == 0 && this.mixScaleX == 0 && this.mixShearY == 0) return;
-
-			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 mixRotate = this.mixRotate, mixX = this.mixX, mixY = this.mixY, mixScaleX = this.mixScaleX,
-				mixScaleY = this.mixScaleY, mixShearY = this.mixShearY;
-			let translate = mixX != 0 || mixY != 0;
-
-			let target = this.target;
-			let ta = target.a, tb = target.b, tc = target.c, td = target.d;
-			let degRadReflect = ta * td - tb * tc > 0 ? MathUtils.degRad : -MathUtils.degRad;
-			let offsetRotation = this.data.offsetRotation * degRadReflect;
-			let offsetShearY = this.data.offsetShearY * degRadReflect;
-
-			let bones = this.bones;
-			for (let i = 0, n = bones.length; i < n; i++) {
-				let bone = bones[i];
-
-				if (mixRotate != 0) {
-					let a = bone.a, b = bone.b, c = bone.c, d = bone.d;
-					let r = Math.atan2(tc, ta) - Math.atan2(c, a) + offsetRotation;
-					if (r > MathUtils.PI)
-						r -= MathUtils.PI2;
-					else if (r < -MathUtils.PI) //
-						r += MathUtils.PI2;
-					r *= mixRotate;
-					let cos = Math.cos(r), sin = Math.sin(r);
-					bone.a = cos * a - sin * c;
-					bone.b = cos * b - sin * d;
-					bone.c = sin * a + cos * c;
-					bone.d = sin * b + cos * d;
-				}
-
-				if (translate) {
-					let temp = this.temp;
-					target.localToWorld(temp.set(this.data.offsetX, this.data.offsetY));
-					bone.worldX += (temp.x - bone.worldX) * mixX;
-					bone.worldY += (temp.y - bone.worldY) * mixY;
-				}
-
-				if (mixScaleX != 0) {
-					let s = Math.sqrt(bone.a * bone.a + bone.c * bone.c);
-					if (s != 0) s = (s + (Math.sqrt(ta * ta + tc * tc) - s + this.data.offsetScaleX) * mixScaleX) / s;
-					bone.a *= s;
-					bone.c *= s;
-				}
-				if (mixScaleY != 0) {
-					let s = Math.sqrt(bone.b * bone.b + bone.d * bone.d);
-					if (s != 0) s = (s + (Math.sqrt(tb * tb + td * td) - s + this.data.offsetScaleY) * mixScaleY) / s;
-					bone.b *= s;
-					bone.d *= s;
-				}
-
-				if (mixShearY > 0) {
-					let b = bone.b, d = bone.d;
-					let by = Math.atan2(d, b);
-					let r = Math.atan2(td, tb) - Math.atan2(tc, ta) - (by - Math.atan2(bone.c, bone.a));
-					if (r > MathUtils.PI)
-						r -= MathUtils.PI2;
-					else if (r < -MathUtils.PI) //
-						r += MathUtils.PI2;
-					r = by + (r + offsetShearY) * mixShearY;
-					let s = Math.sqrt(b * b + d * d);
-					bone.b = Math.cos(r) * s;
-					bone.d = Math.sin(r) * s;
-				}
-
-				bone.updateAppliedTransform();
-			}
-		}
-
-		applyRelativeWorld () {
-			let mixRotate = this.mixRotate, mixX = this.mixX, mixY = this.mixY, mixScaleX = this.mixScaleX,
-				mixScaleY = this.mixScaleY, mixShearY = this.mixShearY;
-			let translate = mixX != 0 || mixY != 0;
-
-			let target = this.target;
-			let ta = target.a, tb = target.b, tc = target.c, td = target.d;
-			let degRadReflect = ta * td - tb * tc > 0 ? MathUtils.degRad : -MathUtils.degRad;
-			let offsetRotation = this.data.offsetRotation * degRadReflect, offsetShearY = this.data.offsetShearY * degRadReflect;
-
-			let bones = this.bones;
-			for (let i = 0, n = bones.length; i < n; i++) {
-				let bone = bones[i];
-
-				if (mixRotate != 0) {
-					let a = bone.a, b = bone.b, c = bone.c, d = bone.d;
-					let r = Math.atan2(tc, ta) + offsetRotation;
-					if (r > MathUtils.PI)
-						r -= MathUtils.PI2;
-					else if (r < -MathUtils.PI) //
-						r += MathUtils.PI2;
-					r *= mixRotate;
-					let cos = Math.cos(r), sin = Math.sin(r);
-					bone.a = cos * a - sin * c;
-					bone.b = cos * b - sin * d;
-					bone.c = sin * a + cos * c;
-					bone.d = sin * b + cos * d;
-				}
-
-				if (translate) {
-					let temp = this.temp;
-					target.localToWorld(temp.set(this.data.offsetX, this.data.offsetY));
-					bone.worldX += temp.x * mixX;
-					bone.worldY += temp.y * mixY;
-				}
-
-				if (mixScaleX != 0) {
-					let s = (Math.sqrt(ta * ta + tc * tc) - 1 + this.data.offsetScaleX) * mixScaleX + 1;
-					bone.a *= s;
-					bone.c *= s;
-				}
-				if (mixScaleY != 0) {
-					let s = (Math.sqrt(tb * tb + td * td) - 1 + this.data.offsetScaleY) * mixScaleY + 1;
-					bone.b *= s;
-					bone.d *= s;
-				}
-
-				if (mixShearY > 0) {
-					let r = Math.atan2(td, tb) - Math.atan2(tc, ta);
-					if (r > MathUtils.PI)
-						r -= MathUtils.PI2;
-					else if (r < -MathUtils.PI) //
-						r += MathUtils.PI2;
-					let b = bone.b, d = bone.d;
-					r = Math.atan2(d, b) + (r - MathUtils.PI / 2 + offsetShearY) * mixShearY;
-					let s = Math.sqrt(b * b + d * d);
-					bone.b = Math.cos(r) * s;
-					bone.d = Math.sin(r) * s;
-				}
-
-				bone.updateAppliedTransform();
-			}
-		}
-
-		applyAbsoluteLocal () {
-			let mixRotate = this.mixRotate, mixX = this.mixX, mixY = this.mixY, mixScaleX = this.mixScaleX,
-				mixScaleY = this.mixScaleY, mixShearY = this.mixShearY;
-
-			let target = this.target;
-
-			let bones = this.bones;
-			for (let i = 0, n = bones.length; i < n; i++) {
-				let bone = bones[i];
-
-				let rotation = bone.arotation;
-				if (mixRotate != 0) {
-					let r = target.arotation - rotation + this.data.offsetRotation;
-					r -= (16384 - ((16384.499999999996 - r / 360) | 0)) * 360;
-					rotation += r * mixRotate;
-				}
-
-				let x = bone.ax, y = bone.ay;
-				x += (target.ax - x + this.data.offsetX) * mixX;
-				y += (target.ay - y + this.data.offsetY) * mixY;
-
-				let scaleX = bone.ascaleX, scaleY = bone.ascaleY;
-				if (mixScaleX != 0 && scaleX != 0)
-					scaleX = (scaleX + (target.ascaleX - scaleX + this.data.offsetScaleX) * mixScaleX) / scaleX;
-				if (mixScaleY != 0 && scaleY != 0)
-					scaleY = (scaleY + (target.ascaleY - scaleY + this.data.offsetScaleY) * mixScaleY) / scaleY;
-
-				let shearY = bone.ashearY;
-				if (mixShearY != 0) {
-					let r = target.ashearY - shearY + this.data.offsetShearY;
-					r -= (16384 - ((16384.499999999996 - r / 360) | 0)) * 360;
-					shearY += r * mixShearY;
-				}
-
-				bone.updateWorldTransformWith(x, y, rotation, scaleX, scaleY, bone.ashearX, shearY);
-			}
-		}
-
-		applyRelativeLocal () {
-			let mixRotate = this.mixRotate, mixX = this.mixX, mixY = this.mixY, mixScaleX = this.mixScaleX,
-				mixScaleY = this.mixScaleY, mixShearY = this.mixShearY;
-
-			let target = this.target;
-
-			let bones = this.bones;
-			for (let i = 0, n = bones.length; i < n; i++) {
-				let bone = bones[i];
-
-				let rotation = bone.arotation + (target.arotation + this.data.offsetRotation) * mixRotate;
-				let x = bone.ax + (target.ax + this.data.offsetX) * mixX;
-				let y = bone.ay + (target.ay + this.data.offsetY) * mixY;
-				let scaleX = (bone.ascaleX * ((target.ascaleX - 1 + this.data.offsetScaleX) * mixScaleX) + 1);
-				let scaleY = (bone.ascaleY * ((target.ascaleY - 1 + this.data.offsetScaleY) * mixScaleY) + 1);
-				let shearY = bone.ashearY + (target.ashearY + this.data.offsetShearY) * mixShearY;
-
-				bone.updateWorldTransformWith(x, y, rotation, scaleX, scaleY, bone.ashearX, shearY);
-			}
-		}
-	}
-}

+ 0 - 269
spine-ts/core/src/Triangulator.ts

@@ -1,269 +0,0 @@
-/******************************************************************************
- * Spine Runtimes License Agreement
- * Last updated January 1, 2020. Replaces all prior versions.
- *
- * Copyright (c) 2013-2020, Esoteric Software LLC
- *
- * Integration of the Spine Runtimes into software or otherwise creating
- * derivative works of the Spine Runtimes is permitted under the terms and
- * conditions of Section 2 of the Spine Editor License Agreement:
- * http://esotericsoftware.com/spine-editor-license
- *
- * Otherwise, it is permitted to integrate the Spine Runtimes into software
- * or otherwise create derivative works of the Spine Runtimes (collectively,
- * "Products"), provided that each user of the Products must obtain their own
- * Spine Editor license and redistribution of the Products in any form must
- * include this license and copyright notice.
- *
- * THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "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 LLC 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
- * THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *****************************************************************************/
-
-module spine {
-	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;
-		}
-	}
-}

+ 0 - 462
spine-ts/core/src/Utils.ts

@@ -1,462 +0,0 @@
-/******************************************************************************
- * Spine Runtimes License Agreement
- * Last updated January 1, 2020. Replaces all prior versions.
- *
- * Copyright (c) 2013-2020, Esoteric Software LLC
- *
- * Integration of the Spine Runtimes into software or otherwise creating
- * derivative works of the Spine Runtimes is permitted under the terms and
- * conditions of Section 2 of the Spine Editor License Agreement:
- * http://esotericsoftware.com/spine-editor-license
- *
- * Otherwise, it is permitted to integrate the Spine Runtimes into software
- * or otherwise create derivative works of the Spine Runtimes (collectively,
- * "Products"), provided that each user of the Products must obtain their own
- * Spine Editor license and redistribution of the Products in any form must
- * include this license and copyright notice.
- *
- * THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "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 LLC 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
- * THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *****************************************************************************/
-
-module spine {
-	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 class StringSet {
-		entries: Map<boolean> = {};
-		size = 0;
-
-		add (value: string): boolean {
-			let contains = this.entries[value];
-			this.entries[value] = true;
-			if (!contains) {
-				this.size++;
-				return true;
-			}
-			return false;
-		}
-
-		addAll (values: string[]): boolean {
-			let oldSize = this.size;
-			for (var i = 0, n = values.length; i < n; i++)
-				this.add(values[i]);
-			return oldSize != this.size;
-		}
-
-		contains (value: string) {
-			return this.entries[value];
-		}
-
-		clear () {
-			this.entries = {};
-			this.size = 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;
-			return this.clamp();
-		}
-
-		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;
-			this.g = parseInt(hex.substr(2, 2), 16) / 255;
-			this.b = parseInt(hex.substr(4, 2), 16) / 255;
-			this.a = hex.length != 8 ? 1 : parseInt(hex.substr(6, 2), 16) / 255;
-			return this;
-		}
-
-		add (r: number, g: number, b: number, a: number) {
-			this.r += r;
-			this.g += g;
-			this.b += b;
-			this.a += a;
-			return this.clamp();
-		}
-
-		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;
-		}
-
-		static rgba8888ToColor (color: Color, value: number) {
-			color.r = ((value & 0xff000000) >>> 24) / 255;
-			color.g = ((value & 0x00ff0000) >>> 16) / 255;
-			color.b = ((value & 0x0000ff00) >>> 8) / 255;
-			color.a = ((value & 0x000000ff)) / 255;
-		}
-
-		static rgb888ToColor (color: Color, value: number) {
-			color.r = ((value & 0x00ff0000) >>> 16) / 255;
-			color.g = ((value & 0x0000ff00) >>> 8) / 255;
-			color.b = ((value & 0x000000ff)) / 255;
-		}
-
-		static fromString (hex: string): Color {
-			return new Color().setFromString(hex);
-		}
-	}
-
-	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));
-		}
-
-		static isPowerOfTwo (value: number) {
-			return value && (value & (value - 1)) === 0;
-		}
-	}
-
-	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 arrayFill<T> (array: ArrayLike<T>, fromIndex: number, toIndex: number, value: T) {
-			for (let i = fromIndex; i < toIndex; i++)
-				array[i] = value;
-		}
-
-		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;
-		}
-
-		// This function is used to fix WebKit 602 specific issue described at http://esotericsoftware.com/forum/iOS-10-disappearing-graphics-10109
-		static webkit602BugfixHelper (alpha: number, blend: MixBlend) {
-		}
-
-		static contains<T> (array: Array<T>, element: T, identity = true) {
-			for (var i = 0; i < array.length; i++)
-				if (array[i] == element) return true;
-			return false;
-		}
-
-		static enumValue (type: any, name: string) {
-			return type[name[0].toUpperCase() + name.slice(1)];
-		}
-	}
-
-	export class DebugUtils {
-		static logBones (skeleton: Skeleton) {
-			for (let i = 0; i < skeleton.bones.length; i++) {
-				let bone = skeleton.bones[i];
-				console.log(bone.data.name + ", " + bone.a + ", " + bone.b + ", " + bone.c + ", " + bone.d + ", " + bone.worldX + ", " + bone.worldY);
-			}
-		}
-	}
-
-	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++)
-				this.free(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 () {
-			let 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;
-			}
-			return 0;
-		}
-	}
-}

+ 0 - 159
spine-ts/core/src/attachments/Attachment.ts

@@ -1,159 +0,0 @@
-/******************************************************************************
- * Spine Runtimes License Agreement
- * Last updated January 1, 2020. Replaces all prior versions.
- *
- * Copyright (c) 2013-2020, Esoteric Software LLC
- *
- * Integration of the Spine Runtimes into software or otherwise creating
- * derivative works of the Spine Runtimes is permitted under the terms and
- * conditions of Section 2 of the Spine Editor License Agreement:
- * http://esotericsoftware.com/spine-editor-license
- *
- * Otherwise, it is permitted to integrate the Spine Runtimes into software
- * or otherwise create derivative works of the Spine Runtimes (collectively,
- * "Products"), provided that each user of the Products must obtain their own
- * Spine Editor license and redistribution of the Products in any form must
- * include this license and copyright notice.
- *
- * THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "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 LLC 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
- * THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *****************************************************************************/
-
-module spine {
-	/** The base class for all attachments. */
-	export abstract class Attachment {
-		name: string;
-
-		constructor (name: string) {
-			if (!name) throw new Error("name cannot be null.");
-			this.name = name;
-		}
-
-		abstract copy (): Attachment;
-	}
-
-	/** Base class for an attachment with vertices that are transformed by one or more bones and can be deformed by a slot's
-	 * {@link Slot#deform}. */
-	export abstract class VertexAttachment extends Attachment {
-		private static nextID = 0;
-
-		/** The unique ID for this attachment. */
-		id = VertexAttachment.nextID++;
-
-		/** The bones which affect the {@link #getVertices()}. The array entries are, for each vertex, the number of bones affecting
-		 * the vertex followed by that many bone indices, which is the index of the bone in {@link Skeleton#bones}. Will be null
-		 * if this attachment has no weights. */
-		bones: Array<number>;
-
-		/** The vertex positions in the bone's coordinate system. For a non-weighted attachment, the values are `x,y`
-		 * entries for each vertex. For a weighted attachment, the values are `x,y,weight` entries for each bone affecting
-		 * each vertex. */
-		vertices: ArrayLike<number>;
-
-		/** The maximum number of world vertex values that can be output by
-		 * {@link #computeWorldVertices()} using the `count` parameter. */
-		worldVerticesLength = 0;
-
-		/** Deform keys for the deform attachment are also applied to this attachment. May be null if no deform keys should be applied. */
-		deformAttachment: VertexAttachment = this;
-
-		constructor (name: string) {
-			super(name);
-		}
-
-		/** Transforms the attachment's local {@link #vertices} to world coordinates. If the slot's {@link Slot#deform} is
-		 * not empty, it is used to deform the vertices.
-		 *
-		 * See [World transforms](http://esotericsoftware.com/spine-runtime-skeletons#World-transforms) in the Spine
-		 * Runtimes Guide.
-		 * @param start The index of the first {@link #vertices} value to transform. Each vertex has 2 values, x and y.
-		 * @param count The number of world vertex values to output. Must be <= {@link #worldVerticesLength} - `start`.
-		 * @param worldVertices The output world vertices. Must have a length >= `offset` + `count` *
-		 *           `stride` / 2.
-		 * @param offset The `worldVertices` index to begin writing values.
-		 * @param stride The number of `worldVertices` entries between the value pairs written. */
-		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.deform;
-			let vertices = this.vertices;
-			let bones = this.bones;
-			if (!bones) {
-				if (deformArray.length > 0) vertices = deformArray;
-				let bone = slot.bone;
-				let x = bone.worldX;
-				let y = bone.worldY;
-				let a = bone.a, b = bone.b, c = bone.c, d = bone.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 bone = skeletonBones[bones[v]];
-						let vx = vertices[b], vy = vertices[b + 1], weight = vertices[b + 2];
-						wx += (vx * bone.a + vy * bone.b + bone.worldX) * weight;
-						wy += (vx * bone.c + vy * bone.d + bone.worldY) * 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 bone = skeletonBones[bones[v]];
-						let vx = vertices[b] + deform[f], vy = vertices[b + 1] + deform[f + 1], weight = vertices[b + 2];
-						wx += (vx * bone.a + vy * bone.b + bone.worldX) * weight;
-						wy += (vx * bone.c + vy * bone.d + bone.worldY) * weight;
-					}
-					worldVertices[w] = wx;
-					worldVertices[w + 1] = wy;
-				}
-			}
-		}
-
-		/** Does not copy id (generated) or name (set on construction). **/
-		copyTo (attachment: VertexAttachment) {
-			if (this.bones) {
-				attachment.bones = new Array<number>(this.bones.length);
-				Utils.arrayCopy(this.bones, 0, attachment.bones, 0, this.bones.length);
-			} else
-				attachment.bones = null;
-
-			if (this.vertices) {
-				attachment.vertices = Utils.newFloatArray(this.vertices.length);
-				Utils.arrayCopy(this.vertices, 0, attachment.vertices, 0, this.vertices.length);
-			} else
-				attachment.vertices = null;
-
-			attachment.worldVerticesLength = this.worldVerticesLength;
-			attachment.deformAttachment = this.deformAttachment;
-		}
-	}
-}

+ 0 - 198
spine-ts/core/src/attachments/MeshAttachment.ts

@@ -1,198 +0,0 @@
-/******************************************************************************
- * Spine Runtimes License Agreement
- * Last updated January 1, 2020. Replaces all prior versions.
- *
- * Copyright (c) 2013-2020, Esoteric Software LLC
- *
- * Integration of the Spine Runtimes into software or otherwise creating
- * derivative works of the Spine Runtimes is permitted under the terms and
- * conditions of Section 2 of the Spine Editor License Agreement:
- * http://esotericsoftware.com/spine-editor-license
- *
- * Otherwise, it is permitted to integrate the Spine Runtimes into software
- * or otherwise create derivative works of the Spine Runtimes (collectively,
- * "Products"), provided that each user of the Products must obtain their own
- * Spine Editor license and redistribution of the Products in any form must
- * include this license and copyright notice.
- *
- * THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "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 LLC 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
- * THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *****************************************************************************/
-
-module spine {
-	/** An attachment that displays a textured mesh. A mesh has hull vertices and internal vertices within the hull. Holes are not
-	 * supported. Each vertex has UVs (texture coordinates) and triangles are used to map an image on to the mesh.
-	 *
-	 * See [Mesh attachments](http://esotericsoftware.com/spine-meshes) in the Spine User Guide. */
-	export class MeshAttachment extends VertexAttachment {
-		region: TextureRegion;
-
-		/** The name of the texture region for this attachment. */
-		path: string;
-
-		/** The UV pair for each vertex, normalized within the texture region. */
-		regionUVs: ArrayLike<number>;
-
-		/** The UV pair for each vertex, normalized within the entire texture.
-		 *
-		 * See {@link #updateUVs}. */
-		uvs: ArrayLike<number>;
-
-		/** Triplets of vertex indices which describe the mesh's triangulation. */
-		triangles: Array<number>;
-
-		/** The color to tint the mesh. */
-		color = new Color(1, 1, 1, 1);
-
-		/** The width of the mesh's image. Available only when nonessential data was exported. */
-		width: number;
-
-		/** The height of the mesh's image. Available only when nonessential data was exported. */
-		height: number;
-
-		/** The number of entries at the beginning of {@link #vertices} that make up the mesh hull. */
-		hullLength: number;
-
-		/** Vertex index pairs describing edges for controling triangulation. Mesh triangles will never cross edges. Only available if
-		 * nonessential data was exported. Triangulation is not performed at runtime. */
-		edges: Array<number>;
-
-		private parentMesh: MeshAttachment;
-		tempColor = new Color(0, 0, 0, 0);
-
-		constructor (name: string) {
-			super(name);
-		}
-
-		/** Calculates {@link #uvs} using {@link #regionUVs} and the {@link #region}. Must be called after changing the region UVs or
-		 * region. */
-		updateUVs () {
-			let regionUVs = this.regionUVs;
-			if (!this.uvs || this.uvs.length != regionUVs.length) this.uvs = Utils.newFloatArray(regionUVs.length);
-			let uvs = this.uvs;
-			let n = this.uvs.length;
-			let u = this.region.u, v = this.region.v, width = 0, height = 0;
-			if (this.region instanceof TextureAtlasRegion) {
-				let region = this.region, image = region.page.texture.getImage();
-				let textureWidth = image.width, textureHeight = image.height;
-				switch (region.degrees) {
-					case 90:
-						u -= (region.originalHeight - region.offsetY - region.height) / textureWidth;
-						v -= (region.originalWidth - region.offsetX - region.width) / textureHeight;
-						width = region.originalHeight / textureWidth;
-						height = region.originalWidth / textureHeight;
-						for (let i = 0; i < n; i += 2) {
-							uvs[i] = u + regionUVs[i + 1] * width;
-							uvs[i + 1] = v + (1 - regionUVs[i]) * height;
-						}
-						return;
-					case 180:
-						u -= (region.originalWidth - region.offsetX - region.width) / textureWidth;
-						v -= region.offsetY / textureHeight;
-						width = region.originalWidth / textureWidth;
-						height = region.originalHeight / textureHeight;
-						for (let i = 0; i < n; i += 2) {
-							uvs[i] = u + (1 - regionUVs[i]) * width;
-							uvs[i + 1] = v + (1 - regionUVs[i + 1]) * height;
-						}
-						return;
-					case 270:
-						u -= region.offsetY / textureWidth;
-						v -= region.offsetX / textureHeight;
-						width = region.originalHeight / textureWidth;
-						height = region.originalWidth / textureHeight;
-						for (let i = 0; i < n; i += 2) {
-							uvs[i] = u + (1 - regionUVs[i + 1]) * width;
-							uvs[i + 1] = v + regionUVs[i] * height;
-						}
-						return;
-				}
-				u -= region.offsetX / textureWidth;
-				v -= (region.originalHeight - region.offsetY - region.height) / textureHeight;
-				width = region.originalWidth / textureWidth;
-				height = region.originalHeight / textureHeight;
-			} else if (!this.region) {
-				u = v = 0;
-				width = height = 1;
-			} else {
-				width = this.region.u2 - u;
-				height = this.region.v2 - v;
-			}
-
-			for (let i = 0; i < n; i += 2) {
-				uvs[i] = u + regionUVs[i] * width;
-				uvs[i + 1] = v + regionUVs[i + 1] * height;
-			}
-		}
-
-		/** The parent mesh if this is a linked mesh, else null. A linked mesh shares the {@link #bones}, {@link #vertices},
-		 * {@link #regionUVs}, {@link #triangles}, {@link #hullLength}, {@link #edges}, {@link #width}, and {@link #height} with the
-		 * parent mesh, but may have a different {@link #name} or {@link #path} (and therefore a different texture). */
-		getParentMesh () {
-			return this.parentMesh;
-		}
-
-		/** @param parentMesh May be null. */
-		setParentMesh (parentMesh: MeshAttachment) {
-			this.parentMesh = parentMesh;
-			if (parentMesh) {
-				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
-			}
-		}
-
-		copy (): Attachment {
-			if (this.parentMesh) return this.newLinkedMesh();
-
-			let copy = new MeshAttachment(this.name);
-			copy.region = this.region;
-			copy.path = this.path;
-			copy.color.setFromColor(this.color);
-
-			this.copyTo(copy);
-			copy.regionUVs = new Array<number>(this.regionUVs.length);
-			Utils.arrayCopy(this.regionUVs, 0, copy.regionUVs, 0, this.regionUVs.length);
-			copy.uvs = new Array<number>(this.uvs.length);
-			Utils.arrayCopy(this.uvs, 0, copy.uvs, 0, this.uvs.length);
-			copy.triangles = new Array<number>(this.triangles.length);
-			Utils.arrayCopy(this.triangles, 0, copy.triangles, 0, this.triangles.length);
-			copy.hullLength = this.hullLength;
-
-			// Nonessential.
-			if (this.edges) {
-				copy.edges = new Array<number>(this.edges.length);
-				Utils.arrayCopy(this.edges, 0, copy.edges, 0, this.edges.length);
-			}
-			copy.width = this.width;
-			copy.height = this.height;
-
-			return copy;
-		}
-
-		/** Returns a new mesh with the {@link #parentMesh} set to this mesh's parent mesh, if any, else to this mesh. **/
-		newLinkedMesh (): MeshAttachment {
-			let copy = new MeshAttachment(this.name);
-			copy.region = this.region;
-			copy.path = this.path;
-			copy.color.setFromColor(this.color);
-			copy.deformAttachment = this.deformAttachment;
-			copy.setParentMesh(this.parentMesh ? this.parentMesh : this);
-			copy.updateUVs();
-			return copy;
-		}
-	}
-
-}

+ 0 - 69
spine-ts/core/src/attachments/PointAttachment.ts

@@ -1,69 +0,0 @@
-/******************************************************************************
- * Spine Runtimes License Agreement
- * Last updated January 1, 2020. Replaces all prior versions.
- *
- * Copyright (c) 2013-2020, Esoteric Software LLC
- *
- * Integration of the Spine Runtimes into software or otherwise creating
- * derivative works of the Spine Runtimes is permitted under the terms and
- * conditions of Section 2 of the Spine Editor License Agreement:
- * http://esotericsoftware.com/spine-editor-license
- *
- * Otherwise, it is permitted to integrate the Spine Runtimes into software
- * or otherwise create derivative works of the Spine Runtimes (collectively,
- * "Products"), provided that each user of the Products must obtain their own
- * Spine Editor license and redistribution of the Products in any form must
- * include this license and copyright notice.
- *
- * THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "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 LLC 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
- * THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *****************************************************************************/
-
-module spine {
-	/** An attachment which is a single point and a rotation. This can be used to spawn projectiles, particles, etc. A bone can be
-	 * used in similar ways, but a PointAttachment is slightly less expensive to compute and can be hidden, shown, and placed in a
-	 * skin.
-	 *
-	 * See [Point Attachments](http://esotericsoftware.com/spine-point-attachments) in the Spine User Guide. */
-	export class PointAttachment extends VertexAttachment {
-		x: number; y: number; rotation: number;
-
-		/** The color of the point attachment as it was in Spine. Available only when nonessential data was exported. Point attachments
-		 * are not usually rendered at runtime. */
-		color = new Color(0.38, 0.94, 0, 1);
-
-		constructor (name: string) {
-			super(name);
-		}
-
-		computeWorldPosition (bone: Bone, point: Vector2) {
-			point.x = this.x * bone.a + this.y * bone.b + bone.worldX;
-			point.y = this.x * bone.c + this.y * bone.d + bone.worldY;
-			return point;
-		}
-
-		computeWorldRotation (bone: Bone) {
-			let cos = MathUtils.cosDeg(this.rotation), sin = MathUtils.sinDeg(this.rotation);
-			let x = cos * bone.a + sin * bone.b;
-			let y = cos * bone.c + sin * bone.d;
-			return Math.atan2(y, x) * MathUtils.radDeg;
-		}
-
-		copy (): Attachment {
-			let copy = new PointAttachment(this.name);
-			copy.x = this.x;
-			copy.y = this.y;
-			copy.rotation = this.rotation;
-			copy.color.setFromColor(this.color);
-			return copy;
-		}
-	}
-}

+ 0 - 226
spine-ts/core/src/attachments/RegionAttachment.ts

@@ -1,226 +0,0 @@
-/******************************************************************************
- * Spine Runtimes License Agreement
- * Last updated January 1, 2020. Replaces all prior versions.
- *
- * Copyright (c) 2013-2020, Esoteric Software LLC
- *
- * Integration of the Spine Runtimes into software or otherwise creating
- * derivative works of the Spine Runtimes is permitted under the terms and
- * conditions of Section 2 of the Spine Editor License Agreement:
- * http://esotericsoftware.com/spine-editor-license
- *
- * Otherwise, it is permitted to integrate the Spine Runtimes into software
- * or otherwise create derivative works of the Spine Runtimes (collectively,
- * "Products"), provided that each user of the Products must obtain their own
- * Spine Editor license and redistribution of the Products in any form must
- * include this license and copyright notice.
- *
- * THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "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 LLC 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
- * THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *****************************************************************************/
-
-module spine {
-
-	/** An attachment that displays a textured quadrilateral.
-	 *
-	 * See [Region attachments](http://esotericsoftware.com/spine-regions) in the Spine User Guide. */
-	export class RegionAttachment extends Attachment {
-		/** The local x translation. */
-		x = 0;
-
-		/** The local y translation. */
-		y = 0;
-
-		/** The local scaleX. */
-		scaleX = 1;
-
-		/** The local scaleY. */
-		scaleY = 1;
-
-		/** The local rotation. */
-		rotation = 0;
-
-		/** The width of the region attachment in Spine. */
-		width = 0;
-
-		/** The height of the region attachment in Spine. */
-		height = 0;
-
-		/** The color to tint the region attachment. */
-		color = new Color(1, 1, 1, 1);
-
-		/** The name of the texture region for this attachment. */
-		path: string;
-
-		rendererObject: any;
-		region: TextureRegion;
-
-		/** For each of the 4 vertices, a pair of <code>x,y</code> values that is the local position of the vertex.
-		 *
-		 * See {@link #updateOffset()}. */
-		offset = Utils.newFloatArray(8);
-
-		uvs = Utils.newFloatArray(8);
-
-		tempColor = new Color(1, 1, 1, 1);
-
-		constructor (name: string) {
-			super(name);
-		}
-
-		/** Calculates the {@link #offset} using the region settings. Must be called after changing region settings. */
-		updateOffset (): void {
-			let region = this.region;
-			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 x = this.x, y = this.y;
-			let localXCos = localX * cos + x;
-			let localXSin = localX * sin;
-			let localYCos = localY * cos + y;
-			let localYSin = localY * sin;
-			let localX2Cos = localX2 * cos + x;
-			let localX2Sin = localX2 * sin;
-			let localY2Cos = localY2 * cos + y;
-			let localY2Sin = localY2 * sin;
-			let offset = this.offset;
-			offset[0] = localXCos - localYSin;
-			offset[1] = localYCos + localXSin;
-			offset[2] = localXCos - localY2Sin;
-			offset[3] = localY2Cos + localXSin;
-			offset[4] = localX2Cos - localY2Sin;
-			offset[5] = localY2Cos + localX2Sin;
-			offset[6] = localX2Cos - localYSin;
-			offset[7] = localYCos + localX2Sin;
-		}
-
-		setRegion (region: TextureRegion): void {
-			this.region = region;
-			let uvs = this.uvs;
-			if (region.degrees == 90) {
-				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;
-			}
-		}
-
-		/** Transforms the attachment's four vertices to world coordinates.
-		 *
-		 * See [World transforms](http://esotericsoftware.com/spine-runtime-skeletons#World-transforms) in the Spine
-		 * Runtimes Guide.
-		 * @param worldVertices The output world vertices. Must have a length >= `offset` + 8.
-		 * @param offset The `worldVertices` index to begin writing values.
-		 * @param stride The number of `worldVertices` entries between the value pairs written. */
-		computeWorldVertices (bone: Bone, worldVertices: ArrayLike<number>, offset: number, stride: number) {
-			let vertexOffset = this.offset;
-			let x = bone.worldX, y = bone.worldY;
-			let a = bone.a, b = bone.b, c = bone.c, d = bone.d;
-			let offsetX = 0, offsetY = 0;
-
-			offsetX = vertexOffset[0];
-			offsetY = vertexOffset[1];
-			worldVertices[offset] = offsetX * a + offsetY * b + x; // br
-			worldVertices[offset + 1] = offsetX * c + offsetY * d + y;
-			offset += stride;
-
-			offsetX = vertexOffset[2];
-			offsetY = vertexOffset[3];
-			worldVertices[offset] = offsetX * a + offsetY * b + x; // bl
-			worldVertices[offset + 1] = offsetX * c + offsetY * d + y;
-			offset += stride;
-
-			offsetX = vertexOffset[4];
-			offsetY = vertexOffset[5];
-			worldVertices[offset] = offsetX * a + offsetY * b + x; // ul
-			worldVertices[offset + 1] = offsetX * c + offsetY * d + y;
-			offset += stride;
-
-			offsetX = vertexOffset[6];
-			offsetY = vertexOffset[7];
-			worldVertices[offset] = offsetX * a + offsetY * b + x; // ur
-			worldVertices[offset + 1] = offsetX * c + offsetY * d + y;
-		}
-
-		copy (): Attachment {
-			let copy = new RegionAttachment(this.name);
-			copy.region = this.region;
-			copy.rendererObject = this.rendererObject;
-			copy.path = this.path;
-			copy.x = this.x;
-			copy.y = this.y;
-			copy.scaleX = this.scaleX;
-			copy.scaleY = this.scaleY;
-			copy.rotation = this.rotation;
-			copy.width = this.width;
-			copy.height = this.height;
-			Utils.arrayCopy(this.uvs, 0, copy.uvs, 0, 8);
-			Utils.arrayCopy(this.offset, 0, copy.offset, 0, 8);
-			copy.color.setFromColor(this.color);
-			return copy;
-		}
-
-		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;
-	}
-}

+ 9023 - 0
spine-ts/package-lock.json

@@ -0,0 +1,9023 @@
+{
+  "name": "spine-ts",
+  "version": "4.0.1",
+  "lockfileVersion": 2,
+  "requires": true,
+  "packages": {
+    "": {
+      "version": "4.0.1",
+      "license": "LicenseRef-LICENSE",
+      "workspaces": [
+        "spine-core/*",
+        "spine-canvas/*",
+        "spine-player/*",
+        "spine-threejs/*",
+        "spine-webgl/*"
+      ],
+      "devDependencies": {
+        "npx": "^10.2.2",
+        "typescript": "^4.3.5"
+      }
+    },
+    "node_modules/npx": {
+      "version": "10.2.2",
+      "resolved": "https://registry.npmjs.org/npx/-/npx-10.2.2.tgz",
+      "integrity": "sha512-eImmySusyeWphzs5iNh791XbZnZG0FSNvM4KSah34pdQQIDsdTDhIwg1sjN3AIVcjGLpbQ/YcfqHPshKZQK1fA==",
+      "bundleDependencies": [
+        "npm",
+        "libnpx"
+      ],
+      "dev": true,
+      "dependencies": {
+        "libnpx": "10.2.2",
+        "npm": "5.1.0"
+      },
+      "bin": {
+        "npx": "index.js"
+      }
+    },
+    "node_modules/npx/node_modules/ansi-align": {
+      "version": "2.0.0",
+      "dev": true,
+      "inBundle": true,
+      "license": "ISC",
+      "dependencies": {
+        "string-width": "^2.0.0"
+      }
+    },
+    "node_modules/npx/node_modules/ansi-regex": {
+      "version": "3.0.0",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=4"
+      }
+    },
+    "node_modules/npx/node_modules/ansi-styles": {
+      "version": "3.2.1",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT",
+      "dependencies": {
+        "color-convert": "^1.9.0"
+      },
+      "engines": {
+        "node": ">=4"
+      }
+    },
+    "node_modules/npx/node_modules/balanced-match": {
+      "version": "1.0.0",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT"
+    },
+    "node_modules/npx/node_modules/boxen": {
+      "version": "1.3.0",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT",
+      "dependencies": {
+        "ansi-align": "^2.0.0",
+        "camelcase": "^4.0.0",
+        "chalk": "^2.0.1",
+        "cli-boxes": "^1.0.0",
+        "string-width": "^2.0.0",
+        "term-size": "^1.2.0",
+        "widest-line": "^2.0.0"
+      },
+      "engines": {
+        "node": ">=4"
+      }
+    },
+    "node_modules/npx/node_modules/brace-expansion": {
+      "version": "1.1.11",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT",
+      "dependencies": {
+        "balanced-match": "^1.0.0",
+        "concat-map": "0.0.1"
+      }
+    },
+    "node_modules/npx/node_modules/builtins": {
+      "version": "1.0.3",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT"
+    },
+    "node_modules/npx/node_modules/camelcase": {
+      "version": "4.1.0",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=4"
+      }
+    },
+    "node_modules/npx/node_modules/capture-stack-trace": {
+      "version": "1.0.1",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/npx/node_modules/chalk": {
+      "version": "2.4.2",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT",
+      "dependencies": {
+        "ansi-styles": "^3.2.1",
+        "escape-string-regexp": "^1.0.5",
+        "supports-color": "^5.3.0"
+      },
+      "engines": {
+        "node": ">=4"
+      }
+    },
+    "node_modules/npx/node_modules/ci-info": {
+      "version": "1.6.0",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT"
+    },
+    "node_modules/npx/node_modules/cli-boxes": {
+      "version": "1.0.0",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/npx/node_modules/cliui": {
+      "version": "4.1.0",
+      "dev": true,
+      "inBundle": true,
+      "license": "ISC",
+      "dependencies": {
+        "string-width": "^2.1.1",
+        "strip-ansi": "^4.0.0",
+        "wrap-ansi": "^2.0.0"
+      }
+    },
+    "node_modules/npx/node_modules/code-point-at": {
+      "version": "1.1.0",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/npx/node_modules/color-convert": {
+      "version": "1.9.3",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT",
+      "dependencies": {
+        "color-name": "1.1.3"
+      }
+    },
+    "node_modules/npx/node_modules/color-name": {
+      "version": "1.1.3",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT"
+    },
+    "node_modules/npx/node_modules/concat-map": {
+      "version": "0.0.1",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT"
+    },
+    "node_modules/npx/node_modules/configstore": {
+      "version": "3.1.2",
+      "dev": true,
+      "inBundle": true,
+      "license": "BSD-2-Clause",
+      "dependencies": {
+        "dot-prop": "^4.1.0",
+        "graceful-fs": "^4.1.2",
+        "make-dir": "^1.0.0",
+        "unique-string": "^1.0.0",
+        "write-file-atomic": "^2.0.0",
+        "xdg-basedir": "^3.0.0"
+      },
+      "engines": {
+        "node": ">=4"
+      }
+    },
+    "node_modules/npx/node_modules/create-error-class": {
+      "version": "3.0.2",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT",
+      "dependencies": {
+        "capture-stack-trace": "^1.0.0"
+      },
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/npx/node_modules/cross-spawn": {
+      "version": "5.1.0",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT",
+      "dependencies": {
+        "lru-cache": "^4.0.1",
+        "shebang-command": "^1.2.0",
+        "which": "^1.2.9"
+      }
+    },
+    "node_modules/npx/node_modules/crypto-random-string": {
+      "version": "1.0.0",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=4"
+      }
+    },
+    "node_modules/npx/node_modules/decamelize": {
+      "version": "1.2.0",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/npx/node_modules/deep-extend": {
+      "version": "0.6.0",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=4.0.0"
+      }
+    },
+    "node_modules/npx/node_modules/dot-prop": {
+      "version": "4.2.0",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT",
+      "dependencies": {
+        "is-obj": "^1.0.0"
+      },
+      "engines": {
+        "node": ">=4"
+      }
+    },
+    "node_modules/npx/node_modules/dotenv": {
+      "version": "5.0.1",
+      "dev": true,
+      "inBundle": true,
+      "license": "BSD-2-Clause",
+      "engines": {
+        "node": ">=4.6.0"
+      }
+    },
+    "node_modules/npx/node_modules/duplexer3": {
+      "version": "0.1.4",
+      "dev": true,
+      "inBundle": true,
+      "license": "BSD-3-Clause"
+    },
+    "node_modules/npx/node_modules/end-of-stream": {
+      "version": "1.4.4",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT",
+      "dependencies": {
+        "once": "^1.4.0"
+      }
+    },
+    "node_modules/npx/node_modules/escape-string-regexp": {
+      "version": "1.0.5",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=0.8.0"
+      }
+    },
+    "node_modules/npx/node_modules/execa": {
+      "version": "0.7.0",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT",
+      "dependencies": {
+        "cross-spawn": "^5.0.1",
+        "get-stream": "^3.0.0",
+        "is-stream": "^1.1.0",
+        "npm-run-path": "^2.0.0",
+        "p-finally": "^1.0.0",
+        "signal-exit": "^3.0.0",
+        "strip-eof": "^1.0.0"
+      },
+      "engines": {
+        "node": ">=4"
+      }
+    },
+    "node_modules/npx/node_modules/find-up": {
+      "version": "2.1.0",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT",
+      "dependencies": {
+        "locate-path": "^2.0.0"
+      },
+      "engines": {
+        "node": ">=4"
+      }
+    },
+    "node_modules/npx/node_modules/fs.realpath": {
+      "version": "1.0.0",
+      "dev": true,
+      "inBundle": true,
+      "license": "ISC"
+    },
+    "node_modules/npx/node_modules/get-caller-file": {
+      "version": "1.0.3",
+      "dev": true,
+      "inBundle": true,
+      "license": "ISC"
+    },
+    "node_modules/npx/node_modules/get-stream": {
+      "version": "3.0.0",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=4"
+      }
+    },
+    "node_modules/npx/node_modules/glob": {
+      "version": "7.1.6",
+      "dev": true,
+      "inBundle": true,
+      "license": "ISC",
+      "dependencies": {
+        "fs.realpath": "^1.0.0",
+        "inflight": "^1.0.4",
+        "inherits": "2",
+        "minimatch": "^3.0.4",
+        "once": "^1.3.0",
+        "path-is-absolute": "^1.0.0"
+      },
+      "engines": {
+        "node": "*"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/isaacs"
+      }
+    },
+    "node_modules/npx/node_modules/global-dirs": {
+      "version": "0.1.1",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT",
+      "dependencies": {
+        "ini": "^1.3.4"
+      },
+      "engines": {
+        "node": ">=4"
+      }
+    },
+    "node_modules/npx/node_modules/got": {
+      "version": "6.7.1",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT",
+      "dependencies": {
+        "create-error-class": "^3.0.0",
+        "duplexer3": "^0.1.4",
+        "get-stream": "^3.0.0",
+        "is-redirect": "^1.0.0",
+        "is-retry-allowed": "^1.0.0",
+        "is-stream": "^1.0.0",
+        "lowercase-keys": "^1.0.0",
+        "safe-buffer": "^5.0.1",
+        "timed-out": "^4.0.0",
+        "unzip-response": "^2.0.1",
+        "url-parse-lax": "^1.0.0"
+      },
+      "engines": {
+        "node": ">=4"
+      }
+    },
+    "node_modules/npx/node_modules/graceful-fs": {
+      "version": "4.2.3",
+      "dev": true,
+      "inBundle": true,
+      "license": "ISC"
+    },
+    "node_modules/npx/node_modules/has-flag": {
+      "version": "3.0.0",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=4"
+      }
+    },
+    "node_modules/npx/node_modules/hosted-git-info": {
+      "version": "2.8.5",
+      "dev": true,
+      "inBundle": true,
+      "license": "ISC"
+    },
+    "node_modules/npx/node_modules/import-lazy": {
+      "version": "2.1.0",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=4"
+      }
+    },
+    "node_modules/npx/node_modules/imurmurhash": {
+      "version": "0.1.4",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=0.8.19"
+      }
+    },
+    "node_modules/npx/node_modules/inflight": {
+      "version": "1.0.6",
+      "dev": true,
+      "inBundle": true,
+      "license": "ISC",
+      "dependencies": {
+        "once": "^1.3.0",
+        "wrappy": "1"
+      }
+    },
+    "node_modules/npx/node_modules/inherits": {
+      "version": "2.0.4",
+      "dev": true,
+      "inBundle": true,
+      "license": "ISC"
+    },
+    "node_modules/npx/node_modules/ini": {
+      "version": "1.3.5",
+      "dev": true,
+      "inBundle": true,
+      "license": "ISC",
+      "engines": {
+        "node": "*"
+      }
+    },
+    "node_modules/npx/node_modules/invert-kv": {
+      "version": "2.0.0",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=4"
+      }
+    },
+    "node_modules/npx/node_modules/is-ci": {
+      "version": "1.2.1",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT",
+      "dependencies": {
+        "ci-info": "^1.5.0"
+      },
+      "bin": {
+        "is-ci": "bin.js"
+      }
+    },
+    "node_modules/npx/node_modules/is-fullwidth-code-point": {
+      "version": "2.0.0",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=4"
+      }
+    },
+    "node_modules/npx/node_modules/is-installed-globally": {
+      "version": "0.1.0",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT",
+      "dependencies": {
+        "global-dirs": "^0.1.0",
+        "is-path-inside": "^1.0.0"
+      },
+      "engines": {
+        "node": ">=4"
+      }
+    },
+    "node_modules/npx/node_modules/is-npm": {
+      "version": "1.0.0",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/npx/node_modules/is-obj": {
+      "version": "1.0.1",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/npx/node_modules/is-path-inside": {
+      "version": "1.0.1",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT",
+      "dependencies": {
+        "path-is-inside": "^1.0.1"
+      },
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/npx/node_modules/is-redirect": {
+      "version": "1.0.0",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/npx/node_modules/is-retry-allowed": {
+      "version": "1.2.0",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/npx/node_modules/is-stream": {
+      "version": "1.1.0",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/npx/node_modules/isexe": {
+      "version": "2.0.0",
+      "dev": true,
+      "inBundle": true,
+      "license": "ISC"
+    },
+    "node_modules/npx/node_modules/latest-version": {
+      "version": "3.1.0",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT",
+      "dependencies": {
+        "package-json": "^4.0.0"
+      },
+      "engines": {
+        "node": ">=4"
+      }
+    },
+    "node_modules/npx/node_modules/lcid": {
+      "version": "2.0.0",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT",
+      "dependencies": {
+        "invert-kv": "^2.0.0"
+      },
+      "engines": {
+        "node": ">=6"
+      }
+    },
+    "node_modules/npx/node_modules/libnpx": {
+      "version": "10.2.2",
+      "dev": true,
+      "inBundle": true,
+      "license": "ISC",
+      "dependencies": {
+        "dotenv": "^5.0.1",
+        "npm-package-arg": "^6.0.0",
+        "rimraf": "^2.6.2",
+        "safe-buffer": "^5.1.0",
+        "update-notifier": "^2.3.0",
+        "which": "^1.3.0",
+        "y18n": "^4.0.0",
+        "yargs": "^11.0.0"
+      },
+      "engines": {
+        "node": ">=4"
+      }
+    },
+    "node_modules/npx/node_modules/locate-path": {
+      "version": "2.0.0",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT",
+      "dependencies": {
+        "p-locate": "^2.0.0",
+        "path-exists": "^3.0.0"
+      },
+      "engines": {
+        "node": ">=4"
+      }
+    },
+    "node_modules/npx/node_modules/lowercase-keys": {
+      "version": "1.0.1",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/npx/node_modules/lru-cache": {
+      "version": "4.1.5",
+      "dev": true,
+      "inBundle": true,
+      "license": "ISC",
+      "dependencies": {
+        "pseudomap": "^1.0.2",
+        "yallist": "^2.1.2"
+      }
+    },
+    "node_modules/npx/node_modules/make-dir": {
+      "version": "1.3.0",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT",
+      "dependencies": {
+        "pify": "^3.0.0"
+      },
+      "engines": {
+        "node": ">=4"
+      }
+    },
+    "node_modules/npx/node_modules/map-age-cleaner": {
+      "version": "0.1.3",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT",
+      "dependencies": {
+        "p-defer": "^1.0.0"
+      },
+      "engines": {
+        "node": ">=6"
+      }
+    },
+    "node_modules/npx/node_modules/mem": {
+      "version": "4.3.0",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT",
+      "dependencies": {
+        "map-age-cleaner": "^0.1.1",
+        "mimic-fn": "^2.0.0",
+        "p-is-promise": "^2.0.0"
+      },
+      "engines": {
+        "node": ">=6"
+      }
+    },
+    "node_modules/npx/node_modules/mimic-fn": {
+      "version": "2.1.0",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=6"
+      }
+    },
+    "node_modules/npx/node_modules/minimatch": {
+      "version": "3.0.4",
+      "dev": true,
+      "inBundle": true,
+      "license": "ISC",
+      "dependencies": {
+        "brace-expansion": "^1.1.7"
+      },
+      "engines": {
+        "node": "*"
+      }
+    },
+    "node_modules/npx/node_modules/minimist": {
+      "version": "1.2.0",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT"
+    },
+    "node_modules/npx/node_modules/nice-try": {
+      "version": "1.0.5",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT"
+    },
+    "node_modules/npx/node_modules/npm": {
+      "version": "5.1.0",
+      "bundleDependencies": [
+        "abbrev",
+        "ansi-regex",
+        "ansicolors",
+        "ansistyles",
+        "aproba",
+        "archy",
+        "cacache",
+        "call-limit",
+        "bluebird",
+        "chownr",
+        "cmd-shim",
+        "columnify",
+        "config-chain",
+        "debuglog",
+        "detect-indent",
+        "dezalgo",
+        "editor",
+        "fs-vacuum",
+        "fs-write-stream-atomic",
+        "fstream",
+        "fstream-npm",
+        "glob",
+        "graceful-fs",
+        "has-unicode",
+        "hosted-git-info",
+        "iferr",
+        "imurmurhash",
+        "inflight",
+        "inherits",
+        "ini",
+        "init-package-json",
+        "JSONStream",
+        "lazy-property",
+        "lockfile",
+        "lodash._baseindexof",
+        "lodash._baseuniq",
+        "lodash._bindcallback",
+        "lodash._cacheindexof",
+        "lodash._createcache",
+        "lodash._getnative",
+        "lodash.clonedeep",
+        "lodash.restparam",
+        "lodash.union",
+        "lodash.uniq",
+        "lodash.without",
+        "lru-cache",
+        "mkdirp",
+        "mississippi",
+        "move-concurrently",
+        "node-gyp",
+        "nopt",
+        "normalize-package-data",
+        "npm-cache-filename",
+        "npm-install-checks",
+        "npm-package-arg",
+        "npm-registry-client",
+        "npm-user-validate",
+        "npmlog",
+        "once",
+        "opener",
+        "osenv",
+        "pacote",
+        "path-is-inside",
+        "promise-inflight",
+        "read",
+        "read-cmd-shim",
+        "read-installed",
+        "read-package-json",
+        "read-package-tree",
+        "readable-stream",
+        "readdir-scoped-modules",
+        "request",
+        "retry",
+        "rimraf",
+        "semver",
+        "sha",
+        "slide",
+        "sorted-object",
+        "sorted-union-stream",
+        "ssri",
+        "strip-ansi",
+        "tar",
+        "text-table",
+        "uid-number",
+        "umask",
+        "unique-filename",
+        "unpipe",
+        "update-notifier",
+        "uuid",
+        "validate-npm-package-license",
+        "validate-npm-package-name",
+        "which",
+        "wrappy",
+        "write-file-atomic",
+        "safe-buffer",
+        "worker-farm"
+      ],
+      "dev": true,
+      "inBundle": true,
+      "license": "Artistic-2.0",
+      "dependencies": {
+        "abbrev": "~1.1.0",
+        "ansi-regex": "~3.0.0",
+        "ansicolors": "~0.3.2",
+        "ansistyles": "~0.1.3",
+        "aproba": "~1.1.2",
+        "archy": "~1.0.0",
+        "bluebird": "~3.5.0",
+        "cacache": "~9.2.9",
+        "call-limit": "~1.1.0",
+        "chownr": "~1.0.1",
+        "cmd-shim": "~2.0.2",
+        "columnify": "~1.5.4",
+        "config-chain": "~1.1.11",
+        "debuglog": "*",
+        "detect-indent": "~5.0.0",
+        "dezalgo": "~1.0.3",
+        "editor": "~1.0.0",
+        "fs-vacuum": "~1.2.10",
+        "fs-write-stream-atomic": "~1.0.10",
+        "fstream": "~1.0.11",
+        "fstream-npm": "~1.2.1",
+        "glob": "~7.1.2",
+        "graceful-fs": "~4.1.11",
+        "has-unicode": "~2.0.1",
+        "hosted-git-info": "~2.5.0",
+        "iferr": "~0.1.5",
+        "imurmurhash": "*",
+        "inflight": "~1.0.6",
+        "inherits": "~2.0.3",
+        "ini": "~1.3.4",
+        "init-package-json": "~1.10.1",
+        "JSONStream": "~1.3.1",
+        "lazy-property": "~1.0.0",
+        "lockfile": "~1.0.3",
+        "lodash._baseindexof": "*",
+        "lodash._baseuniq": "~4.6.0",
+        "lodash._bindcallback": "*",
+        "lodash._cacheindexof": "*",
+        "lodash._createcache": "*",
+        "lodash._getnative": "*",
+        "lodash.clonedeep": "~4.5.0",
+        "lodash.restparam": "*",
+        "lodash.union": "~4.6.0",
+        "lodash.uniq": "~4.5.0",
+        "lodash.without": "~4.4.0",
+        "lru-cache": "~4.1.1",
+        "mississippi": "~1.3.0",
+        "mkdirp": "~0.5.1",
+        "move-concurrently": "~1.0.1",
+        "node-gyp": "~3.6.2",
+        "nopt": "~4.0.1",
+        "normalize-package-data": "~2.4.0",
+        "npm-cache-filename": "~1.0.2",
+        "npm-install-checks": "~3.0.0",
+        "npm-package-arg": "~5.1.2",
+        "npm-registry-client": "~8.4.0",
+        "npm-user-validate": "~1.0.0",
+        "npmlog": "~4.1.2",
+        "once": "~1.4.0",
+        "opener": "~1.4.3",
+        "osenv": "~0.1.4",
+        "pacote": "~2.7.38",
+        "path-is-inside": "~1.0.2",
+        "promise-inflight": "~1.0.1",
+        "read": "~1.0.7",
+        "read-cmd-shim": "~1.0.1",
+        "read-installed": "~4.0.3",
+        "read-package-json": "~2.0.9",
+        "read-package-tree": "~5.1.6",
+        "readable-stream": "~2.3.2",
+        "readdir-scoped-modules": "*",
+        "request": "~2.81.0",
+        "retry": "~0.10.1",
+        "rimraf": "~2.6.1",
+        "safe-buffer": "~5.1.1",
+        "semver": "~5.3.0",
+        "sha": "~2.0.1",
+        "slide": "~1.1.6",
+        "sorted-object": "~2.0.1",
+        "sorted-union-stream": "~2.1.3",
+        "ssri": "~4.1.6",
+        "strip-ansi": "~4.0.0",
+        "tar": "~2.2.1",
+        "text-table": "~0.2.0",
+        "uid-number": "0.0.6",
+        "umask": "~1.1.0",
+        "unique-filename": "~1.1.0",
+        "unpipe": "~1.0.0",
+        "update-notifier": "~2.2.0",
+        "uuid": "~3.1.0",
+        "validate-npm-package-license": "*",
+        "validate-npm-package-name": "~3.0.0",
+        "which": "~1.2.14",
+        "worker-farm": "~1.3.1",
+        "wrappy": "~1.0.2",
+        "write-file-atomic": "~2.1.0"
+      },
+      "bin": {
+        "npm": "bin/npm-cli.js"
+      }
+    },
+    "node_modules/npx/node_modules/npm-package-arg": {
+      "version": "6.1.1",
+      "dev": true,
+      "inBundle": true,
+      "license": "ISC",
+      "dependencies": {
+        "hosted-git-info": "^2.7.1",
+        "osenv": "^0.1.5",
+        "semver": "^5.6.0",
+        "validate-npm-package-name": "^3.0.0"
+      }
+    },
+    "node_modules/npx/node_modules/npm-run-path": {
+      "version": "2.0.2",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT",
+      "dependencies": {
+        "path-key": "^2.0.0"
+      },
+      "engines": {
+        "node": ">=4"
+      }
+    },
+    "node_modules/npx/node_modules/npm/node_modules/abbrev": {
+      "version": "1.1.0",
+      "dev": true,
+      "inBundle": true,
+      "license": "ISC"
+    },
+    "node_modules/npx/node_modules/npm/node_modules/ansi-regex": {
+      "version": "3.0.0",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=4"
+      }
+    },
+    "node_modules/npx/node_modules/npm/node_modules/ansicolors": {
+      "version": "0.3.2",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT"
+    },
+    "node_modules/npx/node_modules/npm/node_modules/ansistyles": {
+      "version": "0.1.3",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT"
+    },
+    "node_modules/npx/node_modules/npm/node_modules/aproba": {
+      "version": "1.1.2",
+      "dev": true,
+      "inBundle": true,
+      "license": "ISC"
+    },
+    "node_modules/npx/node_modules/npm/node_modules/archy": {
+      "version": "1.0.0",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT"
+    },
+    "node_modules/npx/node_modules/npm/node_modules/bluebird": {
+      "version": "3.5.0",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT"
+    },
+    "node_modules/npx/node_modules/npm/node_modules/cacache": {
+      "version": "9.2.9",
+      "dev": true,
+      "inBundle": true,
+      "license": "CC0-1.0",
+      "dependencies": {
+        "bluebird": "^3.5.0",
+        "chownr": "^1.0.1",
+        "glob": "^7.1.2",
+        "graceful-fs": "^4.1.11",
+        "lru-cache": "^4.1.1",
+        "mississippi": "^1.3.0",
+        "mkdirp": "^0.5.1",
+        "move-concurrently": "^1.0.1",
+        "promise-inflight": "^1.0.1",
+        "rimraf": "^2.6.1",
+        "ssri": "^4.1.6",
+        "unique-filename": "^1.1.0",
+        "y18n": "^3.2.1"
+      }
+    },
+    "node_modules/npx/node_modules/npm/node_modules/cacache/node_modules/lru-cache": {
+      "version": "4.1.1",
+      "dev": true,
+      "inBundle": true,
+      "license": "ISC",
+      "dependencies": {
+        "pseudomap": "^1.0.2",
+        "yallist": "^2.1.2"
+      }
+    },
+    "node_modules/npx/node_modules/npm/node_modules/cacache/node_modules/lru-cache/node_modules/pseudomap": {
+      "version": "1.0.2",
+      "dev": true,
+      "inBundle": true,
+      "license": "ISC"
+    },
+    "node_modules/npx/node_modules/npm/node_modules/cacache/node_modules/lru-cache/node_modules/yallist": {
+      "version": "2.1.2",
+      "dev": true,
+      "inBundle": true,
+      "license": "ISC"
+    },
+    "node_modules/npx/node_modules/npm/node_modules/cacache/node_modules/y18n": {
+      "version": "3.2.1",
+      "dev": true,
+      "inBundle": true,
+      "license": "ISC"
+    },
+    "node_modules/npx/node_modules/npm/node_modules/call-limit": {
+      "version": "1.1.0",
+      "dev": true,
+      "inBundle": true,
+      "license": "ISC"
+    },
+    "node_modules/npx/node_modules/npm/node_modules/chownr": {
+      "version": "1.0.1",
+      "dev": true,
+      "inBundle": true,
+      "license": "ISC"
+    },
+    "node_modules/npx/node_modules/npm/node_modules/cmd-shim": {
+      "version": "2.0.2",
+      "dev": true,
+      "inBundle": true,
+      "license": "BSD-2-Clause",
+      "dependencies": {
+        "graceful-fs": "^4.1.2",
+        "mkdirp": "~0.5.0"
+      }
+    },
+    "node_modules/npx/node_modules/npm/node_modules/columnify": {
+      "version": "1.5.4",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT",
+      "dependencies": {
+        "strip-ansi": "^3.0.0",
+        "wcwidth": "^1.0.0"
+      }
+    },
+    "node_modules/npx/node_modules/npm/node_modules/columnify/node_modules/strip-ansi": {
+      "version": "3.0.1",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT",
+      "dependencies": {
+        "ansi-regex": "^2.0.0"
+      },
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/npx/node_modules/npm/node_modules/columnify/node_modules/strip-ansi/node_modules/ansi-regex": {
+      "version": "2.1.1",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/npx/node_modules/npm/node_modules/columnify/node_modules/wcwidth": {
+      "version": "1.0.1",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT",
+      "dependencies": {
+        "defaults": "^1.0.3"
+      }
+    },
+    "node_modules/npx/node_modules/npm/node_modules/columnify/node_modules/wcwidth/node_modules/defaults": {
+      "version": "1.0.3",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT",
+      "dependencies": {
+        "clone": "^1.0.2"
+      }
+    },
+    "node_modules/npx/node_modules/npm/node_modules/columnify/node_modules/wcwidth/node_modules/defaults/node_modules/clone": {
+      "version": "1.0.2",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=0.8"
+      }
+    },
+    "node_modules/npx/node_modules/npm/node_modules/config-chain": {
+      "version": "1.1.11",
+      "dev": true,
+      "inBundle": true,
+      "dependencies": {
+        "ini": "^1.3.4",
+        "proto-list": "~1.2.1"
+      }
+    },
+    "node_modules/npx/node_modules/npm/node_modules/config-chain/node_modules/proto-list": {
+      "version": "1.2.4",
+      "dev": true,
+      "inBundle": true,
+      "license": "ISC"
+    },
+    "node_modules/npx/node_modules/npm/node_modules/debuglog": {
+      "version": "1.0.1",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT",
+      "engines": {
+        "node": "*"
+      }
+    },
+    "node_modules/npx/node_modules/npm/node_modules/detect-indent": {
+      "version": "5.0.0",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=4"
+      }
+    },
+    "node_modules/npx/node_modules/npm/node_modules/dezalgo": {
+      "version": "1.0.3",
+      "dev": true,
+      "inBundle": true,
+      "license": "ISC",
+      "dependencies": {
+        "asap": "^2.0.0",
+        "wrappy": "1"
+      }
+    },
+    "node_modules/npx/node_modules/npm/node_modules/dezalgo/node_modules/asap": {
+      "version": "2.0.5",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT"
+    },
+    "node_modules/npx/node_modules/npm/node_modules/editor": {
+      "version": "1.0.0",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT"
+    },
+    "node_modules/npx/node_modules/npm/node_modules/fs-vacuum": {
+      "version": "1.2.10",
+      "dev": true,
+      "inBundle": true,
+      "license": "ISC",
+      "dependencies": {
+        "graceful-fs": "^4.1.2",
+        "path-is-inside": "^1.0.1",
+        "rimraf": "^2.5.2"
+      }
+    },
+    "node_modules/npx/node_modules/npm/node_modules/fs-write-stream-atomic": {
+      "version": "1.0.10",
+      "dev": true,
+      "inBundle": true,
+      "license": "ISC",
+      "dependencies": {
+        "graceful-fs": "^4.1.2",
+        "iferr": "^0.1.5",
+        "imurmurhash": "^0.1.4",
+        "readable-stream": "1 || 2"
+      }
+    },
+    "node_modules/npx/node_modules/npm/node_modules/fstream": {
+      "version": "1.0.11",
+      "dev": true,
+      "inBundle": true,
+      "license": "ISC",
+      "dependencies": {
+        "graceful-fs": "^4.1.2",
+        "inherits": "~2.0.0",
+        "mkdirp": ">=0.5 0",
+        "rimraf": "2"
+      },
+      "engines": {
+        "node": ">=0.6"
+      }
+    },
+    "node_modules/npx/node_modules/npm/node_modules/fstream-npm": {
+      "version": "1.2.1",
+      "dev": true,
+      "inBundle": true,
+      "license": "ISC",
+      "dependencies": {
+        "fstream-ignore": "^1.0.0",
+        "inherits": "2"
+      }
+    },
+    "node_modules/npx/node_modules/npm/node_modules/fstream-npm/node_modules/fstream-ignore": {
+      "version": "1.0.5",
+      "dev": true,
+      "inBundle": true,
+      "license": "ISC",
+      "dependencies": {
+        "fstream": "^1.0.0",
+        "inherits": "2",
+        "minimatch": "^3.0.0"
+      }
+    },
+    "node_modules/npx/node_modules/npm/node_modules/fstream-npm/node_modules/fstream-ignore/node_modules/minimatch": {
+      "version": "3.0.4",
+      "dev": true,
+      "inBundle": true,
+      "license": "ISC",
+      "dependencies": {
+        "brace-expansion": "^1.1.7"
+      },
+      "engines": {
+        "node": "*"
+      }
+    },
+    "node_modules/npx/node_modules/npm/node_modules/fstream-npm/node_modules/fstream-ignore/node_modules/minimatch/node_modules/brace-expansion": {
+      "version": "1.1.8",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT",
+      "dependencies": {
+        "balanced-match": "^1.0.0",
+        "concat-map": "0.0.1"
+      }
+    },
+    "node_modules/npx/node_modules/npm/node_modules/fstream-npm/node_modules/fstream-ignore/node_modules/minimatch/node_modules/brace-expansion/node_modules/balanced-match": {
+      "version": "1.0.0",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT"
+    },
+    "node_modules/npx/node_modules/npm/node_modules/fstream-npm/node_modules/fstream-ignore/node_modules/minimatch/node_modules/brace-expansion/node_modules/concat-map": {
+      "version": "0.0.1",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT"
+    },
+    "node_modules/npx/node_modules/npm/node_modules/glob": {
+      "version": "7.1.2",
+      "dev": true,
+      "inBundle": true,
+      "license": "ISC",
+      "dependencies": {
+        "fs.realpath": "^1.0.0",
+        "inflight": "^1.0.4",
+        "inherits": "2",
+        "minimatch": "^3.0.4",
+        "once": "^1.3.0",
+        "path-is-absolute": "^1.0.0"
+      },
+      "engines": {
+        "node": "*"
+      }
+    },
+    "node_modules/npx/node_modules/npm/node_modules/glob/node_modules/fs.realpath": {
+      "version": "1.0.0",
+      "dev": true,
+      "inBundle": true,
+      "license": "ISC"
+    },
+    "node_modules/npx/node_modules/npm/node_modules/glob/node_modules/minimatch": {
+      "version": "3.0.4",
+      "dev": true,
+      "inBundle": true,
+      "license": "ISC",
+      "dependencies": {
+        "brace-expansion": "^1.1.7"
+      },
+      "engines": {
+        "node": "*"
+      }
+    },
+    "node_modules/npx/node_modules/npm/node_modules/glob/node_modules/minimatch/node_modules/brace-expansion": {
+      "version": "1.1.8",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT",
+      "dependencies": {
+        "balanced-match": "^1.0.0",
+        "concat-map": "0.0.1"
+      }
+    },
+    "node_modules/npx/node_modules/npm/node_modules/glob/node_modules/minimatch/node_modules/brace-expansion/node_modules/balanced-match": {
+      "version": "1.0.0",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT"
+    },
+    "node_modules/npx/node_modules/npm/node_modules/glob/node_modules/minimatch/node_modules/brace-expansion/node_modules/concat-map": {
+      "version": "0.0.1",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT"
+    },
+    "node_modules/npx/node_modules/npm/node_modules/glob/node_modules/path-is-absolute": {
+      "version": "1.0.1",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/npx/node_modules/npm/node_modules/graceful-fs": {
+      "version": "4.1.11",
+      "dev": true,
+      "inBundle": true,
+      "license": "ISC",
+      "engines": {
+        "node": ">=0.4.0"
+      }
+    },
+    "node_modules/npx/node_modules/npm/node_modules/has-unicode": {
+      "version": "2.0.1",
+      "dev": true,
+      "inBundle": true,
+      "license": "ISC"
+    },
+    "node_modules/npx/node_modules/npm/node_modules/hosted-git-info": {
+      "version": "2.5.0",
+      "dev": true,
+      "inBundle": true,
+      "license": "ISC"
+    },
+    "node_modules/npx/node_modules/npm/node_modules/iferr": {
+      "version": "0.1.5",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT"
+    },
+    "node_modules/npx/node_modules/npm/node_modules/imurmurhash": {
+      "version": "0.1.4",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=0.8.19"
+      }
+    },
+    "node_modules/npx/node_modules/npm/node_modules/inflight": {
+      "version": "1.0.6",
+      "dev": true,
+      "inBundle": true,
+      "license": "ISC",
+      "dependencies": {
+        "once": "^1.3.0",
+        "wrappy": "1"
+      }
+    },
+    "node_modules/npx/node_modules/npm/node_modules/inherits": {
+      "version": "2.0.3",
+      "dev": true,
+      "inBundle": true,
+      "license": "ISC"
+    },
+    "node_modules/npx/node_modules/npm/node_modules/ini": {
+      "version": "1.3.4",
+      "dev": true,
+      "inBundle": true,
+      "license": "ISC",
+      "engines": {
+        "node": "*"
+      }
+    },
+    "node_modules/npx/node_modules/npm/node_modules/init-package-json": {
+      "version": "1.10.1",
+      "dev": true,
+      "inBundle": true,
+      "license": "ISC",
+      "dependencies": {
+        "glob": "^7.1.1",
+        "npm-package-arg": "^4.0.0 || ^5.0.0",
+        "promzard": "^0.3.0",
+        "read": "~1.0.1",
+        "read-package-json": "1 || 2",
+        "semver": "2.x || 3.x || 4 || 5",
+        "validate-npm-package-license": "^3.0.1",
+        "validate-npm-package-name": "^3.0.0"
+      }
+    },
+    "node_modules/npx/node_modules/npm/node_modules/init-package-json/node_modules/promzard": {
+      "version": "0.3.0",
+      "dev": true,
+      "inBundle": true,
+      "license": "ISC",
+      "dependencies": {
+        "read": "1"
+      }
+    },
+    "node_modules/npx/node_modules/npm/node_modules/JSONStream": {
+      "version": "1.3.1",
+      "dev": true,
+      "inBundle": true,
+      "license": "(MIT OR Apache-2.0)",
+      "dependencies": {
+        "jsonparse": "^1.2.0",
+        "through": ">=2.2.7 <3"
+      },
+      "bin": {
+        "JSONStream": "index.js"
+      },
+      "engines": {
+        "node": "*"
+      }
+    },
+    "node_modules/npx/node_modules/npm/node_modules/JSONStream/node_modules/jsonparse": {
+      "version": "1.3.1",
+      "dev": true,
+      "engines": [
+        "node >= 0.2.0"
+      ],
+      "inBundle": true,
+      "license": "MIT"
+    },
+    "node_modules/npx/node_modules/npm/node_modules/JSONStream/node_modules/through": {
+      "version": "2.3.8",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT"
+    },
+    "node_modules/npx/node_modules/npm/node_modules/lazy-property": {
+      "version": "1.0.0",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT"
+    },
+    "node_modules/npx/node_modules/npm/node_modules/lockfile": {
+      "version": "1.0.3",
+      "dev": true,
+      "inBundle": true,
+      "license": "ISC"
+    },
+    "node_modules/npx/node_modules/npm/node_modules/lodash._baseindexof": {
+      "version": "3.1.0",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT"
+    },
+    "node_modules/npx/node_modules/npm/node_modules/lodash._baseuniq": {
+      "version": "4.6.0",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT",
+      "dependencies": {
+        "lodash._createset": "~4.0.0",
+        "lodash._root": "~3.0.0"
+      }
+    },
+    "node_modules/npx/node_modules/npm/node_modules/lodash._baseuniq/node_modules/lodash._createset": {
+      "version": "4.0.3",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT"
+    },
+    "node_modules/npx/node_modules/npm/node_modules/lodash._baseuniq/node_modules/lodash._root": {
+      "version": "3.0.1",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT"
+    },
+    "node_modules/npx/node_modules/npm/node_modules/lodash._bindcallback": {
+      "version": "3.0.1",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT"
+    },
+    "node_modules/npx/node_modules/npm/node_modules/lodash._cacheindexof": {
+      "version": "3.0.2",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT"
+    },
+    "node_modules/npx/node_modules/npm/node_modules/lodash._createcache": {
+      "version": "3.1.2",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT",
+      "dependencies": {
+        "lodash._getnative": "^3.0.0"
+      }
+    },
+    "node_modules/npx/node_modules/npm/node_modules/lodash._getnative": {
+      "version": "3.9.1",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT"
+    },
+    "node_modules/npx/node_modules/npm/node_modules/lodash.clonedeep": {
+      "version": "4.5.0",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT"
+    },
+    "node_modules/npx/node_modules/npm/node_modules/lodash.restparam": {
+      "version": "3.6.1",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT"
+    },
+    "node_modules/npx/node_modules/npm/node_modules/lodash.union": {
+      "version": "4.6.0",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT"
+    },
+    "node_modules/npx/node_modules/npm/node_modules/lodash.uniq": {
+      "version": "4.5.0",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT"
+    },
+    "node_modules/npx/node_modules/npm/node_modules/lodash.without": {
+      "version": "4.4.0",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT"
+    },
+    "node_modules/npx/node_modules/npm/node_modules/lru-cache": {
+      "version": "4.1.1",
+      "dev": true,
+      "inBundle": true,
+      "license": "ISC",
+      "dependencies": {
+        "pseudomap": "^1.0.2",
+        "yallist": "^2.1.2"
+      }
+    },
+    "node_modules/npx/node_modules/npm/node_modules/lru-cache/node_modules/pseudomap": {
+      "version": "1.0.2",
+      "dev": true,
+      "inBundle": true,
+      "license": "ISC"
+    },
+    "node_modules/npx/node_modules/npm/node_modules/lru-cache/node_modules/yallist": {
+      "version": "2.1.2",
+      "dev": true,
+      "inBundle": true,
+      "license": "ISC"
+    },
+    "node_modules/npx/node_modules/npm/node_modules/mississippi": {
+      "version": "1.3.0",
+      "dev": true,
+      "inBundle": true,
+      "license": "BSD-2-Clause",
+      "dependencies": {
+        "concat-stream": "^1.5.0",
+        "duplexify": "^3.4.2",
+        "end-of-stream": "^1.1.0",
+        "flush-write-stream": "^1.0.0",
+        "from2": "^2.1.0",
+        "parallel-transform": "^1.1.0",
+        "pump": "^1.0.0",
+        "pumpify": "^1.3.3",
+        "stream-each": "^1.1.0",
+        "through2": "^2.0.0"
+      }
+    },
+    "node_modules/npx/node_modules/npm/node_modules/mississippi/node_modules/concat-stream": {
+      "version": "1.6.0",
+      "dev": true,
+      "engines": [
+        "node >= 0.8"
+      ],
+      "inBundle": true,
+      "license": "MIT",
+      "dependencies": {
+        "inherits": "^2.0.3",
+        "readable-stream": "^2.2.2",
+        "typedarray": "^0.0.6"
+      }
+    },
+    "node_modules/npx/node_modules/npm/node_modules/mississippi/node_modules/concat-stream/node_modules/typedarray": {
+      "version": "0.0.6",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT"
+    },
+    "node_modules/npx/node_modules/npm/node_modules/mississippi/node_modules/duplexify": {
+      "version": "3.5.0",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT",
+      "dependencies": {
+        "end-of-stream": "1.0.0",
+        "inherits": "^2.0.1",
+        "readable-stream": "^2.0.0",
+        "stream-shift": "^1.0.0"
+      }
+    },
+    "node_modules/npx/node_modules/npm/node_modules/mississippi/node_modules/duplexify/node_modules/end-of-stream": {
+      "version": "1.0.0",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT",
+      "dependencies": {
+        "once": "~1.3.0"
+      }
+    },
+    "node_modules/npx/node_modules/npm/node_modules/mississippi/node_modules/duplexify/node_modules/end-of-stream/node_modules/once": {
+      "version": "1.3.3",
+      "dev": true,
+      "inBundle": true,
+      "license": "ISC",
+      "dependencies": {
+        "wrappy": "1"
+      }
+    },
+    "node_modules/npx/node_modules/npm/node_modules/mississippi/node_modules/duplexify/node_modules/stream-shift": {
+      "version": "1.0.0",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT"
+    },
+    "node_modules/npx/node_modules/npm/node_modules/mississippi/node_modules/end-of-stream": {
+      "version": "1.4.0",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT",
+      "dependencies": {
+        "once": "^1.4.0"
+      }
+    },
+    "node_modules/npx/node_modules/npm/node_modules/mississippi/node_modules/flush-write-stream": {
+      "version": "1.0.2",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT",
+      "dependencies": {
+        "inherits": "^2.0.1",
+        "readable-stream": "^2.0.4"
+      }
+    },
+    "node_modules/npx/node_modules/npm/node_modules/mississippi/node_modules/from2": {
+      "version": "2.3.0",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT",
+      "dependencies": {
+        "inherits": "^2.0.1",
+        "readable-stream": "^2.0.0"
+      }
+    },
+    "node_modules/npx/node_modules/npm/node_modules/mississippi/node_modules/parallel-transform": {
+      "version": "1.1.0",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT",
+      "dependencies": {
+        "cyclist": "~0.2.2",
+        "inherits": "^2.0.3",
+        "readable-stream": "^2.1.5"
+      }
+    },
+    "node_modules/npx/node_modules/npm/node_modules/mississippi/node_modules/parallel-transform/node_modules/cyclist": {
+      "version": "0.2.2",
+      "dev": true,
+      "inBundle": true
+    },
+    "node_modules/npx/node_modules/npm/node_modules/mississippi/node_modules/pump": {
+      "version": "1.0.2",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT",
+      "dependencies": {
+        "end-of-stream": "^1.1.0",
+        "once": "^1.3.1"
+      }
+    },
+    "node_modules/npx/node_modules/npm/node_modules/mississippi/node_modules/pumpify": {
+      "version": "1.3.5",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT",
+      "dependencies": {
+        "duplexify": "^3.1.2",
+        "inherits": "^2.0.1",
+        "pump": "^1.0.0"
+      }
+    },
+    "node_modules/npx/node_modules/npm/node_modules/mississippi/node_modules/stream-each": {
+      "version": "1.2.0",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT",
+      "dependencies": {
+        "end-of-stream": "^1.1.0",
+        "stream-shift": "^1.0.0"
+      }
+    },
+    "node_modules/npx/node_modules/npm/node_modules/mississippi/node_modules/stream-each/node_modules/stream-shift": {
+      "version": "1.0.0",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT"
+    },
+    "node_modules/npx/node_modules/npm/node_modules/mississippi/node_modules/through2": {
+      "version": "2.0.3",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT",
+      "dependencies": {
+        "readable-stream": "^2.1.5",
+        "xtend": "~4.0.1"
+      }
+    },
+    "node_modules/npx/node_modules/npm/node_modules/mississippi/node_modules/through2/node_modules/xtend": {
+      "version": "4.0.1",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=0.4"
+      }
+    },
+    "node_modules/npx/node_modules/npm/node_modules/mkdirp": {
+      "version": "0.5.1",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT",
+      "dependencies": {
+        "minimist": "0.0.8"
+      },
+      "bin": {
+        "mkdirp": "bin/cmd.js"
+      }
+    },
+    "node_modules/npx/node_modules/npm/node_modules/mkdirp/node_modules/minimist": {
+      "version": "0.0.8",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT"
+    },
+    "node_modules/npx/node_modules/npm/node_modules/move-concurrently": {
+      "version": "1.0.1",
+      "dev": true,
+      "inBundle": true,
+      "license": "ISC",
+      "dependencies": {
+        "aproba": "^1.1.1",
+        "copy-concurrently": "^1.0.0",
+        "fs-write-stream-atomic": "^1.0.8",
+        "mkdirp": "^0.5.1",
+        "rimraf": "^2.5.4",
+        "run-queue": "^1.0.3"
+      }
+    },
+    "node_modules/npx/node_modules/npm/node_modules/move-concurrently/node_modules/copy-concurrently": {
+      "version": "1.0.3",
+      "dev": true,
+      "inBundle": true,
+      "license": "ISC",
+      "dependencies": {
+        "aproba": "^1.1.1",
+        "fs-write-stream-atomic": "^1.0.8",
+        "iferr": "^0.1.5",
+        "mkdirp": "^0.5.1",
+        "rimraf": "^2.5.4",
+        "run-queue": "^1.0.0"
+      }
+    },
+    "node_modules/npx/node_modules/npm/node_modules/move-concurrently/node_modules/run-queue": {
+      "version": "1.0.3",
+      "dev": true,
+      "inBundle": true,
+      "license": "ISC",
+      "dependencies": {
+        "aproba": "^1.1.1"
+      }
+    },
+    "node_modules/npx/node_modules/npm/node_modules/node-gyp": {
+      "version": "3.6.2",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT",
+      "dependencies": {
+        "fstream": "^1.0.0",
+        "glob": "^7.0.3",
+        "graceful-fs": "^4.1.2",
+        "minimatch": "^3.0.2",
+        "mkdirp": "^0.5.0",
+        "nopt": "2 || 3",
+        "npmlog": "0 || 1 || 2 || 3 || 4",
+        "osenv": "0",
+        "request": "2",
+        "rimraf": "2",
+        "semver": "~5.3.0",
+        "tar": "^2.0.0",
+        "which": "1"
+      },
+      "bin": {
+        "node-gyp": "bin/node-gyp.js"
+      },
+      "engines": {
+        "node": ">= 0.8.0"
+      }
+    },
+    "node_modules/npx/node_modules/npm/node_modules/node-gyp/node_modules/minimatch": {
+      "version": "3.0.4",
+      "dev": true,
+      "inBundle": true,
+      "license": "ISC",
+      "dependencies": {
+        "brace-expansion": "^1.1.7"
+      },
+      "engines": {
+        "node": "*"
+      }
+    },
+    "node_modules/npx/node_modules/npm/node_modules/node-gyp/node_modules/minimatch/node_modules/brace-expansion": {
+      "version": "1.1.8",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT",
+      "dependencies": {
+        "balanced-match": "^1.0.0",
+        "concat-map": "0.0.1"
+      }
+    },
+    "node_modules/npx/node_modules/npm/node_modules/node-gyp/node_modules/minimatch/node_modules/brace-expansion/node_modules/balanced-match": {
+      "version": "1.0.0",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT"
+    },
+    "node_modules/npx/node_modules/npm/node_modules/node-gyp/node_modules/minimatch/node_modules/brace-expansion/node_modules/concat-map": {
+      "version": "0.0.1",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT"
+    },
+    "node_modules/npx/node_modules/npm/node_modules/node-gyp/node_modules/nopt": {
+      "version": "3.0.6",
+      "dev": true,
+      "inBundle": true,
+      "license": "ISC",
+      "dependencies": {
+        "abbrev": "1"
+      },
+      "bin": {
+        "nopt": "bin/nopt.js"
+      }
+    },
+    "node_modules/npx/node_modules/npm/node_modules/nopt": {
+      "version": "4.0.1",
+      "dev": true,
+      "inBundle": true,
+      "license": "ISC",
+      "dependencies": {
+        "abbrev": "1",
+        "osenv": "^0.1.4"
+      },
+      "bin": {
+        "nopt": "bin/nopt.js"
+      }
+    },
+    "node_modules/npx/node_modules/npm/node_modules/normalize-package-data": {
+      "version": "2.4.0",
+      "dev": true,
+      "inBundle": true,
+      "license": "BSD-2-Clause",
+      "dependencies": {
+        "hosted-git-info": "^2.1.4",
+        "is-builtin-module": "^1.0.0",
+        "semver": "2 || 3 || 4 || 5",
+        "validate-npm-package-license": "^3.0.1"
+      }
+    },
+    "node_modules/npx/node_modules/npm/node_modules/normalize-package-data/node_modules/is-builtin-module": {
+      "version": "1.0.0",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT",
+      "dependencies": {
+        "builtin-modules": "^1.0.0"
+      },
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/npx/node_modules/npm/node_modules/normalize-package-data/node_modules/is-builtin-module/node_modules/builtin-modules": {
+      "version": "1.1.1",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/npx/node_modules/npm/node_modules/npm-cache-filename": {
+      "version": "1.0.2",
+      "dev": true,
+      "inBundle": true,
+      "license": "ISC"
+    },
+    "node_modules/npx/node_modules/npm/node_modules/npm-install-checks": {
+      "version": "3.0.0",
+      "dev": true,
+      "inBundle": true,
+      "license": "BSD-2-Clause",
+      "dependencies": {
+        "semver": "^2.3.0 || 3.x || 4 || 5"
+      }
+    },
+    "node_modules/npx/node_modules/npm/node_modules/npm-package-arg": {
+      "version": "5.1.2",
+      "dev": true,
+      "inBundle": true,
+      "license": "ISC",
+      "dependencies": {
+        "hosted-git-info": "^2.4.2",
+        "osenv": "^0.1.4",
+        "semver": "^5.1.0",
+        "validate-npm-package-name": "^3.0.0"
+      }
+    },
+    "node_modules/npx/node_modules/npm/node_modules/npm-registry-client": {
+      "version": "8.4.0",
+      "dev": true,
+      "inBundle": true,
+      "license": "ISC",
+      "dependencies": {
+        "concat-stream": "^1.5.2",
+        "graceful-fs": "^4.1.6",
+        "normalize-package-data": "~1.0.1 || ^2.0.0",
+        "npm-package-arg": "^3.0.0 || ^4.0.0 || ^5.0.0",
+        "once": "^1.3.3",
+        "request": "^2.74.0",
+        "retry": "^0.10.0",
+        "semver": "2 >=2.2.1 || 3.x || 4 || 5",
+        "slide": "^1.1.3",
+        "ssri": "^4.1.2"
+      },
+      "optionalDependencies": {
+        "npmlog": "2 || ^3.1.0 || ^4.0.0"
+      }
+    },
+    "node_modules/npx/node_modules/npm/node_modules/npm-registry-client/node_modules/concat-stream": {
+      "version": "1.6.0",
+      "dev": true,
+      "engines": [
+        "node >= 0.8"
+      ],
+      "inBundle": true,
+      "license": "MIT",
+      "dependencies": {
+        "inherits": "^2.0.3",
+        "readable-stream": "^2.2.2",
+        "typedarray": "^0.0.6"
+      }
+    },
+    "node_modules/npx/node_modules/npm/node_modules/npm-registry-client/node_modules/concat-stream/node_modules/typedarray": {
+      "version": "0.0.6",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT"
+    },
+    "node_modules/npx/node_modules/npm/node_modules/npm-user-validate": {
+      "version": "1.0.0",
+      "dev": true,
+      "inBundle": true,
+      "license": "BSD-2-Clause"
+    },
+    "node_modules/npx/node_modules/npm/node_modules/npmlog": {
+      "version": "4.1.2",
+      "dev": true,
+      "inBundle": true,
+      "license": "ISC",
+      "dependencies": {
+        "are-we-there-yet": "~1.1.2",
+        "console-control-strings": "~1.1.0",
+        "gauge": "~2.7.3",
+        "set-blocking": "~2.0.0"
+      }
+    },
+    "node_modules/npx/node_modules/npm/node_modules/npmlog/node_modules/are-we-there-yet": {
+      "version": "1.1.4",
+      "dev": true,
+      "inBundle": true,
+      "license": "ISC",
+      "dependencies": {
+        "delegates": "^1.0.0",
+        "readable-stream": "^2.0.6"
+      }
+    },
+    "node_modules/npx/node_modules/npm/node_modules/npmlog/node_modules/are-we-there-yet/node_modules/delegates": {
+      "version": "1.0.0",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT"
+    },
+    "node_modules/npx/node_modules/npm/node_modules/npmlog/node_modules/console-control-strings": {
+      "version": "1.1.0",
+      "dev": true,
+      "inBundle": true,
+      "license": "ISC"
+    },
+    "node_modules/npx/node_modules/npm/node_modules/npmlog/node_modules/gauge": {
+      "version": "2.7.4",
+      "dev": true,
+      "inBundle": true,
+      "license": "ISC",
+      "dependencies": {
+        "aproba": "^1.0.3",
+        "console-control-strings": "^1.0.0",
+        "has-unicode": "^2.0.0",
+        "object-assign": "^4.1.0",
+        "signal-exit": "^3.0.0",
+        "string-width": "^1.0.1",
+        "strip-ansi": "^3.0.1",
+        "wide-align": "^1.1.0"
+      }
+    },
+    "node_modules/npx/node_modules/npm/node_modules/npmlog/node_modules/gauge/node_modules/object-assign": {
+      "version": "4.1.1",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/npx/node_modules/npm/node_modules/npmlog/node_modules/gauge/node_modules/signal-exit": {
+      "version": "3.0.2",
+      "dev": true,
+      "inBundle": true,
+      "license": "ISC"
+    },
+    "node_modules/npx/node_modules/npm/node_modules/npmlog/node_modules/gauge/node_modules/string-width": {
+      "version": "1.0.2",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT",
+      "dependencies": {
+        "code-point-at": "^1.0.0",
+        "is-fullwidth-code-point": "^1.0.0",
+        "strip-ansi": "^3.0.0"
+      },
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/npx/node_modules/npm/node_modules/npmlog/node_modules/gauge/node_modules/string-width/node_modules/code-point-at": {
+      "version": "1.1.0",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/npx/node_modules/npm/node_modules/npmlog/node_modules/gauge/node_modules/string-width/node_modules/is-fullwidth-code-point": {
+      "version": "1.0.0",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT",
+      "dependencies": {
+        "number-is-nan": "^1.0.0"
+      },
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/npx/node_modules/npm/node_modules/npmlog/node_modules/gauge/node_modules/string-width/node_modules/is-fullwidth-code-point/node_modules/number-is-nan": {
+      "version": "1.0.1",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/npx/node_modules/npm/node_modules/npmlog/node_modules/gauge/node_modules/strip-ansi": {
+      "version": "3.0.1",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT",
+      "dependencies": {
+        "ansi-regex": "^2.0.0"
+      },
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/npx/node_modules/npm/node_modules/npmlog/node_modules/gauge/node_modules/strip-ansi/node_modules/ansi-regex": {
+      "version": "2.1.1",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/npx/node_modules/npm/node_modules/npmlog/node_modules/gauge/node_modules/wide-align": {
+      "version": "1.1.2",
+      "dev": true,
+      "inBundle": true,
+      "license": "ISC",
+      "dependencies": {
+        "string-width": "^1.0.2"
+      }
+    },
+    "node_modules/npx/node_modules/npm/node_modules/npmlog/node_modules/set-blocking": {
+      "version": "2.0.0",
+      "dev": true,
+      "inBundle": true,
+      "license": "ISC"
+    },
+    "node_modules/npx/node_modules/npm/node_modules/once": {
+      "version": "1.4.0",
+      "dev": true,
+      "inBundle": true,
+      "license": "ISC",
+      "dependencies": {
+        "wrappy": "1"
+      }
+    },
+    "node_modules/npx/node_modules/npm/node_modules/opener": {
+      "version": "1.4.3",
+      "dev": true,
+      "inBundle": true,
+      "license": "(WTFPL OR MIT)",
+      "bin": {
+        "opener": "opener.js"
+      }
+    },
+    "node_modules/npx/node_modules/npm/node_modules/osenv": {
+      "version": "0.1.4",
+      "dev": true,
+      "inBundle": true,
+      "license": "ISC",
+      "dependencies": {
+        "os-homedir": "^1.0.0",
+        "os-tmpdir": "^1.0.0"
+      }
+    },
+    "node_modules/npx/node_modules/npm/node_modules/osenv/node_modules/os-homedir": {
+      "version": "1.0.2",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/npx/node_modules/npm/node_modules/osenv/node_modules/os-tmpdir": {
+      "version": "1.0.2",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/npx/node_modules/npm/node_modules/pacote": {
+      "version": "2.7.38",
+      "dev": true,
+      "inBundle": true,
+      "license": "CC0-1.0",
+      "dependencies": {
+        "bluebird": "^3.5.0",
+        "cacache": "^9.2.9",
+        "glob": "^7.1.2",
+        "lru-cache": "^4.1.1",
+        "make-fetch-happen": "^2.4.13",
+        "minimatch": "^3.0.4",
+        "mississippi": "^1.2.0",
+        "normalize-package-data": "^2.4.0",
+        "npm-package-arg": "^5.1.2",
+        "npm-pick-manifest": "^1.0.4",
+        "osenv": "^0.1.4",
+        "promise-inflight": "^1.0.1",
+        "promise-retry": "^1.1.1",
+        "protoduck": "^4.0.0",
+        "safe-buffer": "^5.1.1",
+        "semver": "^5.3.0",
+        "ssri": "^4.1.6",
+        "tar-fs": "^1.15.3",
+        "tar-stream": "^1.5.4",
+        "unique-filename": "^1.1.0",
+        "which": "^1.2.12"
+      }
+    },
+    "node_modules/npx/node_modules/npm/node_modules/pacote/node_modules/make-fetch-happen": {
+      "version": "2.4.13",
+      "dev": true,
+      "inBundle": true,
+      "license": "CC0-1.0",
+      "dependencies": {
+        "agentkeepalive": "^3.3.0",
+        "cacache": "^9.2.9",
+        "http-cache-semantics": "^3.7.3",
+        "http-proxy-agent": "^2.0.0",
+        "https-proxy-agent": "^2.0.0",
+        "lru-cache": "^4.1.1",
+        "mississippi": "^1.2.0",
+        "node-fetch-npm": "^2.0.1",
+        "promise-retry": "^1.1.1",
+        "socks-proxy-agent": "^3.0.0",
+        "ssri": "^4.1.6"
+      }
+    },
+    "node_modules/npx/node_modules/npm/node_modules/pacote/node_modules/make-fetch-happen/node_modules/agentkeepalive": {
+      "version": "3.3.0",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT",
+      "dependencies": {
+        "humanize-ms": "^1.2.1"
+      },
+      "engines": {
+        "node": ">= 4.0.0"
+      }
+    },
+    "node_modules/npx/node_modules/npm/node_modules/pacote/node_modules/make-fetch-happen/node_modules/agentkeepalive/node_modules/humanize-ms": {
+      "version": "1.2.1",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT",
+      "dependencies": {
+        "ms": "^2.0.0"
+      }
+    },
+    "node_modules/npx/node_modules/npm/node_modules/pacote/node_modules/make-fetch-happen/node_modules/agentkeepalive/node_modules/humanize-ms/node_modules/ms": {
+      "version": "2.0.0",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT"
+    },
+    "node_modules/npx/node_modules/npm/node_modules/pacote/node_modules/make-fetch-happen/node_modules/http-cache-semantics": {
+      "version": "3.7.3",
+      "dev": true,
+      "inBundle": true,
+      "license": "BSD-2-Clause"
+    },
+    "node_modules/npx/node_modules/npm/node_modules/pacote/node_modules/make-fetch-happen/node_modules/http-proxy-agent": {
+      "version": "2.0.0",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT",
+      "dependencies": {
+        "agent-base": "4",
+        "debug": "2"
+      }
+    },
+    "node_modules/npx/node_modules/npm/node_modules/pacote/node_modules/make-fetch-happen/node_modules/http-proxy-agent/node_modules/agent-base": {
+      "version": "4.1.0",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT",
+      "dependencies": {
+        "es6-promisify": "^5.0.0"
+      },
+      "engines": {
+        "node": ">= 4.0.0"
+      }
+    },
+    "node_modules/npx/node_modules/npm/node_modules/pacote/node_modules/make-fetch-happen/node_modules/http-proxy-agent/node_modules/agent-base/node_modules/es6-promisify": {
+      "version": "5.0.0",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT",
+      "dependencies": {
+        "es6-promise": "^4.0.3"
+      }
+    },
+    "node_modules/npx/node_modules/npm/node_modules/pacote/node_modules/make-fetch-happen/node_modules/http-proxy-agent/node_modules/agent-base/node_modules/es6-promisify/node_modules/es6-promise": {
+      "version": "4.1.1",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT"
+    },
+    "node_modules/npx/node_modules/npm/node_modules/pacote/node_modules/make-fetch-happen/node_modules/http-proxy-agent/node_modules/debug": {
+      "version": "2.6.8",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT",
+      "dependencies": {
+        "ms": "2.0.0"
+      }
+    },
+    "node_modules/npx/node_modules/npm/node_modules/pacote/node_modules/make-fetch-happen/node_modules/http-proxy-agent/node_modules/debug/node_modules/ms": {
+      "version": "2.0.0",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT"
+    },
+    "node_modules/npx/node_modules/npm/node_modules/pacote/node_modules/make-fetch-happen/node_modules/https-proxy-agent": {
+      "version": "2.0.0",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT",
+      "dependencies": {
+        "agent-base": "^4.1.0",
+        "debug": "^2.4.1"
+      }
+    },
+    "node_modules/npx/node_modules/npm/node_modules/pacote/node_modules/make-fetch-happen/node_modules/https-proxy-agent/node_modules/agent-base": {
+      "version": "4.1.0",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT",
+      "dependencies": {
+        "es6-promisify": "^5.0.0"
+      },
+      "engines": {
+        "node": ">= 4.0.0"
+      }
+    },
+    "node_modules/npx/node_modules/npm/node_modules/pacote/node_modules/make-fetch-happen/node_modules/https-proxy-agent/node_modules/agent-base/node_modules/es6-promisify": {
+      "version": "5.0.0",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT",
+      "dependencies": {
+        "es6-promise": "^4.0.3"
+      }
+    },
+    "node_modules/npx/node_modules/npm/node_modules/pacote/node_modules/make-fetch-happen/node_modules/https-proxy-agent/node_modules/agent-base/node_modules/es6-promisify/node_modules/es6-promise": {
+      "version": "4.1.1",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT"
+    },
+    "node_modules/npx/node_modules/npm/node_modules/pacote/node_modules/make-fetch-happen/node_modules/https-proxy-agent/node_modules/debug": {
+      "version": "2.6.8",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT",
+      "dependencies": {
+        "ms": "2.0.0"
+      }
+    },
+    "node_modules/npx/node_modules/npm/node_modules/pacote/node_modules/make-fetch-happen/node_modules/https-proxy-agent/node_modules/debug/node_modules/ms": {
+      "version": "2.0.0",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT"
+    },
+    "node_modules/npx/node_modules/npm/node_modules/pacote/node_modules/make-fetch-happen/node_modules/node-fetch-npm": {
+      "version": "2.0.1",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT",
+      "dependencies": {
+        "encoding": "^0.1.11",
+        "json-parse-helpfulerror": "^1.0.3",
+        "safe-buffer": "^5.0.1"
+      },
+      "engines": {
+        "node": ">=4"
+      }
+    },
+    "node_modules/npx/node_modules/npm/node_modules/pacote/node_modules/make-fetch-happen/node_modules/node-fetch-npm/node_modules/encoding": {
+      "version": "0.1.12",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT",
+      "dependencies": {
+        "iconv-lite": "~0.4.13"
+      }
+    },
+    "node_modules/npx/node_modules/npm/node_modules/pacote/node_modules/make-fetch-happen/node_modules/node-fetch-npm/node_modules/encoding/node_modules/iconv-lite": {
+      "version": "0.4.18",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/npx/node_modules/npm/node_modules/pacote/node_modules/make-fetch-happen/node_modules/node-fetch-npm/node_modules/json-parse-helpfulerror": {
+      "version": "1.0.3",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT",
+      "dependencies": {
+        "jju": "^1.1.0"
+      }
+    },
+    "node_modules/npx/node_modules/npm/node_modules/pacote/node_modules/make-fetch-happen/node_modules/node-fetch-npm/node_modules/json-parse-helpfulerror/node_modules/jju": {
+      "version": "1.3.0",
+      "dev": true,
+      "inBundle": true,
+      "license": "WTFPL"
+    },
+    "node_modules/npx/node_modules/npm/node_modules/pacote/node_modules/make-fetch-happen/node_modules/socks-proxy-agent": {
+      "version": "3.0.0",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT",
+      "dependencies": {
+        "agent-base": "^4.0.1",
+        "socks": "^1.1.10"
+      }
+    },
+    "node_modules/npx/node_modules/npm/node_modules/pacote/node_modules/make-fetch-happen/node_modules/socks-proxy-agent/node_modules/agent-base": {
+      "version": "4.1.0",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT",
+      "dependencies": {
+        "es6-promisify": "^5.0.0"
+      },
+      "engines": {
+        "node": ">= 4.0.0"
+      }
+    },
+    "node_modules/npx/node_modules/npm/node_modules/pacote/node_modules/make-fetch-happen/node_modules/socks-proxy-agent/node_modules/agent-base/node_modules/es6-promisify": {
+      "version": "5.0.0",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT",
+      "dependencies": {
+        "es6-promise": "^4.0.3"
+      }
+    },
+    "node_modules/npx/node_modules/npm/node_modules/pacote/node_modules/make-fetch-happen/node_modules/socks-proxy-agent/node_modules/agent-base/node_modules/es6-promisify/node_modules/es6-promise": {
+      "version": "4.1.1",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT"
+    },
+    "node_modules/npx/node_modules/npm/node_modules/pacote/node_modules/make-fetch-happen/node_modules/socks-proxy-agent/node_modules/socks": {
+      "version": "1.1.10",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT",
+      "dependencies": {
+        "ip": "^1.1.4",
+        "smart-buffer": "^1.0.13"
+      },
+      "engines": {
+        "node": ">= 0.10.0",
+        "npm": ">= 1.3.5"
+      }
+    },
+    "node_modules/npx/node_modules/npm/node_modules/pacote/node_modules/make-fetch-happen/node_modules/socks-proxy-agent/node_modules/socks/node_modules/ip": {
+      "version": "1.1.5",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT"
+    },
+    "node_modules/npx/node_modules/npm/node_modules/pacote/node_modules/make-fetch-happen/node_modules/socks-proxy-agent/node_modules/socks/node_modules/smart-buffer": {
+      "version": "1.1.15",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.10.15",
+        "npm": ">= 1.3.5"
+      }
+    },
+    "node_modules/npx/node_modules/npm/node_modules/pacote/node_modules/minimatch": {
+      "version": "3.0.4",
+      "dev": true,
+      "inBundle": true,
+      "license": "ISC",
+      "dependencies": {
+        "brace-expansion": "^1.1.7"
+      },
+      "engines": {
+        "node": "*"
+      }
+    },
+    "node_modules/npx/node_modules/npm/node_modules/pacote/node_modules/minimatch/node_modules/brace-expansion": {
+      "version": "1.1.8",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT",
+      "dependencies": {
+        "balanced-match": "^1.0.0",
+        "concat-map": "0.0.1"
+      }
+    },
+    "node_modules/npx/node_modules/npm/node_modules/pacote/node_modules/minimatch/node_modules/brace-expansion/node_modules/balanced-match": {
+      "version": "1.0.0",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT"
+    },
+    "node_modules/npx/node_modules/npm/node_modules/pacote/node_modules/minimatch/node_modules/brace-expansion/node_modules/concat-map": {
+      "version": "0.0.1",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT"
+    },
+    "node_modules/npx/node_modules/npm/node_modules/pacote/node_modules/npm-pick-manifest": {
+      "version": "1.0.4",
+      "dev": true,
+      "inBundle": true,
+      "license": "CC0-1.0",
+      "dependencies": {
+        "npm-package-arg": "^5.1.2",
+        "semver": "^5.3.0"
+      }
+    },
+    "node_modules/npx/node_modules/npm/node_modules/pacote/node_modules/promise-retry": {
+      "version": "1.1.1",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT",
+      "dependencies": {
+        "err-code": "^1.0.0",
+        "retry": "^0.10.0"
+      },
+      "engines": {
+        "node": ">=0.12"
+      }
+    },
+    "node_modules/npx/node_modules/npm/node_modules/pacote/node_modules/promise-retry/node_modules/err-code": {
+      "version": "1.1.2",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT"
+    },
+    "node_modules/npx/node_modules/npm/node_modules/pacote/node_modules/protoduck": {
+      "version": "4.0.0",
+      "dev": true,
+      "inBundle": true,
+      "license": "CC0-1.0",
+      "dependencies": {
+        "genfun": "^4.0.1"
+      }
+    },
+    "node_modules/npx/node_modules/npm/node_modules/pacote/node_modules/protoduck/node_modules/genfun": {
+      "version": "4.0.1",
+      "dev": true,
+      "inBundle": true,
+      "license": "CC0-1.0"
+    },
+    "node_modules/npx/node_modules/npm/node_modules/pacote/node_modules/tar-fs": {
+      "version": "1.15.3",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT",
+      "dependencies": {
+        "chownr": "^1.0.1",
+        "mkdirp": "^0.5.1",
+        "pump": "^1.0.0",
+        "tar-stream": "^1.1.2"
+      }
+    },
+    "node_modules/npx/node_modules/npm/node_modules/pacote/node_modules/tar-fs/node_modules/pump": {
+      "version": "1.0.2",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT",
+      "dependencies": {
+        "end-of-stream": "^1.1.0",
+        "once": "^1.3.1"
+      }
+    },
+    "node_modules/npx/node_modules/npm/node_modules/pacote/node_modules/tar-fs/node_modules/pump/node_modules/end-of-stream": {
+      "version": "1.4.0",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT",
+      "dependencies": {
+        "once": "^1.4.0"
+      }
+    },
+    "node_modules/npx/node_modules/npm/node_modules/pacote/node_modules/tar-stream": {
+      "version": "1.5.4",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT",
+      "dependencies": {
+        "bl": "^1.0.0",
+        "end-of-stream": "^1.0.0",
+        "readable-stream": "^2.0.0",
+        "xtend": "^4.0.0"
+      },
+      "engines": {
+        "node": ">= 0.8.0"
+      }
+    },
+    "node_modules/npx/node_modules/npm/node_modules/pacote/node_modules/tar-stream/node_modules/bl": {
+      "version": "1.2.1",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT",
+      "dependencies": {
+        "readable-stream": "^2.0.5"
+      }
+    },
+    "node_modules/npx/node_modules/npm/node_modules/pacote/node_modules/tar-stream/node_modules/end-of-stream": {
+      "version": "1.4.0",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT",
+      "dependencies": {
+        "once": "^1.4.0"
+      }
+    },
+    "node_modules/npx/node_modules/npm/node_modules/pacote/node_modules/tar-stream/node_modules/xtend": {
+      "version": "4.0.1",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=0.4"
+      }
+    },
+    "node_modules/npx/node_modules/npm/node_modules/path-is-inside": {
+      "version": "1.0.2",
+      "dev": true,
+      "inBundle": true,
+      "license": "(WTFPL OR MIT)"
+    },
+    "node_modules/npx/node_modules/npm/node_modules/promise-inflight": {
+      "version": "1.0.1",
+      "dev": true,
+      "inBundle": true,
+      "license": "ISC"
+    },
+    "node_modules/npx/node_modules/npm/node_modules/read": {
+      "version": "1.0.7",
+      "dev": true,
+      "inBundle": true,
+      "license": "ISC",
+      "dependencies": {
+        "mute-stream": "~0.0.4"
+      },
+      "engines": {
+        "node": ">=0.8"
+      }
+    },
+    "node_modules/npx/node_modules/npm/node_modules/read-cmd-shim": {
+      "version": "1.0.1",
+      "dev": true,
+      "inBundle": true,
+      "license": "ISC",
+      "dependencies": {
+        "graceful-fs": "^4.1.2"
+      }
+    },
+    "node_modules/npx/node_modules/npm/node_modules/read-installed": {
+      "version": "4.0.3",
+      "dev": true,
+      "inBundle": true,
+      "license": "ISC",
+      "dependencies": {
+        "debuglog": "^1.0.1",
+        "read-package-json": "^2.0.0",
+        "readdir-scoped-modules": "^1.0.0",
+        "semver": "2 || 3 || 4 || 5",
+        "slide": "~1.1.3",
+        "util-extend": "^1.0.1"
+      },
+      "optionalDependencies": {
+        "graceful-fs": "^4.1.2"
+      }
+    },
+    "node_modules/npx/node_modules/npm/node_modules/read-installed/node_modules/util-extend": {
+      "version": "1.0.3",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT"
+    },
+    "node_modules/npx/node_modules/npm/node_modules/read-package-json": {
+      "version": "2.0.9",
+      "dev": true,
+      "inBundle": true,
+      "license": "ISC",
+      "dependencies": {
+        "glob": "^7.1.1",
+        "json-parse-helpfulerror": "^1.0.2",
+        "normalize-package-data": "^2.0.0"
+      },
+      "optionalDependencies": {
+        "graceful-fs": "^4.1.2"
+      }
+    },
+    "node_modules/npx/node_modules/npm/node_modules/read-package-json/node_modules/json-parse-helpfulerror": {
+      "version": "1.0.3",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT",
+      "dependencies": {
+        "jju": "^1.1.0"
+      }
+    },
+    "node_modules/npx/node_modules/npm/node_modules/read-package-json/node_modules/json-parse-helpfulerror/node_modules/jju": {
+      "version": "1.3.0",
+      "dev": true,
+      "inBundle": true,
+      "license": "WTFPL"
+    },
+    "node_modules/npx/node_modules/npm/node_modules/read-package-tree": {
+      "version": "5.1.6",
+      "dev": true,
+      "inBundle": true,
+      "license": "ISC",
+      "dependencies": {
+        "debuglog": "^1.0.1",
+        "dezalgo": "^1.0.0",
+        "once": "^1.3.0",
+        "read-package-json": "^2.0.0",
+        "readdir-scoped-modules": "^1.0.0"
+      }
+    },
+    "node_modules/npx/node_modules/npm/node_modules/read/node_modules/mute-stream": {
+      "version": "0.0.7",
+      "dev": true,
+      "inBundle": true,
+      "license": "ISC"
+    },
+    "node_modules/npx/node_modules/npm/node_modules/readable-stream": {
+      "version": "2.3.2",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT",
+      "dependencies": {
+        "core-util-is": "~1.0.0",
+        "inherits": "~2.0.3",
+        "isarray": "~1.0.0",
+        "process-nextick-args": "~1.0.6",
+        "safe-buffer": "~5.1.0",
+        "string_decoder": "~1.0.0",
+        "util-deprecate": "~1.0.1"
+      }
+    },
+    "node_modules/npx/node_modules/npm/node_modules/readable-stream/node_modules/core-util-is": {
+      "version": "1.0.2",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT"
+    },
+    "node_modules/npx/node_modules/npm/node_modules/readable-stream/node_modules/isarray": {
+      "version": "1.0.0",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT"
+    },
+    "node_modules/npx/node_modules/npm/node_modules/readable-stream/node_modules/process-nextick-args": {
+      "version": "1.0.7",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT"
+    },
+    "node_modules/npx/node_modules/npm/node_modules/readable-stream/node_modules/string_decoder": {
+      "version": "1.0.3",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT",
+      "dependencies": {
+        "safe-buffer": "~5.1.0"
+      }
+    },
+    "node_modules/npx/node_modules/npm/node_modules/readable-stream/node_modules/util-deprecate": {
+      "version": "1.0.2",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT"
+    },
+    "node_modules/npx/node_modules/npm/node_modules/readdir-scoped-modules": {
+      "version": "1.0.2",
+      "dev": true,
+      "inBundle": true,
+      "license": "ISC",
+      "dependencies": {
+        "debuglog": "^1.0.1",
+        "dezalgo": "^1.0.0",
+        "graceful-fs": "^4.1.2",
+        "once": "^1.3.0"
+      }
+    },
+    "node_modules/npx/node_modules/npm/node_modules/request": {
+      "version": "2.81.0",
+      "dev": true,
+      "inBundle": true,
+      "license": "Apache-2.0",
+      "dependencies": {
+        "aws-sign2": "~0.6.0",
+        "aws4": "^1.2.1",
+        "caseless": "~0.12.0",
+        "combined-stream": "~1.0.5",
+        "extend": "~3.0.0",
+        "forever-agent": "~0.6.1",
+        "form-data": "~2.1.1",
+        "har-validator": "~4.2.1",
+        "hawk": "~3.1.3",
+        "http-signature": "~1.1.0",
+        "is-typedarray": "~1.0.0",
+        "isstream": "~0.1.2",
+        "json-stringify-safe": "~5.0.1",
+        "mime-types": "~2.1.7",
+        "oauth-sign": "~0.8.1",
+        "performance-now": "^0.2.0",
+        "qs": "~6.4.0",
+        "safe-buffer": "^5.0.1",
+        "stringstream": "~0.0.4",
+        "tough-cookie": "~2.3.0",
+        "tunnel-agent": "^0.6.0",
+        "uuid": "^3.0.0"
+      },
+      "engines": {
+        "node": ">= 4"
+      }
+    },
+    "node_modules/npx/node_modules/npm/node_modules/request/node_modules/aws-sign2": {
+      "version": "0.6.0",
+      "dev": true,
+      "inBundle": true,
+      "license": "Apache-2.0",
+      "engines": {
+        "node": "*"
+      }
+    },
+    "node_modules/npx/node_modules/npm/node_modules/request/node_modules/aws4": {
+      "version": "1.6.0",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT"
+    },
+    "node_modules/npx/node_modules/npm/node_modules/request/node_modules/caseless": {
+      "version": "0.12.0",
+      "dev": true,
+      "inBundle": true,
+      "license": "Apache-2.0"
+    },
+    "node_modules/npx/node_modules/npm/node_modules/request/node_modules/combined-stream": {
+      "version": "1.0.5",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT",
+      "dependencies": {
+        "delayed-stream": "~1.0.0"
+      },
+      "engines": {
+        "node": ">= 0.8"
+      }
+    },
+    "node_modules/npx/node_modules/npm/node_modules/request/node_modules/combined-stream/node_modules/delayed-stream": {
+      "version": "1.0.0",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=0.4.0"
+      }
+    },
+    "node_modules/npx/node_modules/npm/node_modules/request/node_modules/extend": {
+      "version": "3.0.1",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT"
+    },
+    "node_modules/npx/node_modules/npm/node_modules/request/node_modules/forever-agent": {
+      "version": "0.6.1",
+      "dev": true,
+      "inBundle": true,
+      "license": "Apache-2.0",
+      "engines": {
+        "node": "*"
+      }
+    },
+    "node_modules/npx/node_modules/npm/node_modules/request/node_modules/form-data": {
+      "version": "2.1.4",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT",
+      "dependencies": {
+        "asynckit": "^0.4.0",
+        "combined-stream": "^1.0.5",
+        "mime-types": "^2.1.12"
+      },
+      "engines": {
+        "node": ">= 0.12"
+      }
+    },
+    "node_modules/npx/node_modules/npm/node_modules/request/node_modules/form-data/node_modules/asynckit": {
+      "version": "0.4.0",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT"
+    },
+    "node_modules/npx/node_modules/npm/node_modules/request/node_modules/har-validator": {
+      "version": "4.2.1",
+      "dev": true,
+      "inBundle": true,
+      "license": "ISC",
+      "dependencies": {
+        "ajv": "^4.9.1",
+        "har-schema": "^1.0.5"
+      },
+      "engines": {
+        "node": ">=4"
+      }
+    },
+    "node_modules/npx/node_modules/npm/node_modules/request/node_modules/har-validator/node_modules/ajv": {
+      "version": "4.11.8",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT",
+      "dependencies": {
+        "co": "^4.6.0",
+        "json-stable-stringify": "^1.0.1"
+      }
+    },
+    "node_modules/npx/node_modules/npm/node_modules/request/node_modules/har-validator/node_modules/ajv/node_modules/co": {
+      "version": "4.6.0",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT",
+      "engines": {
+        "iojs": ">= 1.0.0",
+        "node": ">= 0.12.0"
+      }
+    },
+    "node_modules/npx/node_modules/npm/node_modules/request/node_modules/har-validator/node_modules/ajv/node_modules/json-stable-stringify": {
+      "version": "1.0.1",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT",
+      "dependencies": {
+        "jsonify": "~0.0.0"
+      }
+    },
+    "node_modules/npx/node_modules/npm/node_modules/request/node_modules/har-validator/node_modules/ajv/node_modules/json-stable-stringify/node_modules/jsonify": {
+      "version": "0.0.0",
+      "dev": true,
+      "inBundle": true,
+      "license": "Public Domain",
+      "engines": {
+        "node": "*"
+      }
+    },
+    "node_modules/npx/node_modules/npm/node_modules/request/node_modules/har-validator/node_modules/har-schema": {
+      "version": "1.0.5",
+      "dev": true,
+      "inBundle": true,
+      "license": "ISC",
+      "engines": {
+        "node": ">=4"
+      }
+    },
+    "node_modules/npx/node_modules/npm/node_modules/request/node_modules/hawk": {
+      "version": "3.1.3",
+      "dev": true,
+      "inBundle": true,
+      "license": "BSD-3-Clause",
+      "dependencies": {
+        "boom": "2.x.x",
+        "cryptiles": "2.x.x",
+        "hoek": "2.x.x",
+        "sntp": "1.x.x"
+      },
+      "engines": {
+        "node": ">=0.10.32"
+      }
+    },
+    "node_modules/npx/node_modules/npm/node_modules/request/node_modules/hawk/node_modules/boom": {
+      "version": "2.10.1",
+      "dev": true,
+      "inBundle": true,
+      "license": "BSD-3-Clause",
+      "dependencies": {
+        "hoek": "2.x.x"
+      },
+      "engines": {
+        "node": ">=0.10.40"
+      }
+    },
+    "node_modules/npx/node_modules/npm/node_modules/request/node_modules/hawk/node_modules/cryptiles": {
+      "version": "2.0.5",
+      "dev": true,
+      "inBundle": true,
+      "license": "BSD-3-Clause",
+      "dependencies": {
+        "boom": "2.x.x"
+      },
+      "engines": {
+        "node": ">=0.10.40"
+      }
+    },
+    "node_modules/npx/node_modules/npm/node_modules/request/node_modules/hawk/node_modules/hoek": {
+      "version": "2.16.3",
+      "dev": true,
+      "inBundle": true,
+      "license": "BSD-3-Clause",
+      "engines": {
+        "node": ">=0.10.40"
+      }
+    },
+    "node_modules/npx/node_modules/npm/node_modules/request/node_modules/hawk/node_modules/sntp": {
+      "version": "1.0.9",
+      "dev": true,
+      "inBundle": true,
+      "dependencies": {
+        "hoek": "2.x.x"
+      },
+      "engines": {
+        "node": ">=0.8.0"
+      }
+    },
+    "node_modules/npx/node_modules/npm/node_modules/request/node_modules/http-signature": {
+      "version": "1.1.1",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT",
+      "dependencies": {
+        "assert-plus": "^0.2.0",
+        "jsprim": "^1.2.2",
+        "sshpk": "^1.7.0"
+      },
+      "engines": {
+        "node": ">=0.8",
+        "npm": ">=1.3.7"
+      }
+    },
+    "node_modules/npx/node_modules/npm/node_modules/request/node_modules/http-signature/node_modules/assert-plus": {
+      "version": "0.2.0",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=0.8"
+      }
+    },
+    "node_modules/npx/node_modules/npm/node_modules/request/node_modules/http-signature/node_modules/jsprim": {
+      "version": "1.4.0",
+      "dev": true,
+      "engines": [
+        "node >=0.6.0"
+      ],
+      "inBundle": true,
+      "license": "MIT",
+      "dependencies": {
+        "assert-plus": "1.0.0",
+        "extsprintf": "1.0.2",
+        "json-schema": "0.2.3",
+        "verror": "1.3.6"
+      }
+    },
+    "node_modules/npx/node_modules/npm/node_modules/request/node_modules/http-signature/node_modules/jsprim/node_modules/assert-plus": {
+      "version": "1.0.0",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=0.8"
+      }
+    },
+    "node_modules/npx/node_modules/npm/node_modules/request/node_modules/http-signature/node_modules/jsprim/node_modules/extsprintf": {
+      "version": "1.0.2",
+      "dev": true,
+      "engines": [
+        "node >=0.6.0"
+      ],
+      "inBundle": true
+    },
+    "node_modules/npx/node_modules/npm/node_modules/request/node_modules/http-signature/node_modules/jsprim/node_modules/json-schema": {
+      "version": "0.2.3",
+      "dev": true,
+      "inBundle": true
+    },
+    "node_modules/npx/node_modules/npm/node_modules/request/node_modules/http-signature/node_modules/jsprim/node_modules/verror": {
+      "version": "1.3.6",
+      "dev": true,
+      "engines": [
+        "node >=0.6.0"
+      ],
+      "inBundle": true,
+      "dependencies": {
+        "extsprintf": "1.0.2"
+      }
+    },
+    "node_modules/npx/node_modules/npm/node_modules/request/node_modules/http-signature/node_modules/sshpk": {
+      "version": "1.13.1",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT",
+      "dependencies": {
+        "asn1": "~0.2.3",
+        "assert-plus": "^1.0.0",
+        "dashdash": "^1.12.0",
+        "getpass": "^0.1.1"
+      },
+      "bin": {
+        "sshpk-conv": "bin/sshpk-conv",
+        "sshpk-sign": "bin/sshpk-sign",
+        "sshpk-verify": "bin/sshpk-verify"
+      },
+      "engines": {
+        "node": ">=0.10.0"
+      },
+      "optionalDependencies": {
+        "bcrypt-pbkdf": "^1.0.0",
+        "ecc-jsbn": "~0.1.1",
+        "jsbn": "~0.1.0",
+        "tweetnacl": "~0.14.0"
+      }
+    },
+    "node_modules/npx/node_modules/npm/node_modules/request/node_modules/http-signature/node_modules/sshpk/node_modules/asn1": {
+      "version": "0.2.3",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT"
+    },
+    "node_modules/npx/node_modules/npm/node_modules/request/node_modules/http-signature/node_modules/sshpk/node_modules/assert-plus": {
+      "version": "1.0.0",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=0.8"
+      }
+    },
+    "node_modules/npx/node_modules/npm/node_modules/request/node_modules/http-signature/node_modules/sshpk/node_modules/bcrypt-pbkdf": {
+      "version": "1.0.1",
+      "dev": true,
+      "inBundle": true,
+      "license": "BSD-3-Clause",
+      "optional": true,
+      "dependencies": {
+        "tweetnacl": "^0.14.3"
+      }
+    },
+    "node_modules/npx/node_modules/npm/node_modules/request/node_modules/http-signature/node_modules/sshpk/node_modules/dashdash": {
+      "version": "1.14.1",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT",
+      "dependencies": {
+        "assert-plus": "^1.0.0"
+      },
+      "engines": {
+        "node": ">=0.10"
+      }
+    },
+    "node_modules/npx/node_modules/npm/node_modules/request/node_modules/http-signature/node_modules/sshpk/node_modules/ecc-jsbn": {
+      "version": "0.1.1",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT",
+      "optional": true,
+      "dependencies": {
+        "jsbn": "~0.1.0"
+      }
+    },
+    "node_modules/npx/node_modules/npm/node_modules/request/node_modules/http-signature/node_modules/sshpk/node_modules/getpass": {
+      "version": "0.1.7",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT",
+      "dependencies": {
+        "assert-plus": "^1.0.0"
+      }
+    },
+    "node_modules/npx/node_modules/npm/node_modules/request/node_modules/http-signature/node_modules/sshpk/node_modules/jsbn": {
+      "version": "0.1.1",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT",
+      "optional": true
+    },
+    "node_modules/npx/node_modules/npm/node_modules/request/node_modules/http-signature/node_modules/sshpk/node_modules/tweetnacl": {
+      "version": "0.14.5",
+      "dev": true,
+      "inBundle": true,
+      "license": "Unlicense",
+      "optional": true
+    },
+    "node_modules/npx/node_modules/npm/node_modules/request/node_modules/is-typedarray": {
+      "version": "1.0.0",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT"
+    },
+    "node_modules/npx/node_modules/npm/node_modules/request/node_modules/isstream": {
+      "version": "0.1.2",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT"
+    },
+    "node_modules/npx/node_modules/npm/node_modules/request/node_modules/json-stringify-safe": {
+      "version": "5.0.1",
+      "dev": true,
+      "inBundle": true,
+      "license": "ISC"
+    },
+    "node_modules/npx/node_modules/npm/node_modules/request/node_modules/mime-types": {
+      "version": "2.1.15",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT",
+      "dependencies": {
+        "mime-db": "~1.27.0"
+      },
+      "engines": {
+        "node": ">= 0.6"
+      }
+    },
+    "node_modules/npx/node_modules/npm/node_modules/request/node_modules/mime-types/node_modules/mime-db": {
+      "version": "1.27.0",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.6"
+      }
+    },
+    "node_modules/npx/node_modules/npm/node_modules/request/node_modules/oauth-sign": {
+      "version": "0.8.2",
+      "dev": true,
+      "inBundle": true,
+      "license": "Apache-2.0",
+      "engines": {
+        "node": "*"
+      }
+    },
+    "node_modules/npx/node_modules/npm/node_modules/request/node_modules/performance-now": {
+      "version": "0.2.0",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT"
+    },
+    "node_modules/npx/node_modules/npm/node_modules/request/node_modules/qs": {
+      "version": "6.4.0",
+      "dev": true,
+      "inBundle": true,
+      "license": "BSD-3-Clause",
+      "engines": {
+        "node": ">=0.6"
+      }
+    },
+    "node_modules/npx/node_modules/npm/node_modules/request/node_modules/stringstream": {
+      "version": "0.0.5",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT"
+    },
+    "node_modules/npx/node_modules/npm/node_modules/request/node_modules/tough-cookie": {
+      "version": "2.3.2",
+      "dev": true,
+      "inBundle": true,
+      "license": "BSD-3-Clause",
+      "dependencies": {
+        "punycode": "^1.4.1"
+      },
+      "engines": {
+        "node": ">=0.8"
+      }
+    },
+    "node_modules/npx/node_modules/npm/node_modules/request/node_modules/tough-cookie/node_modules/punycode": {
+      "version": "1.4.1",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT"
+    },
+    "node_modules/npx/node_modules/npm/node_modules/request/node_modules/tunnel-agent": {
+      "version": "0.6.0",
+      "dev": true,
+      "inBundle": true,
+      "license": "Apache-2.0",
+      "dependencies": {
+        "safe-buffer": "^5.0.1"
+      },
+      "engines": {
+        "node": "*"
+      }
+    },
+    "node_modules/npx/node_modules/npm/node_modules/retry": {
+      "version": "0.10.1",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT",
+      "engines": {
+        "node": "*"
+      }
+    },
+    "node_modules/npx/node_modules/npm/node_modules/rimraf": {
+      "version": "2.6.1",
+      "dev": true,
+      "inBundle": true,
+      "license": "ISC",
+      "dependencies": {
+        "glob": "^7.0.5"
+      },
+      "bin": {
+        "rimraf": "bin.js"
+      }
+    },
+    "node_modules/npx/node_modules/npm/node_modules/safe-buffer": {
+      "version": "5.1.1",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT"
+    },
+    "node_modules/npx/node_modules/npm/node_modules/semver": {
+      "version": "5.3.0",
+      "dev": true,
+      "inBundle": true,
+      "license": "ISC",
+      "bin": {
+        "semver": "bin/semver"
+      }
+    },
+    "node_modules/npx/node_modules/npm/node_modules/sha": {
+      "version": "2.0.1",
+      "dev": true,
+      "inBundle": true,
+      "license": "(BSD-2-Clause OR MIT)",
+      "dependencies": {
+        "graceful-fs": "^4.1.2",
+        "readable-stream": "^2.0.2"
+      }
+    },
+    "node_modules/npx/node_modules/npm/node_modules/slide": {
+      "version": "1.1.6",
+      "dev": true,
+      "inBundle": true,
+      "license": "ISC",
+      "engines": {
+        "node": "*"
+      }
+    },
+    "node_modules/npx/node_modules/npm/node_modules/sorted-object": {
+      "version": "2.0.1",
+      "dev": true,
+      "inBundle": true,
+      "license": "(WTFPL OR MIT)"
+    },
+    "node_modules/npx/node_modules/npm/node_modules/sorted-union-stream": {
+      "version": "2.1.3",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT",
+      "dependencies": {
+        "from2": "^1.3.0",
+        "stream-iterate": "^1.1.0"
+      }
+    },
+    "node_modules/npx/node_modules/npm/node_modules/sorted-union-stream/node_modules/from2": {
+      "version": "1.3.0",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT",
+      "dependencies": {
+        "inherits": "~2.0.1",
+        "readable-stream": "~1.1.10"
+      }
+    },
+    "node_modules/npx/node_modules/npm/node_modules/sorted-union-stream/node_modules/from2/node_modules/readable-stream": {
+      "version": "1.1.14",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT",
+      "dependencies": {
+        "core-util-is": "~1.0.0",
+        "inherits": "~2.0.1",
+        "isarray": "0.0.1",
+        "string_decoder": "~0.10.x"
+      }
+    },
+    "node_modules/npx/node_modules/npm/node_modules/sorted-union-stream/node_modules/from2/node_modules/readable-stream/node_modules/core-util-is": {
+      "version": "1.0.2",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT"
+    },
+    "node_modules/npx/node_modules/npm/node_modules/sorted-union-stream/node_modules/from2/node_modules/readable-stream/node_modules/isarray": {
+      "version": "0.0.1",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT"
+    },
+    "node_modules/npx/node_modules/npm/node_modules/sorted-union-stream/node_modules/from2/node_modules/readable-stream/node_modules/string_decoder": {
+      "version": "0.10.31",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT"
+    },
+    "node_modules/npx/node_modules/npm/node_modules/sorted-union-stream/node_modules/stream-iterate": {
+      "version": "1.2.0",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT",
+      "dependencies": {
+        "readable-stream": "^2.1.5",
+        "stream-shift": "^1.0.0"
+      }
+    },
+    "node_modules/npx/node_modules/npm/node_modules/sorted-union-stream/node_modules/stream-iterate/node_modules/stream-shift": {
+      "version": "1.0.0",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT"
+    },
+    "node_modules/npx/node_modules/npm/node_modules/ssri": {
+      "version": "4.1.6",
+      "dev": true,
+      "inBundle": true,
+      "license": "CC0-1.0",
+      "dependencies": {
+        "safe-buffer": "^5.1.0"
+      }
+    },
+    "node_modules/npx/node_modules/npm/node_modules/strip-ansi": {
+      "version": "4.0.0",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT",
+      "dependencies": {
+        "ansi-regex": "^3.0.0"
+      },
+      "engines": {
+        "node": ">=4"
+      }
+    },
+    "node_modules/npx/node_modules/npm/node_modules/strip-ansi/node_modules/ansi-regex": {
+      "version": "3.0.0",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=4"
+      }
+    },
+    "node_modules/npx/node_modules/npm/node_modules/tar": {
+      "version": "2.2.1",
+      "dev": true,
+      "inBundle": true,
+      "license": "ISC",
+      "dependencies": {
+        "block-stream": "*",
+        "fstream": "^1.0.2",
+        "inherits": "2"
+      }
+    },
+    "node_modules/npx/node_modules/npm/node_modules/tar/node_modules/block-stream": {
+      "version": "0.0.9",
+      "dev": true,
+      "inBundle": true,
+      "license": "ISC",
+      "dependencies": {
+        "inherits": "~2.0.0"
+      },
+      "engines": {
+        "node": "0.4 || >=0.5.8"
+      }
+    },
+    "node_modules/npx/node_modules/npm/node_modules/text-table": {
+      "version": "0.2.0",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT"
+    },
+    "node_modules/npx/node_modules/npm/node_modules/uid-number": {
+      "version": "0.0.6",
+      "dev": true,
+      "inBundle": true,
+      "license": "ISC",
+      "engines": {
+        "node": "*"
+      }
+    },
+    "node_modules/npx/node_modules/npm/node_modules/umask": {
+      "version": "1.1.0",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT"
+    },
+    "node_modules/npx/node_modules/npm/node_modules/unique-filename": {
+      "version": "1.1.0",
+      "dev": true,
+      "inBundle": true,
+      "license": "ISC",
+      "dependencies": {
+        "unique-slug": "^2.0.0"
+      }
+    },
+    "node_modules/npx/node_modules/npm/node_modules/unique-filename/node_modules/unique-slug": {
+      "version": "2.0.0",
+      "dev": true,
+      "inBundle": true,
+      "license": "ISC",
+      "dependencies": {
+        "imurmurhash": "^0.1.4"
+      }
+    },
+    "node_modules/npx/node_modules/npm/node_modules/unpipe": {
+      "version": "1.0.0",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.8"
+      }
+    },
+    "node_modules/npx/node_modules/npm/node_modules/update-notifier": {
+      "version": "2.2.0",
+      "dev": true,
+      "inBundle": true,
+      "license": "BSD-2-Clause",
+      "dependencies": {
+        "boxen": "^1.0.0",
+        "chalk": "^1.0.0",
+        "configstore": "^3.0.0",
+        "import-lazy": "^2.1.0",
+        "is-npm": "^1.0.0",
+        "latest-version": "^3.0.0",
+        "semver-diff": "^2.0.0",
+        "xdg-basedir": "^3.0.0"
+      },
+      "engines": {
+        "node": ">=4"
+      }
+    },
+    "node_modules/npx/node_modules/npm/node_modules/update-notifier/node_modules/boxen": {
+      "version": "1.1.0",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT",
+      "dependencies": {
+        "ansi-align": "^2.0.0",
+        "camelcase": "^4.0.0",
+        "chalk": "^1.1.1",
+        "cli-boxes": "^1.0.0",
+        "string-width": "^2.0.0",
+        "term-size": "^0.1.0",
+        "widest-line": "^1.0.0"
+      },
+      "engines": {
+        "node": ">=4"
+      }
+    },
+    "node_modules/npx/node_modules/npm/node_modules/update-notifier/node_modules/boxen/node_modules/ansi-align": {
+      "version": "2.0.0",
+      "dev": true,
+      "inBundle": true,
+      "license": "ISC",
+      "dependencies": {
+        "string-width": "^2.0.0"
+      }
+    },
+    "node_modules/npx/node_modules/npm/node_modules/update-notifier/node_modules/boxen/node_modules/camelcase": {
+      "version": "4.1.0",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=4"
+      }
+    },
+    "node_modules/npx/node_modules/npm/node_modules/update-notifier/node_modules/boxen/node_modules/cli-boxes": {
+      "version": "1.0.0",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/npx/node_modules/npm/node_modules/update-notifier/node_modules/boxen/node_modules/string-width": {
+      "version": "2.1.0",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT",
+      "dependencies": {
+        "is-fullwidth-code-point": "^2.0.0",
+        "strip-ansi": "^4.0.0"
+      },
+      "engines": {
+        "node": ">=4"
+      }
+    },
+    "node_modules/npx/node_modules/npm/node_modules/update-notifier/node_modules/boxen/node_modules/string-width/node_modules/is-fullwidth-code-point": {
+      "version": "2.0.0",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=4"
+      }
+    },
+    "node_modules/npx/node_modules/npm/node_modules/update-notifier/node_modules/boxen/node_modules/string-width/node_modules/strip-ansi": {
+      "version": "4.0.0",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT",
+      "dependencies": {
+        "ansi-regex": "^3.0.0"
+      },
+      "engines": {
+        "node": ">=4"
+      }
+    },
+    "node_modules/npx/node_modules/npm/node_modules/update-notifier/node_modules/boxen/node_modules/term-size": {
+      "version": "0.1.1",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT",
+      "dependencies": {
+        "execa": "^0.4.0"
+      },
+      "engines": {
+        "node": ">=4"
+      }
+    },
+    "node_modules/npx/node_modules/npm/node_modules/update-notifier/node_modules/boxen/node_modules/term-size/node_modules/execa": {
+      "version": "0.4.0",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT",
+      "dependencies": {
+        "cross-spawn-async": "^2.1.1",
+        "is-stream": "^1.1.0",
+        "npm-run-path": "^1.0.0",
+        "object-assign": "^4.0.1",
+        "path-key": "^1.0.0",
+        "strip-eof": "^1.0.0"
+      },
+      "engines": {
+        "node": ">=0.12"
+      }
+    },
+    "node_modules/npx/node_modules/npm/node_modules/update-notifier/node_modules/boxen/node_modules/term-size/node_modules/execa/node_modules/cross-spawn-async": {
+      "version": "2.2.5",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT",
+      "dependencies": {
+        "lru-cache": "^4.0.0",
+        "which": "^1.2.8"
+      }
+    },
+    "node_modules/npx/node_modules/npm/node_modules/update-notifier/node_modules/boxen/node_modules/term-size/node_modules/execa/node_modules/is-stream": {
+      "version": "1.1.0",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/npx/node_modules/npm/node_modules/update-notifier/node_modules/boxen/node_modules/term-size/node_modules/execa/node_modules/npm-run-path": {
+      "version": "1.0.0",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT",
+      "dependencies": {
+        "path-key": "^1.0.0"
+      },
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/npx/node_modules/npm/node_modules/update-notifier/node_modules/boxen/node_modules/term-size/node_modules/execa/node_modules/object-assign": {
+      "version": "4.1.1",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/npx/node_modules/npm/node_modules/update-notifier/node_modules/boxen/node_modules/term-size/node_modules/execa/node_modules/path-key": {
+      "version": "1.0.0",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/npx/node_modules/npm/node_modules/update-notifier/node_modules/boxen/node_modules/term-size/node_modules/execa/node_modules/strip-eof": {
+      "version": "1.0.0",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/npx/node_modules/npm/node_modules/update-notifier/node_modules/boxen/node_modules/widest-line": {
+      "version": "1.0.0",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT",
+      "dependencies": {
+        "string-width": "^1.0.1"
+      },
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/npx/node_modules/npm/node_modules/update-notifier/node_modules/boxen/node_modules/widest-line/node_modules/string-width": {
+      "version": "1.0.2",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT",
+      "dependencies": {
+        "code-point-at": "^1.0.0",
+        "is-fullwidth-code-point": "^1.0.0",
+        "strip-ansi": "^3.0.0"
+      },
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/npx/node_modules/npm/node_modules/update-notifier/node_modules/boxen/node_modules/widest-line/node_modules/string-width/node_modules/code-point-at": {
+      "version": "1.1.0",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/npx/node_modules/npm/node_modules/update-notifier/node_modules/boxen/node_modules/widest-line/node_modules/string-width/node_modules/is-fullwidth-code-point": {
+      "version": "1.0.0",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT",
+      "dependencies": {
+        "number-is-nan": "^1.0.0"
+      },
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/npx/node_modules/npm/node_modules/update-notifier/node_modules/boxen/node_modules/widest-line/node_modules/string-width/node_modules/is-fullwidth-code-point/node_modules/number-is-nan": {
+      "version": "1.0.1",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/npx/node_modules/npm/node_modules/update-notifier/node_modules/boxen/node_modules/widest-line/node_modules/string-width/node_modules/strip-ansi": {
+      "version": "3.0.1",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT",
+      "dependencies": {
+        "ansi-regex": "^2.0.0"
+      },
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/npx/node_modules/npm/node_modules/update-notifier/node_modules/boxen/node_modules/widest-line/node_modules/string-width/node_modules/strip-ansi/node_modules/ansi-regex": {
+      "version": "2.1.1",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/npx/node_modules/npm/node_modules/update-notifier/node_modules/chalk": {
+      "version": "1.1.3",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT",
+      "dependencies": {
+        "ansi-styles": "^2.2.1",
+        "escape-string-regexp": "^1.0.2",
+        "has-ansi": "^2.0.0",
+        "strip-ansi": "^3.0.0",
+        "supports-color": "^2.0.0"
+      },
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/npx/node_modules/npm/node_modules/update-notifier/node_modules/chalk/node_modules/ansi-styles": {
+      "version": "2.2.1",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/npx/node_modules/npm/node_modules/update-notifier/node_modules/chalk/node_modules/escape-string-regexp": {
+      "version": "1.0.5",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=0.8.0"
+      }
+    },
+    "node_modules/npx/node_modules/npm/node_modules/update-notifier/node_modules/chalk/node_modules/has-ansi": {
+      "version": "2.0.0",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT",
+      "dependencies": {
+        "ansi-regex": "^2.0.0"
+      },
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/npx/node_modules/npm/node_modules/update-notifier/node_modules/chalk/node_modules/has-ansi/node_modules/ansi-regex": {
+      "version": "2.1.1",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/npx/node_modules/npm/node_modules/update-notifier/node_modules/chalk/node_modules/strip-ansi": {
+      "version": "3.0.1",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT",
+      "dependencies": {
+        "ansi-regex": "^2.0.0"
+      },
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/npx/node_modules/npm/node_modules/update-notifier/node_modules/chalk/node_modules/strip-ansi/node_modules/ansi-regex": {
+      "version": "2.1.1",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/npx/node_modules/npm/node_modules/update-notifier/node_modules/chalk/node_modules/supports-color": {
+      "version": "2.0.0",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=0.8.0"
+      }
+    },
+    "node_modules/npx/node_modules/npm/node_modules/update-notifier/node_modules/configstore": {
+      "version": "3.1.0",
+      "dev": true,
+      "inBundle": true,
+      "license": "BSD-2-Clause",
+      "dependencies": {
+        "dot-prop": "^4.1.0",
+        "graceful-fs": "^4.1.2",
+        "make-dir": "^1.0.0",
+        "unique-string": "^1.0.0",
+        "write-file-atomic": "^2.0.0",
+        "xdg-basedir": "^3.0.0"
+      },
+      "engines": {
+        "node": ">=4"
+      }
+    },
+    "node_modules/npx/node_modules/npm/node_modules/update-notifier/node_modules/configstore/node_modules/dot-prop": {
+      "version": "4.1.1",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT",
+      "dependencies": {
+        "is-obj": "^1.0.0"
+      },
+      "engines": {
+        "node": ">=4"
+      }
+    },
+    "node_modules/npx/node_modules/npm/node_modules/update-notifier/node_modules/configstore/node_modules/dot-prop/node_modules/is-obj": {
+      "version": "1.0.1",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/npx/node_modules/npm/node_modules/update-notifier/node_modules/configstore/node_modules/make-dir": {
+      "version": "1.0.0",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT",
+      "dependencies": {
+        "pify": "^2.3.0"
+      },
+      "engines": {
+        "node": ">=4"
+      }
+    },
+    "node_modules/npx/node_modules/npm/node_modules/update-notifier/node_modules/configstore/node_modules/make-dir/node_modules/pify": {
+      "version": "2.3.0",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/npx/node_modules/npm/node_modules/update-notifier/node_modules/configstore/node_modules/unique-string": {
+      "version": "1.0.0",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT",
+      "dependencies": {
+        "crypto-random-string": "^1.0.0"
+      },
+      "engines": {
+        "node": ">=4"
+      }
+    },
+    "node_modules/npx/node_modules/npm/node_modules/update-notifier/node_modules/configstore/node_modules/unique-string/node_modules/crypto-random-string": {
+      "version": "1.0.0",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=4"
+      }
+    },
+    "node_modules/npx/node_modules/npm/node_modules/update-notifier/node_modules/import-lazy": {
+      "version": "2.1.0",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=4"
+      }
+    },
+    "node_modules/npx/node_modules/npm/node_modules/update-notifier/node_modules/is-npm": {
+      "version": "1.0.0",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/npx/node_modules/npm/node_modules/update-notifier/node_modules/latest-version": {
+      "version": "3.1.0",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT",
+      "dependencies": {
+        "package-json": "^4.0.0"
+      },
+      "engines": {
+        "node": ">=4"
+      }
+    },
+    "node_modules/npx/node_modules/npm/node_modules/update-notifier/node_modules/latest-version/node_modules/package-json": {
+      "version": "4.0.1",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT",
+      "dependencies": {
+        "got": "^6.7.1",
+        "registry-auth-token": "^3.0.1",
+        "registry-url": "^3.0.3",
+        "semver": "^5.1.0"
+      },
+      "engines": {
+        "node": ">=4"
+      }
+    },
+    "node_modules/npx/node_modules/npm/node_modules/update-notifier/node_modules/latest-version/node_modules/package-json/node_modules/got": {
+      "version": "6.7.1",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT",
+      "dependencies": {
+        "create-error-class": "^3.0.0",
+        "duplexer3": "^0.1.4",
+        "get-stream": "^3.0.0",
+        "is-redirect": "^1.0.0",
+        "is-retry-allowed": "^1.0.0",
+        "is-stream": "^1.0.0",
+        "lowercase-keys": "^1.0.0",
+        "safe-buffer": "^5.0.1",
+        "timed-out": "^4.0.0",
+        "unzip-response": "^2.0.1",
+        "url-parse-lax": "^1.0.0"
+      },
+      "engines": {
+        "node": ">=4"
+      }
+    },
+    "node_modules/npx/node_modules/npm/node_modules/update-notifier/node_modules/latest-version/node_modules/package-json/node_modules/got/node_modules/create-error-class": {
+      "version": "3.0.2",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT",
+      "dependencies": {
+        "capture-stack-trace": "^1.0.0"
+      },
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/npx/node_modules/npm/node_modules/update-notifier/node_modules/latest-version/node_modules/package-json/node_modules/got/node_modules/create-error-class/node_modules/capture-stack-trace": {
+      "version": "1.0.0",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/npx/node_modules/npm/node_modules/update-notifier/node_modules/latest-version/node_modules/package-json/node_modules/got/node_modules/duplexer3": {
+      "version": "0.1.4",
+      "dev": true,
+      "inBundle": true,
+      "license": "BSD-3-Clause"
+    },
+    "node_modules/npx/node_modules/npm/node_modules/update-notifier/node_modules/latest-version/node_modules/package-json/node_modules/got/node_modules/get-stream": {
+      "version": "3.0.0",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=4"
+      }
+    },
+    "node_modules/npx/node_modules/npm/node_modules/update-notifier/node_modules/latest-version/node_modules/package-json/node_modules/got/node_modules/is-redirect": {
+      "version": "1.0.0",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/npx/node_modules/npm/node_modules/update-notifier/node_modules/latest-version/node_modules/package-json/node_modules/got/node_modules/is-retry-allowed": {
+      "version": "1.1.0",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/npx/node_modules/npm/node_modules/update-notifier/node_modules/latest-version/node_modules/package-json/node_modules/got/node_modules/is-stream": {
+      "version": "1.1.0",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/npx/node_modules/npm/node_modules/update-notifier/node_modules/latest-version/node_modules/package-json/node_modules/got/node_modules/lowercase-keys": {
+      "version": "1.0.0",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/npx/node_modules/npm/node_modules/update-notifier/node_modules/latest-version/node_modules/package-json/node_modules/got/node_modules/timed-out": {
+      "version": "4.0.1",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/npx/node_modules/npm/node_modules/update-notifier/node_modules/latest-version/node_modules/package-json/node_modules/got/node_modules/unzip-response": {
+      "version": "2.0.1",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=4"
+      }
+    },
+    "node_modules/npx/node_modules/npm/node_modules/update-notifier/node_modules/latest-version/node_modules/package-json/node_modules/got/node_modules/url-parse-lax": {
+      "version": "1.0.0",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT",
+      "dependencies": {
+        "prepend-http": "^1.0.1"
+      },
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/npx/node_modules/npm/node_modules/update-notifier/node_modules/latest-version/node_modules/package-json/node_modules/got/node_modules/url-parse-lax/node_modules/prepend-http": {
+      "version": "1.0.4",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/npx/node_modules/npm/node_modules/update-notifier/node_modules/latest-version/node_modules/package-json/node_modules/registry-auth-token": {
+      "version": "3.3.1",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT",
+      "dependencies": {
+        "rc": "^1.1.6",
+        "safe-buffer": "^5.0.1"
+      }
+    },
+    "node_modules/npx/node_modules/npm/node_modules/update-notifier/node_modules/latest-version/node_modules/package-json/node_modules/registry-auth-token/node_modules/rc": {
+      "version": "1.2.1",
+      "dev": true,
+      "inBundle": true,
+      "license": "(BSD-2-Clause OR MIT OR Apache-2.0)",
+      "dependencies": {
+        "deep-extend": "~0.4.0",
+        "ini": "~1.3.0",
+        "minimist": "^1.2.0",
+        "strip-json-comments": "~2.0.1"
+      },
+      "bin": {
+        "rc": "index.js"
+      }
+    },
+    "node_modules/npx/node_modules/npm/node_modules/update-notifier/node_modules/latest-version/node_modules/package-json/node_modules/registry-auth-token/node_modules/rc/node_modules/deep-extend": {
+      "version": "0.4.2",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT",
+      "engines": {
+        "iojs": ">=1.0.0",
+        "node": ">=0.12.0"
+      }
+    },
+    "node_modules/npx/node_modules/npm/node_modules/update-notifier/node_modules/latest-version/node_modules/package-json/node_modules/registry-auth-token/node_modules/rc/node_modules/minimist": {
+      "version": "1.2.0",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT"
+    },
+    "node_modules/npx/node_modules/npm/node_modules/update-notifier/node_modules/latest-version/node_modules/package-json/node_modules/registry-auth-token/node_modules/rc/node_modules/strip-json-comments": {
+      "version": "2.0.1",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/npx/node_modules/npm/node_modules/update-notifier/node_modules/latest-version/node_modules/package-json/node_modules/registry-url": {
+      "version": "3.1.0",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT",
+      "dependencies": {
+        "rc": "^1.0.1"
+      },
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/npx/node_modules/npm/node_modules/update-notifier/node_modules/latest-version/node_modules/package-json/node_modules/registry-url/node_modules/rc": {
+      "version": "1.2.1",
+      "dev": true,
+      "inBundle": true,
+      "license": "(BSD-2-Clause OR MIT OR Apache-2.0)",
+      "dependencies": {
+        "deep-extend": "~0.4.0",
+        "ini": "~1.3.0",
+        "minimist": "^1.2.0",
+        "strip-json-comments": "~2.0.1"
+      },
+      "bin": {
+        "rc": "index.js"
+      }
+    },
+    "node_modules/npx/node_modules/npm/node_modules/update-notifier/node_modules/latest-version/node_modules/package-json/node_modules/registry-url/node_modules/rc/node_modules/deep-extend": {
+      "version": "0.4.2",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT",
+      "engines": {
+        "iojs": ">=1.0.0",
+        "node": ">=0.12.0"
+      }
+    },
+    "node_modules/npx/node_modules/npm/node_modules/update-notifier/node_modules/latest-version/node_modules/package-json/node_modules/registry-url/node_modules/rc/node_modules/minimist": {
+      "version": "1.2.0",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT"
+    },
+    "node_modules/npx/node_modules/npm/node_modules/update-notifier/node_modules/latest-version/node_modules/package-json/node_modules/registry-url/node_modules/rc/node_modules/strip-json-comments": {
+      "version": "2.0.1",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/npx/node_modules/npm/node_modules/update-notifier/node_modules/semver-diff": {
+      "version": "2.1.0",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT",
+      "dependencies": {
+        "semver": "^5.0.3"
+      },
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/npx/node_modules/npm/node_modules/update-notifier/node_modules/xdg-basedir": {
+      "version": "3.0.0",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=4"
+      }
+    },
+    "node_modules/npx/node_modules/npm/node_modules/uuid": {
+      "version": "3.1.0",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT",
+      "bin": {
+        "uuid": "bin/uuid"
+      }
+    },
+    "node_modules/npx/node_modules/npm/node_modules/validate-npm-package-license": {
+      "version": "3.0.1",
+      "dev": true,
+      "inBundle": true,
+      "license": "Apache-2.0",
+      "dependencies": {
+        "spdx-correct": "~1.0.0",
+        "spdx-expression-parse": "~1.0.0"
+      }
+    },
+    "node_modules/npx/node_modules/npm/node_modules/validate-npm-package-license/node_modules/spdx-correct": {
+      "version": "1.0.2",
+      "dev": true,
+      "inBundle": true,
+      "license": "Apache-2.0",
+      "dependencies": {
+        "spdx-license-ids": "^1.0.2"
+      }
+    },
+    "node_modules/npx/node_modules/npm/node_modules/validate-npm-package-license/node_modules/spdx-correct/node_modules/spdx-license-ids": {
+      "version": "1.2.2",
+      "dev": true,
+      "inBundle": true,
+      "license": "Unlicense"
+    },
+    "node_modules/npx/node_modules/npm/node_modules/validate-npm-package-license/node_modules/spdx-expression-parse": {
+      "version": "1.0.4",
+      "dev": true,
+      "inBundle": true,
+      "license": "(MIT AND CC-BY-3.0)"
+    },
+    "node_modules/npx/node_modules/npm/node_modules/validate-npm-package-name": {
+      "version": "3.0.0",
+      "dev": true,
+      "inBundle": true,
+      "license": "ISC",
+      "dependencies": {
+        "builtins": "^1.0.3"
+      }
+    },
+    "node_modules/npx/node_modules/npm/node_modules/validate-npm-package-name/node_modules/builtins": {
+      "version": "1.0.3",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT"
+    },
+    "node_modules/npx/node_modules/npm/node_modules/which": {
+      "version": "1.2.14",
+      "dev": true,
+      "inBundle": true,
+      "license": "ISC",
+      "dependencies": {
+        "isexe": "^2.0.0"
+      },
+      "bin": {
+        "which": "bin/which"
+      }
+    },
+    "node_modules/npx/node_modules/npm/node_modules/which/node_modules/isexe": {
+      "version": "2.0.0",
+      "dev": true,
+      "inBundle": true,
+      "license": "ISC"
+    },
+    "node_modules/npx/node_modules/npm/node_modules/worker-farm": {
+      "version": "1.3.1",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT",
+      "dependencies": {
+        "errno": ">=0.1.1 <0.2.0-0",
+        "xtend": ">=4.0.0 <4.1.0-0"
+      }
+    },
+    "node_modules/npx/node_modules/npm/node_modules/worker-farm/node_modules/errno": {
+      "version": "0.1.4",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT",
+      "dependencies": {
+        "prr": "~0.0.0"
+      },
+      "bin": {
+        "errno": "cli.js"
+      }
+    },
+    "node_modules/npx/node_modules/npm/node_modules/worker-farm/node_modules/errno/node_modules/prr": {
+      "version": "0.0.0",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT"
+    },
+    "node_modules/npx/node_modules/npm/node_modules/worker-farm/node_modules/xtend": {
+      "version": "4.0.1",
+      "dev": true,
+      "inBundle": true,
+      "engines": {
+        "node": ">=0.4"
+      }
+    },
+    "node_modules/npx/node_modules/npm/node_modules/wrappy": {
+      "version": "1.0.2",
+      "dev": true,
+      "inBundle": true,
+      "license": "ISC"
+    },
+    "node_modules/npx/node_modules/npm/node_modules/write-file-atomic": {
+      "version": "2.1.0",
+      "dev": true,
+      "inBundle": true,
+      "license": "ISC",
+      "dependencies": {
+        "graceful-fs": "^4.1.11",
+        "imurmurhash": "^0.1.4",
+        "slide": "^1.1.5"
+      }
+    },
+    "node_modules/npx/node_modules/number-is-nan": {
+      "version": "1.0.1",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/npx/node_modules/once": {
+      "version": "1.4.0",
+      "dev": true,
+      "inBundle": true,
+      "license": "ISC",
+      "dependencies": {
+        "wrappy": "1"
+      }
+    },
+    "node_modules/npx/node_modules/os-homedir": {
+      "version": "1.0.2",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/npx/node_modules/os-locale": {
+      "version": "3.1.0",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT",
+      "dependencies": {
+        "execa": "^1.0.0",
+        "lcid": "^2.0.0",
+        "mem": "^4.0.0"
+      },
+      "engines": {
+        "node": ">=6"
+      }
+    },
+    "node_modules/npx/node_modules/os-locale/node_modules/cross-spawn": {
+      "version": "6.0.5",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT",
+      "dependencies": {
+        "nice-try": "^1.0.4",
+        "path-key": "^2.0.1",
+        "semver": "^5.5.0",
+        "shebang-command": "^1.2.0",
+        "which": "^1.2.9"
+      },
+      "engines": {
+        "node": ">=4.8"
+      }
+    },
+    "node_modules/npx/node_modules/os-locale/node_modules/execa": {
+      "version": "1.0.0",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT",
+      "dependencies": {
+        "cross-spawn": "^6.0.0",
+        "get-stream": "^4.0.0",
+        "is-stream": "^1.1.0",
+        "npm-run-path": "^2.0.0",
+        "p-finally": "^1.0.0",
+        "signal-exit": "^3.0.0",
+        "strip-eof": "^1.0.0"
+      },
+      "engines": {
+        "node": ">=6"
+      }
+    },
+    "node_modules/npx/node_modules/os-locale/node_modules/get-stream": {
+      "version": "4.1.0",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT",
+      "dependencies": {
+        "pump": "^3.0.0"
+      },
+      "engines": {
+        "node": ">=6"
+      }
+    },
+    "node_modules/npx/node_modules/os-tmpdir": {
+      "version": "1.0.2",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/npx/node_modules/osenv": {
+      "version": "0.1.5",
+      "dev": true,
+      "inBundle": true,
+      "license": "ISC",
+      "dependencies": {
+        "os-homedir": "^1.0.0",
+        "os-tmpdir": "^1.0.0"
+      }
+    },
+    "node_modules/npx/node_modules/p-defer": {
+      "version": "1.0.0",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=4"
+      }
+    },
+    "node_modules/npx/node_modules/p-finally": {
+      "version": "1.0.0",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=4"
+      }
+    },
+    "node_modules/npx/node_modules/p-is-promise": {
+      "version": "2.1.0",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=6"
+      }
+    },
+    "node_modules/npx/node_modules/p-limit": {
+      "version": "1.3.0",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT",
+      "dependencies": {
+        "p-try": "^1.0.0"
+      },
+      "engines": {
+        "node": ">=4"
+      }
+    },
+    "node_modules/npx/node_modules/p-locate": {
+      "version": "2.0.0",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT",
+      "dependencies": {
+        "p-limit": "^1.1.0"
+      },
+      "engines": {
+        "node": ">=4"
+      }
+    },
+    "node_modules/npx/node_modules/p-try": {
+      "version": "1.0.0",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=4"
+      }
+    },
+    "node_modules/npx/node_modules/package-json": {
+      "version": "4.0.1",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT",
+      "dependencies": {
+        "got": "^6.7.1",
+        "registry-auth-token": "^3.0.1",
+        "registry-url": "^3.0.3",
+        "semver": "^5.1.0"
+      },
+      "engines": {
+        "node": ">=4"
+      }
+    },
+    "node_modules/npx/node_modules/path-exists": {
+      "version": "3.0.0",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=4"
+      }
+    },
+    "node_modules/npx/node_modules/path-is-absolute": {
+      "version": "1.0.1",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/npx/node_modules/path-is-inside": {
+      "version": "1.0.2",
+      "dev": true,
+      "inBundle": true,
+      "license": "(WTFPL OR MIT)"
+    },
+    "node_modules/npx/node_modules/path-key": {
+      "version": "2.0.1",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=4"
+      }
+    },
+    "node_modules/npx/node_modules/pify": {
+      "version": "3.0.0",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=4"
+      }
+    },
+    "node_modules/npx/node_modules/prepend-http": {
+      "version": "1.0.4",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/npx/node_modules/pseudomap": {
+      "version": "1.0.2",
+      "dev": true,
+      "inBundle": true,
+      "license": "ISC"
+    },
+    "node_modules/npx/node_modules/pump": {
+      "version": "3.0.0",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT",
+      "dependencies": {
+        "end-of-stream": "^1.1.0",
+        "once": "^1.3.1"
+      }
+    },
+    "node_modules/npx/node_modules/rc": {
+      "version": "1.2.8",
+      "dev": true,
+      "inBundle": true,
+      "license": "(BSD-2-Clause OR MIT OR Apache-2.0)",
+      "dependencies": {
+        "deep-extend": "^0.6.0",
+        "ini": "~1.3.0",
+        "minimist": "^1.2.0",
+        "strip-json-comments": "~2.0.1"
+      },
+      "bin": {
+        "rc": "cli.js"
+      }
+    },
+    "node_modules/npx/node_modules/registry-auth-token": {
+      "version": "3.4.0",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT",
+      "dependencies": {
+        "rc": "^1.1.6",
+        "safe-buffer": "^5.0.1"
+      }
+    },
+    "node_modules/npx/node_modules/registry-url": {
+      "version": "3.1.0",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT",
+      "dependencies": {
+        "rc": "^1.0.1"
+      },
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/npx/node_modules/require-directory": {
+      "version": "2.1.1",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/npx/node_modules/require-main-filename": {
+      "version": "1.0.1",
+      "dev": true,
+      "inBundle": true,
+      "license": "ISC"
+    },
+    "node_modules/npx/node_modules/rimraf": {
+      "version": "2.7.1",
+      "dev": true,
+      "inBundle": true,
+      "license": "ISC",
+      "dependencies": {
+        "glob": "^7.1.3"
+      },
+      "bin": {
+        "rimraf": "bin.js"
+      }
+    },
+    "node_modules/npx/node_modules/safe-buffer": {
+      "version": "5.2.0",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT"
+    },
+    "node_modules/npx/node_modules/semver": {
+      "version": "5.7.1",
+      "dev": true,
+      "inBundle": true,
+      "license": "ISC",
+      "bin": {
+        "semver": "bin/semver"
+      }
+    },
+    "node_modules/npx/node_modules/semver-diff": {
+      "version": "2.1.0",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT",
+      "dependencies": {
+        "semver": "^5.0.3"
+      },
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/npx/node_modules/set-blocking": {
+      "version": "2.0.0",
+      "dev": true,
+      "inBundle": true,
+      "license": "ISC"
+    },
+    "node_modules/npx/node_modules/shebang-command": {
+      "version": "1.2.0",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT",
+      "dependencies": {
+        "shebang-regex": "^1.0.0"
+      },
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/npx/node_modules/shebang-regex": {
+      "version": "1.0.0",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/npx/node_modules/signal-exit": {
+      "version": "3.0.2",
+      "dev": true,
+      "inBundle": true,
+      "license": "ISC"
+    },
+    "node_modules/npx/node_modules/string-width": {
+      "version": "2.1.1",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT",
+      "dependencies": {
+        "is-fullwidth-code-point": "^2.0.0",
+        "strip-ansi": "^4.0.0"
+      },
+      "engines": {
+        "node": ">=4"
+      }
+    },
+    "node_modules/npx/node_modules/strip-ansi": {
+      "version": "4.0.0",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT",
+      "dependencies": {
+        "ansi-regex": "^3.0.0"
+      },
+      "engines": {
+        "node": ">=4"
+      }
+    },
+    "node_modules/npx/node_modules/strip-eof": {
+      "version": "1.0.0",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/npx/node_modules/strip-json-comments": {
+      "version": "2.0.1",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/npx/node_modules/supports-color": {
+      "version": "5.5.0",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT",
+      "dependencies": {
+        "has-flag": "^3.0.0"
+      },
+      "engines": {
+        "node": ">=4"
+      }
+    },
+    "node_modules/npx/node_modules/term-size": {
+      "version": "1.2.0",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT",
+      "dependencies": {
+        "execa": "^0.7.0"
+      },
+      "engines": {
+        "node": ">=4"
+      }
+    },
+    "node_modules/npx/node_modules/timed-out": {
+      "version": "4.0.1",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/npx/node_modules/unique-string": {
+      "version": "1.0.0",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT",
+      "dependencies": {
+        "crypto-random-string": "^1.0.0"
+      },
+      "engines": {
+        "node": ">=4"
+      }
+    },
+    "node_modules/npx/node_modules/unzip-response": {
+      "version": "2.0.1",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=4"
+      }
+    },
+    "node_modules/npx/node_modules/update-notifier": {
+      "version": "2.5.0",
+      "dev": true,
+      "inBundle": true,
+      "license": "BSD-2-Clause",
+      "dependencies": {
+        "boxen": "^1.2.1",
+        "chalk": "^2.0.1",
+        "configstore": "^3.0.0",
+        "import-lazy": "^2.1.0",
+        "is-ci": "^1.0.10",
+        "is-installed-globally": "^0.1.0",
+        "is-npm": "^1.0.0",
+        "latest-version": "^3.0.0",
+        "semver-diff": "^2.0.0",
+        "xdg-basedir": "^3.0.0"
+      },
+      "engines": {
+        "node": ">=4"
+      }
+    },
+    "node_modules/npx/node_modules/url-parse-lax": {
+      "version": "1.0.0",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT",
+      "dependencies": {
+        "prepend-http": "^1.0.1"
+      },
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/npx/node_modules/validate-npm-package-name": {
+      "version": "3.0.0",
+      "dev": true,
+      "inBundle": true,
+      "license": "ISC",
+      "dependencies": {
+        "builtins": "^1.0.3"
+      }
+    },
+    "node_modules/npx/node_modules/which": {
+      "version": "1.3.1",
+      "dev": true,
+      "inBundle": true,
+      "license": "ISC",
+      "dependencies": {
+        "isexe": "^2.0.0"
+      },
+      "bin": {
+        "which": "bin/which"
+      }
+    },
+    "node_modules/npx/node_modules/which-module": {
+      "version": "2.0.0",
+      "dev": true,
+      "inBundle": true,
+      "license": "ISC"
+    },
+    "node_modules/npx/node_modules/widest-line": {
+      "version": "2.0.1",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT",
+      "dependencies": {
+        "string-width": "^2.1.1"
+      },
+      "engines": {
+        "node": ">=4"
+      }
+    },
+    "node_modules/npx/node_modules/wrap-ansi": {
+      "version": "2.1.0",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT",
+      "dependencies": {
+        "string-width": "^1.0.1",
+        "strip-ansi": "^3.0.1"
+      },
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/npx/node_modules/wrap-ansi/node_modules/ansi-regex": {
+      "version": "2.1.1",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/npx/node_modules/wrap-ansi/node_modules/is-fullwidth-code-point": {
+      "version": "1.0.0",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT",
+      "dependencies": {
+        "number-is-nan": "^1.0.0"
+      },
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/npx/node_modules/wrap-ansi/node_modules/string-width": {
+      "version": "1.0.2",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT",
+      "dependencies": {
+        "code-point-at": "^1.0.0",
+        "is-fullwidth-code-point": "^1.0.0",
+        "strip-ansi": "^3.0.0"
+      },
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/npx/node_modules/wrap-ansi/node_modules/strip-ansi": {
+      "version": "3.0.1",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT",
+      "dependencies": {
+        "ansi-regex": "^2.0.0"
+      },
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/npx/node_modules/wrappy": {
+      "version": "1.0.2",
+      "dev": true,
+      "inBundle": true,
+      "license": "ISC"
+    },
+    "node_modules/npx/node_modules/write-file-atomic": {
+      "version": "2.4.3",
+      "dev": true,
+      "inBundle": true,
+      "license": "ISC",
+      "dependencies": {
+        "graceful-fs": "^4.1.11",
+        "imurmurhash": "^0.1.4",
+        "signal-exit": "^3.0.2"
+      }
+    },
+    "node_modules/npx/node_modules/xdg-basedir": {
+      "version": "3.0.0",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=4"
+      }
+    },
+    "node_modules/npx/node_modules/y18n": {
+      "version": "4.0.0",
+      "dev": true,
+      "inBundle": true,
+      "license": "ISC"
+    },
+    "node_modules/npx/node_modules/yallist": {
+      "version": "2.1.2",
+      "dev": true,
+      "inBundle": true,
+      "license": "ISC"
+    },
+    "node_modules/npx/node_modules/yargs": {
+      "version": "11.1.1",
+      "dev": true,
+      "inBundle": true,
+      "license": "MIT",
+      "dependencies": {
+        "cliui": "^4.0.0",
+        "decamelize": "^1.1.1",
+        "find-up": "^2.1.0",
+        "get-caller-file": "^1.0.1",
+        "os-locale": "^3.1.0",
+        "require-directory": "^2.1.1",
+        "require-main-filename": "^1.0.1",
+        "set-blocking": "^2.0.0",
+        "string-width": "^2.0.0",
+        "which-module": "^2.0.0",
+        "y18n": "^3.2.1",
+        "yargs-parser": "^9.0.2"
+      }
+    },
+    "node_modules/npx/node_modules/yargs-parser": {
+      "version": "9.0.2",
+      "dev": true,
+      "inBundle": true,
+      "license": "ISC",
+      "dependencies": {
+        "camelcase": "^4.1.0"
+      }
+    },
+    "node_modules/npx/node_modules/yargs/node_modules/y18n": {
+      "version": "3.2.1",
+      "dev": true,
+      "inBundle": true,
+      "license": "ISC"
+    },
+    "node_modules/typescript": {
+      "version": "4.3.5",
+      "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.3.5.tgz",
+      "integrity": "sha512-DqQgihaQ9cUrskJo9kIyW/+g0Vxsk8cDtZ52a3NGh0YNTfpUSArXSohyUGnvbPazEPLu398C0UxmKSOrPumUzA==",
+      "dev": true,
+      "bin": {
+        "tsc": "bin/tsc",
+        "tsserver": "bin/tsserver"
+      },
+      "engines": {
+        "node": ">=4.2.0"
+      }
+    }
+  },
+  "dependencies": {
+    "npx": {
+      "version": "10.2.2",
+      "resolved": "https://registry.npmjs.org/npx/-/npx-10.2.2.tgz",
+      "integrity": "sha512-eImmySusyeWphzs5iNh791XbZnZG0FSNvM4KSah34pdQQIDsdTDhIwg1sjN3AIVcjGLpbQ/YcfqHPshKZQK1fA==",
+      "dev": true,
+      "requires": {
+        "libnpx": "10.2.2",
+        "npm": "5.1.0"
+      },
+      "dependencies": {
+        "ansi-align": {
+          "version": "2.0.0",
+          "bundled": true,
+          "dev": true,
+          "requires": {
+            "string-width": "^2.0.0"
+          }
+        },
+        "ansi-regex": {
+          "version": "3.0.0",
+          "bundled": true,
+          "dev": true
+        },
+        "ansi-styles": {
+          "version": "3.2.1",
+          "bundled": true,
+          "dev": true,
+          "requires": {
+            "color-convert": "^1.9.0"
+          }
+        },
+        "balanced-match": {
+          "version": "1.0.0",
+          "bundled": true,
+          "dev": true
+        },
+        "boxen": {
+          "version": "1.3.0",
+          "bundled": true,
+          "dev": true,
+          "requires": {
+            "ansi-align": "^2.0.0",
+            "camelcase": "^4.0.0",
+            "chalk": "^2.0.1",
+            "cli-boxes": "^1.0.0",
+            "string-width": "^2.0.0",
+            "term-size": "^1.2.0",
+            "widest-line": "^2.0.0"
+          }
+        },
+        "brace-expansion": {
+          "version": "1.1.11",
+          "bundled": true,
+          "dev": true,
+          "requires": {
+            "balanced-match": "^1.0.0",
+            "concat-map": "0.0.1"
+          }
+        },
+        "builtins": {
+          "version": "1.0.3",
+          "bundled": true,
+          "dev": true
+        },
+        "camelcase": {
+          "version": "4.1.0",
+          "bundled": true,
+          "dev": true
+        },
+        "capture-stack-trace": {
+          "version": "1.0.1",
+          "bundled": true,
+          "dev": true
+        },
+        "chalk": {
+          "version": "2.4.2",
+          "bundled": true,
+          "dev": true,
+          "requires": {
+            "ansi-styles": "^3.2.1",
+            "escape-string-regexp": "^1.0.5",
+            "supports-color": "^5.3.0"
+          }
+        },
+        "ci-info": {
+          "version": "1.6.0",
+          "bundled": true,
+          "dev": true
+        },
+        "cli-boxes": {
+          "version": "1.0.0",
+          "bundled": true,
+          "dev": true
+        },
+        "cliui": {
+          "version": "4.1.0",
+          "bundled": true,
+          "dev": true,
+          "requires": {
+            "string-width": "^2.1.1",
+            "strip-ansi": "^4.0.0",
+            "wrap-ansi": "^2.0.0"
+          }
+        },
+        "code-point-at": {
+          "version": "1.1.0",
+          "bundled": true,
+          "dev": true
+        },
+        "color-convert": {
+          "version": "1.9.3",
+          "bundled": true,
+          "dev": true,
+          "requires": {
+            "color-name": "1.1.3"
+          }
+        },
+        "color-name": {
+          "version": "1.1.3",
+          "bundled": true,
+          "dev": true
+        },
+        "concat-map": {
+          "version": "0.0.1",
+          "bundled": true,
+          "dev": true
+        },
+        "configstore": {
+          "version": "3.1.2",
+          "bundled": true,
+          "dev": true,
+          "requires": {
+            "dot-prop": "^4.1.0",
+            "graceful-fs": "^4.1.2",
+            "make-dir": "^1.0.0",
+            "unique-string": "^1.0.0",
+            "write-file-atomic": "^2.0.0",
+            "xdg-basedir": "^3.0.0"
+          }
+        },
+        "create-error-class": {
+          "version": "3.0.2",
+          "bundled": true,
+          "dev": true,
+          "requires": {
+            "capture-stack-trace": "^1.0.0"
+          }
+        },
+        "cross-spawn": {
+          "version": "5.1.0",
+          "bundled": true,
+          "dev": true,
+          "requires": {
+            "lru-cache": "^4.0.1",
+            "shebang-command": "^1.2.0",
+            "which": "^1.2.9"
+          }
+        },
+        "crypto-random-string": {
+          "version": "1.0.0",
+          "bundled": true,
+          "dev": true
+        },
+        "decamelize": {
+          "version": "1.2.0",
+          "bundled": true,
+          "dev": true
+        },
+        "deep-extend": {
+          "version": "0.6.0",
+          "bundled": true,
+          "dev": true
+        },
+        "dot-prop": {
+          "version": "4.2.0",
+          "bundled": true,
+          "dev": true,
+          "requires": {
+            "is-obj": "^1.0.0"
+          }
+        },
+        "dotenv": {
+          "version": "5.0.1",
+          "bundled": true,
+          "dev": true
+        },
+        "duplexer3": {
+          "version": "0.1.4",
+          "bundled": true,
+          "dev": true
+        },
+        "end-of-stream": {
+          "version": "1.4.4",
+          "bundled": true,
+          "dev": true,
+          "requires": {
+            "once": "^1.4.0"
+          }
+        },
+        "escape-string-regexp": {
+          "version": "1.0.5",
+          "bundled": true,
+          "dev": true
+        },
+        "execa": {
+          "version": "0.7.0",
+          "bundled": true,
+          "dev": true,
+          "requires": {
+            "cross-spawn": "^5.0.1",
+            "get-stream": "^3.0.0",
+            "is-stream": "^1.1.0",
+            "npm-run-path": "^2.0.0",
+            "p-finally": "^1.0.0",
+            "signal-exit": "^3.0.0",
+            "strip-eof": "^1.0.0"
+          }
+        },
+        "find-up": {
+          "version": "2.1.0",
+          "bundled": true,
+          "dev": true,
+          "requires": {
+            "locate-path": "^2.0.0"
+          }
+        },
+        "fs.realpath": {
+          "version": "1.0.0",
+          "bundled": true,
+          "dev": true
+        },
+        "get-caller-file": {
+          "version": "1.0.3",
+          "bundled": true,
+          "dev": true
+        },
+        "get-stream": {
+          "version": "3.0.0",
+          "bundled": true,
+          "dev": true
+        },
+        "glob": {
+          "version": "7.1.6",
+          "bundled": true,
+          "dev": true,
+          "requires": {
+            "fs.realpath": "^1.0.0",
+            "inflight": "^1.0.4",
+            "inherits": "2",
+            "minimatch": "^3.0.4",
+            "once": "^1.3.0",
+            "path-is-absolute": "^1.0.0"
+          }
+        },
+        "global-dirs": {
+          "version": "0.1.1",
+          "bundled": true,
+          "dev": true,
+          "requires": {
+            "ini": "^1.3.4"
+          }
+        },
+        "got": {
+          "version": "6.7.1",
+          "bundled": true,
+          "dev": true,
+          "requires": {
+            "create-error-class": "^3.0.0",
+            "duplexer3": "^0.1.4",
+            "get-stream": "^3.0.0",
+            "is-redirect": "^1.0.0",
+            "is-retry-allowed": "^1.0.0",
+            "is-stream": "^1.0.0",
+            "lowercase-keys": "^1.0.0",
+            "safe-buffer": "^5.0.1",
+            "timed-out": "^4.0.0",
+            "unzip-response": "^2.0.1",
+            "url-parse-lax": "^1.0.0"
+          }
+        },
+        "graceful-fs": {
+          "version": "4.2.3",
+          "bundled": true,
+          "dev": true
+        },
+        "has-flag": {
+          "version": "3.0.0",
+          "bundled": true,
+          "dev": true
+        },
+        "hosted-git-info": {
+          "version": "2.8.5",
+          "bundled": true,
+          "dev": true
+        },
+        "import-lazy": {
+          "version": "2.1.0",
+          "bundled": true,
+          "dev": true
+        },
+        "imurmurhash": {
+          "version": "0.1.4",
+          "bundled": true,
+          "dev": true
+        },
+        "inflight": {
+          "version": "1.0.6",
+          "bundled": true,
+          "dev": true,
+          "requires": {
+            "once": "^1.3.0",
+            "wrappy": "1"
+          }
+        },
+        "inherits": {
+          "version": "2.0.4",
+          "bundled": true,
+          "dev": true
+        },
+        "ini": {
+          "version": "1.3.5",
+          "bundled": true,
+          "dev": true
+        },
+        "invert-kv": {
+          "version": "2.0.0",
+          "bundled": true,
+          "dev": true
+        },
+        "is-ci": {
+          "version": "1.2.1",
+          "bundled": true,
+          "dev": true,
+          "requires": {
+            "ci-info": "^1.5.0"
+          }
+        },
+        "is-fullwidth-code-point": {
+          "version": "2.0.0",
+          "bundled": true,
+          "dev": true
+        },
+        "is-installed-globally": {
+          "version": "0.1.0",
+          "bundled": true,
+          "dev": true,
+          "requires": {
+            "global-dirs": "^0.1.0",
+            "is-path-inside": "^1.0.0"
+          }
+        },
+        "is-npm": {
+          "version": "1.0.0",
+          "bundled": true,
+          "dev": true
+        },
+        "is-obj": {
+          "version": "1.0.1",
+          "bundled": true,
+          "dev": true
+        },
+        "is-path-inside": {
+          "version": "1.0.1",
+          "bundled": true,
+          "dev": true,
+          "requires": {
+            "path-is-inside": "^1.0.1"
+          }
+        },
+        "is-redirect": {
+          "version": "1.0.0",
+          "bundled": true,
+          "dev": true
+        },
+        "is-retry-allowed": {
+          "version": "1.2.0",
+          "bundled": true,
+          "dev": true
+        },
+        "is-stream": {
+          "version": "1.1.0",
+          "bundled": true,
+          "dev": true
+        },
+        "isexe": {
+          "version": "2.0.0",
+          "bundled": true,
+          "dev": true
+        },
+        "latest-version": {
+          "version": "3.1.0",
+          "bundled": true,
+          "dev": true,
+          "requires": {
+            "package-json": "^4.0.0"
+          }
+        },
+        "lcid": {
+          "version": "2.0.0",
+          "bundled": true,
+          "dev": true,
+          "requires": {
+            "invert-kv": "^2.0.0"
+          }
+        },
+        "libnpx": {
+          "version": "10.2.2",
+          "bundled": true,
+          "dev": true,
+          "requires": {
+            "dotenv": "^5.0.1",
+            "npm-package-arg": "^6.0.0",
+            "rimraf": "^2.6.2",
+            "safe-buffer": "^5.1.0",
+            "update-notifier": "^2.3.0",
+            "which": "^1.3.0",
+            "y18n": "^4.0.0",
+            "yargs": "^11.0.0"
+          }
+        },
+        "locate-path": {
+          "version": "2.0.0",
+          "bundled": true,
+          "dev": true,
+          "requires": {
+            "p-locate": "^2.0.0",
+            "path-exists": "^3.0.0"
+          }
+        },
+        "lowercase-keys": {
+          "version": "1.0.1",
+          "bundled": true,
+          "dev": true
+        },
+        "lru-cache": {
+          "version": "4.1.5",
+          "bundled": true,
+          "dev": true,
+          "requires": {
+            "pseudomap": "^1.0.2",
+            "yallist": "^2.1.2"
+          }
+        },
+        "make-dir": {
+          "version": "1.3.0",
+          "bundled": true,
+          "dev": true,
+          "requires": {
+            "pify": "^3.0.0"
+          }
+        },
+        "map-age-cleaner": {
+          "version": "0.1.3",
+          "bundled": true,
+          "dev": true,
+          "requires": {
+            "p-defer": "^1.0.0"
+          }
+        },
+        "mem": {
+          "version": "4.3.0",
+          "bundled": true,
+          "dev": true,
+          "requires": {
+            "map-age-cleaner": "^0.1.1",
+            "mimic-fn": "^2.0.0",
+            "p-is-promise": "^2.0.0"
+          }
+        },
+        "mimic-fn": {
+          "version": "2.1.0",
+          "bundled": true,
+          "dev": true
+        },
+        "minimatch": {
+          "version": "3.0.4",
+          "bundled": true,
+          "dev": true,
+          "requires": {
+            "brace-expansion": "^1.1.7"
+          }
+        },
+        "minimist": {
+          "version": "1.2.0",
+          "bundled": true,
+          "dev": true
+        },
+        "nice-try": {
+          "version": "1.0.5",
+          "bundled": true,
+          "dev": true
+        },
+        "npm": {
+          "version": "5.1.0",
+          "bundled": true,
+          "dev": true,
+          "requires": {
+            "abbrev": "~1.1.0",
+            "ansi-regex": "~3.0.0",
+            "ansicolors": "~0.3.2",
+            "ansistyles": "~0.1.3",
+            "aproba": "~1.1.2",
+            "archy": "~1.0.0",
+            "bluebird": "~3.5.0",
+            "cacache": "~9.2.9",
+            "call-limit": "~1.1.0",
+            "chownr": "~1.0.1",
+            "cmd-shim": "~2.0.2",
+            "columnify": "~1.5.4",
+            "config-chain": "~1.1.11",
+            "debuglog": "*",
+            "detect-indent": "~5.0.0",
+            "dezalgo": "~1.0.3",
+            "editor": "~1.0.0",
+            "fs-vacuum": "~1.2.10",
+            "fs-write-stream-atomic": "~1.0.10",
+            "fstream": "~1.0.11",
+            "fstream-npm": "~1.2.1",
+            "glob": "~7.1.2",
+            "graceful-fs": "~4.1.11",
+            "has-unicode": "~2.0.1",
+            "hosted-git-info": "~2.5.0",
+            "iferr": "~0.1.5",
+            "imurmurhash": "*",
+            "inflight": "~1.0.6",
+            "inherits": "~2.0.3",
+            "ini": "~1.3.4",
+            "init-package-json": "~1.10.1",
+            "JSONStream": "~1.3.1",
+            "lazy-property": "~1.0.0",
+            "lockfile": "~1.0.3",
+            "lodash._baseindexof": "*",
+            "lodash._baseuniq": "~4.6.0",
+            "lodash._bindcallback": "*",
+            "lodash._cacheindexof": "*",
+            "lodash._createcache": "*",
+            "lodash._getnative": "*",
+            "lodash.clonedeep": "~4.5.0",
+            "lodash.restparam": "*",
+            "lodash.union": "~4.6.0",
+            "lodash.uniq": "~4.5.0",
+            "lodash.without": "~4.4.0",
+            "lru-cache": "~4.1.1",
+            "mississippi": "~1.3.0",
+            "mkdirp": "~0.5.1",
+            "move-concurrently": "~1.0.1",
+            "node-gyp": "~3.6.2",
+            "nopt": "~4.0.1",
+            "normalize-package-data": "~2.4.0",
+            "npm-cache-filename": "~1.0.2",
+            "npm-install-checks": "~3.0.0",
+            "npm-package-arg": "~5.1.2",
+            "npm-registry-client": "~8.4.0",
+            "npm-user-validate": "~1.0.0",
+            "npmlog": "~4.1.2",
+            "once": "~1.4.0",
+            "opener": "~1.4.3",
+            "osenv": "~0.1.4",
+            "pacote": "~2.7.38",
+            "path-is-inside": "~1.0.2",
+            "promise-inflight": "~1.0.1",
+            "read": "~1.0.7",
+            "read-cmd-shim": "~1.0.1",
+            "read-installed": "~4.0.3",
+            "read-package-json": "~2.0.9",
+            "read-package-tree": "~5.1.6",
+            "readable-stream": "~2.3.2",
+            "readdir-scoped-modules": "*",
+            "request": "~2.81.0",
+            "retry": "~0.10.1",
+            "rimraf": "~2.6.1",
+            "safe-buffer": "~5.1.1",
+            "semver": "~5.3.0",
+            "sha": "~2.0.1",
+            "slide": "~1.1.6",
+            "sorted-object": "~2.0.1",
+            "sorted-union-stream": "~2.1.3",
+            "ssri": "~4.1.6",
+            "strip-ansi": "~4.0.0",
+            "tar": "~2.2.1",
+            "text-table": "~0.2.0",
+            "uid-number": "0.0.6",
+            "umask": "~1.1.0",
+            "unique-filename": "~1.1.0",
+            "unpipe": "~1.0.0",
+            "update-notifier": "~2.2.0",
+            "uuid": "~3.1.0",
+            "validate-npm-package-license": "*",
+            "validate-npm-package-name": "~3.0.0",
+            "which": "~1.2.14",
+            "worker-farm": "~1.3.1",
+            "wrappy": "~1.0.2",
+            "write-file-atomic": "~2.1.0"
+          },
+          "dependencies": {
+            "abbrev": {
+              "version": "1.1.0",
+              "bundled": true,
+              "dev": true
+            },
+            "ansi-regex": {
+              "version": "3.0.0",
+              "bundled": true,
+              "dev": true
+            },
+            "ansicolors": {
+              "version": "0.3.2",
+              "bundled": true,
+              "dev": true
+            },
+            "ansistyles": {
+              "version": "0.1.3",
+              "bundled": true,
+              "dev": true
+            },
+            "aproba": {
+              "version": "1.1.2",
+              "bundled": true,
+              "dev": true
+            },
+            "archy": {
+              "version": "1.0.0",
+              "bundled": true,
+              "dev": true
+            },
+            "bluebird": {
+              "version": "3.5.0",
+              "bundled": true,
+              "dev": true
+            },
+            "cacache": {
+              "version": "9.2.9",
+              "bundled": true,
+              "dev": true,
+              "requires": {
+                "bluebird": "^3.5.0",
+                "chownr": "^1.0.1",
+                "glob": "^7.1.2",
+                "graceful-fs": "^4.1.11",
+                "lru-cache": "^4.1.1",
+                "mississippi": "^1.3.0",
+                "mkdirp": "^0.5.1",
+                "move-concurrently": "^1.0.1",
+                "promise-inflight": "^1.0.1",
+                "rimraf": "^2.6.1",
+                "ssri": "^4.1.6",
+                "unique-filename": "^1.1.0",
+                "y18n": "^3.2.1"
+              },
+              "dependencies": {
+                "lru-cache": {
+                  "version": "4.1.1",
+                  "bundled": true,
+                  "dev": true,
+                  "requires": {
+                    "pseudomap": "^1.0.2",
+                    "yallist": "^2.1.2"
+                  },
+                  "dependencies": {
+                    "pseudomap": {
+                      "version": "1.0.2",
+                      "bundled": true,
+                      "dev": true
+                    },
+                    "yallist": {
+                      "version": "2.1.2",
+                      "bundled": true,
+                      "dev": true
+                    }
+                  }
+                },
+                "y18n": {
+                  "version": "3.2.1",
+                  "bundled": true,
+                  "dev": true
+                }
+              }
+            },
+            "call-limit": {
+              "version": "1.1.0",
+              "bundled": true,
+              "dev": true
+            },
+            "chownr": {
+              "version": "1.0.1",
+              "bundled": true,
+              "dev": true
+            },
+            "cmd-shim": {
+              "version": "2.0.2",
+              "bundled": true,
+              "dev": true,
+              "requires": {
+                "graceful-fs": "^4.1.2",
+                "mkdirp": "~0.5.0"
+              }
+            },
+            "columnify": {
+              "version": "1.5.4",
+              "bundled": true,
+              "dev": true,
+              "requires": {
+                "strip-ansi": "^3.0.0",
+                "wcwidth": "^1.0.0"
+              },
+              "dependencies": {
+                "strip-ansi": {
+                  "version": "3.0.1",
+                  "bundled": true,
+                  "dev": true,
+                  "requires": {
+                    "ansi-regex": "^2.0.0"
+                  },
+                  "dependencies": {
+                    "ansi-regex": {
+                      "version": "2.1.1",
+                      "bundled": true,
+                      "dev": true
+                    }
+                  }
+                },
+                "wcwidth": {
+                  "version": "1.0.1",
+                  "bundled": true,
+                  "dev": true,
+                  "requires": {
+                    "defaults": "^1.0.3"
+                  },
+                  "dependencies": {
+                    "defaults": {
+                      "version": "1.0.3",
+                      "bundled": true,
+                      "dev": true,
+                      "requires": {
+                        "clone": "^1.0.2"
+                      },
+                      "dependencies": {
+                        "clone": {
+                          "version": "1.0.2",
+                          "bundled": true,
+                          "dev": true
+                        }
+                      }
+                    }
+                  }
+                }
+              }
+            },
+            "config-chain": {
+              "version": "1.1.11",
+              "bundled": true,
+              "dev": true,
+              "requires": {
+                "ini": "^1.3.4",
+                "proto-list": "~1.2.1"
+              },
+              "dependencies": {
+                "proto-list": {
+                  "version": "1.2.4",
+                  "bundled": true,
+                  "dev": true
+                }
+              }
+            },
+            "debuglog": {
+              "version": "1.0.1",
+              "bundled": true,
+              "dev": true
+            },
+            "detect-indent": {
+              "version": "5.0.0",
+              "bundled": true,
+              "dev": true
+            },
+            "dezalgo": {
+              "version": "1.0.3",
+              "bundled": true,
+              "dev": true,
+              "requires": {
+                "asap": "^2.0.0",
+                "wrappy": "1"
+              },
+              "dependencies": {
+                "asap": {
+                  "version": "2.0.5",
+                  "bundled": true,
+                  "dev": true
+                }
+              }
+            },
+            "editor": {
+              "version": "1.0.0",
+              "bundled": true,
+              "dev": true
+            },
+            "fs-vacuum": {
+              "version": "1.2.10",
+              "bundled": true,
+              "dev": true,
+              "requires": {
+                "graceful-fs": "^4.1.2",
+                "path-is-inside": "^1.0.1",
+                "rimraf": "^2.5.2"
+              }
+            },
+            "fs-write-stream-atomic": {
+              "version": "1.0.10",
+              "bundled": true,
+              "dev": true,
+              "requires": {
+                "graceful-fs": "^4.1.2",
+                "iferr": "^0.1.5",
+                "imurmurhash": "^0.1.4",
+                "readable-stream": "1 || 2"
+              }
+            },
+            "fstream": {
+              "version": "1.0.11",
+              "bundled": true,
+              "dev": true,
+              "requires": {
+                "graceful-fs": "^4.1.2",
+                "inherits": "~2.0.0",
+                "mkdirp": ">=0.5 0",
+                "rimraf": "2"
+              }
+            },
+            "fstream-npm": {
+              "version": "1.2.1",
+              "bundled": true,
+              "dev": true,
+              "requires": {
+                "fstream-ignore": "^1.0.0",
+                "inherits": "2"
+              },
+              "dependencies": {
+                "fstream-ignore": {
+                  "version": "1.0.5",
+                  "bundled": true,
+                  "dev": true,
+                  "requires": {
+                    "fstream": "^1.0.0",
+                    "inherits": "2",
+                    "minimatch": "^3.0.0"
+                  },
+                  "dependencies": {
+                    "minimatch": {
+                      "version": "3.0.4",
+                      "bundled": true,
+                      "dev": true,
+                      "requires": {
+                        "brace-expansion": "^1.1.7"
+                      },
+                      "dependencies": {
+                        "brace-expansion": {
+                          "version": "1.1.8",
+                          "bundled": true,
+                          "dev": true,
+                          "requires": {
+                            "balanced-match": "^1.0.0",
+                            "concat-map": "0.0.1"
+                          },
+                          "dependencies": {
+                            "balanced-match": {
+                              "version": "1.0.0",
+                              "bundled": true,
+                              "dev": true
+                            },
+                            "concat-map": {
+                              "version": "0.0.1",
+                              "bundled": true,
+                              "dev": true
+                            }
+                          }
+                        }
+                      }
+                    }
+                  }
+                }
+              }
+            },
+            "glob": {
+              "version": "7.1.2",
+              "bundled": true,
+              "dev": true,
+              "requires": {
+                "fs.realpath": "^1.0.0",
+                "inflight": "^1.0.4",
+                "inherits": "2",
+                "minimatch": "^3.0.4",
+                "once": "^1.3.0",
+                "path-is-absolute": "^1.0.0"
+              },
+              "dependencies": {
+                "fs.realpath": {
+                  "version": "1.0.0",
+                  "bundled": true,
+                  "dev": true
+                },
+                "minimatch": {
+                  "version": "3.0.4",
+                  "bundled": true,
+                  "dev": true,
+                  "requires": {
+                    "brace-expansion": "^1.1.7"
+                  },
+                  "dependencies": {
+                    "brace-expansion": {
+                      "version": "1.1.8",
+                      "bundled": true,
+                      "dev": true,
+                      "requires": {
+                        "balanced-match": "^1.0.0",
+                        "concat-map": "0.0.1"
+                      },
+                      "dependencies": {
+                        "balanced-match": {
+                          "version": "1.0.0",
+                          "bundled": true,
+                          "dev": true
+                        },
+                        "concat-map": {
+                          "version": "0.0.1",
+                          "bundled": true,
+                          "dev": true
+                        }
+                      }
+                    }
+                  }
+                },
+                "path-is-absolute": {
+                  "version": "1.0.1",
+                  "bundled": true,
+                  "dev": true
+                }
+              }
+            },
+            "graceful-fs": {
+              "version": "4.1.11",
+              "bundled": true,
+              "dev": true
+            },
+            "has-unicode": {
+              "version": "2.0.1",
+              "bundled": true,
+              "dev": true
+            },
+            "hosted-git-info": {
+              "version": "2.5.0",
+              "bundled": true,
+              "dev": true
+            },
+            "iferr": {
+              "version": "0.1.5",
+              "bundled": true,
+              "dev": true
+            },
+            "imurmurhash": {
+              "version": "0.1.4",
+              "bundled": true,
+              "dev": true
+            },
+            "inflight": {
+              "version": "1.0.6",
+              "bundled": true,
+              "dev": true,
+              "requires": {
+                "once": "^1.3.0",
+                "wrappy": "1"
+              }
+            },
+            "inherits": {
+              "version": "2.0.3",
+              "bundled": true,
+              "dev": true
+            },
+            "ini": {
+              "version": "1.3.4",
+              "bundled": true,
+              "dev": true
+            },
+            "init-package-json": {
+              "version": "1.10.1",
+              "bundled": true,
+              "dev": true,
+              "requires": {
+                "glob": "^7.1.1",
+                "npm-package-arg": "^4.0.0 || ^5.0.0",
+                "promzard": "^0.3.0",
+                "read": "~1.0.1",
+                "read-package-json": "1 || 2",
+                "semver": "2.x || 3.x || 4 || 5",
+                "validate-npm-package-license": "^3.0.1",
+                "validate-npm-package-name": "^3.0.0"
+              },
+              "dependencies": {
+                "promzard": {
+                  "version": "0.3.0",
+                  "bundled": true,
+                  "dev": true,
+                  "requires": {
+                    "read": "1"
+                  }
+                }
+              }
+            },
+            "JSONStream": {
+              "version": "1.3.1",
+              "bundled": true,
+              "dev": true,
+              "requires": {
+                "jsonparse": "^1.2.0",
+                "through": ">=2.2.7 <3"
+              },
+              "dependencies": {
+                "jsonparse": {
+                  "version": "1.3.1",
+                  "bundled": true,
+                  "dev": true
+                },
+                "through": {
+                  "version": "2.3.8",
+                  "bundled": true,
+                  "dev": true
+                }
+              }
+            },
+            "lazy-property": {
+              "version": "1.0.0",
+              "bundled": true,
+              "dev": true
+            },
+            "lockfile": {
+              "version": "1.0.3",
+              "bundled": true,
+              "dev": true
+            },
+            "lodash._baseindexof": {
+              "version": "3.1.0",
+              "bundled": true,
+              "dev": true
+            },
+            "lodash._baseuniq": {
+              "version": "4.6.0",
+              "bundled": true,
+              "dev": true,
+              "requires": {
+                "lodash._createset": "~4.0.0",
+                "lodash._root": "~3.0.0"
+              },
+              "dependencies": {
+                "lodash._createset": {
+                  "version": "4.0.3",
+                  "bundled": true,
+                  "dev": true
+                },
+                "lodash._root": {
+                  "version": "3.0.1",
+                  "bundled": true,
+                  "dev": true
+                }
+              }
+            },
+            "lodash._bindcallback": {
+              "version": "3.0.1",
+              "bundled": true,
+              "dev": true
+            },
+            "lodash._cacheindexof": {
+              "version": "3.0.2",
+              "bundled": true,
+              "dev": true
+            },
+            "lodash._createcache": {
+              "version": "3.1.2",
+              "bundled": true,
+              "dev": true,
+              "requires": {
+                "lodash._getnative": "^3.0.0"
+              }
+            },
+            "lodash._getnative": {
+              "version": "3.9.1",
+              "bundled": true,
+              "dev": true
+            },
+            "lodash.clonedeep": {
+              "version": "4.5.0",
+              "bundled": true,
+              "dev": true
+            },
+            "lodash.restparam": {
+              "version": "3.6.1",
+              "bundled": true,
+              "dev": true
+            },
+            "lodash.union": {
+              "version": "4.6.0",
+              "bundled": true,
+              "dev": true
+            },
+            "lodash.uniq": {
+              "version": "4.5.0",
+              "bundled": true,
+              "dev": true
+            },
+            "lodash.without": {
+              "version": "4.4.0",
+              "bundled": true,
+              "dev": true
+            },
+            "lru-cache": {
+              "version": "4.1.1",
+              "bundled": true,
+              "dev": true,
+              "requires": {
+                "pseudomap": "^1.0.2",
+                "yallist": "^2.1.2"
+              },
+              "dependencies": {
+                "pseudomap": {
+                  "version": "1.0.2",
+                  "bundled": true,
+                  "dev": true
+                },
+                "yallist": {
+                  "version": "2.1.2",
+                  "bundled": true,
+                  "dev": true
+                }
+              }
+            },
+            "mississippi": {
+              "version": "1.3.0",
+              "bundled": true,
+              "dev": true,
+              "requires": {
+                "concat-stream": "^1.5.0",
+                "duplexify": "^3.4.2",
+                "end-of-stream": "^1.1.0",
+                "flush-write-stream": "^1.0.0",
+                "from2": "^2.1.0",
+                "parallel-transform": "^1.1.0",
+                "pump": "^1.0.0",
+                "pumpify": "^1.3.3",
+                "stream-each": "^1.1.0",
+                "through2": "^2.0.0"
+              },
+              "dependencies": {
+                "concat-stream": {
+                  "version": "1.6.0",
+                  "bundled": true,
+                  "dev": true,
+                  "requires": {
+                    "inherits": "^2.0.3",
+                    "readable-stream": "^2.2.2",
+                    "typedarray": "^0.0.6"
+                  },
+                  "dependencies": {
+                    "typedarray": {
+                      "version": "0.0.6",
+                      "bundled": true,
+                      "dev": true
+                    }
+                  }
+                },
+                "duplexify": {
+                  "version": "3.5.0",
+                  "bundled": true,
+                  "dev": true,
+                  "requires": {
+                    "end-of-stream": "1.0.0",
+                    "inherits": "^2.0.1",
+                    "readable-stream": "^2.0.0",
+                    "stream-shift": "^1.0.0"
+                  },
+                  "dependencies": {
+                    "end-of-stream": {
+                      "version": "1.0.0",
+                      "bundled": true,
+                      "dev": true,
+                      "requires": {
+                        "once": "~1.3.0"
+                      },
+                      "dependencies": {
+                        "once": {
+                          "version": "1.3.3",
+                          "bundled": true,
+                          "dev": true,
+                          "requires": {
+                            "wrappy": "1"
+                          }
+                        }
+                      }
+                    },
+                    "stream-shift": {
+                      "version": "1.0.0",
+                      "bundled": true,
+                      "dev": true
+                    }
+                  }
+                },
+                "end-of-stream": {
+                  "version": "1.4.0",
+                  "bundled": true,
+                  "dev": true,
+                  "requires": {
+                    "once": "^1.4.0"
+                  }
+                },
+                "flush-write-stream": {
+                  "version": "1.0.2",
+                  "bundled": true,
+                  "dev": true,
+                  "requires": {
+                    "inherits": "^2.0.1",
+                    "readable-stream": "^2.0.4"
+                  }
+                },
+                "from2": {
+                  "version": "2.3.0",
+                  "bundled": true,
+                  "dev": true,
+                  "requires": {
+                    "inherits": "^2.0.1",
+                    "readable-stream": "^2.0.0"
+                  }
+                },
+                "parallel-transform": {
+                  "version": "1.1.0",
+                  "bundled": true,
+                  "dev": true,
+                  "requires": {
+                    "cyclist": "~0.2.2",
+                    "inherits": "^2.0.3",
+                    "readable-stream": "^2.1.5"
+                  },
+                  "dependencies": {
+                    "cyclist": {
+                      "version": "0.2.2",
+                      "bundled": true,
+                      "dev": true
+                    }
+                  }
+                },
+                "pump": {
+                  "version": "1.0.2",
+                  "bundled": true,
+                  "dev": true,
+                  "requires": {
+                    "end-of-stream": "^1.1.0",
+                    "once": "^1.3.1"
+                  }
+                },
+                "pumpify": {
+                  "version": "1.3.5",
+                  "bundled": true,
+                  "dev": true,
+                  "requires": {
+                    "duplexify": "^3.1.2",
+                    "inherits": "^2.0.1",
+                    "pump": "^1.0.0"
+                  }
+                },
+                "stream-each": {
+                  "version": "1.2.0",
+                  "bundled": true,
+                  "dev": true,
+                  "requires": {
+                    "end-of-stream": "^1.1.0",
+                    "stream-shift": "^1.0.0"
+                  },
+                  "dependencies": {
+                    "stream-shift": {
+                      "version": "1.0.0",
+                      "bundled": true,
+                      "dev": true
+                    }
+                  }
+                },
+                "through2": {
+                  "version": "2.0.3",
+                  "bundled": true,
+                  "dev": true,
+                  "requires": {
+                    "readable-stream": "^2.1.5",
+                    "xtend": "~4.0.1"
+                  },
+                  "dependencies": {
+                    "xtend": {
+                      "version": "4.0.1",
+                      "bundled": true,
+                      "dev": true
+                    }
+                  }
+                }
+              }
+            },
+            "mkdirp": {
+              "version": "0.5.1",
+              "bundled": true,
+              "dev": true,
+              "requires": {
+                "minimist": "0.0.8"
+              },
+              "dependencies": {
+                "minimist": {
+                  "version": "0.0.8",
+                  "bundled": true,
+                  "dev": true
+                }
+              }
+            },
+            "move-concurrently": {
+              "version": "1.0.1",
+              "bundled": true,
+              "dev": true,
+              "requires": {
+                "aproba": "^1.1.1",
+                "copy-concurrently": "^1.0.0",
+                "fs-write-stream-atomic": "^1.0.8",
+                "mkdirp": "^0.5.1",
+                "rimraf": "^2.5.4",
+                "run-queue": "^1.0.3"
+              },
+              "dependencies": {
+                "copy-concurrently": {
+                  "version": "1.0.3",
+                  "bundled": true,
+                  "dev": true,
+                  "requires": {
+                    "aproba": "^1.1.1",
+                    "fs-write-stream-atomic": "^1.0.8",
+                    "iferr": "^0.1.5",
+                    "mkdirp": "^0.5.1",
+                    "rimraf": "^2.5.4",
+                    "run-queue": "^1.0.0"
+                  }
+                },
+                "run-queue": {
+                  "version": "1.0.3",
+                  "bundled": true,
+                  "dev": true,
+                  "requires": {
+                    "aproba": "^1.1.1"
+                  }
+                }
+              }
+            },
+            "node-gyp": {
+              "version": "3.6.2",
+              "bundled": true,
+              "dev": true,
+              "requires": {
+                "fstream": "^1.0.0",
+                "glob": "^7.0.3",
+                "graceful-fs": "^4.1.2",
+                "minimatch": "^3.0.2",
+                "mkdirp": "^0.5.0",
+                "nopt": "2 || 3",
+                "npmlog": "0 || 1 || 2 || 3 || 4",
+                "osenv": "0",
+                "request": "2",
+                "rimraf": "2",
+                "semver": "~5.3.0",
+                "tar": "^2.0.0",
+                "which": "1"
+              },
+              "dependencies": {
+                "minimatch": {
+                  "version": "3.0.4",
+                  "bundled": true,
+                  "dev": true,
+                  "requires": {
+                    "brace-expansion": "^1.1.7"
+                  },
+                  "dependencies": {
+                    "brace-expansion": {
+                      "version": "1.1.8",
+                      "bundled": true,
+                      "dev": true,
+                      "requires": {
+                        "balanced-match": "^1.0.0",
+                        "concat-map": "0.0.1"
+                      },
+                      "dependencies": {
+                        "balanced-match": {
+                          "version": "1.0.0",
+                          "bundled": true,
+                          "dev": true
+                        },
+                        "concat-map": {
+                          "version": "0.0.1",
+                          "bundled": true,
+                          "dev": true
+                        }
+                      }
+                    }
+                  }
+                },
+                "nopt": {
+                  "version": "3.0.6",
+                  "bundled": true,
+                  "dev": true,
+                  "requires": {
+                    "abbrev": "1"
+                  }
+                }
+              }
+            },
+            "nopt": {
+              "version": "4.0.1",
+              "bundled": true,
+              "dev": true,
+              "requires": {
+                "abbrev": "1",
+                "osenv": "^0.1.4"
+              }
+            },
+            "normalize-package-data": {
+              "version": "2.4.0",
+              "bundled": true,
+              "dev": true,
+              "requires": {
+                "hosted-git-info": "^2.1.4",
+                "is-builtin-module": "^1.0.0",
+                "semver": "2 || 3 || 4 || 5",
+                "validate-npm-package-license": "^3.0.1"
+              },
+              "dependencies": {
+                "is-builtin-module": {
+                  "version": "1.0.0",
+                  "bundled": true,
+                  "dev": true,
+                  "requires": {
+                    "builtin-modules": "^1.0.0"
+                  },
+                  "dependencies": {
+                    "builtin-modules": {
+                      "version": "1.1.1",
+                      "bundled": true,
+                      "dev": true
+                    }
+                  }
+                }
+              }
+            },
+            "npm-cache-filename": {
+              "version": "1.0.2",
+              "bundled": true,
+              "dev": true
+            },
+            "npm-install-checks": {
+              "version": "3.0.0",
+              "bundled": true,
+              "dev": true,
+              "requires": {
+                "semver": "^2.3.0 || 3.x || 4 || 5"
+              }
+            },
+            "npm-package-arg": {
+              "version": "5.1.2",
+              "bundled": true,
+              "dev": true,
+              "requires": {
+                "hosted-git-info": "^2.4.2",
+                "osenv": "^0.1.4",
+                "semver": "^5.1.0",
+                "validate-npm-package-name": "^3.0.0"
+              }
+            },
+            "npm-registry-client": {
+              "version": "8.4.0",
+              "bundled": true,
+              "dev": true,
+              "requires": {
+                "concat-stream": "^1.5.2",
+                "graceful-fs": "^4.1.6",
+                "normalize-package-data": "~1.0.1 || ^2.0.0",
+                "npm-package-arg": "^3.0.0 || ^4.0.0 || ^5.0.0",
+                "npmlog": "2 || ^3.1.0 || ^4.0.0",
+                "once": "^1.3.3",
+                "request": "^2.74.0",
+                "retry": "^0.10.0",
+                "semver": "2 >=2.2.1 || 3.x || 4 || 5",
+                "slide": "^1.1.3",
+                "ssri": "^4.1.2"
+              },
+              "dependencies": {
+                "concat-stream": {
+                  "version": "1.6.0",
+                  "bundled": true,
+                  "dev": true,
+                  "requires": {
+                    "inherits": "^2.0.3",
+                    "readable-stream": "^2.2.2",
+                    "typedarray": "^0.0.6"
+                  },
+                  "dependencies": {
+                    "typedarray": {
+                      "version": "0.0.6",
+                      "bundled": true,
+                      "dev": true
+                    }
+                  }
+                }
+              }
+            },
+            "npm-user-validate": {
+              "version": "1.0.0",
+              "bundled": true,
+              "dev": true
+            },
+            "npmlog": {
+              "version": "4.1.2",
+              "bundled": true,
+              "dev": true,
+              "requires": {
+                "are-we-there-yet": "~1.1.2",
+                "console-control-strings": "~1.1.0",
+                "gauge": "~2.7.3",
+                "set-blocking": "~2.0.0"
+              },
+              "dependencies": {
+                "are-we-there-yet": {
+                  "version": "1.1.4",
+                  "bundled": true,
+                  "dev": true,
+                  "requires": {
+                    "delegates": "^1.0.0",
+                    "readable-stream": "^2.0.6"
+                  },
+                  "dependencies": {
+                    "delegates": {
+                      "version": "1.0.0",
+                      "bundled": true,
+                      "dev": true
+                    }
+                  }
+                },
+                "console-control-strings": {
+                  "version": "1.1.0",
+                  "bundled": true,
+                  "dev": true
+                },
+                "gauge": {
+                  "version": "2.7.4",
+                  "bundled": true,
+                  "dev": true,
+                  "requires": {
+                    "aproba": "^1.0.3",
+                    "console-control-strings": "^1.0.0",
+                    "has-unicode": "^2.0.0",
+                    "object-assign": "^4.1.0",
+                    "signal-exit": "^3.0.0",
+                    "string-width": "^1.0.1",
+                    "strip-ansi": "^3.0.1",
+                    "wide-align": "^1.1.0"
+                  },
+                  "dependencies": {
+                    "object-assign": {
+                      "version": "4.1.1",
+                      "bundled": true,
+                      "dev": true
+                    },
+                    "signal-exit": {
+                      "version": "3.0.2",
+                      "bundled": true,
+                      "dev": true
+                    },
+                    "string-width": {
+                      "version": "1.0.2",
+                      "bundled": true,
+                      "dev": true,
+                      "requires": {
+                        "code-point-at": "^1.0.0",
+                        "is-fullwidth-code-point": "^1.0.0",
+                        "strip-ansi": "^3.0.0"
+                      },
+                      "dependencies": {
+                        "code-point-at": {
+                          "version": "1.1.0",
+                          "bundled": true,
+                          "dev": true
+                        },
+                        "is-fullwidth-code-point": {
+                          "version": "1.0.0",
+                          "bundled": true,
+                          "dev": true,
+                          "requires": {
+                            "number-is-nan": "^1.0.0"
+                          },
+                          "dependencies": {
+                            "number-is-nan": {
+                              "version": "1.0.1",
+                              "bundled": true,
+                              "dev": true
+                            }
+                          }
+                        }
+                      }
+                    },
+                    "strip-ansi": {
+                      "version": "3.0.1",
+                      "bundled": true,
+                      "dev": true,
+                      "requires": {
+                        "ansi-regex": "^2.0.0"
+                      },
+                      "dependencies": {
+                        "ansi-regex": {
+                          "version": "2.1.1",
+                          "bundled": true,
+                          "dev": true
+                        }
+                      }
+                    },
+                    "wide-align": {
+                      "version": "1.1.2",
+                      "bundled": true,
+                      "dev": true,
+                      "requires": {
+                        "string-width": "^1.0.2"
+                      }
+                    }
+                  }
+                },
+                "set-blocking": {
+                  "version": "2.0.0",
+                  "bundled": true,
+                  "dev": true
+                }
+              }
+            },
+            "once": {
+              "version": "1.4.0",
+              "bundled": true,
+              "dev": true,
+              "requires": {
+                "wrappy": "1"
+              }
+            },
+            "opener": {
+              "version": "1.4.3",
+              "bundled": true,
+              "dev": true
+            },
+            "osenv": {
+              "version": "0.1.4",
+              "bundled": true,
+              "dev": true,
+              "requires": {
+                "os-homedir": "^1.0.0",
+                "os-tmpdir": "^1.0.0"
+              },
+              "dependencies": {
+                "os-homedir": {
+                  "version": "1.0.2",
+                  "bundled": true,
+                  "dev": true
+                },
+                "os-tmpdir": {
+                  "version": "1.0.2",
+                  "bundled": true,
+                  "dev": true
+                }
+              }
+            },
+            "pacote": {
+              "version": "2.7.38",
+              "bundled": true,
+              "dev": true,
+              "requires": {
+                "bluebird": "^3.5.0",
+                "cacache": "^9.2.9",
+                "glob": "^7.1.2",
+                "lru-cache": "^4.1.1",
+                "make-fetch-happen": "^2.4.13",
+                "minimatch": "^3.0.4",
+                "mississippi": "^1.2.0",
+                "normalize-package-data": "^2.4.0",
+                "npm-package-arg": "^5.1.2",
+                "npm-pick-manifest": "^1.0.4",
+                "osenv": "^0.1.4",
+                "promise-inflight": "^1.0.1",
+                "promise-retry": "^1.1.1",
+                "protoduck": "^4.0.0",
+                "safe-buffer": "^5.1.1",
+                "semver": "^5.3.0",
+                "ssri": "^4.1.6",
+                "tar-fs": "^1.15.3",
+                "tar-stream": "^1.5.4",
+                "unique-filename": "^1.1.0",
+                "which": "^1.2.12"
+              },
+              "dependencies": {
+                "make-fetch-happen": {
+                  "version": "2.4.13",
+                  "bundled": true,
+                  "dev": true,
+                  "requires": {
+                    "agentkeepalive": "^3.3.0",
+                    "cacache": "^9.2.9",
+                    "http-cache-semantics": "^3.7.3",
+                    "http-proxy-agent": "^2.0.0",
+                    "https-proxy-agent": "^2.0.0",
+                    "lru-cache": "^4.1.1",
+                    "mississippi": "^1.2.0",
+                    "node-fetch-npm": "^2.0.1",
+                    "promise-retry": "^1.1.1",
+                    "socks-proxy-agent": "^3.0.0",
+                    "ssri": "^4.1.6"
+                  },
+                  "dependencies": {
+                    "agentkeepalive": {
+                      "version": "3.3.0",
+                      "bundled": true,
+                      "dev": true,
+                      "requires": {
+                        "humanize-ms": "^1.2.1"
+                      },
+                      "dependencies": {
+                        "humanize-ms": {
+                          "version": "1.2.1",
+                          "bundled": true,
+                          "dev": true,
+                          "requires": {
+                            "ms": "^2.0.0"
+                          },
+                          "dependencies": {
+                            "ms": {
+                              "version": "2.0.0",
+                              "bundled": true,
+                              "dev": true
+                            }
+                          }
+                        }
+                      }
+                    },
+                    "http-cache-semantics": {
+                      "version": "3.7.3",
+                      "bundled": true,
+                      "dev": true
+                    },
+                    "http-proxy-agent": {
+                      "version": "2.0.0",
+                      "bundled": true,
+                      "dev": true,
+                      "requires": {
+                        "agent-base": "4",
+                        "debug": "2"
+                      },
+                      "dependencies": {
+                        "agent-base": {
+                          "version": "4.1.0",
+                          "bundled": true,
+                          "dev": true,
+                          "requires": {
+                            "es6-promisify": "^5.0.0"
+                          },
+                          "dependencies": {
+                            "es6-promisify": {
+                              "version": "5.0.0",
+                              "bundled": true,
+                              "dev": true,
+                              "requires": {
+                                "es6-promise": "^4.0.3"
+                              },
+                              "dependencies": {
+                                "es6-promise": {
+                                  "version": "4.1.1",
+                                  "bundled": true,
+                                  "dev": true
+                                }
+                              }
+                            }
+                          }
+                        },
+                        "debug": {
+                          "version": "2.6.8",
+                          "bundled": true,
+                          "dev": true,
+                          "requires": {
+                            "ms": "2.0.0"
+                          },
+                          "dependencies": {
+                            "ms": {
+                              "version": "2.0.0",
+                              "bundled": true,
+                              "dev": true
+                            }
+                          }
+                        }
+                      }
+                    },
+                    "https-proxy-agent": {
+                      "version": "2.0.0",
+                      "bundled": true,
+                      "dev": true,
+                      "requires": {
+                        "agent-base": "^4.1.0",
+                        "debug": "^2.4.1"
+                      },
+                      "dependencies": {
+                        "agent-base": {
+                          "version": "4.1.0",
+                          "bundled": true,
+                          "dev": true,
+                          "requires": {
+                            "es6-promisify": "^5.0.0"
+                          },
+                          "dependencies": {
+                            "es6-promisify": {
+                              "version": "5.0.0",
+                              "bundled": true,
+                              "dev": true,
+                              "requires": {
+                                "es6-promise": "^4.0.3"
+                              },
+                              "dependencies": {
+                                "es6-promise": {
+                                  "version": "4.1.1",
+                                  "bundled": true,
+                                  "dev": true
+                                }
+                              }
+                            }
+                          }
+                        },
+                        "debug": {
+                          "version": "2.6.8",
+                          "bundled": true,
+                          "dev": true,
+                          "requires": {
+                            "ms": "2.0.0"
+                          },
+                          "dependencies": {
+                            "ms": {
+                              "version": "2.0.0",
+                              "bundled": true,
+                              "dev": true
+                            }
+                          }
+                        }
+                      }
+                    },
+                    "node-fetch-npm": {
+                      "version": "2.0.1",
+                      "bundled": true,
+                      "dev": true,
+                      "requires": {
+                        "encoding": "^0.1.11",
+                        "json-parse-helpfulerror": "^1.0.3",
+                        "safe-buffer": "^5.0.1"
+                      },
+                      "dependencies": {
+                        "encoding": {
+                          "version": "0.1.12",
+                          "bundled": true,
+                          "dev": true,
+                          "requires": {
+                            "iconv-lite": "~0.4.13"
+                          },
+                          "dependencies": {
+                            "iconv-lite": {
+                              "version": "0.4.18",
+                              "bundled": true,
+                              "dev": true
+                            }
+                          }
+                        },
+                        "json-parse-helpfulerror": {
+                          "version": "1.0.3",
+                          "bundled": true,
+                          "dev": true,
+                          "requires": {
+                            "jju": "^1.1.0"
+                          },
+                          "dependencies": {
+                            "jju": {
+                              "version": "1.3.0",
+                              "bundled": true,
+                              "dev": true
+                            }
+                          }
+                        }
+                      }
+                    },
+                    "socks-proxy-agent": {
+                      "version": "3.0.0",
+                      "bundled": true,
+                      "dev": true,
+                      "requires": {
+                        "agent-base": "^4.0.1",
+                        "socks": "^1.1.10"
+                      },
+                      "dependencies": {
+                        "agent-base": {
+                          "version": "4.1.0",
+                          "bundled": true,
+                          "dev": true,
+                          "requires": {
+                            "es6-promisify": "^5.0.0"
+                          },
+                          "dependencies": {
+                            "es6-promisify": {
+                              "version": "5.0.0",
+                              "bundled": true,
+                              "dev": true,
+                              "requires": {
+                                "es6-promise": "^4.0.3"
+                              },
+                              "dependencies": {
+                                "es6-promise": {
+                                  "version": "4.1.1",
+                                  "bundled": true,
+                                  "dev": true
+                                }
+                              }
+                            }
+                          }
+                        },
+                        "socks": {
+                          "version": "1.1.10",
+                          "bundled": true,
+                          "dev": true,
+                          "requires": {
+                            "ip": "^1.1.4",
+                            "smart-buffer": "^1.0.13"
+                          },
+                          "dependencies": {
+                            "ip": {
+                              "version": "1.1.5",
+                              "bundled": true,
+                              "dev": true
+                            },
+                            "smart-buffer": {
+                              "version": "1.1.15",
+                              "bundled": true,
+                              "dev": true
+                            }
+                          }
+                        }
+                      }
+                    }
+                  }
+                },
+                "minimatch": {
+                  "version": "3.0.4",
+                  "bundled": true,
+                  "dev": true,
+                  "requires": {
+                    "brace-expansion": "^1.1.7"
+                  },
+                  "dependencies": {
+                    "brace-expansion": {
+                      "version": "1.1.8",
+                      "bundled": true,
+                      "dev": true,
+                      "requires": {
+                        "balanced-match": "^1.0.0",
+                        "concat-map": "0.0.1"
+                      },
+                      "dependencies": {
+                        "balanced-match": {
+                          "version": "1.0.0",
+                          "bundled": true,
+                          "dev": true
+                        },
+                        "concat-map": {
+                          "version": "0.0.1",
+                          "bundled": true,
+                          "dev": true
+                        }
+                      }
+                    }
+                  }
+                },
+                "npm-pick-manifest": {
+                  "version": "1.0.4",
+                  "bundled": true,
+                  "dev": true,
+                  "requires": {
+                    "npm-package-arg": "^5.1.2",
+                    "semver": "^5.3.0"
+                  }
+                },
+                "promise-retry": {
+                  "version": "1.1.1",
+                  "bundled": true,
+                  "dev": true,
+                  "requires": {
+                    "err-code": "^1.0.0",
+                    "retry": "^0.10.0"
+                  },
+                  "dependencies": {
+                    "err-code": {
+                      "version": "1.1.2",
+                      "bundled": true,
+                      "dev": true
+                    }
+                  }
+                },
+                "protoduck": {
+                  "version": "4.0.0",
+                  "bundled": true,
+                  "dev": true,
+                  "requires": {
+                    "genfun": "^4.0.1"
+                  },
+                  "dependencies": {
+                    "genfun": {
+                      "version": "4.0.1",
+                      "bundled": true,
+                      "dev": true
+                    }
+                  }
+                },
+                "tar-fs": {
+                  "version": "1.15.3",
+                  "bundled": true,
+                  "dev": true,
+                  "requires": {
+                    "chownr": "^1.0.1",
+                    "mkdirp": "^0.5.1",
+                    "pump": "^1.0.0",
+                    "tar-stream": "^1.1.2"
+                  },
+                  "dependencies": {
+                    "pump": {
+                      "version": "1.0.2",
+                      "bundled": true,
+                      "dev": true,
+                      "requires": {
+                        "end-of-stream": "^1.1.0",
+                        "once": "^1.3.1"
+                      },
+                      "dependencies": {
+                        "end-of-stream": {
+                          "version": "1.4.0",
+                          "bundled": true,
+                          "dev": true,
+                          "requires": {
+                            "once": "^1.4.0"
+                          }
+                        }
+                      }
+                    }
+                  }
+                },
+                "tar-stream": {
+                  "version": "1.5.4",
+                  "bundled": true,
+                  "dev": true,
+                  "requires": {
+                    "bl": "^1.0.0",
+                    "end-of-stream": "^1.0.0",
+                    "readable-stream": "^2.0.0",
+                    "xtend": "^4.0.0"
+                  },
+                  "dependencies": {
+                    "bl": {
+                      "version": "1.2.1",
+                      "bundled": true,
+                      "dev": true,
+                      "requires": {
+                        "readable-stream": "^2.0.5"
+                      }
+                    },
+                    "end-of-stream": {
+                      "version": "1.4.0",
+                      "bundled": true,
+                      "dev": true,
+                      "requires": {
+                        "once": "^1.4.0"
+                      }
+                    },
+                    "xtend": {
+                      "version": "4.0.1",
+                      "bundled": true,
+                      "dev": true
+                    }
+                  }
+                }
+              }
+            },
+            "path-is-inside": {
+              "version": "1.0.2",
+              "bundled": true,
+              "dev": true
+            },
+            "promise-inflight": {
+              "version": "1.0.1",
+              "bundled": true,
+              "dev": true
+            },
+            "read": {
+              "version": "1.0.7",
+              "bundled": true,
+              "dev": true,
+              "requires": {
+                "mute-stream": "~0.0.4"
+              },
+              "dependencies": {
+                "mute-stream": {
+                  "version": "0.0.7",
+                  "bundled": true,
+                  "dev": true
+                }
+              }
+            },
+            "read-cmd-shim": {
+              "version": "1.0.1",
+              "bundled": true,
+              "dev": true,
+              "requires": {
+                "graceful-fs": "^4.1.2"
+              }
+            },
+            "read-installed": {
+              "version": "4.0.3",
+              "bundled": true,
+              "dev": true,
+              "requires": {
+                "debuglog": "^1.0.1",
+                "graceful-fs": "^4.1.2",
+                "read-package-json": "^2.0.0",
+                "readdir-scoped-modules": "^1.0.0",
+                "semver": "2 || 3 || 4 || 5",
+                "slide": "~1.1.3",
+                "util-extend": "^1.0.1"
+              },
+              "dependencies": {
+                "util-extend": {
+                  "version": "1.0.3",
+                  "bundled": true,
+                  "dev": true
+                }
+              }
+            },
+            "read-package-json": {
+              "version": "2.0.9",
+              "bundled": true,
+              "dev": true,
+              "requires": {
+                "glob": "^7.1.1",
+                "graceful-fs": "^4.1.2",
+                "json-parse-helpfulerror": "^1.0.2",
+                "normalize-package-data": "^2.0.0"
+              },
+              "dependencies": {
+                "json-parse-helpfulerror": {
+                  "version": "1.0.3",
+                  "bundled": true,
+                  "dev": true,
+                  "requires": {
+                    "jju": "^1.1.0"
+                  },
+                  "dependencies": {
+                    "jju": {
+                      "version": "1.3.0",
+                      "bundled": true,
+                      "dev": true
+                    }
+                  }
+                }
+              }
+            },
+            "read-package-tree": {
+              "version": "5.1.6",
+              "bundled": true,
+              "dev": true,
+              "requires": {
+                "debuglog": "^1.0.1",
+                "dezalgo": "^1.0.0",
+                "once": "^1.3.0",
+                "read-package-json": "^2.0.0",
+                "readdir-scoped-modules": "^1.0.0"
+              }
+            },
+            "readable-stream": {
+              "version": "2.3.2",
+              "bundled": true,
+              "dev": true,
+              "requires": {
+                "core-util-is": "~1.0.0",
+                "inherits": "~2.0.3",
+                "isarray": "~1.0.0",
+                "process-nextick-args": "~1.0.6",
+                "safe-buffer": "~5.1.0",
+                "string_decoder": "~1.0.0",
+                "util-deprecate": "~1.0.1"
+              },
+              "dependencies": {
+                "core-util-is": {
+                  "version": "1.0.2",
+                  "bundled": true,
+                  "dev": true
+                },
+                "isarray": {
+                  "version": "1.0.0",
+                  "bundled": true,
+                  "dev": true
+                },
+                "process-nextick-args": {
+                  "version": "1.0.7",
+                  "bundled": true,
+                  "dev": true
+                },
+                "string_decoder": {
+                  "version": "1.0.3",
+                  "bundled": true,
+                  "dev": true,
+                  "requires": {
+                    "safe-buffer": "~5.1.0"
+                  }
+                },
+                "util-deprecate": {
+                  "version": "1.0.2",
+                  "bundled": true,
+                  "dev": true
+                }
+              }
+            },
+            "readdir-scoped-modules": {
+              "version": "1.0.2",
+              "bundled": true,
+              "dev": true,
+              "requires": {
+                "debuglog": "^1.0.1",
+                "dezalgo": "^1.0.0",
+                "graceful-fs": "^4.1.2",
+                "once": "^1.3.0"
+              }
+            },
+            "request": {
+              "version": "2.81.0",
+              "bundled": true,
+              "dev": true,
+              "requires": {
+                "aws-sign2": "~0.6.0",
+                "aws4": "^1.2.1",
+                "caseless": "~0.12.0",
+                "combined-stream": "~1.0.5",
+                "extend": "~3.0.0",
+                "forever-agent": "~0.6.1",
+                "form-data": "~2.1.1",
+                "har-validator": "~4.2.1",
+                "hawk": "~3.1.3",
+                "http-signature": "~1.1.0",
+                "is-typedarray": "~1.0.0",
+                "isstream": "~0.1.2",
+                "json-stringify-safe": "~5.0.1",
+                "mime-types": "~2.1.7",
+                "oauth-sign": "~0.8.1",
+                "performance-now": "^0.2.0",
+                "qs": "~6.4.0",
+                "safe-buffer": "^5.0.1",
+                "stringstream": "~0.0.4",
+                "tough-cookie": "~2.3.0",
+                "tunnel-agent": "^0.6.0",
+                "uuid": "^3.0.0"
+              },
+              "dependencies": {
+                "aws-sign2": {
+                  "version": "0.6.0",
+                  "bundled": true,
+                  "dev": true
+                },
+                "aws4": {
+                  "version": "1.6.0",
+                  "bundled": true,
+                  "dev": true
+                },
+                "caseless": {
+                  "version": "0.12.0",
+                  "bundled": true,
+                  "dev": true
+                },
+                "combined-stream": {
+                  "version": "1.0.5",
+                  "bundled": true,
+                  "dev": true,
+                  "requires": {
+                    "delayed-stream": "~1.0.0"
+                  },
+                  "dependencies": {
+                    "delayed-stream": {
+                      "version": "1.0.0",
+                      "bundled": true,
+                      "dev": true
+                    }
+                  }
+                },
+                "extend": {
+                  "version": "3.0.1",
+                  "bundled": true,
+                  "dev": true
+                },
+                "forever-agent": {
+                  "version": "0.6.1",
+                  "bundled": true,
+                  "dev": true
+                },
+                "form-data": {
+                  "version": "2.1.4",
+                  "bundled": true,
+                  "dev": true,
+                  "requires": {
+                    "asynckit": "^0.4.0",
+                    "combined-stream": "^1.0.5",
+                    "mime-types": "^2.1.12"
+                  },
+                  "dependencies": {
+                    "asynckit": {
+                      "version": "0.4.0",
+                      "bundled": true,
+                      "dev": true
+                    }
+                  }
+                },
+                "har-validator": {
+                  "version": "4.2.1",
+                  "bundled": true,
+                  "dev": true,
+                  "requires": {
+                    "ajv": "^4.9.1",
+                    "har-schema": "^1.0.5"
+                  },
+                  "dependencies": {
+                    "ajv": {
+                      "version": "4.11.8",
+                      "bundled": true,
+                      "dev": true,
+                      "requires": {
+                        "co": "^4.6.0",
+                        "json-stable-stringify": "^1.0.1"
+                      },
+                      "dependencies": {
+                        "co": {
+                          "version": "4.6.0",
+                          "bundled": true,
+                          "dev": true
+                        },
+                        "json-stable-stringify": {
+                          "version": "1.0.1",
+                          "bundled": true,
+                          "dev": true,
+                          "requires": {
+                            "jsonify": "~0.0.0"
+                          },
+                          "dependencies": {
+                            "jsonify": {
+                              "version": "0.0.0",
+                              "bundled": true,
+                              "dev": true
+                            }
+                          }
+                        }
+                      }
+                    },
+                    "har-schema": {
+                      "version": "1.0.5",
+                      "bundled": true,
+                      "dev": true
+                    }
+                  }
+                },
+                "hawk": {
+                  "version": "3.1.3",
+                  "bundled": true,
+                  "dev": true,
+                  "requires": {
+                    "boom": "2.x.x",
+                    "cryptiles": "2.x.x",
+                    "hoek": "2.x.x",
+                    "sntp": "1.x.x"
+                  },
+                  "dependencies": {
+                    "boom": {
+                      "version": "2.10.1",
+                      "bundled": true,
+                      "dev": true,
+                      "requires": {
+                        "hoek": "2.x.x"
+                      }
+                    },
+                    "cryptiles": {
+                      "version": "2.0.5",
+                      "bundled": true,
+                      "dev": true,
+                      "requires": {
+                        "boom": "2.x.x"
+                      }
+                    },
+                    "hoek": {
+                      "version": "2.16.3",
+                      "bundled": true,
+                      "dev": true
+                    },
+                    "sntp": {
+                      "version": "1.0.9",
+                      "bundled": true,
+                      "dev": true,
+                      "requires": {
+                        "hoek": "2.x.x"
+                      }
+                    }
+                  }
+                },
+                "http-signature": {
+                  "version": "1.1.1",
+                  "bundled": true,
+                  "dev": true,
+                  "requires": {
+                    "assert-plus": "^0.2.0",
+                    "jsprim": "^1.2.2",
+                    "sshpk": "^1.7.0"
+                  },
+                  "dependencies": {
+                    "assert-plus": {
+                      "version": "0.2.0",
+                      "bundled": true,
+                      "dev": true
+                    },
+                    "jsprim": {
+                      "version": "1.4.0",
+                      "bundled": true,
+                      "dev": true,
+                      "requires": {
+                        "assert-plus": "1.0.0",
+                        "extsprintf": "1.0.2",
+                        "json-schema": "0.2.3",
+                        "verror": "1.3.6"
+                      },
+                      "dependencies": {
+                        "assert-plus": {
+                          "version": "1.0.0",
+                          "bundled": true,
+                          "dev": true
+                        },
+                        "extsprintf": {
+                          "version": "1.0.2",
+                          "bundled": true,
+                          "dev": true
+                        },
+                        "json-schema": {
+                          "version": "0.2.3",
+                          "bundled": true,
+                          "dev": true
+                        },
+                        "verror": {
+                          "version": "1.3.6",
+                          "bundled": true,
+                          "dev": true,
+                          "requires": {
+                            "extsprintf": "1.0.2"
+                          }
+                        }
+                      }
+                    },
+                    "sshpk": {
+                      "version": "1.13.1",
+                      "bundled": true,
+                      "dev": true,
+                      "requires": {
+                        "asn1": "~0.2.3",
+                        "assert-plus": "^1.0.0",
+                        "bcrypt-pbkdf": "^1.0.0",
+                        "dashdash": "^1.12.0",
+                        "ecc-jsbn": "~0.1.1",
+                        "getpass": "^0.1.1",
+                        "jsbn": "~0.1.0",
+                        "tweetnacl": "~0.14.0"
+                      },
+                      "dependencies": {
+                        "asn1": {
+                          "version": "0.2.3",
+                          "bundled": true,
+                          "dev": true
+                        },
+                        "assert-plus": {
+                          "version": "1.0.0",
+                          "bundled": true,
+                          "dev": true
+                        },
+                        "bcrypt-pbkdf": {
+                          "version": "1.0.1",
+                          "bundled": true,
+                          "dev": true,
+                          "optional": true,
+                          "requires": {
+                            "tweetnacl": "^0.14.3"
+                          }
+                        },
+                        "dashdash": {
+                          "version": "1.14.1",
+                          "bundled": true,
+                          "dev": true,
+                          "requires": {
+                            "assert-plus": "^1.0.0"
+                          }
+                        },
+                        "ecc-jsbn": {
+                          "version": "0.1.1",
+                          "bundled": true,
+                          "dev": true,
+                          "optional": true,
+                          "requires": {
+                            "jsbn": "~0.1.0"
+                          }
+                        },
+                        "getpass": {
+                          "version": "0.1.7",
+                          "bundled": true,
+                          "dev": true,
+                          "requires": {
+                            "assert-plus": "^1.0.0"
+                          }
+                        },
+                        "jsbn": {
+                          "version": "0.1.1",
+                          "bundled": true,
+                          "dev": true,
+                          "optional": true
+                        },
+                        "tweetnacl": {
+                          "version": "0.14.5",
+                          "bundled": true,
+                          "dev": true,
+                          "optional": true
+                        }
+                      }
+                    }
+                  }
+                },
+                "is-typedarray": {
+                  "version": "1.0.0",
+                  "bundled": true,
+                  "dev": true
+                },
+                "isstream": {
+                  "version": "0.1.2",
+                  "bundled": true,
+                  "dev": true
+                },
+                "json-stringify-safe": {
+                  "version": "5.0.1",
+                  "bundled": true,
+                  "dev": true
+                },
+                "mime-types": {
+                  "version": "2.1.15",
+                  "bundled": true,
+                  "dev": true,
+                  "requires": {
+                    "mime-db": "~1.27.0"
+                  },
+                  "dependencies": {
+                    "mime-db": {
+                      "version": "1.27.0",
+                      "bundled": true,
+                      "dev": true
+                    }
+                  }
+                },
+                "oauth-sign": {
+                  "version": "0.8.2",
+                  "bundled": true,
+                  "dev": true
+                },
+                "performance-now": {
+                  "version": "0.2.0",
+                  "bundled": true,
+                  "dev": true
+                },
+                "qs": {
+                  "version": "6.4.0",
+                  "bundled": true,
+                  "dev": true
+                },
+                "stringstream": {
+                  "version": "0.0.5",
+                  "bundled": true,
+                  "dev": true
+                },
+                "tough-cookie": {
+                  "version": "2.3.2",
+                  "bundled": true,
+                  "dev": true,
+                  "requires": {
+                    "punycode": "^1.4.1"
+                  },
+                  "dependencies": {
+                    "punycode": {
+                      "version": "1.4.1",
+                      "bundled": true,
+                      "dev": true
+                    }
+                  }
+                },
+                "tunnel-agent": {
+                  "version": "0.6.0",
+                  "bundled": true,
+                  "dev": true,
+                  "requires": {
+                    "safe-buffer": "^5.0.1"
+                  }
+                }
+              }
+            },
+            "retry": {
+              "version": "0.10.1",
+              "bundled": true,
+              "dev": true
+            },
+            "rimraf": {
+              "version": "2.6.1",
+              "bundled": true,
+              "dev": true,
+              "requires": {
+                "glob": "^7.0.5"
+              }
+            },
+            "safe-buffer": {
+              "version": "5.1.1",
+              "bundled": true,
+              "dev": true
+            },
+            "semver": {
+              "version": "5.3.0",
+              "bundled": true,
+              "dev": true
+            },
+            "sha": {
+              "version": "2.0.1",
+              "bundled": true,
+              "dev": true,
+              "requires": {
+                "graceful-fs": "^4.1.2",
+                "readable-stream": "^2.0.2"
+              }
+            },
+            "slide": {
+              "version": "1.1.6",
+              "bundled": true,
+              "dev": true
+            },
+            "sorted-object": {
+              "version": "2.0.1",
+              "bundled": true,
+              "dev": true
+            },
+            "sorted-union-stream": {
+              "version": "2.1.3",
+              "bundled": true,
+              "dev": true,
+              "requires": {
+                "from2": "^1.3.0",
+                "stream-iterate": "^1.1.0"
+              },
+              "dependencies": {
+                "from2": {
+                  "version": "1.3.0",
+                  "bundled": true,
+                  "dev": true,
+                  "requires": {
+                    "inherits": "~2.0.1",
+                    "readable-stream": "~1.1.10"
+                  },
+                  "dependencies": {
+                    "readable-stream": {
+                      "version": "1.1.14",
+                      "bundled": true,
+                      "dev": true,
+                      "requires": {
+                        "core-util-is": "~1.0.0",
+                        "inherits": "~2.0.1",
+                        "isarray": "0.0.1",
+                        "string_decoder": "~0.10.x"
+                      },
+                      "dependencies": {
+                        "core-util-is": {
+                          "version": "1.0.2",
+                          "bundled": true,
+                          "dev": true
+                        },
+                        "isarray": {
+                          "version": "0.0.1",
+                          "bundled": true,
+                          "dev": true
+                        },
+                        "string_decoder": {
+                          "version": "0.10.31",
+                          "bundled": true,
+                          "dev": true
+                        }
+                      }
+                    }
+                  }
+                },
+                "stream-iterate": {
+                  "version": "1.2.0",
+                  "bundled": true,
+                  "dev": true,
+                  "requires": {
+                    "readable-stream": "^2.1.5",
+                    "stream-shift": "^1.0.0"
+                  },
+                  "dependencies": {
+                    "stream-shift": {
+                      "version": "1.0.0",
+                      "bundled": true,
+                      "dev": true
+                    }
+                  }
+                }
+              }
+            },
+            "ssri": {
+              "version": "4.1.6",
+              "bundled": true,
+              "dev": true,
+              "requires": {
+                "safe-buffer": "^5.1.0"
+              }
+            },
+            "strip-ansi": {
+              "version": "4.0.0",
+              "bundled": true,
+              "dev": true,
+              "requires": {
+                "ansi-regex": "^3.0.0"
+              },
+              "dependencies": {
+                "ansi-regex": {
+                  "version": "3.0.0",
+                  "bundled": true,
+                  "dev": true
+                }
+              }
+            },
+            "tar": {
+              "version": "2.2.1",
+              "bundled": true,
+              "dev": true,
+              "requires": {
+                "block-stream": "*",
+                "fstream": "^1.0.2",
+                "inherits": "2"
+              },
+              "dependencies": {
+                "block-stream": {
+                  "version": "0.0.9",
+                  "bundled": true,
+                  "dev": true,
+                  "requires": {
+                    "inherits": "~2.0.0"
+                  }
+                }
+              }
+            },
+            "text-table": {
+              "version": "0.2.0",
+              "bundled": true,
+              "dev": true
+            },
+            "uid-number": {
+              "version": "0.0.6",
+              "bundled": true,
+              "dev": true
+            },
+            "umask": {
+              "version": "1.1.0",
+              "bundled": true,
+              "dev": true
+            },
+            "unique-filename": {
+              "version": "1.1.0",
+              "bundled": true,
+              "dev": true,
+              "requires": {
+                "unique-slug": "^2.0.0"
+              },
+              "dependencies": {
+                "unique-slug": {
+                  "version": "2.0.0",
+                  "bundled": true,
+                  "dev": true,
+                  "requires": {
+                    "imurmurhash": "^0.1.4"
+                  }
+                }
+              }
+            },
+            "unpipe": {
+              "version": "1.0.0",
+              "bundled": true,
+              "dev": true
+            },
+            "update-notifier": {
+              "version": "2.2.0",
+              "bundled": true,
+              "dev": true,
+              "requires": {
+                "boxen": "^1.0.0",
+                "chalk": "^1.0.0",
+                "configstore": "^3.0.0",
+                "import-lazy": "^2.1.0",
+                "is-npm": "^1.0.0",
+                "latest-version": "^3.0.0",
+                "semver-diff": "^2.0.0",
+                "xdg-basedir": "^3.0.0"
+              },
+              "dependencies": {
+                "boxen": {
+                  "version": "1.1.0",
+                  "bundled": true,
+                  "dev": true,
+                  "requires": {
+                    "ansi-align": "^2.0.0",
+                    "camelcase": "^4.0.0",
+                    "chalk": "^1.1.1",
+                    "cli-boxes": "^1.0.0",
+                    "string-width": "^2.0.0",
+                    "term-size": "^0.1.0",
+                    "widest-line": "^1.0.0"
+                  },
+                  "dependencies": {
+                    "ansi-align": {
+                      "version": "2.0.0",
+                      "bundled": true,
+                      "dev": true,
+                      "requires": {
+                        "string-width": "^2.0.0"
+                      }
+                    },
+                    "camelcase": {
+                      "version": "4.1.0",
+                      "bundled": true,
+                      "dev": true
+                    },
+                    "cli-boxes": {
+                      "version": "1.0.0",
+                      "bundled": true,
+                      "dev": true
+                    },
+                    "string-width": {
+                      "version": "2.1.0",
+                      "bundled": true,
+                      "dev": true,
+                      "requires": {
+                        "is-fullwidth-code-point": "^2.0.0",
+                        "strip-ansi": "^4.0.0"
+                      },
+                      "dependencies": {
+                        "is-fullwidth-code-point": {
+                          "version": "2.0.0",
+                          "bundled": true,
+                          "dev": true
+                        },
+                        "strip-ansi": {
+                          "version": "4.0.0",
+                          "bundled": true,
+                          "dev": true,
+                          "requires": {
+                            "ansi-regex": "^3.0.0"
+                          }
+                        }
+                      }
+                    },
+                    "term-size": {
+                      "version": "0.1.1",
+                      "bundled": true,
+                      "dev": true,
+                      "requires": {
+                        "execa": "^0.4.0"
+                      },
+                      "dependencies": {
+                        "execa": {
+                          "version": "0.4.0",
+                          "bundled": true,
+                          "dev": true,
+                          "requires": {
+                            "cross-spawn-async": "^2.1.1",
+                            "is-stream": "^1.1.0",
+                            "npm-run-path": "^1.0.0",
+                            "object-assign": "^4.0.1",
+                            "path-key": "^1.0.0",
+                            "strip-eof": "^1.0.0"
+                          },
+                          "dependencies": {
+                            "cross-spawn-async": {
+                              "version": "2.2.5",
+                              "bundled": true,
+                              "dev": true,
+                              "requires": {
+                                "lru-cache": "^4.0.0",
+                                "which": "^1.2.8"
+                              }
+                            },
+                            "is-stream": {
+                              "version": "1.1.0",
+                              "bundled": true,
+                              "dev": true
+                            },
+                            "npm-run-path": {
+                              "version": "1.0.0",
+                              "bundled": true,
+                              "dev": true,
+                              "requires": {
+                                "path-key": "^1.0.0"
+                              }
+                            },
+                            "object-assign": {
+                              "version": "4.1.1",
+                              "bundled": true,
+                              "dev": true
+                            },
+                            "path-key": {
+                              "version": "1.0.0",
+                              "bundled": true,
+                              "dev": true
+                            },
+                            "strip-eof": {
+                              "version": "1.0.0",
+                              "bundled": true,
+                              "dev": true
+                            }
+                          }
+                        }
+                      }
+                    },
+                    "widest-line": {
+                      "version": "1.0.0",
+                      "bundled": true,
+                      "dev": true,
+                      "requires": {
+                        "string-width": "^1.0.1"
+                      },
+                      "dependencies": {
+                        "string-width": {
+                          "version": "1.0.2",
+                          "bundled": true,
+                          "dev": true,
+                          "requires": {
+                            "code-point-at": "^1.0.0",
+                            "is-fullwidth-code-point": "^1.0.0",
+                            "strip-ansi": "^3.0.0"
+                          },
+                          "dependencies": {
+                            "code-point-at": {
+                              "version": "1.1.0",
+                              "bundled": true,
+                              "dev": true
+                            },
+                            "is-fullwidth-code-point": {
+                              "version": "1.0.0",
+                              "bundled": true,
+                              "dev": true,
+                              "requires": {
+                                "number-is-nan": "^1.0.0"
+                              },
+                              "dependencies": {
+                                "number-is-nan": {
+                                  "version": "1.0.1",
+                                  "bundled": true,
+                                  "dev": true
+                                }
+                              }
+                            },
+                            "strip-ansi": {
+                              "version": "3.0.1",
+                              "bundled": true,
+                              "dev": true,
+                              "requires": {
+                                "ansi-regex": "^2.0.0"
+                              },
+                              "dependencies": {
+                                "ansi-regex": {
+                                  "version": "2.1.1",
+                                  "bundled": true,
+                                  "dev": true
+                                }
+                              }
+                            }
+                          }
+                        }
+                      }
+                    }
+                  }
+                },
+                "chalk": {
+                  "version": "1.1.3",
+                  "bundled": true,
+                  "dev": true,
+                  "requires": {
+                    "ansi-styles": "^2.2.1",
+                    "escape-string-regexp": "^1.0.2",
+                    "has-ansi": "^2.0.0",
+                    "strip-ansi": "^3.0.0",
+                    "supports-color": "^2.0.0"
+                  },
+                  "dependencies": {
+                    "ansi-styles": {
+                      "version": "2.2.1",
+                      "bundled": true,
+                      "dev": true
+                    },
+                    "escape-string-regexp": {
+                      "version": "1.0.5",
+                      "bundled": true,
+                      "dev": true
+                    },
+                    "has-ansi": {
+                      "version": "2.0.0",
+                      "bundled": true,
+                      "dev": true,
+                      "requires": {
+                        "ansi-regex": "^2.0.0"
+                      },
+                      "dependencies": {
+                        "ansi-regex": {
+                          "version": "2.1.1",
+                          "bundled": true,
+                          "dev": true
+                        }
+                      }
+                    },
+                    "strip-ansi": {
+                      "version": "3.0.1",
+                      "bundled": true,
+                      "dev": true,
+                      "requires": {
+                        "ansi-regex": "^2.0.0"
+                      },
+                      "dependencies": {
+                        "ansi-regex": {
+                          "version": "2.1.1",
+                          "bundled": true,
+                          "dev": true
+                        }
+                      }
+                    },
+                    "supports-color": {
+                      "version": "2.0.0",
+                      "bundled": true,
+                      "dev": true
+                    }
+                  }
+                },
+                "configstore": {
+                  "version": "3.1.0",
+                  "bundled": true,
+                  "dev": true,
+                  "requires": {
+                    "dot-prop": "^4.1.0",
+                    "graceful-fs": "^4.1.2",
+                    "make-dir": "^1.0.0",
+                    "unique-string": "^1.0.0",
+                    "write-file-atomic": "^2.0.0",
+                    "xdg-basedir": "^3.0.0"
+                  },
+                  "dependencies": {
+                    "dot-prop": {
+                      "version": "4.1.1",
+                      "bundled": true,
+                      "dev": true,
+                      "requires": {
+                        "is-obj": "^1.0.0"
+                      },
+                      "dependencies": {
+                        "is-obj": {
+                          "version": "1.0.1",
+                          "bundled": true,
+                          "dev": true
+                        }
+                      }
+                    },
+                    "make-dir": {
+                      "version": "1.0.0",
+                      "bundled": true,
+                      "dev": true,
+                      "requires": {
+                        "pify": "^2.3.0"
+                      },
+                      "dependencies": {
+                        "pify": {
+                          "version": "2.3.0",
+                          "bundled": true,
+                          "dev": true
+                        }
+                      }
+                    },
+                    "unique-string": {
+                      "version": "1.0.0",
+                      "bundled": true,
+                      "dev": true,
+                      "requires": {
+                        "crypto-random-string": "^1.0.0"
+                      },
+                      "dependencies": {
+                        "crypto-random-string": {
+                          "version": "1.0.0",
+                          "bundled": true,
+                          "dev": true
+                        }
+                      }
+                    }
+                  }
+                },
+                "import-lazy": {
+                  "version": "2.1.0",
+                  "bundled": true,
+                  "dev": true
+                },
+                "is-npm": {
+                  "version": "1.0.0",
+                  "bundled": true,
+                  "dev": true
+                },
+                "latest-version": {
+                  "version": "3.1.0",
+                  "bundled": true,
+                  "dev": true,
+                  "requires": {
+                    "package-json": "^4.0.0"
+                  },
+                  "dependencies": {
+                    "package-json": {
+                      "version": "4.0.1",
+                      "bundled": true,
+                      "dev": true,
+                      "requires": {
+                        "got": "^6.7.1",
+                        "registry-auth-token": "^3.0.1",
+                        "registry-url": "^3.0.3",
+                        "semver": "^5.1.0"
+                      },
+                      "dependencies": {
+                        "got": {
+                          "version": "6.7.1",
+                          "bundled": true,
+                          "dev": true,
+                          "requires": {
+                            "create-error-class": "^3.0.0",
+                            "duplexer3": "^0.1.4",
+                            "get-stream": "^3.0.0",
+                            "is-redirect": "^1.0.0",
+                            "is-retry-allowed": "^1.0.0",
+                            "is-stream": "^1.0.0",
+                            "lowercase-keys": "^1.0.0",
+                            "safe-buffer": "^5.0.1",
+                            "timed-out": "^4.0.0",
+                            "unzip-response": "^2.0.1",
+                            "url-parse-lax": "^1.0.0"
+                          },
+                          "dependencies": {
+                            "create-error-class": {
+                              "version": "3.0.2",
+                              "bundled": true,
+                              "dev": true,
+                              "requires": {
+                                "capture-stack-trace": "^1.0.0"
+                              },
+                              "dependencies": {
+                                "capture-stack-trace": {
+                                  "version": "1.0.0",
+                                  "bundled": true,
+                                  "dev": true
+                                }
+                              }
+                            },
+                            "duplexer3": {
+                              "version": "0.1.4",
+                              "bundled": true,
+                              "dev": true
+                            },
+                            "get-stream": {
+                              "version": "3.0.0",
+                              "bundled": true,
+                              "dev": true
+                            },
+                            "is-redirect": {
+                              "version": "1.0.0",
+                              "bundled": true,
+                              "dev": true
+                            },
+                            "is-retry-allowed": {
+                              "version": "1.1.0",
+                              "bundled": true,
+                              "dev": true
+                            },
+                            "is-stream": {
+                              "version": "1.1.0",
+                              "bundled": true,
+                              "dev": true
+                            },
+                            "lowercase-keys": {
+                              "version": "1.0.0",
+                              "bundled": true,
+                              "dev": true
+                            },
+                            "timed-out": {
+                              "version": "4.0.1",
+                              "bundled": true,
+                              "dev": true
+                            },
+                            "unzip-response": {
+                              "version": "2.0.1",
+                              "bundled": true,
+                              "dev": true
+                            },
+                            "url-parse-lax": {
+                              "version": "1.0.0",
+                              "bundled": true,
+                              "dev": true,
+                              "requires": {
+                                "prepend-http": "^1.0.1"
+                              },
+                              "dependencies": {
+                                "prepend-http": {
+                                  "version": "1.0.4",
+                                  "bundled": true,
+                                  "dev": true
+                                }
+                              }
+                            }
+                          }
+                        },
+                        "registry-auth-token": {
+                          "version": "3.3.1",
+                          "bundled": true,
+                          "dev": true,
+                          "requires": {
+                            "rc": "^1.1.6",
+                            "safe-buffer": "^5.0.1"
+                          },
+                          "dependencies": {
+                            "rc": {
+                              "version": "1.2.1",
+                              "bundled": true,
+                              "dev": true,
+                              "requires": {
+                                "deep-extend": "~0.4.0",
+                                "ini": "~1.3.0",
+                                "minimist": "^1.2.0",
+                                "strip-json-comments": "~2.0.1"
+                              },
+                              "dependencies": {
+                                "deep-extend": {
+                                  "version": "0.4.2",
+                                  "bundled": true,
+                                  "dev": true
+                                },
+                                "minimist": {
+                                  "version": "1.2.0",
+                                  "bundled": true,
+                                  "dev": true
+                                },
+                                "strip-json-comments": {
+                                  "version": "2.0.1",
+                                  "bundled": true,
+                                  "dev": true
+                                }
+                              }
+                            }
+                          }
+                        },
+                        "registry-url": {
+                          "version": "3.1.0",
+                          "bundled": true,
+                          "dev": true,
+                          "requires": {
+                            "rc": "^1.0.1"
+                          },
+                          "dependencies": {
+                            "rc": {
+                              "version": "1.2.1",
+                              "bundled": true,
+                              "dev": true,
+                              "requires": {
+                                "deep-extend": "~0.4.0",
+                                "ini": "~1.3.0",
+                                "minimist": "^1.2.0",
+                                "strip-json-comments": "~2.0.1"
+                              },
+                              "dependencies": {
+                                "deep-extend": {
+                                  "version": "0.4.2",
+                                  "bundled": true,
+                                  "dev": true
+                                },
+                                "minimist": {
+                                  "version": "1.2.0",
+                                  "bundled": true,
+                                  "dev": true
+                                },
+                                "strip-json-comments": {
+                                  "version": "2.0.1",
+                                  "bundled": true,
+                                  "dev": true
+                                }
+                              }
+                            }
+                          }
+                        }
+                      }
+                    }
+                  }
+                },
+                "semver-diff": {
+                  "version": "2.1.0",
+                  "bundled": true,
+                  "dev": true,
+                  "requires": {
+                    "semver": "^5.0.3"
+                  }
+                },
+                "xdg-basedir": {
+                  "version": "3.0.0",
+                  "bundled": true,
+                  "dev": true
+                }
+              }
+            },
+            "uuid": {
+              "version": "3.1.0",
+              "bundled": true,
+              "dev": true
+            },
+            "validate-npm-package-license": {
+              "version": "3.0.1",
+              "bundled": true,
+              "dev": true,
+              "requires": {
+                "spdx-correct": "~1.0.0",
+                "spdx-expression-parse": "~1.0.0"
+              },
+              "dependencies": {
+                "spdx-correct": {
+                  "version": "1.0.2",
+                  "bundled": true,
+                  "dev": true,
+                  "requires": {
+                    "spdx-license-ids": "^1.0.2"
+                  },
+                  "dependencies": {
+                    "spdx-license-ids": {
+                      "version": "1.2.2",
+                      "bundled": true,
+                      "dev": true
+                    }
+                  }
+                },
+                "spdx-expression-parse": {
+                  "version": "1.0.4",
+                  "bundled": true,
+                  "dev": true
+                }
+              }
+            },
+            "validate-npm-package-name": {
+              "version": "3.0.0",
+              "bundled": true,
+              "dev": true,
+              "requires": {
+                "builtins": "^1.0.3"
+              },
+              "dependencies": {
+                "builtins": {
+                  "version": "1.0.3",
+                  "bundled": true,
+                  "dev": true
+                }
+              }
+            },
+            "which": {
+              "version": "1.2.14",
+              "bundled": true,
+              "dev": true,
+              "requires": {
+                "isexe": "^2.0.0"
+              },
+              "dependencies": {
+                "isexe": {
+                  "version": "2.0.0",
+                  "bundled": true,
+                  "dev": true
+                }
+              }
+            },
+            "worker-farm": {
+              "version": "1.3.1",
+              "bundled": true,
+              "dev": true,
+              "requires": {
+                "errno": ">=0.1.1 <0.2.0-0",
+                "xtend": ">=4.0.0 <4.1.0-0"
+              },
+              "dependencies": {
+                "errno": {
+                  "version": "0.1.4",
+                  "bundled": true,
+                  "dev": true,
+                  "requires": {
+                    "prr": "~0.0.0"
+                  },
+                  "dependencies": {
+                    "prr": {
+                      "version": "0.0.0",
+                      "bundled": true,
+                      "dev": true
+                    }
+                  }
+                },
+                "xtend": {
+                  "version": "4.0.1",
+                  "bundled": true,
+                  "dev": true
+                }
+              }
+            },
+            "wrappy": {
+              "version": "1.0.2",
+              "bundled": true,
+              "dev": true
+            },
+            "write-file-atomic": {
+              "version": "2.1.0",
+              "bundled": true,
+              "dev": true,
+              "requires": {
+                "graceful-fs": "^4.1.11",
+                "imurmurhash": "^0.1.4",
+                "slide": "^1.1.5"
+              }
+            }
+          }
+        },
+        "npm-package-arg": {
+          "version": "6.1.1",
+          "bundled": true,
+          "dev": true,
+          "requires": {
+            "hosted-git-info": "^2.7.1",
+            "osenv": "^0.1.5",
+            "semver": "^5.6.0",
+            "validate-npm-package-name": "^3.0.0"
+          }
+        },
+        "npm-run-path": {
+          "version": "2.0.2",
+          "bundled": true,
+          "dev": true,
+          "requires": {
+            "path-key": "^2.0.0"
+          }
+        },
+        "number-is-nan": {
+          "version": "1.0.1",
+          "bundled": true,
+          "dev": true
+        },
+        "once": {
+          "version": "1.4.0",
+          "bundled": true,
+          "dev": true,
+          "requires": {
+            "wrappy": "1"
+          }
+        },
+        "os-homedir": {
+          "version": "1.0.2",
+          "bundled": true,
+          "dev": true
+        },
+        "os-locale": {
+          "version": "3.1.0",
+          "bundled": true,
+          "dev": true,
+          "requires": {
+            "execa": "^1.0.0",
+            "lcid": "^2.0.0",
+            "mem": "^4.0.0"
+          },
+          "dependencies": {
+            "cross-spawn": {
+              "version": "6.0.5",
+              "bundled": true,
+              "dev": true,
+              "requires": {
+                "nice-try": "^1.0.4",
+                "path-key": "^2.0.1",
+                "semver": "^5.5.0",
+                "shebang-command": "^1.2.0",
+                "which": "^1.2.9"
+              }
+            },
+            "execa": {
+              "version": "1.0.0",
+              "bundled": true,
+              "dev": true,
+              "requires": {
+                "cross-spawn": "^6.0.0",
+                "get-stream": "^4.0.0",
+                "is-stream": "^1.1.0",
+                "npm-run-path": "^2.0.0",
+                "p-finally": "^1.0.0",
+                "signal-exit": "^3.0.0",
+                "strip-eof": "^1.0.0"
+              }
+            },
+            "get-stream": {
+              "version": "4.1.0",
+              "bundled": true,
+              "dev": true,
+              "requires": {
+                "pump": "^3.0.0"
+              }
+            }
+          }
+        },
+        "os-tmpdir": {
+          "version": "1.0.2",
+          "bundled": true,
+          "dev": true
+        },
+        "osenv": {
+          "version": "0.1.5",
+          "bundled": true,
+          "dev": true,
+          "requires": {
+            "os-homedir": "^1.0.0",
+            "os-tmpdir": "^1.0.0"
+          }
+        },
+        "p-defer": {
+          "version": "1.0.0",
+          "bundled": true,
+          "dev": true
+        },
+        "p-finally": {
+          "version": "1.0.0",
+          "bundled": true,
+          "dev": true
+        },
+        "p-is-promise": {
+          "version": "2.1.0",
+          "bundled": true,
+          "dev": true
+        },
+        "p-limit": {
+          "version": "1.3.0",
+          "bundled": true,
+          "dev": true,
+          "requires": {
+            "p-try": "^1.0.0"
+          }
+        },
+        "p-locate": {
+          "version": "2.0.0",
+          "bundled": true,
+          "dev": true,
+          "requires": {
+            "p-limit": "^1.1.0"
+          }
+        },
+        "p-try": {
+          "version": "1.0.0",
+          "bundled": true,
+          "dev": true
+        },
+        "package-json": {
+          "version": "4.0.1",
+          "bundled": true,
+          "dev": true,
+          "requires": {
+            "got": "^6.7.1",
+            "registry-auth-token": "^3.0.1",
+            "registry-url": "^3.0.3",
+            "semver": "^5.1.0"
+          }
+        },
+        "path-exists": {
+          "version": "3.0.0",
+          "bundled": true,
+          "dev": true
+        },
+        "path-is-absolute": {
+          "version": "1.0.1",
+          "bundled": true,
+          "dev": true
+        },
+        "path-is-inside": {
+          "version": "1.0.2",
+          "bundled": true,
+          "dev": true
+        },
+        "path-key": {
+          "version": "2.0.1",
+          "bundled": true,
+          "dev": true
+        },
+        "pify": {
+          "version": "3.0.0",
+          "bundled": true,
+          "dev": true
+        },
+        "prepend-http": {
+          "version": "1.0.4",
+          "bundled": true,
+          "dev": true
+        },
+        "pseudomap": {
+          "version": "1.0.2",
+          "bundled": true,
+          "dev": true
+        },
+        "pump": {
+          "version": "3.0.0",
+          "bundled": true,
+          "dev": true,
+          "requires": {
+            "end-of-stream": "^1.1.0",
+            "once": "^1.3.1"
+          }
+        },
+        "rc": {
+          "version": "1.2.8",
+          "bundled": true,
+          "dev": true,
+          "requires": {
+            "deep-extend": "^0.6.0",
+            "ini": "~1.3.0",
+            "minimist": "^1.2.0",
+            "strip-json-comments": "~2.0.1"
+          }
+        },
+        "registry-auth-token": {
+          "version": "3.4.0",
+          "bundled": true,
+          "dev": true,
+          "requires": {
+            "rc": "^1.1.6",
+            "safe-buffer": "^5.0.1"
+          }
+        },
+        "registry-url": {
+          "version": "3.1.0",
+          "bundled": true,
+          "dev": true,
+          "requires": {
+            "rc": "^1.0.1"
+          }
+        },
+        "require-directory": {
+          "version": "2.1.1",
+          "bundled": true,
+          "dev": true
+        },
+        "require-main-filename": {
+          "version": "1.0.1",
+          "bundled": true,
+          "dev": true
+        },
+        "rimraf": {
+          "version": "2.7.1",
+          "bundled": true,
+          "dev": true,
+          "requires": {
+            "glob": "^7.1.3"
+          }
+        },
+        "safe-buffer": {
+          "version": "5.2.0",
+          "bundled": true,
+          "dev": true
+        },
+        "semver": {
+          "version": "5.7.1",
+          "bundled": true,
+          "dev": true
+        },
+        "semver-diff": {
+          "version": "2.1.0",
+          "bundled": true,
+          "dev": true,
+          "requires": {
+            "semver": "^5.0.3"
+          }
+        },
+        "set-blocking": {
+          "version": "2.0.0",
+          "bundled": true,
+          "dev": true
+        },
+        "shebang-command": {
+          "version": "1.2.0",
+          "bundled": true,
+          "dev": true,
+          "requires": {
+            "shebang-regex": "^1.0.0"
+          }
+        },
+        "shebang-regex": {
+          "version": "1.0.0",
+          "bundled": true,
+          "dev": true
+        },
+        "signal-exit": {
+          "version": "3.0.2",
+          "bundled": true,
+          "dev": true
+        },
+        "string-width": {
+          "version": "2.1.1",
+          "bundled": true,
+          "dev": true,
+          "requires": {
+            "is-fullwidth-code-point": "^2.0.0",
+            "strip-ansi": "^4.0.0"
+          }
+        },
+        "strip-ansi": {
+          "version": "4.0.0",
+          "bundled": true,
+          "dev": true,
+          "requires": {
+            "ansi-regex": "^3.0.0"
+          }
+        },
+        "strip-eof": {
+          "version": "1.0.0",
+          "bundled": true,
+          "dev": true
+        },
+        "strip-json-comments": {
+          "version": "2.0.1",
+          "bundled": true,
+          "dev": true
+        },
+        "supports-color": {
+          "version": "5.5.0",
+          "bundled": true,
+          "dev": true,
+          "requires": {
+            "has-flag": "^3.0.0"
+          }
+        },
+        "term-size": {
+          "version": "1.2.0",
+          "bundled": true,
+          "dev": true,
+          "requires": {
+            "execa": "^0.7.0"
+          }
+        },
+        "timed-out": {
+          "version": "4.0.1",
+          "bundled": true,
+          "dev": true
+        },
+        "unique-string": {
+          "version": "1.0.0",
+          "bundled": true,
+          "dev": true,
+          "requires": {
+            "crypto-random-string": "^1.0.0"
+          }
+        },
+        "unzip-response": {
+          "version": "2.0.1",
+          "bundled": true,
+          "dev": true
+        },
+        "update-notifier": {
+          "version": "2.5.0",
+          "bundled": true,
+          "dev": true,
+          "requires": {
+            "boxen": "^1.2.1",
+            "chalk": "^2.0.1",
+            "configstore": "^3.0.0",
+            "import-lazy": "^2.1.0",
+            "is-ci": "^1.0.10",
+            "is-installed-globally": "^0.1.0",
+            "is-npm": "^1.0.0",
+            "latest-version": "^3.0.0",
+            "semver-diff": "^2.0.0",
+            "xdg-basedir": "^3.0.0"
+          }
+        },
+        "url-parse-lax": {
+          "version": "1.0.0",
+          "bundled": true,
+          "dev": true,
+          "requires": {
+            "prepend-http": "^1.0.1"
+          }
+        },
+        "validate-npm-package-name": {
+          "version": "3.0.0",
+          "bundled": true,
+          "dev": true,
+          "requires": {
+            "builtins": "^1.0.3"
+          }
+        },
+        "which": {
+          "version": "1.3.1",
+          "bundled": true,
+          "dev": true,
+          "requires": {
+            "isexe": "^2.0.0"
+          }
+        },
+        "which-module": {
+          "version": "2.0.0",
+          "bundled": true,
+          "dev": true
+        },
+        "widest-line": {
+          "version": "2.0.1",
+          "bundled": true,
+          "dev": true,
+          "requires": {
+            "string-width": "^2.1.1"
+          }
+        },
+        "wrap-ansi": {
+          "version": "2.1.0",
+          "bundled": true,
+          "dev": true,
+          "requires": {
+            "string-width": "^1.0.1",
+            "strip-ansi": "^3.0.1"
+          },
+          "dependencies": {
+            "ansi-regex": {
+              "version": "2.1.1",
+              "bundled": true,
+              "dev": true
+            },
+            "is-fullwidth-code-point": {
+              "version": "1.0.0",
+              "bundled": true,
+              "dev": true,
+              "requires": {
+                "number-is-nan": "^1.0.0"
+              }
+            },
+            "string-width": {
+              "version": "1.0.2",
+              "bundled": true,
+              "dev": true,
+              "requires": {
+                "code-point-at": "^1.0.0",
+                "is-fullwidth-code-point": "^1.0.0",
+                "strip-ansi": "^3.0.0"
+              }
+            },
+            "strip-ansi": {
+              "version": "3.0.1",
+              "bundled": true,
+              "dev": true,
+              "requires": {
+                "ansi-regex": "^2.0.0"
+              }
+            }
+          }
+        },
+        "wrappy": {
+          "version": "1.0.2",
+          "bundled": true,
+          "dev": true
+        },
+        "write-file-atomic": {
+          "version": "2.4.3",
+          "bundled": true,
+          "dev": true,
+          "requires": {
+            "graceful-fs": "^4.1.11",
+            "imurmurhash": "^0.1.4",
+            "signal-exit": "^3.0.2"
+          }
+        },
+        "xdg-basedir": {
+          "version": "3.0.0",
+          "bundled": true,
+          "dev": true
+        },
+        "y18n": {
+          "version": "4.0.0",
+          "bundled": true,
+          "dev": true
+        },
+        "yallist": {
+          "version": "2.1.2",
+          "bundled": true,
+          "dev": true
+        },
+        "yargs": {
+          "version": "11.1.1",
+          "bundled": true,
+          "dev": true,
+          "requires": {
+            "cliui": "^4.0.0",
+            "decamelize": "^1.1.1",
+            "find-up": "^2.1.0",
+            "get-caller-file": "^1.0.1",
+            "os-locale": "^3.1.0",
+            "require-directory": "^2.1.1",
+            "require-main-filename": "^1.0.1",
+            "set-blocking": "^2.0.0",
+            "string-width": "^2.0.0",
+            "which-module": "^2.0.0",
+            "y18n": "^3.2.1",
+            "yargs-parser": "^9.0.2"
+          },
+          "dependencies": {
+            "y18n": {
+              "version": "3.2.1",
+              "bundled": true,
+              "dev": true
+            }
+          }
+        },
+        "yargs-parser": {
+          "version": "9.0.2",
+          "bundled": true,
+          "dev": true,
+          "requires": {
+            "camelcase": "^4.1.0"
+          }
+        }
+      }
+    },
+    "typescript": {
+      "version": "4.3.5",
+      "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.3.5.tgz",
+      "integrity": "sha512-DqQgihaQ9cUrskJo9kIyW/+g0Vxsk8cDtZ52a3NGh0YNTfpUSArXSohyUGnvbPazEPLu398C0UxmKSOrPumUzA==",
+      "dev": true
+    }
+  }
+}

+ 37 - 0
spine-ts/package.json

@@ -0,0 +1,37 @@
+{
+  "name": "spine-ts",
+  "version": "4.0.1",
+  "description": "The official Spine Runtimes for the web.",
+  "scripts": {},
+  "repository": {
+    "type": "git",
+    "url": "git+https://github.com/esotericsoftware/spine-runtimes.git"
+  },
+  "keywords": [
+    "gamedev",
+    "animations",
+    "2d",
+    "spine",
+    "game-dev",
+    "runtimes",
+    "skeletal"
+  ],
+  "author": "Esoteric Software LLC",
+  "license": "LicenseRef-LICENSE",
+  "bugs": {
+    "url": "https://github.com/esotericsoftware/spine-runtimes/issues"
+  },
+  "homepage": "https://github.com/esotericsoftware/spine-runtimes#readme",
+  "private": true,
+  "workspaces": [
+    "spine-core/*",
+    "spine-canvas/*",
+    "spine-player/*",
+    "spine-threejs/*",
+    "spine-webgl/*"
+  ],
+  "devDependencies": {
+    "npx": "^10.2.2",
+    "typescript": "^4.3.5"
+  }
+}

+ 0 - 0
spine-ts/canvas/example/assets/spineboy-ess.json → spine-ts/spine-canvas/example/assets/spineboy-ess.json


+ 0 - 0
spine-ts/canvas/example/assets/spineboy.atlas → spine-ts/spine-canvas/example/assets/spineboy.atlas


+ 0 - 0
spine-ts/canvas/example/assets/spineboy.png → spine-ts/spine-canvas/example/assets/spineboy.png


+ 0 - 0
spine-ts/canvas/example/index.html → spine-ts/spine-canvas/example/index.html


+ 0 - 0
spine-ts/canvas/src/AssetManager.ts → spine-ts/spine-canvas/src/AssetManager.ts


+ 0 - 0
spine-ts/canvas/src/CanvasTexture.ts → spine-ts/spine-canvas/src/CanvasTexture.ts


+ 7 - 7
spine-ts/canvas/src/SkeletonRenderer.ts → spine-ts/spine-canvas/src/SkeletonRenderer.ts

@@ -39,16 +39,16 @@ module spine.canvas {
 		private vertices = Utils.newFloatArray(8 * 1024);
 		private tempColor = new Color();
 
-		constructor (context: CanvasRenderingContext2D) {
+		constructor(context: CanvasRenderingContext2D) {
 			this.ctx = context;
 		}
 
-		draw (skeleton: Skeleton) {
+		draw(skeleton: Skeleton) {
 			if (this.triangleRendering) this.drawTriangles(skeleton);
 			else this.drawImages(skeleton);
 		}
 
-		private drawImages (skeleton: Skeleton) {
+		private drawImages(skeleton: Skeleton) {
 			let ctx = this.ctx;
 			let color = this.tempColor;
 			let skeletonColor = skeleton.color;
@@ -105,7 +105,7 @@ module spine.canvas {
 			}
 		}
 
-		private drawTriangles (skeleton: Skeleton) {
+		private drawTriangles(skeleton: Skeleton) {
 			let ctx = this.ctx;
 			let color = this.tempColor;
 			let skeletonColor = skeleton.color;
@@ -180,7 +180,7 @@ module spine.canvas {
 
 		// Adapted from http://extremelysatisfactorytotalitarianism.com/blog/?p=2120
 		// Apache 2 licensed
-		private drawTriangle (img: HTMLImageElement, x0: number, y0: number, u0: number, v0: number,
+		private drawTriangle(img: HTMLImageElement, x0: number, y0: number, u0: number, v0: number,
 			x1: number, y1: number, u1: number, v1: number,
 			x2: number, y2: number, u2: number, v2: number) {
 			let ctx = this.ctx;
@@ -227,7 +227,7 @@ module spine.canvas {
 			ctx.restore();
 		}
 
-		private computeRegionVertices (slot: Slot, region: RegionAttachment, pma: boolean) {
+		private computeRegionVertices(slot: Slot, region: RegionAttachment, pma: boolean) {
 			let skeletonColor = slot.bone.skeleton.color;
 			let slotColor = slot.color;
 			let regionColor = region.color;
@@ -275,7 +275,7 @@ module spine.canvas {
 			return vertices;
 		}
 
-		private computeMeshVertices (slot: Slot, mesh: MeshAttachment, pma: boolean) {
+		private computeMeshVertices(slot: Slot, mesh: MeshAttachment, pma: boolean) {
 			let skeletonColor = slot.bone.skeleton.color;
 			let slotColor = slot.color;
 			let regionColor = mesh.color;

+ 25 - 0
spine-ts/spine-core/package.json

@@ -0,0 +1,25 @@
+{
+  "name": "spine-core",
+  "version": "4.0.1",
+  "description": "The official Spine Runtimes for the web.",
+  "scripts": {},
+  "repository": {
+    "type": "git",
+    "url": "git+https://github.com/esotericsoftware/spine-runtimes.git"
+  },
+  "keywords": [
+    "gamedev",
+    "animations",
+    "2d",
+    "spine",
+    "game-dev",
+    "runtimes",
+    "skeletal"
+  ],
+  "author": "Esoteric Software LLC",
+  "license": "LicenseRef-LICENSE",
+  "bugs": {
+    "url": "https://github.com/esotericsoftware/spine-runtimes/issues"
+  },
+  "homepage": "https://github.com/esotericsoftware/spine-runtimes#readme"  
+}

+ 2151 - 0
spine-ts/spine-core/src/Animation.ts

@@ -0,0 +1,2151 @@
+/******************************************************************************
+ * Spine Runtimes License Agreement
+ * Last updated January 1, 2020. Replaces all prior versions.
+ *
+ * Copyright (c) 2013-2020, Esoteric Software LLC
+ *
+ * Integration of the Spine Runtimes into software or otherwise creating
+ * derivative works of the Spine Runtimes is permitted under the terms and
+ * conditions of Section 2 of the Spine Editor License Agreement:
+ * http://esotericsoftware.com/spine-editor-license
+ *
+ * Otherwise, it is permitted to integrate the Spine Runtimes into software
+ * or otherwise create derivative works of the Spine Runtimes (collectively,
+ * "Products"), provided that each user of the Products must obtain their own
+ * Spine Editor license and redistribution of the Products in any form must
+ * include this license and copyright notice.
+ *
+ * THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "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 LLC 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
+ * THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *****************************************************************************/
+
+import { VertexAttachment, Attachment } from "./attachments/Attachment";
+import { IkConstraint } from "./IkConstraint";
+import { PathConstraint } from "./PathConstraint";
+import { Skeleton } from "./Skeleton";
+import { Slot } from "./Slot";
+import { TransformConstraint } from "./TransformConstraint";
+import { StringSet, Utils, MathUtils, NumberArrayLike } from "./Utils";
+import { Event } from "./Event";
+
+/** A simple container for a list of timelines and a name. */
+export class Animation {
+	/** The animation's name, which is unique across all animations in the skeleton. */
+	name: string;
+	timelines: Array<Timeline>;
+	timelineIds: StringSet;
+
+	/** The duration of the animation in seconds, which is the highest time of all keys in the timeline. */
+	duration: number;
+
+	constructor(name: string, timelines: Array<Timeline>, duration: number) {
+		if (!name) throw new Error("name cannot be null.");
+		this.name = name;
+		this.setTimelines(timelines);
+		this.duration = duration;
+	}
+
+	setTimelines(timelines: Array<Timeline>) {
+		if (!timelines) throw new Error("timelines cannot be null.");
+		this.timelines = timelines;
+		this.timelineIds = new StringSet();
+		for (var i = 0; i < timelines.length; i++)
+			this.timelineIds.addAll(timelines[i].getPropertyIds());
+	}
+
+	hasTimeline(ids: string[]): boolean {
+		for (let i = 0; i < ids.length; i++)
+			if (this.timelineIds.contains(ids[i])) return true;
+		return false;
+	}
+
+	/** Applies all the animation's timelines to the specified skeleton.
+	 *
+	 * See Timeline {@link Timeline#apply(Skeleton, float, float, Array, float, MixBlend, MixDirection)}.
+	 * @param loop If true, the animation repeats after {@link #getDuration()}.
+	 * @param events May be null to ignore fired events. */
+	apply(skeleton: Skeleton, lastTime: number, time: number, loop: boolean, events: Array<Event>, alpha: number, blend: MixBlend, direction: MixDirection) {
+		if (!skeleton) 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, blend, direction);
+	}
+}
+
+/** Controls how a timeline value is mixed with the setup pose value or current pose value when a timeline's `alpha`
+ * < 1.
+ *
+ * See Timeline {@link Timeline#apply(Skeleton, float, float, Array, float, MixBlend, MixDirection)}. */
+export enum MixBlend {
+	/** Transitions from the setup value to the timeline value (the current value is not used). Before the first key, the setup
+	 * value is set. */
+	setup,
+	/** Transitions from the current value to the timeline value. Before the first key, transitions from the current value to
+	 * the setup value. Timelines which perform instant transitions, such as {@link DrawOrderTimeline} or
+	 * {@link AttachmentTimeline}, use the setup value before the first key.
+	 *
+	 * `first` is intended for the first animations applied, not for animations layered on top of those. */
+	first,
+	/** Transitions from the current value to the timeline value. No change is made before the first key (the current value is
+	 * kept until the first key).
+	 *
+	 * `replace` is intended for animations layered on top of others, not for the first animations applied. */
+	replace,
+	/** Transitions from the current value to the current value plus the timeline value. No change is made before the first key
+	 * (the current value is kept until the first key).
+	 *
+	 * `add` is intended for animations layered on top of others, not for the first animations applied. Properties
+	 * keyed by additive animations must be set manually or by another animation before applying the additive animations, else
+	 * the property values will increase continually. */
+	add
+}
+
+/** Indicates whether a timeline's `alpha` is mixing out over time toward 0 (the setup or current pose value) or
+ * mixing in toward 1 (the timeline's value).
+ *
+ * See Timeline {@link Timeline#apply(Skeleton, float, float, Array, float, MixBlend, MixDirection)}. */
+export enum MixDirection {
+	mixIn, mixOut
+}
+
+const Property = {
+	rotate: 0,
+	x: 1,
+	y: 2,
+	scaleX: 3,
+	scaleY: 4,
+	shearX: 5,
+	shearY: 6,
+
+	rgb: 7,
+	alpha: 8,
+	rgb2: 9,
+
+	attachment: 10,
+	deform: 11,
+
+	event: 12,
+	drawOrder: 13,
+
+	ikConstraint: 14,
+	transformConstraint: 15,
+
+	pathConstraintPosition: 16,
+	pathConstraintSpacing: 17,
+	pathConstraintMix: 18
+}
+
+/** The interface for all timelines. */
+export abstract class Timeline {
+	propertyIds: string[];
+	frames: NumberArrayLike;
+
+	constructor(frameCount: number, propertyIds: string[]) {
+		this.propertyIds = propertyIds;
+		this.frames = Utils.newFloatArray(frameCount * this.getFrameEntries());
+	}
+
+	getPropertyIds() {
+		return this.propertyIds;
+	}
+
+	getFrameEntries(): number {
+		return 1;
+	}
+
+	getFrameCount() {
+		return this.frames.length / this.getFrameEntries();
+	}
+
+	getDuration(): number {
+		return this.frames[this.frames.length - this.getFrameEntries()];
+	}
+
+	abstract apply(skeleton: Skeleton, lastTime: number, time: number, events: Array<Event>, alpha: number, blend: MixBlend, direction: MixDirection): void;
+
+	static search1(frames: NumberArrayLike, time: number) {
+		let n = frames.length;
+		for (let i = 1; i < n; i++)
+			if (frames[i] > time) return i - 1;
+		return n - 1;
+	}
+
+	static search(frames: NumberArrayLike, time: number, step: number) {
+		let n = frames.length;
+		for (let i = step; i < n; i += step)
+			if (frames[i] > time) return i - step;
+		return n - step;
+	}
+}
+
+export interface BoneTimeline {
+	/** The index of the bone in {@link Skeleton#bones} that will be changed. */
+	boneIndex: number;
+}
+
+export interface SlotTimeline {
+	/** The index of the slot in {@link Skeleton#slots} that will be changed. */
+	slotIndex: number;
+}
+
+/** The base class for timelines that use interpolation between key frame values. */
+export abstract class CurveTimeline extends Timeline {
+	protected curves: NumberArrayLike; // type, x, y, ...
+
+	constructor(frameCount: number, bezierCount: number, propertyIds: string[]) {
+		super(frameCount, propertyIds);
+		this.curves = Utils.newFloatArray(frameCount + bezierCount * 18/*BEZIER_SIZE*/);
+		this.curves[frameCount - 1] = 1/*STEPPED*/;
+	}
+
+	/** Sets the specified key frame to linear interpolation. */
+	setLinear(frame: number) {
+		this.curves[frame] = 0/*LINEAR*/;
+	}
+
+	/** Sets the specified key frame to stepped interpolation. */
+	setStepped(frame: number) {
+		this.curves[frame] = 1/*STEPPED*/;
+	}
+
+	/** Shrinks the storage for Bezier curves, for use when <code>bezierCount</code> (specified in the constructor) was larger
+	 * than the actual number of Bezier curves. */
+	shrink(bezierCount: number) {
+		let size = this.getFrameCount() + bezierCount * 18/*BEZIER_SIZE*/;
+		if (this.curves.length > size) {
+			let newCurves = Utils.newFloatArray(size);
+			Utils.arrayCopy(this.curves, 0, newCurves, 0, size);
+			this.curves = newCurves;
+		}
+	}
+
+	/** Stores the segments for the specified Bezier curve. For timelines that modify multiple values, there may be more than
+	 * one curve per frame.
+	 * @param bezier The ordinal of this Bezier curve for this timeline, between 0 and <code>bezierCount - 1</code> (specified
+	 *           in the constructor), inclusive.
+	 * @param frame Between 0 and <code>frameCount - 1</code>, inclusive.
+	 * @param value The index of the value for this frame that this curve is used for.
+	 * @param time1 The time for the first key.
+	 * @param value1 The value for the first key.
+	 * @param cx1 The time for the first Bezier handle.
+	 * @param cy1 The value for the first Bezier handle.
+	 * @param cx2 The time of the second Bezier handle.
+	 * @param cy2 The value for the second Bezier handle.
+	 * @param time2 The time for the second key.
+	 * @param value2 The value for the second key. */
+	setBezier(bezier: number, frame: number, value: number, time1: number, value1: number, cx1: number, cy1: number, cx2: number,
+		cy2: number, time2: number, value2: number) {
+		let curves = this.curves;
+		let i = this.getFrameCount() + bezier * 18/*BEZIER_SIZE*/;
+		if (value == 0) curves[frame] = 2/*BEZIER*/ + i;
+		let tmpx = (time1 - cx1 * 2 + cx2) * 0.03, tmpy = (value1 - cy1 * 2 + cy2) * 0.03;
+		let dddx = ((cx1 - cx2) * 3 - time1 + time2) * 0.006, dddy = ((cy1 - cy2) * 3 - value1 + value2) * 0.006;
+		let ddx = tmpx * 2 + dddx, ddy = tmpy * 2 + dddy;
+		let dx = (cx1 - time1) * 0.3 + tmpx + dddx * 0.16666667, dy = (cy1 - value1) * 0.3 + tmpy + dddy * 0.16666667;
+		let x = time1 + dx, y = value1 + dy;
+		for (let n = i + 18/*BEZIER_SIZE*/; i < n; i += 2) {
+			curves[i] = x;
+			curves[i + 1] = y;
+			dx += ddx;
+			dy += ddy;
+			ddx += dddx;
+			ddy += dddy;
+			x += dx;
+			y += dy;
+		}
+	}
+
+	/** Returns the Bezier interpolated value for the specified time.
+	 * @param frameIndex The index into {@link #getFrames()} for the values of the frame before <code>time</code>.
+	 * @param valueOffset The offset from <code>frameIndex</code> to the value this curve is used for.
+	 * @param i The index of the Bezier segments. See {@link #getCurveType(int)}. */
+	getBezierValue(time: number, frameIndex: number, valueOffset: number, i: number) {
+		let curves = this.curves;
+		if (curves[i] > time) {
+			let x = this.frames[frameIndex], y = this.frames[frameIndex + valueOffset];
+			return y + (time - x) / (curves[i] - x) * (curves[i + 1] - y);
+		}
+		let n = i + 18/*BEZIER_SIZE*/;
+		for (i += 2; i < n; i += 2) {
+			if (curves[i] >= time) {
+				let x = curves[i - 2], y = curves[i - 1];
+				return y + (time - x) / (curves[i] - x) * (curves[i + 1] - y);
+			}
+		}
+		frameIndex += this.getFrameEntries();
+		let x = curves[n - 2], y = curves[n - 1];
+		return y + (time - x) / (this.frames[frameIndex] - x) * (this.frames[frameIndex + valueOffset] - y);
+	}
+}
+
+export abstract class CurveTimeline1 extends CurveTimeline {
+	constructor(frameCount: number, bezierCount: number, propertyId: string) {
+		super(frameCount, bezierCount, [propertyId]);
+	}
+
+	getFrameEntries() {
+		return 2/*ENTRIES*/;
+	}
+
+	/** Sets the time and value for the specified frame.
+	 * @param frame Between 0 and <code>frameCount</code>, inclusive.
+	 * @param time The frame time in seconds. */
+	setFrame(frame: number, time: number, value: number) {
+		frame <<= 1;
+		this.frames[frame] = time;
+		this.frames[frame + 1/*VALUE*/] = value;
+	}
+
+	/** Returns the interpolated value for the specified time. */
+	getCurveValue(time: number) {
+		let frames = this.frames;
+		let i = frames.length - 2;
+		for (let ii = 2; ii <= i; ii += 2) {
+			if (frames[ii] > time) {
+				i = ii - 2;
+				break;
+			}
+		}
+
+		let curveType = this.curves[i >> 1];
+		switch (curveType) {
+			case 0/*LINEAR*/:
+				let before = frames[i], value = frames[i + 1/*VALUE*/];
+				return value + (time - before) / (frames[i + 2/*ENTRIES*/] - before) * (frames[i + 2/*ENTRIES*/ + 1/*VALUE*/] - value);
+			case 1/*STEPPED*/:
+				return frames[i + 1/*VALUE*/];
+		}
+		return this.getBezierValue(time, i, 1/*VALUE*/, curveType - 2/*BEZIER*/);
+	}
+}
+
+/** The base class for a {@link CurveTimeline} which sets two properties. */
+export abstract class CurveTimeline2 extends CurveTimeline {
+	/** @param bezierCount The maximum number of Bezier curves. See {@link #shrink(int)}.
+	 * @param propertyIds Unique identifiers for the properties the timeline modifies. */
+	constructor(frameCount: number, bezierCount: number, propertyId1: string, propertyId2: string) {
+		super(frameCount, bezierCount, [propertyId1, propertyId2]);
+	}
+
+	getFrameEntries() {
+		return 3/*ENTRIES*/;
+	}
+
+	/** Sets the time and values for the specified frame.
+	 * @param frame Between 0 and <code>frameCount</code>, inclusive.
+	 * @param time The frame time in seconds. */
+	setFrame(frame: number, time: number, value1: number, value2: number) {
+		frame *= 3/*ENTRIES*/;
+		this.frames[frame] = time;
+		this.frames[frame + 1/*VALUE1*/] = value1;
+		this.frames[frame + 2/*VALUE2*/] = value2;
+	}
+}
+
+/** Changes a bone's local {@link Bone#rotation}. */
+export class RotateTimeline extends CurveTimeline1 implements BoneTimeline {
+	boneIndex = 0;
+
+	constructor(frameCount: number, bezierCount: number, boneIndex: number) {
+		super(frameCount, bezierCount, Property.rotate + "|" + boneIndex);
+		this.boneIndex = boneIndex;
+	}
+
+	apply(skeleton: Skeleton, lastTime: number, time: number, events: Array<Event>, alpha: number, blend: MixBlend, direction: MixDirection) {
+		let bone = skeleton.bones[this.boneIndex];
+		if (!bone.active) return;
+
+		let frames = this.frames;
+		if (time < frames[0]) {
+			switch (blend) {
+				case MixBlend.setup:
+					bone.rotation = bone.data.rotation;
+					return;
+				case MixBlend.first:
+					bone.rotation += (bone.data.rotation - bone.rotation) * alpha;
+			}
+			return;
+		}
+
+		let r = this.getCurveValue(time);
+		switch (blend) {
+			case MixBlend.setup:
+				bone.rotation = bone.data.rotation + r * alpha;
+				break;
+			case MixBlend.first:
+			case MixBlend.replace:
+				r += bone.data.rotation - bone.rotation;
+			case MixBlend.add:
+				bone.rotation += r * alpha;
+		}
+	}
+}
+
+/** Changes a bone's local {@link Bone#x} and {@link Bone#y}. */
+export class TranslateTimeline extends CurveTimeline2 implements BoneTimeline {
+	boneIndex = 0;
+
+	constructor(frameCount: number, bezierCount: number, boneIndex: number) {
+		super(frameCount, bezierCount,
+			Property.x + "|" + boneIndex,
+			Property.y + "|" + boneIndex,
+		);
+		this.boneIndex = boneIndex;
+	}
+
+	apply(skeleton: Skeleton, lastTime: number, time: number, events: Array<Event>, alpha: number, blend: MixBlend, direction: MixDirection) {
+		let bone = skeleton.bones[this.boneIndex];
+		if (!bone.active) return;
+
+		let frames = this.frames;
+		if (time < frames[0]) {
+			switch (blend) {
+				case MixBlend.setup:
+					bone.x = bone.data.x;
+					bone.y = bone.data.y;
+					return;
+				case MixBlend.first:
+					bone.x += (bone.data.x - bone.x) * alpha;
+					bone.y += (bone.data.y - bone.y) * alpha;
+			}
+			return;
+		}
+
+		let x = 0, y = 0;
+		let i = Timeline.search(frames, time, 3/*ENTRIES*/);
+		let curveType = this.curves[i / 3/*ENTRIES*/];
+		switch (curveType) {
+			case 0/*LINEAR*/:
+				let before = frames[i];
+				x = frames[i + 1/*VALUE1*/];
+				y = frames[i + 2/*VALUE2*/];
+				let t = (time - before) / (frames[i + 3/*ENTRIES*/] - before);
+				x += (frames[i + 3/*ENTRIES*/ + 1/*VALUE1*/] - x) * t;
+				y += (frames[i + 3/*ENTRIES*/ + 2/*VALUE2*/] - y) * t;
+				break;
+			case 1/*STEPPED*/:
+				x = frames[i + 1/*VALUE1*/];
+				y = frames[i + 2/*VALUE2*/];
+				break;
+			default:
+				x = this.getBezierValue(time, i, 1/*VALUE1*/, curveType - 2/*BEZIER*/);
+				y = this.getBezierValue(time, i, 2/*VALUE2*/, curveType + 18/*BEZIER_SIZE*/ - 2/*BEZIER*/);
+		}
+
+		switch (blend) {
+			case MixBlend.setup:
+				bone.x = bone.data.x + x * alpha;
+				bone.y = bone.data.y + y * alpha;
+				break;
+			case MixBlend.first:
+			case MixBlend.replace:
+				bone.x += (bone.data.x + x - bone.x) * alpha;
+				bone.y += (bone.data.y + y - bone.y) * alpha;
+				break;
+			case MixBlend.add:
+				bone.x += x * alpha;
+				bone.y += y * alpha;
+		}
+	}
+}
+
+/** Changes a bone's local {@link Bone#x}. */
+export class TranslateXTimeline extends CurveTimeline1 implements BoneTimeline {
+	boneIndex = 0;
+
+	constructor(frameCount: number, bezierCount: number, boneIndex: number) {
+		super(frameCount, bezierCount, Property.x + "|" + boneIndex);
+		this.boneIndex = boneIndex;
+	}
+
+	apply(skeleton: Skeleton, lastTime: number, time: number, events: Array<Event>, alpha: number, blend: MixBlend, direction: MixDirection) {
+		let bone = skeleton.bones[this.boneIndex];
+		if (!bone.active) return;
+
+		let frames = this.frames;
+		if (time < frames[0]) {
+			switch (blend) {
+				case MixBlend.setup:
+					bone.x = bone.data.x;
+					return;
+				case MixBlend.first:
+					bone.x += (bone.data.x - bone.x) * alpha;
+			}
+			return;
+		}
+
+		let x = this.getCurveValue(time);
+		switch (blend) {
+			case MixBlend.setup:
+				bone.x = bone.data.x + x * alpha;
+				break;
+			case MixBlend.first:
+			case MixBlend.replace:
+				bone.x += (bone.data.x + x - bone.x) * alpha;
+				break;
+			case MixBlend.add:
+				bone.x += x * alpha;
+		}
+	}
+}
+
+/** Changes a bone's local {@link Bone#x}. */
+export class TranslateYTimeline extends CurveTimeline1 implements BoneTimeline {
+	boneIndex = 0;
+
+	constructor(frameCount: number, bezierCount: number, boneIndex: number) {
+		super(frameCount, bezierCount, Property.y + "|" + boneIndex);
+		this.boneIndex = boneIndex;
+	}
+
+	apply(skeleton: Skeleton, lastTime: number, time: number, events: Array<Event>, alpha: number, blend: MixBlend, direction: MixDirection) {
+		let bone = skeleton.bones[this.boneIndex];
+		if (!bone.active) return;
+
+		let frames = this.frames;
+		if (time < frames[0]) {
+			switch (blend) {
+				case MixBlend.setup:
+					bone.y = bone.data.y;
+					return;
+				case MixBlend.first:
+					bone.y += (bone.data.y - bone.y) * alpha;
+			}
+			return;
+		}
+
+		let y = this.getCurveValue(time);
+		switch (blend) {
+			case MixBlend.setup:
+				bone.y = bone.data.y + y * alpha;
+				break;
+			case MixBlend.first:
+			case MixBlend.replace:
+				bone.y += (bone.data.y + y - bone.y) * alpha;
+				break;
+			case MixBlend.add:
+				bone.y += y * alpha;
+		}
+	}
+}
+
+/** Changes a bone's local {@link Bone#scaleX)} and {@link Bone#scaleY}. */
+export class ScaleTimeline extends CurveTimeline2 implements BoneTimeline {
+	boneIndex = 0;
+
+	constructor(frameCount: number, bezierCount: number, boneIndex: number) {
+		super(frameCount, bezierCount,
+			Property.scaleX + "|" + boneIndex,
+			Property.scaleY + "|" + boneIndex
+		);
+		this.boneIndex = boneIndex;
+	}
+
+	apply(skeleton: Skeleton, lastTime: number, time: number, events: Array<Event>, alpha: number, blend: MixBlend, direction: MixDirection) {
+		let bone = skeleton.bones[this.boneIndex];
+		if (!bone.active) return;
+
+		let frames = this.frames;
+		if (time < frames[0]) {
+			switch (blend) {
+				case MixBlend.setup:
+					bone.scaleX = bone.data.scaleX;
+					bone.scaleY = bone.data.scaleY;
+					return;
+				case MixBlend.first:
+					bone.scaleX += (bone.data.scaleX - bone.scaleX) * alpha;
+					bone.scaleY += (bone.data.scaleY - bone.scaleY) * alpha;
+			}
+			return;
+		}
+
+		let x, y;
+		let i = Timeline.search(frames, time, 3/*ENTRIES*/);
+		let curveType = this.curves[i / 3/*ENTRIES*/];
+		switch (curveType) {
+			case 0/*LINEAR*/:
+				let before = frames[i];
+				x = frames[i + 1/*VALUE1*/];
+				y = frames[i + 2/*VALUE2*/];
+				let t = (time - before) / (frames[i + 3/*ENTRIES*/] - before);
+				x += (frames[i + 3/*ENTRIES*/ + 1/*VALUE1*/] - x) * t;
+				y += (frames[i + 3/*ENTRIES*/ + 2/*VALUE2*/] - y) * t;
+				break;
+			case 1/*STEPPED*/:
+				x = frames[i + 1/*VALUE1*/];
+				y = frames[i + 2/*VALUE2*/];
+				break;
+			default:
+				x = this.getBezierValue(time, i, 1/*VALUE1*/, curveType - 2/*BEZIER*/);
+				y = this.getBezierValue(time, i, 2/*VALUE2*/, curveType + 18/*BEZIER_SIZE*/ - 2/*BEZIER*/);
+		}
+		x *= bone.data.scaleX;
+		y *= bone.data.scaleY;
+
+		if (alpha == 1) {
+			if (blend == MixBlend.add) {
+				bone.scaleX += x - bone.data.scaleX;
+				bone.scaleY += y - bone.data.scaleY;
+			} else {
+				bone.scaleX = x;
+				bone.scaleY = y;
+			}
+		} else {
+			let bx = 0, by = 0;
+			if (direction == MixDirection.mixOut) {
+				switch (blend) {
+					case MixBlend.setup:
+						bx = bone.data.scaleX;
+						by = bone.data.scaleY;
+						bone.scaleX = bx + (Math.abs(x) * MathUtils.signum(bx) - bx) * alpha;
+						bone.scaleY = by + (Math.abs(y) * MathUtils.signum(by) - by) * alpha;
+						break;
+					case MixBlend.first:
+					case MixBlend.replace:
+						bx = bone.scaleX;
+						by = bone.scaleY;
+						bone.scaleX = bx + (Math.abs(x) * MathUtils.signum(bx) - bx) * alpha;
+						bone.scaleY = by + (Math.abs(y) * MathUtils.signum(by) - by) * alpha;
+						break;
+					case MixBlend.add:
+						bx = bone.scaleX;
+						by = bone.scaleY;
+						bone.scaleX = bx + (Math.abs(x) * MathUtils.signum(bx) - bone.data.scaleX) * alpha;
+						bone.scaleY = by + (Math.abs(y) * MathUtils.signum(by) - bone.data.scaleY) * alpha;
+				}
+			} else {
+				switch (blend) {
+					case MixBlend.setup:
+						bx = Math.abs(bone.data.scaleX) * MathUtils.signum(x);
+						by = Math.abs(bone.data.scaleY) * MathUtils.signum(y);
+						bone.scaleX = bx + (x - bx) * alpha;
+						bone.scaleY = by + (y - by) * alpha;
+						break;
+					case MixBlend.first:
+					case MixBlend.replace:
+						bx = Math.abs(bone.scaleX) * MathUtils.signum(x);
+						by = Math.abs(bone.scaleY) * MathUtils.signum(y);
+						bone.scaleX = bx + (x - bx) * alpha;
+						bone.scaleY = by + (y - by) * alpha;
+						break;
+					case MixBlend.add:
+						bx = MathUtils.signum(x);
+						by = MathUtils.signum(y);
+						bone.scaleX = Math.abs(bone.scaleX) * bx + (x - Math.abs(bone.data.scaleX) * bx) * alpha;
+						bone.scaleY = Math.abs(bone.scaleY) * by + (y - Math.abs(bone.data.scaleY) * by) * alpha;
+				}
+			}
+		}
+	}
+}
+
+/** Changes a bone's local {@link Bone#scaleX)} and {@link Bone#scaleY}. */
+export class ScaleXTimeline extends CurveTimeline1 implements BoneTimeline {
+	boneIndex = 0;
+
+	constructor(frameCount: number, bezierCount: number, boneIndex: number) {
+		super(frameCount, bezierCount, Property.scaleX + "|" + boneIndex);
+		this.boneIndex = boneIndex;
+	}
+
+	apply(skeleton: Skeleton, lastTime: number, time: number, events: Array<Event>, alpha: number, blend: MixBlend, direction: MixDirection) {
+		let bone = skeleton.bones[this.boneIndex];
+		if (!bone.active) return;
+
+		let frames = this.frames;
+		if (time < frames[0]) {
+			switch (blend) {
+				case MixBlend.setup:
+					bone.scaleX = bone.data.scaleX;
+					return;
+				case MixBlend.first:
+					bone.scaleX += (bone.data.scaleX - bone.scaleX) * alpha;
+			}
+			return;
+		}
+
+		let x = this.getCurveValue(time) * bone.data.scaleX;
+		if (alpha == 1) {
+			if (blend == MixBlend.add)
+				bone.scaleX += x - bone.data.scaleX;
+			else
+				bone.scaleX = x;
+		} else {
+			// Mixing out uses sign of setup or current pose, else use sign of key.
+			let bx = 0;
+			if (direction == MixDirection.mixOut) {
+				switch (blend) {
+					case MixBlend.setup:
+						bx = bone.data.scaleX;
+						bone.scaleX = bx + (Math.abs(x) * MathUtils.signum(bx) - bx) * alpha;
+						break;
+					case MixBlend.first:
+					case MixBlend.replace:
+						bx = bone.scaleX;
+						bone.scaleX = bx + (Math.abs(x) * MathUtils.signum(bx) - bx) * alpha;
+						break;
+					case MixBlend.add:
+						bx = bone.scaleX;
+						bone.scaleX = bx + (Math.abs(x) * MathUtils.signum(bx) - bone.data.scaleX) * alpha;
+				}
+			} else {
+				switch (blend) {
+					case MixBlend.setup:
+						bx = Math.abs(bone.data.scaleX) * MathUtils.signum(x);
+						bone.scaleX = bx + (x - bx) * alpha;
+						break;
+					case MixBlend.first:
+					case MixBlend.replace:
+						bx = Math.abs(bone.scaleX) * MathUtils.signum(x);
+						bone.scaleX = bx + (x - bx) * alpha;
+						break;
+					case MixBlend.add:
+						bx = MathUtils.signum(x);
+						bone.scaleX = Math.abs(bone.scaleX) * bx + (x - Math.abs(bone.data.scaleX) * bx) * alpha;
+				}
+			}
+		}
+	}
+}
+
+/** Changes a bone's local {@link Bone#scaleX)} and {@link Bone#scaleY}. */
+export class ScaleYTimeline extends CurveTimeline1 implements BoneTimeline {
+	boneIndex = 0;
+
+	constructor(frameCount: number, bezierCount: number, boneIndex: number) {
+		super(frameCount, bezierCount, Property.scaleY + "|" + boneIndex);
+		this.boneIndex = boneIndex;
+	}
+
+	apply(skeleton: Skeleton, lastTime: number, time: number, events: Array<Event>, alpha: number, blend: MixBlend, direction: MixDirection) {
+		let bone = skeleton.bones[this.boneIndex];
+		if (!bone.active) return;
+
+		let frames = this.frames;
+		if (time < frames[0]) {
+			switch (blend) {
+				case MixBlend.setup:
+					bone.scaleY = bone.data.scaleY;
+					return;
+				case MixBlend.first:
+					bone.scaleY += (bone.data.scaleY - bone.scaleY) * alpha;
+			}
+			return;
+		}
+
+		let y = this.getCurveValue(time) * bone.data.scaleY;
+		if (alpha == 1) {
+			if (blend == MixBlend.add)
+				bone.scaleY += y - bone.data.scaleY;
+			else
+				bone.scaleY = y;
+		} else {
+			// Mixing out uses sign of setup or current pose, else use sign of key.
+			let by = 0;
+			if (direction == MixDirection.mixOut) {
+				switch (blend) {
+					case MixBlend.setup:
+						by = bone.data.scaleY;
+						bone.scaleY = by + (Math.abs(y) * MathUtils.signum(by) - by) * alpha;
+						break;
+					case MixBlend.first:
+					case MixBlend.replace:
+						by = bone.scaleY;
+						bone.scaleY = by + (Math.abs(y) * MathUtils.signum(by) - by) * alpha;
+						break;
+					case MixBlend.add:
+						by = bone.scaleY;
+						bone.scaleY = by + (Math.abs(y) * MathUtils.signum(by) - bone.data.scaleY) * alpha;
+				}
+			} else {
+				switch (blend) {
+					case MixBlend.setup:
+						by = Math.abs(bone.data.scaleY) * MathUtils.signum(y);
+						bone.scaleY = by + (y - by) * alpha;
+						break;
+					case MixBlend.first:
+					case MixBlend.replace:
+						by = Math.abs(bone.scaleY) * MathUtils.signum(y);
+						bone.scaleY = by + (y - by) * alpha;
+						break;
+					case MixBlend.add:
+						by = MathUtils.signum(y);
+						bone.scaleY = Math.abs(bone.scaleY) * by + (y - Math.abs(bone.data.scaleY) * by) * alpha;
+				}
+			}
+		}
+	}
+}
+
+/** Changes a bone's local {@link Bone#shearX} and {@link Bone#shearY}. */
+export class ShearTimeline extends CurveTimeline2 implements BoneTimeline {
+	boneIndex = 0;
+
+	constructor(frameCount: number, bezierCount: number, boneIndex: number) {
+		super(frameCount, bezierCount,
+			Property.shearX + "|" + boneIndex,
+			Property.shearY + "|" + boneIndex
+		);
+		this.boneIndex = boneIndex;
+	}
+
+	apply(skeleton: Skeleton, lastTime: number, time: number, events: Array<Event>, alpha: number, blend: MixBlend, direction: MixDirection) {
+		let bone = skeleton.bones[this.boneIndex];
+		if (!bone.active) return;
+
+		let frames = this.frames;
+		if (time < frames[0]) {
+			switch (blend) {
+				case MixBlend.setup:
+					bone.shearX = bone.data.shearX;
+					bone.shearY = bone.data.shearY;
+					return;
+				case MixBlend.first:
+					bone.shearX += (bone.data.shearX - bone.shearX) * alpha;
+					bone.shearY += (bone.data.shearY - bone.shearY) * alpha;
+			}
+			return;
+		}
+
+		let x = 0, y = 0;
+		let i = Timeline.search(frames, time, 3/*ENTRIES*/);
+		let curveType = this.curves[i / 3/*ENTRIES*/];
+		switch (curveType) {
+			case 0/*LINEAR*/:
+				let before = frames[i];
+				x = frames[i + 1/*VALUE1*/];
+				y = frames[i + 2/*VALUE2*/];
+				let t = (time - before) / (frames[i + 3/*ENTRIES*/] - before);
+				x += (frames[i + 3/*ENTRIES*/ + 1/*VALUE1*/] - x) * t;
+				y += (frames[i + 3/*ENTRIES*/ + 2/*VALUE2*/] - y) * t;
+				break;
+			case 1/*STEPPED*/:
+				x = frames[i + 1/*VALUE1*/];
+				y = frames[i + 2/*VALUE2*/];
+				break;
+			default:
+				x = this.getBezierValue(time, i, 1/*VALUE1*/, curveType - 2/*BEZIER*/);
+				y = this.getBezierValue(time, i, 2/*VALUE2*/, curveType + 18/*BEZIER_SIZE*/ - 2/*BEZIER*/);
+		}
+
+		switch (blend) {
+			case MixBlend.setup:
+				bone.shearX = bone.data.shearX + x * alpha;
+				bone.shearY = bone.data.shearY + y * alpha;
+				break;
+			case MixBlend.first:
+			case MixBlend.replace:
+				bone.shearX += (bone.data.shearX + x - bone.shearX) * alpha;
+				bone.shearY += (bone.data.shearY + y - bone.shearY) * alpha;
+				break;
+			case MixBlend.add:
+				bone.shearX += x * alpha;
+				bone.shearY += y * alpha;
+		}
+	}
+}
+
+/** Changes a bone's local {@link Bone#shearX} and {@link Bone#shearY}. */
+export class ShearXTimeline extends CurveTimeline1 implements BoneTimeline {
+	boneIndex = 0;
+
+	constructor(frameCount: number, bezierCount: number, boneIndex: number) {
+		super(frameCount, bezierCount, Property.shearX + "|" + boneIndex);
+		this.boneIndex = boneIndex;
+	}
+
+	apply(skeleton: Skeleton, lastTime: number, time: number, events: Array<Event>, alpha: number, blend: MixBlend, direction: MixDirection) {
+		let bone = skeleton.bones[this.boneIndex];
+		if (!bone.active) return;
+
+		let frames = this.frames;
+		if (time < frames[0]) {
+			switch (blend) {
+				case MixBlend.setup:
+					bone.shearX = bone.data.shearX;
+					return;
+				case MixBlend.first:
+					bone.shearX += (bone.data.shearX - bone.shearX) * alpha;
+			}
+			return;
+		}
+
+		let x = this.getCurveValue(time);
+		switch (blend) {
+			case MixBlend.setup:
+				bone.shearX = bone.data.shearX + x * alpha;
+				break;
+			case MixBlend.first:
+			case MixBlend.replace:
+				bone.shearX += (bone.data.shearX + x - bone.shearX) * alpha;
+				break;
+			case MixBlend.add:
+				bone.shearX += x * alpha;
+		}
+	}
+}
+
+/** Changes a bone's local {@link Bone#shearX} and {@link Bone#shearY}. */
+export class ShearYTimeline extends CurveTimeline1 implements BoneTimeline {
+	boneIndex = 0;
+
+	constructor(frameCount: number, bezierCount: number, boneIndex: number) {
+		super(frameCount, bezierCount, Property.shearY + "|" + boneIndex);
+		this.boneIndex = boneIndex;
+	}
+
+	apply(skeleton: Skeleton, lastTime: number, time: number, events: Array<Event>, alpha: number, blend: MixBlend, direction: MixDirection) {
+		let bone = skeleton.bones[this.boneIndex];
+		if (!bone.active) return;
+
+		let frames = this.frames;
+		if (time < frames[0]) {
+			switch (blend) {
+				case MixBlend.setup:
+					bone.shearY = bone.data.shearY;
+					return;
+				case MixBlend.first:
+					bone.shearY += (bone.data.shearY - bone.shearY) * alpha;
+			}
+			return;
+		}
+
+		let y = this.getCurveValue(time);
+		switch (blend) {
+			case MixBlend.setup:
+				bone.shearY = bone.data.shearY + y * alpha;
+				break;
+			case MixBlend.first:
+			case MixBlend.replace:
+				bone.shearY += (bone.data.shearY + y - bone.shearY) * alpha;
+				break;
+			case MixBlend.add:
+				bone.shearY += y * alpha;
+		}
+	}
+}
+
+/** Changes a slot's {@link Slot#color}. */
+export class RGBATimeline extends CurveTimeline implements SlotTimeline {
+	slotIndex = 0;
+
+	constructor(frameCount: number, bezierCount: number, slotIndex: number) {
+		super(frameCount, bezierCount, [
+			Property.rgb + "|" + slotIndex,
+			Property.alpha + "|" + slotIndex
+		]);
+		this.slotIndex = slotIndex;
+	}
+
+	getFrameEntries() {
+		return 5/*ENTRIES*/;
+	}
+
+	/** Sets the time in seconds, red, green, blue, and alpha for the specified key frame. */
+	setFrame(frame: number, time: number, r: number, g: number, b: number, a: number) {
+		frame *= 5/*ENTRIES*/;
+		this.frames[frame] = time;
+		this.frames[frame + 1/*R*/] = r;
+		this.frames[frame + 2/*G*/] = g;
+		this.frames[frame + 3/*B*/] = b;
+		this.frames[frame + 4/*A*/] = a;
+	}
+
+	apply(skeleton: Skeleton, lastTime: number, time: number, events: Array<Event>, alpha: number, blend: MixBlend, direction: MixDirection) {
+		let slot = skeleton.slots[this.slotIndex];
+		if (!slot.bone.active) return;
+
+		let frames = this.frames;
+		let color = slot.color;
+		if (time < frames[0]) {
+			let setup = slot.data.color;
+			switch (blend) {
+				case MixBlend.setup:
+					color.setFromColor(setup);
+					return;
+				case MixBlend.first:
+					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;
+		let i = Timeline.search(frames, time, 5/*ENTRIES*/);
+		let curveType = this.curves[i / 5/*ENTRIES*/];
+		switch (curveType) {
+			case 0/*LINEAR*/:
+				let before = frames[i];
+				r = frames[i + 1/*R*/];
+				g = frames[i + 2/*G*/];
+				b = frames[i + 3/*B*/];
+				a = frames[i + 4/*A*/];
+				let t = (time - before) / (frames[i + 5/*ENTRIES*/] - before);
+				r += (frames[i + 5/*ENTRIES*/ + 1/*R*/] - r) * t;
+				g += (frames[i + 5/*ENTRIES*/ + 2/*G*/] - g) * t;
+				b += (frames[i + 5/*ENTRIES*/ + 3/*B*/] - b) * t;
+				a += (frames[i + 5/*ENTRIES*/ + 4/*A*/] - a) * t;
+				break;
+			case 1/*STEPPED*/:
+				r = frames[i + 1/*R*/];
+				g = frames[i + 2/*G*/];
+				b = frames[i + 3/*B*/];
+				a = frames[i + 4/*A*/];
+				break;
+			default:
+				r = this.getBezierValue(time, i, 1/*R*/, curveType - 2/*BEZIER*/);
+				g = this.getBezierValue(time, i, 2/*G*/, curveType + 18/*BEZIER_SIZE*/ - 2/*BEZIER*/);
+				b = this.getBezierValue(time, i, 3/*B*/, curveType + 18/*BEZIER_SIZE*/ * 2 - 2/*BEZIER*/);
+				a = this.getBezierValue(time, i, 4/*A*/, curveType + 18/*BEZIER_SIZE*/ * 3 - 2/*BEZIER*/);
+		}
+		if (alpha == 1)
+			color.set(r, g, b, a);
+		else {
+			if (blend == MixBlend.setup) color.setFromColor(slot.data.color);
+			color.add((r - color.r) * alpha, (g - color.g) * alpha, (b - color.b) * alpha, (a - color.a) * alpha);
+		}
+	}
+}
+
+/** Changes a slot's {@link Slot#color}. */
+export class RGBTimeline extends CurveTimeline implements SlotTimeline {
+	slotIndex = 0;
+
+	constructor(frameCount: number, bezierCount: number, slotIndex: number) {
+		super(frameCount, bezierCount, [
+			Property.rgb + "|" + slotIndex
+		]);
+		this.slotIndex = slotIndex;
+	}
+
+	getFrameEntries() {
+		return 4/*ENTRIES*/;
+	}
+
+	/** Sets the time in seconds, red, green, blue, and alpha for the specified key frame. */
+	setFrame(frame: number, time: number, r: number, g: number, b: number) {
+		frame <<= 2;
+		this.frames[frame] = time;
+		this.frames[frame + 1/*R*/] = r;
+		this.frames[frame + 2/*G*/] = g;
+		this.frames[frame + 3/*B*/] = b;
+	}
+
+	apply(skeleton: Skeleton, lastTime: number, time: number, events: Array<Event>, alpha: number, blend: MixBlend, direction: MixDirection) {
+		let slot = skeleton.slots[this.slotIndex];
+		if (!slot.bone.active) return;
+
+		let frames = this.frames;
+		let color = slot.color;
+		if (time < frames[0]) {
+			let setup = slot.data.color;
+			switch (blend) {
+				case MixBlend.setup:
+					color.r = setup.r;
+					color.g = setup.g;
+					color.b = setup.b;
+					return;
+				case MixBlend.first:
+					color.r += (setup.r - color.r) * alpha;
+					color.g += (setup.g - color.g) * alpha;
+					color.b += (setup.b - color.b) * alpha;
+			}
+			return;
+		}
+
+		let r = 0, g = 0, b = 0;
+		let i = Timeline.search(frames, time, 4/*ENTRIES*/);
+		let curveType = this.curves[i >> 2];
+		switch (curveType) {
+			case 0/*LINEAR*/:
+				let before = frames[i];
+				r = frames[i + 1/*R*/];
+				g = frames[i + 2/*G*/];
+				b = frames[i + 3/*B*/];
+				let t = (time - before) / (frames[i + 4/*ENTRIES*/] - before);
+				r += (frames[i + 4/*ENTRIES*/ + 1/*R*/] - r) * t;
+				g += (frames[i + 4/*ENTRIES*/ + 2/*G*/] - g) * t;
+				b += (frames[i + 4/*ENTRIES*/ + 3/*B*/] - b) * t;
+				break;
+			case 1/*STEPPED*/:
+				r = frames[i + 1/*R*/];
+				g = frames[i + 2/*G*/];
+				b = frames[i + 3/*B*/];
+				break;
+			default:
+				r = this.getBezierValue(time, i, 1/*R*/, curveType - 2/*BEZIER*/);
+				g = this.getBezierValue(time, i, 2/*G*/, curveType + 18/*BEZIER_SIZE*/ - 2/*BEZIER*/);
+				b = this.getBezierValue(time, i, 3/*B*/, curveType + 18/*BEZIER_SIZE*/ * 2 - 2/*BEZIER*/);
+		}
+		if (alpha == 1) {
+			color.r = r;
+			color.g = g;
+			color.b = b;
+		} else {
+			if (blend == MixBlend.setup) {
+				let setup = slot.data.color;
+				color.r = setup.r;
+				color.g = setup.g;
+				color.b = setup.b;
+			}
+			color.r += (r - color.r) * alpha;
+			color.g += (g - color.g) * alpha;
+			color.b += (b - color.b) * alpha;
+		}
+	}
+}
+
+/** Changes a bone's local {@link Bone#shearX} and {@link Bone#shearY}. */
+export class AlphaTimeline extends CurveTimeline1 implements SlotTimeline {
+	slotIndex = 0;
+
+	constructor(frameCount: number, bezierCount: number, slotIndex: number) {
+		super(frameCount, bezierCount, Property.alpha + "|" + slotIndex);
+		this.slotIndex = slotIndex;
+	}
+
+	apply(skeleton: Skeleton, lastTime: number, time: number, events: Array<Event>, alpha: number, blend: MixBlend, direction: MixDirection) {
+		let slot = skeleton.slots[this.slotIndex];
+		if (!slot.bone.active) return;
+
+		let color = slot.color;
+		if (time < this.frames[0]) { // Time is before first frame.
+			let setup = slot.data.color;
+			switch (blend) {
+				case MixBlend.setup:
+					color.a = setup.a;
+					return;
+				case MixBlend.first:
+					color.a += (setup.a - color.a) * alpha;
+			}
+			return;
+		}
+
+		let a = this.getCurveValue(time);
+		if (alpha == 1)
+			color.a = a;
+		else {
+			if (blend == MixBlend.setup) color.a = slot.data.color.a;
+			color.a += (a - color.a) * alpha;
+		}
+	}
+}
+
+/** Changes a slot's {@link Slot#color} and {@link Slot#darkColor} for two color tinting. */
+export class RGBA2Timeline extends CurveTimeline implements SlotTimeline {
+	slotIndex = 0;
+
+	constructor(frameCount: number, bezierCount: number, slotIndex: number) {
+		super(frameCount, bezierCount, [
+			Property.rgb + "|" + slotIndex,
+			Property.alpha + "|" + slotIndex,
+			Property.rgb2 + "|" + slotIndex
+		]);
+		this.slotIndex = slotIndex;
+	}
+
+	getFrameEntries() {
+		return 8/*ENTRIES*/;
+	}
+
+	/** Sets the time in seconds, light, and dark colors for the specified key frame. */
+	setFrame(frame: number, time: number, r: number, g: number, b: number, a: number, r2: number, g2: number, b2: number) {
+		frame <<= 3;
+		this.frames[frame] = time;
+		this.frames[frame + 1/*R*/] = r;
+		this.frames[frame + 2/*G*/] = g;
+		this.frames[frame + 3/*B*/] = b;
+		this.frames[frame + 4/*A*/] = a;
+		this.frames[frame + 5/*R2*/] = r2;
+		this.frames[frame + 6/*G2*/] = g2;
+		this.frames[frame + 7/*B2*/] = b2;
+	}
+
+	apply(skeleton: Skeleton, lastTime: number, time: number, events: Array<Event>, alpha: number, blend: MixBlend, direction: MixDirection) {
+		let slot = skeleton.slots[this.slotIndex];
+		if (!slot.bone.active) return;
+
+		let frames = this.frames;
+		let light = slot.color, dark = slot.darkColor;
+		if (time < frames[0]) {
+			let setupLight = slot.data.color, setupDark = slot.data.darkColor;
+			switch (blend) {
+				case MixBlend.setup:
+					light.setFromColor(setupLight);
+					dark.r = setupDark.r;
+					dark.g = setupDark.g;
+					dark.b = setupDark.b;
+					return;
+				case MixBlend.first:
+					light.add((setupLight.r - light.r) * alpha, (setupLight.g - light.g) * alpha, (setupLight.b - light.b) * alpha,
+						(setupLight.a - light.a) * alpha);
+					dark.r += (setupDark.r - dark.r) * alpha;
+					dark.g += (setupDark.g - dark.g) * alpha;
+					dark.b += (setupDark.b - dark.b) * alpha;
+			}
+			return;
+		}
+
+		let r = 0, g = 0, b = 0, a = 0, r2 = 0, g2 = 0, b2 = 0;
+		let i = Timeline.search(frames, time, 8/*ENTRIES*/);
+		let curveType = this.curves[i >> 3];
+		switch (curveType) {
+			case 0/*LINEAR*/:
+				let before = frames[i];
+				r = frames[i + 1/*R*/];
+				g = frames[i + 2/*G*/];
+				b = frames[i + 3/*B*/];
+				a = frames[i + 4/*A*/];
+				r2 = frames[i + 5/*R2*/];
+				g2 = frames[i + 6/*G2*/];
+				b2 = frames[i + 7/*B2*/];
+				let t = (time - before) / (frames[i + 8/*ENTRIES*/] - before);
+				r += (frames[i + 8/*ENTRIES*/ + 1/*R*/] - r) * t;
+				g += (frames[i + 8/*ENTRIES*/ + 2/*G*/] - g) * t;
+				b += (frames[i + 8/*ENTRIES*/ + 3/*B*/] - b) * t;
+				a += (frames[i + 8/*ENTRIES*/ + 4/*A*/] - a) * t;
+				r2 += (frames[i + 8/*ENTRIES*/ + 5/*R2*/] - r2) * t;
+				g2 += (frames[i + 8/*ENTRIES*/ + 6/*G2*/] - g2) * t;
+				b2 += (frames[i + 8/*ENTRIES*/ + 7/*B2*/] - b2) * t;
+				break;
+			case 1/*STEPPED*/:
+				r = frames[i + 1/*R*/];
+				g = frames[i + 2/*G*/];
+				b = frames[i + 3/*B*/];
+				a = frames[i + 4/*A*/];
+				r2 = frames[i + 5/*R2*/];
+				g2 = frames[i + 6/*G2*/];
+				b2 = frames[i + 7/*B2*/];
+				break;
+			default:
+				r = this.getBezierValue(time, i, 1/*R*/, curveType - 2/*BEZIER*/);
+				g = this.getBezierValue(time, i, 2/*G*/, curveType + 18/*BEZIER_SIZE*/ - 2/*BEZIER*/);
+				b = this.getBezierValue(time, i, 3/*B*/, curveType + 18/*BEZIER_SIZE*/ * 2 - 2/*BEZIER*/);
+				a = this.getBezierValue(time, i, 4/*A*/, curveType + 18/*BEZIER_SIZE*/ * 3 - 2/*BEZIER*/);
+				r2 = this.getBezierValue(time, i, 5/*R2*/, curveType + 18/*BEZIER_SIZE*/ * 4 - 2/*BEZIER*/);
+				g2 = this.getBezierValue(time, i, 6/*G2*/, curveType + 18/*BEZIER_SIZE*/ * 5 - 2/*BEZIER*/);
+				b2 = this.getBezierValue(time, i, 7/*B2*/, curveType + 18/*BEZIER_SIZE*/ * 6 - 2/*BEZIER*/);
+		}
+
+		if (alpha == 1) {
+			light.set(r, g, b, a);
+			dark.r = r2;
+			dark.g = g2;
+			dark.b = b2;
+		} else {
+			if (blend == MixBlend.setup) {
+				light.setFromColor(slot.data.color);
+				let setupDark = slot.data.darkColor;
+				dark.r = setupDark.r;
+				dark.g = setupDark.g;
+				dark.b = setupDark.b;
+			}
+			light.add((r - light.r) * alpha, (g - light.g) * alpha, (b - light.b) * alpha, (a - light.a) * alpha);
+			dark.r += (r2 - dark.r) * alpha;
+			dark.g += (g2 - dark.g) * alpha;
+			dark.b += (b2 - dark.b) * alpha;
+		}
+	}
+}
+
+/** Changes a slot's {@link Slot#color} and {@link Slot#darkColor} for two color tinting. */
+export class RGB2Timeline extends CurveTimeline implements SlotTimeline {
+	slotIndex = 0;
+
+	constructor(frameCount: number, bezierCount: number, slotIndex: number) {
+		super(frameCount, bezierCount, [
+			Property.rgb + "|" + slotIndex,
+			Property.rgb2 + "|" + slotIndex
+		]);
+		this.slotIndex = slotIndex;
+	}
+
+	getFrameEntries() {
+		return 7/*ENTRIES*/;
+	}
+
+	/** Sets the time in seconds, light, and dark colors for the specified key frame. */
+	setFrame(frame: number, time: number, r: number, g: number, b: number, r2: number, g2: number, b2: number) {
+		frame *= 7/*ENTRIES*/;
+		this.frames[frame] = time;
+		this.frames[frame + 1/*R*/] = r;
+		this.frames[frame + 2/*G*/] = g;
+		this.frames[frame + 3/*B*/] = b;
+		this.frames[frame + 4/*R2*/] = r2;
+		this.frames[frame + 5/*G2*/] = g2;
+		this.frames[frame + 6/*B2*/] = b2;
+	}
+
+	apply(skeleton: Skeleton, lastTime: number, time: number, events: Array<Event>, alpha: number, blend: MixBlend, direction: MixDirection) {
+		let slot = skeleton.slots[this.slotIndex];
+		if (!slot.bone.active) return;
+
+		let frames = this.frames;
+		let light = slot.color, dark = slot.darkColor;
+		if (time < frames[0]) {
+			let setupLight = slot.data.color, setupDark = slot.data.darkColor;
+			switch (blend) {
+				case MixBlend.setup:
+					light.r = setupLight.r;
+					light.g = setupLight.g;
+					light.b = setupLight.b;
+					dark.r = setupDark.r;
+					dark.g = setupDark.g;
+					dark.b = setupDark.b;
+					return;
+				case MixBlend.first:
+					light.r += (setupLight.r - light.r) * alpha;
+					light.g += (setupLight.g - light.g) * alpha;
+					light.b += (setupLight.b - light.b) * alpha;
+					dark.r += (setupDark.r - dark.r) * alpha;
+					dark.g += (setupDark.g - dark.g) * alpha;
+					dark.b += (setupDark.b - dark.b) * alpha;
+			}
+			return;
+		}
+
+		let r = 0, g = 0, b = 0, a = 0, r2 = 0, g2 = 0, b2 = 0;
+		let i = Timeline.search(frames, time, 7/*ENTRIES*/);
+		let curveType = this.curves[i / 7/*ENTRIES*/];
+		switch (curveType) {
+			case 0/*LINEAR*/:
+				let before = frames[i];
+				r = frames[i + 1/*R*/];
+				g = frames[i + 2/*G*/];
+				b = frames[i + 3/*B*/];
+				r2 = frames[i + 4/*R2*/];
+				g2 = frames[i + 5/*G2*/];
+				b2 = frames[i + 6/*B2*/];
+				let t = (time - before) / (frames[i + 7/*ENTRIES*/] - before);
+				r += (frames[i + 7/*ENTRIES*/ + 1/*R*/] - r) * t;
+				g += (frames[i + 7/*ENTRIES*/ + 2/*G*/] - g) * t;
+				b += (frames[i + 7/*ENTRIES*/ + 3/*B*/] - b) * t;
+				r2 += (frames[i + 7/*ENTRIES*/ + 4/*R2*/] - r2) * t;
+				g2 += (frames[i + 7/*ENTRIES*/ + 5/*G2*/] - g2) * t;
+				b2 += (frames[i + 7/*ENTRIES*/ + 6/*B2*/] - b2) * t;
+				break;
+			case 1/*STEPPED*/:
+				r = frames[i + 1/*R*/];
+				g = frames[i + 2/*G*/];
+				b = frames[i + 3/*B*/];
+				r2 = frames[i + 4/*R2*/];
+				g2 = frames[i + 5/*G2*/];
+				b2 = frames[i + 6/*B2*/];
+				break;
+			default:
+				r = this.getBezierValue(time, i, 1/*R*/, curveType - 2/*BEZIER*/);
+				g = this.getBezierValue(time, i, 2/*G*/, curveType + 18/*BEZIER_SIZE*/ - 2/*BEZIER*/);
+				b = this.getBezierValue(time, i, 3/*B*/, curveType + 18/*BEZIER_SIZE*/ * 2 - 2/*BEZIER*/);
+				r2 = this.getBezierValue(time, i, 4/*R2*/, curveType + 18/*BEZIER_SIZE*/ * 3 - 2/*BEZIER*/);
+				g2 = this.getBezierValue(time, i, 5/*G2*/, curveType + 18/*BEZIER_SIZE*/ * 4 - 2/*BEZIER*/);
+				b2 = this.getBezierValue(time, i, 6/*B2*/, curveType + 18/*BEZIER_SIZE*/ * 5 - 2/*BEZIER*/);
+		}
+
+		if (alpha == 1) {
+			light.r = r;
+			light.g = g;
+			light.b = b;
+			dark.r = r2;
+			dark.g = g2;
+			dark.b = b2;
+		} else {
+			if (blend == MixBlend.setup) {
+				let setupLight = slot.data.color, setupDark = slot.data.darkColor;
+				light.r = setupLight.r;
+				light.g = setupLight.g;
+				light.b = setupLight.b;
+				dark.r = setupDark.r;
+				dark.g = setupDark.g;
+				dark.b = setupDark.b;
+			}
+			light.r += (r - light.r) * alpha;
+			light.g += (g - light.g) * alpha;
+			light.b += (b - light.b) * alpha;
+			dark.r += (r2 - dark.r) * alpha;
+			dark.g += (g2 - dark.g) * alpha;
+			dark.b += (b2 - dark.b) * alpha;
+		}
+	}
+}
+
+/** Changes a slot's {@link Slot#attachment}. */
+export class AttachmentTimeline extends Timeline implements SlotTimeline {
+	slotIndex = 0;
+
+	/** The attachment name for each key frame. May contain null values to clear the attachment. */
+	attachmentNames: Array<string>;
+
+	constructor(frameCount: number, slotIndex: number) {
+		super(frameCount, [
+			Property.attachment + "|" + slotIndex
+		]);
+		this.slotIndex = slotIndex;
+		this.attachmentNames = new Array<string>(frameCount);
+	}
+
+	getFrameCount() {
+		return this.frames.length;
+	}
+
+	/** Sets the time in seconds and the attachment name for the specified key frame. */
+	setFrame(frame: number, time: number, attachmentName: string) {
+		this.frames[frame] = time;
+		this.attachmentNames[frame] = attachmentName;
+	}
+
+	apply(skeleton: Skeleton, lastTime: number, time: number, events: Array<Event>, alpha: number, blend: MixBlend, direction: MixDirection) {
+		let slot = skeleton.slots[this.slotIndex];
+		if (!slot.bone.active) return;
+
+		if (direction == MixDirection.mixOut) {
+			if (blend == MixBlend.setup) this.setAttachment(skeleton, slot, slot.data.attachmentName);
+			return;
+		}
+
+		if (time < this.frames[0]) {
+			if (blend == MixBlend.setup || blend == MixBlend.first) this.setAttachment(skeleton, slot, slot.data.attachmentName);
+			return;
+		}
+
+		this.setAttachment(skeleton, slot, this.attachmentNames[Timeline.search1(this.frames, time)]);
+	}
+
+	setAttachment(skeleton: Skeleton, slot: Slot, attachmentName: string) {
+		slot.setAttachment(!attachmentName ? null : skeleton.getAttachment(this.slotIndex, attachmentName));
+	}
+}
+
+/** Changes a slot's {@link Slot#deform} to deform a {@link VertexAttachment}. */
+export class DeformTimeline extends CurveTimeline implements SlotTimeline {
+	slotIndex = 0;
+
+	/** The attachment that will be deformed. */
+	attachment: VertexAttachment;
+
+	/** The vertices for each key frame. */
+	vertices: Array<NumberArrayLike>;
+
+	constructor(frameCount: number, bezierCount: number, slotIndex: number, attachment: VertexAttachment) {
+		super(frameCount, bezierCount, [
+			Property.deform + "|" + slotIndex + "|" + attachment.id
+		]);
+		this.slotIndex = slotIndex;
+		this.attachment = attachment;
+		this.vertices = new Array<NumberArrayLike>(frameCount);
+	}
+
+	getFrameCount() {
+		return this.frames.length;
+	}
+
+	/** Sets the time in seconds and the vertices for the specified key frame.
+	 * @param vertices Vertex positions for an unweighted VertexAttachment, or deform offsets if it has weights. */
+	setFrame(frame: number, time: number, vertices: NumberArrayLike) {
+		this.frames[frame] = time;
+		this.vertices[frame] = vertices;
+	}
+
+	/** @param value1 Ignored (0 is used for a deform timeline).
+	 * @param value2 Ignored (1 is used for a deform timeline). */
+	setBezier(bezier: number, frame: number, value: number, time1: number, value1: number, cx1: number, cy1: number, cx2: number,
+		cy2: number, time2: number, value2: number) {
+		let curves = this.curves;
+		let i = this.getFrameCount() + bezier * 18/*BEZIER_SIZE*/;
+		if (value == 0) curves[frame] = 2/*BEZIER*/ + i;
+		let tmpx = (time1 - cx1 * 2 + cx2) * 0.03, tmpy = cy2 * 0.03 - cy1 * 0.06;
+		let dddx = ((cx1 - cx2) * 3 - time1 + time2) * 0.006, dddy = (cy1 - cy2 + 0.33333333) * 0.018;
+		let ddx = tmpx * 2 + dddx, ddy = tmpy * 2 + dddy;
+		let dx = (cx1 - time1) * 0.3 + tmpx + dddx * 0.16666667, dy = cy1 * 0.3 + tmpy + dddy * 0.16666667;
+		let x = time1 + dx, y = dy;
+		for (let n = i + 18/*BEZIER_SIZE*/; i < n; i += 2) {
+			curves[i] = x;
+			curves[i + 1] = y;
+			dx += ddx;
+			dy += ddy;
+			ddx += dddx;
+			ddy += dddy;
+			x += dx;
+			y += dy;
+		}
+	}
+
+	getCurvePercent(time: number, frame: number) {
+		let curves = this.curves;
+		let i = curves[frame];
+		switch (i) {
+			case 0/*LINEAR*/:
+				let x = this.frames[frame];
+				return (time - x) / (this.frames[frame + this.getFrameEntries()] - x);
+			case 1/*STEPPED*/:
+				return 0;
+		}
+		i -= 2/*BEZIER*/;
+		if (curves[i] > time) {
+			let x = this.frames[frame];
+			return curves[i + 1] * (time - x) / (curves[i] - x);
+		}
+		let n = i + 18/*BEZIER_SIZE*/;
+		for (i += 2; i < n; i += 2) {
+			if (curves[i] >= time) {
+				let x = curves[i - 2], y = curves[i - 1];
+				return y + (time - x) / (curves[i] - x) * (curves[i + 1] - y);
+			}
+		}
+		let x = curves[n - 2], y = curves[n - 1];
+		return y + (1 - y) * (time - x) / (this.frames[frame + this.getFrameEntries()] - x);
+	}
+
+	apply(skeleton: Skeleton, lastTime: number, time: number, firedEvents: Array<Event>, alpha: number, blend: MixBlend, direction: MixDirection) {
+		let slot: Slot = skeleton.slots[this.slotIndex];
+		if (!slot.bone.active) return;
+		let slotAttachment: Attachment = slot.getAttachment();
+		if (!(slotAttachment instanceof VertexAttachment) || (<VertexAttachment>slotAttachment).deformAttachment != this.attachment) return;
+
+		let deform: Array<number> = slot.deform;
+		if (deform.length == 0) blend = MixBlend.setup;
+
+		let vertices = this.vertices;
+		let vertexCount = vertices[0].length;
+
+		let frames = this.frames;
+		if (time < frames[0]) {
+			let vertexAttachment = <VertexAttachment>slotAttachment;
+			switch (blend) {
+				case MixBlend.setup:
+					deform.length = 0;
+					return;
+				case MixBlend.first:
+					if (alpha == 1) {
+						deform.length = 0;
+						return;
+					}
+					deform.length = vertexCount;
+					if (!vertexAttachment.bones) {
+						// Unweighted vertex positions.
+						let setupVertices = vertexAttachment.vertices;
+						for (var i = 0; i < vertexCount; i++)
+							deform[i] += (setupVertices[i] - deform[i]) * alpha;
+					} else {
+						// Weighted deform offsets.
+						alpha = 1 - alpha;
+						for (var i = 0; i < vertexCount; i++)
+							deform[i] *= alpha;
+					}
+			}
+			return;
+		}
+
+		deform.length = vertexCount;
+		if (time >= frames[frames.length - 1]) { // Time is after last frame.
+			let lastVertices = vertices[frames.length - 1];
+			if (alpha == 1) {
+				if (blend == MixBlend.add) {
+					let vertexAttachment = slotAttachment as VertexAttachment;
+					if (!vertexAttachment.bones) {
+						// Unweighted vertex positions, with alpha.
+						let setupVertices = vertexAttachment.vertices;
+						for (let i = 0; i < vertexCount; i++)
+							deform[i] += lastVertices[i] - setupVertices[i];
+					} else {
+						// Weighted deform offsets, with alpha.
+						for (let i = 0; i < vertexCount; i++)
+							deform[i] += lastVertices[i];
+					}
+				} else
+					Utils.arrayCopy(lastVertices, 0, deform, 0, vertexCount);
+			} else {
+				switch (blend) {
+					case MixBlend.setup: {
+						let vertexAttachment = slotAttachment as VertexAttachment;
+						if (!vertexAttachment.bones) {
+							// Unweighted vertex positions, with alpha.
+							let setupVertices = vertexAttachment.vertices;
+							for (let i = 0; i < vertexCount; i++) {
+								let setup = setupVertices[i];
+								deform[i] = setup + (lastVertices[i] - setup) * alpha;
+							}
+						} else {
+							// Weighted deform offsets, with alpha.
+							for (let i = 0; i < vertexCount; i++)
+								deform[i] = lastVertices[i] * alpha;
+						}
+						break;
+					}
+					case MixBlend.first:
+					case MixBlend.replace:
+						for (let i = 0; i < vertexCount; i++)
+							deform[i] += (lastVertices[i] - deform[i]) * alpha;
+						break;
+					case MixBlend.add:
+						let vertexAttachment = slotAttachment as VertexAttachment;
+						if (!vertexAttachment.bones) {
+							// Unweighted vertex positions, with alpha.
+							let setupVertices = vertexAttachment.vertices;
+							for (let i = 0; i < vertexCount; i++)
+								deform[i] += (lastVertices[i] - setupVertices[i]) * alpha;
+						} else {
+							// Weighted deform offsets, with alpha.
+							for (let i = 0; i < vertexCount; i++)
+								deform[i] += lastVertices[i] * alpha;
+						}
+				}
+			}
+			return;
+		}
+
+		// Interpolate between the previous frame and the current frame.
+		let frame = Timeline.search1(frames, time);
+		let percent = this.getCurvePercent(time, frame);
+		let prevVertices = vertices[frame];
+		let nextVertices = vertices[frame + 1];
+
+		if (alpha == 1) {
+			if (blend == MixBlend.add) {
+				let vertexAttachment = slotAttachment as VertexAttachment;
+				if (!vertexAttachment.bones) {
+					// Unweighted vertex positions, with alpha.
+					let setupVertices = vertexAttachment.vertices;
+					for (let i = 0; i < vertexCount; i++) {
+						let prev = prevVertices[i];
+						deform[i] += prev + (nextVertices[i] - prev) * percent - setupVertices[i];
+					}
+				} else {
+					// Weighted deform offsets, with alpha.
+					for (let i = 0; i < vertexCount; i++) {
+						let prev = prevVertices[i];
+						deform[i] += prev + (nextVertices[i] - prev) * percent;
+					}
+				}
+			} else {
+				for (let i = 0; i < vertexCount; i++) {
+					let prev = prevVertices[i];
+					deform[i] = prev + (nextVertices[i] - prev) * percent;
+				}
+			}
+		} else {
+			switch (blend) {
+				case MixBlend.setup: {
+					let vertexAttachment = slotAttachment as VertexAttachment;
+					if (!vertexAttachment.bones) {
+						// Unweighted vertex positions, with alpha.
+						let setupVertices = vertexAttachment.vertices;
+						for (let i = 0; i < vertexCount; i++) {
+							let prev = prevVertices[i], setup = setupVertices[i];
+							deform[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];
+							deform[i] = (prev + (nextVertices[i] - prev) * percent) * alpha;
+						}
+					}
+					break;
+				}
+				case MixBlend.first:
+				case MixBlend.replace:
+					for (let i = 0; i < vertexCount; i++) {
+						let prev = prevVertices[i];
+						deform[i] += (prev + (nextVertices[i] - prev) * percent - deform[i]) * alpha;
+					}
+					break;
+				case MixBlend.add:
+					let vertexAttachment = slotAttachment as VertexAttachment;
+					if (!vertexAttachment.bones) {
+						// Unweighted vertex positions, with alpha.
+						let setupVertices = vertexAttachment.vertices;
+						for (let i = 0; i < vertexCount; i++) {
+							let prev = prevVertices[i];
+							deform[i] += (prev + (nextVertices[i] - prev) * percent - setupVertices[i]) * alpha;
+						}
+					} else {
+						// Weighted deform offsets, with alpha.
+						for (let i = 0; i < vertexCount; i++) {
+							let prev = prevVertices[i];
+							deform[i] += (prev + (nextVertices[i] - prev) * percent) * alpha;
+						}
+					}
+			}
+		}
+	}
+}
+
+/** Fires an {@link Event} when specific animation times are reached. */
+export class EventTimeline extends Timeline {
+	static propertyIds = ["" + Property.event];
+
+	/** The event for each key frame. */
+	events: Array<Event>;
+
+	constructor(frameCount: number) {
+		super(frameCount, EventTimeline.propertyIds);
+
+		this.events = new Array<Event>(frameCount);
+	}
+
+	getFrameCount() {
+		return this.frames.length;
+	}
+
+	/** Sets the time in seconds and the event for the specified key frame. */
+	setFrame(frame: number, event: Event) {
+		this.frames[frame] = event.time;
+		this.events[frame] = event;
+	}
+
+	/** Fires events for frames > `lastTime` and <= `time`. */
+	apply(skeleton: Skeleton, lastTime: number, time: number, firedEvents: Array<Event>, alpha: number, blend: MixBlend, direction: MixDirection) {
+		if (!firedEvents) 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, blend, 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 i = 0;
+		if (lastTime < frames[0])
+			i = 0;
+		else {
+			i = Timeline.search1(frames, lastTime) + 1;
+			let frameTime = frames[i];
+			while (i > 0) { // Fire multiple events with the same frame.
+				if (frames[i - 1] != frameTime) break;
+				i--;
+			}
+		}
+		for (; i < frameCount && time >= frames[i]; i++)
+			firedEvents.push(this.events[i]);
+	}
+}
+
+/** Changes a skeleton's {@link Skeleton#drawOrder}. */
+export class DrawOrderTimeline extends Timeline {
+	static propertyIds = ["" + Property.drawOrder];
+
+	/** The draw order for each key frame. See {@link #setFrame(int, float, int[])}. */
+	drawOrders: Array<Array<number>>;
+
+	constructor(frameCount: number) {
+		super(frameCount, DrawOrderTimeline.propertyIds);
+		this.drawOrders = new Array<Array<number>>(frameCount);
+	}
+
+	getFrameCount() {
+		return this.frames.length;
+	}
+
+	/** Sets the time in seconds and the draw order for the specified key frame.
+	 * @param drawOrder For each slot in {@link Skeleton#slots}, the index of the new draw order. May be null to use setup pose
+	 *           draw order. */
+	setFrame(frame: number, time: number, drawOrder: Array<number>) {
+		this.frames[frame] = time;
+		this.drawOrders[frame] = drawOrder;
+	}
+
+	apply(skeleton: Skeleton, lastTime: number, time: number, firedEvents: Array<Event>, alpha: number, blend: MixBlend, direction: MixDirection) {
+		if (direction == MixDirection.mixOut) {
+			if (blend == MixBlend.setup) Utils.arrayCopy(skeleton.slots, 0, skeleton.drawOrder, 0, skeleton.slots.length);
+			return;
+		}
+
+		if (time < this.frames[0]) {
+			if (blend == MixBlend.setup || blend == MixBlend.first) Utils.arrayCopy(skeleton.slots, 0, skeleton.drawOrder, 0, skeleton.slots.length);
+			return;
+		}
+
+		let drawOrderToSetupIndex = this.drawOrders[Timeline.search1(this.frames, time)];
+		if (!drawOrderToSetupIndex)
+			Utils.arrayCopy(skeleton.slots, 0, skeleton.drawOrder, 0, skeleton.slots.length);
+		else {
+			let drawOrder: Array<Slot> = skeleton.drawOrder;
+			let slots: Array<Slot> = skeleton.slots;
+			for (let i = 0, n = drawOrderToSetupIndex.length; i < n; i++)
+				drawOrder[i] = slots[drawOrderToSetupIndex[i]];
+		}
+	}
+}
+
+/** Changes an IK constraint's {@link IkConstraint#mix}, {@link IkConstraint#softness},
+ * {@link IkConstraint#bendDirection}, {@link IkConstraint#stretch}, and {@link IkConstraint#compress}. */
+export class IkConstraintTimeline extends CurveTimeline {
+	/** The index of the IK constraint slot in {@link Skeleton#ikConstraints} that will be changed. */
+	ikConstraintIndex: number;
+
+	constructor(frameCount: number, bezierCount: number, ikConstraintIndex: number) {
+		super(frameCount, bezierCount, [
+			Property.ikConstraint + "|" + ikConstraintIndex
+		]);
+		this.ikConstraintIndex = ikConstraintIndex;
+	}
+
+	getFrameEntries() {
+		return 6/*ENTRIES*/;
+	}
+
+	/** Sets the time in seconds, mix, softness, bend direction, compress, and stretch for the specified key frame. */
+	setFrame(frame: number, time: number, mix: number, softness: number, bendDirection: number, compress: boolean, stretch: boolean) {
+		frame *= 6/*ENTRIES*/;
+		this.frames[frame] = time;
+		this.frames[frame + 1/*MIX*/] = mix;
+		this.frames[frame + 2/*SOFTNESS*/] = softness;
+		this.frames[frame + 3/*BEND_DIRECTION*/] = bendDirection;
+		this.frames[frame + 4/*COMPRESS*/] = compress ? 1 : 0;
+		this.frames[frame + 5/*STRETCH*/] = stretch ? 1 : 0;
+	}
+
+	apply(skeleton: Skeleton, lastTime: number, time: number, firedEvents: Array<Event>, alpha: number, blend: MixBlend, direction: MixDirection) {
+		let constraint: IkConstraint = skeleton.ikConstraints[this.ikConstraintIndex];
+		if (!constraint.active) return;
+
+		let frames = this.frames;
+		if (time < frames[0]) {
+			switch (blend) {
+				case MixBlend.setup:
+					constraint.mix = constraint.data.mix;
+					constraint.softness = constraint.data.softness;
+					constraint.bendDirection = constraint.data.bendDirection;
+					constraint.compress = constraint.data.compress;
+					constraint.stretch = constraint.data.stretch;
+					return;
+				case MixBlend.first:
+					constraint.mix += (constraint.data.mix - constraint.mix) * alpha;
+					constraint.softness += (constraint.data.softness - constraint.softness) * alpha;
+					constraint.bendDirection = constraint.data.bendDirection;
+					constraint.compress = constraint.data.compress;
+					constraint.stretch = constraint.data.stretch;
+			}
+			return;
+		}
+
+		let mix = 0, softness = 0;
+		let i = Timeline.search(frames, time, 6/*ENTRIES*/)
+		let curveType = this.curves[i / 6/*ENTRIES*/];
+		switch (curveType) {
+			case 0/*LINEAR*/:
+				let before = frames[i];
+				mix = frames[i + 1/*MIX*/];
+				softness = frames[i + 2/*SOFTNESS*/];
+				let t = (time - before) / (frames[i + 6/*ENTRIES*/] - before);
+				mix += (frames[i + 6/*ENTRIES*/ + 1/*MIX*/] - mix) * t;
+				softness += (frames[i + 6/*ENTRIES*/ + 2/*SOFTNESS*/] - softness) * t;
+				break;
+			case 1/*STEPPED*/:
+				mix = frames[i + 1/*MIX*/];
+				softness = frames[i + 2/*SOFTNESS*/];
+				break;
+			default:
+				mix = this.getBezierValue(time, i, 1/*MIX*/, curveType - 2/*BEZIER*/);
+				softness = this.getBezierValue(time, i, 2/*SOFTNESS*/, curveType + 18/*BEZIER_SIZE*/ - 2/*BEZIER*/);
+		}
+
+		if (blend == MixBlend.setup) {
+			constraint.mix = constraint.data.mix + (mix - constraint.data.mix) * alpha;
+			constraint.softness = constraint.data.softness + (softness - constraint.data.softness) * alpha;
+
+			if (direction == MixDirection.mixOut) {
+				constraint.bendDirection = constraint.data.bendDirection;
+				constraint.compress = constraint.data.compress;
+				constraint.stretch = constraint.data.stretch;
+			} else {
+				constraint.bendDirection = frames[i + 3/*BEND_DIRECTION*/];
+				constraint.compress = frames[i + 4/*COMPRESS*/] != 0;
+				constraint.stretch = frames[i + 5/*STRETCH*/] != 0;
+			}
+		} else {
+			constraint.mix += (mix - constraint.mix) * alpha;
+			constraint.softness += (softness - constraint.softness) * alpha;
+			if (direction == MixDirection.mixIn) {
+				constraint.bendDirection = frames[i + 3/*BEND_DIRECTION*/];
+				constraint.compress = frames[i + 4/*COMPRESS*/] != 0;
+				constraint.stretch = frames[i + 5/*STRETCH*/] != 0;
+			}
+		}
+	}
+}
+
+/** Changes a transform constraint's {@link TransformConstraint#rotateMix}, {@link TransformConstraint#translateMix},
+ * {@link TransformConstraint#scaleMix}, and {@link TransformConstraint#shearMix}. */
+export class TransformConstraintTimeline extends CurveTimeline {
+	/** The index of the transform constraint slot in {@link Skeleton#transformConstraints} that will be changed. */
+	transformConstraintIndex: number;
+
+	constructor(frameCount: number, bezierCount: number, transformConstraintIndex: number) {
+		super(frameCount, bezierCount, [
+			Property.transformConstraint + "|" + transformConstraintIndex
+		]);
+		this.transformConstraintIndex = transformConstraintIndex;
+	}
+
+	getFrameEntries() {
+		return 7/*ENTRIES*/;
+	}
+
+	/** The time in seconds, rotate mix, translate mix, scale mix, and shear mix for the specified key frame. */
+	setFrame(frame: number, time: number, mixRotate: number, mixX: number, mixY: number, mixScaleX: number, mixScaleY: number,
+		mixShearY: number) {
+		let frames = this.frames;
+		frame *= 7/*ENTRIES*/;
+		frames[frame] = time;
+		frames[frame + 1/*ROTATE*/] = mixRotate;
+		frames[frame + 2/*X*/] = mixX;
+		frames[frame + 3/*Y*/] = mixY;
+		frames[frame + 4/*SCALEX*/] = mixScaleX;
+		frames[frame + 5/*SCALEY*/] = mixScaleY;
+		frames[frame + 6/*SHEARY*/] = mixShearY;
+	}
+
+	apply(skeleton: Skeleton, lastTime: number, time: number, firedEvents: Array<Event>, alpha: number, blend: MixBlend, direction: MixDirection) {
+		let constraint: TransformConstraint = skeleton.transformConstraints[this.transformConstraintIndex];
+		if (!constraint.active) return;
+
+		let frames = this.frames;
+		if (time < frames[0]) {
+			let data = constraint.data;
+			switch (blend) {
+				case MixBlend.setup:
+					constraint.mixRotate = data.mixRotate;
+					constraint.mixX = data.mixX;
+					constraint.mixY = data.mixY;
+					constraint.mixScaleX = data.mixScaleX;
+					constraint.mixScaleY = data.mixScaleY;
+					constraint.mixShearY = data.mixShearY;
+					return;
+				case MixBlend.first:
+					constraint.mixRotate += (data.mixRotate - constraint.mixRotate) * alpha;
+					constraint.mixX += (data.mixX - constraint.mixX) * alpha;
+					constraint.mixY += (data.mixY - constraint.mixY) * alpha;
+					constraint.mixScaleX += (data.mixScaleX - constraint.mixScaleX) * alpha;
+					constraint.mixScaleY += (data.mixScaleY - constraint.mixScaleY) * alpha;
+					constraint.mixShearY += (data.mixShearY - constraint.mixShearY) * alpha;
+			}
+			return;
+		}
+
+		let rotate, x, y, scaleX, scaleY, shearY;
+		let i = Timeline.search(frames, time, 7/*ENTRIES*/);
+		let curveType = this.curves[i / 7/*ENTRIES*/];
+		switch (curveType) {
+			case 0/*LINEAR*/:
+				let before = frames[i];
+				rotate = frames[i + 1/*ROTATE*/];
+				x = frames[i + 2/*X*/];
+				y = frames[i + 3/*Y*/];
+				scaleX = frames[i + 4/*SCALEX*/];
+				scaleY = frames[i + 5/*SCALEY*/];
+				shearY = frames[i + 6/*SHEARY*/];
+				let t = (time - before) / (frames[i + 7/*ENTRIES*/] - before);
+				rotate += (frames[i + 7/*ENTRIES*/ + 1/*ROTATE*/] - rotate) * t;
+				x += (frames[i + 7/*ENTRIES*/ + 2/*X*/] - x) * t;
+				y += (frames[i + 7/*ENTRIES*/ + 3/*Y*/] - y) * t;
+				scaleX += (frames[i + 7/*ENTRIES*/ + 4/*SCALEX*/] - scaleX) * t;
+				scaleY += (frames[i + 7/*ENTRIES*/ + 5/*SCALEY*/] - scaleY) * t;
+				shearY += (frames[i + 7/*ENTRIES*/ + 6/*SHEARY*/] - shearY) * t;
+				break;
+			case 1/*STEPPED*/:
+				rotate = frames[i + 1/*ROTATE*/];
+				x = frames[i + 2/*X*/];
+				y = frames[i + 3/*Y*/];
+				scaleX = frames[i + 4/*SCALEX*/];
+				scaleY = frames[i + 5/*SCALEY*/];
+				shearY = frames[i + 6/*SHEARY*/];
+				break;
+			default:
+				rotate = this.getBezierValue(time, i, 1/*ROTATE*/, curveType - 2/*BEZIER*/);
+				x = this.getBezierValue(time, i, 2/*X*/, curveType + 18/*BEZIER_SIZE*/ - 2/*BEZIER*/);
+				y = this.getBezierValue(time, i, 3/*Y*/, curveType + 18/*BEZIER_SIZE*/ * 2 - 2/*BEZIER*/);
+				scaleX = this.getBezierValue(time, i, 4/*SCALEX*/, curveType + 18/*BEZIER_SIZE*/ * 3 - 2/*BEZIER*/);
+				scaleY = this.getBezierValue(time, i, 5/*SCALEY*/, curveType + 18/*BEZIER_SIZE*/ * 4 - 2/*BEZIER*/);
+				shearY = this.getBezierValue(time, i, 6/*SHEARY*/, curveType + 18/*BEZIER_SIZE*/ * 5 - 2/*BEZIER*/);
+		}
+
+		if (blend == MixBlend.setup) {
+			let data = constraint.data;
+			constraint.mixRotate = data.mixRotate + (rotate - data.mixRotate) * alpha;
+			constraint.mixX = data.mixX + (x - data.mixX) * alpha;
+			constraint.mixY = data.mixY + (y - data.mixY) * alpha;
+			constraint.mixScaleX = data.mixScaleX + (scaleX - data.mixScaleX) * alpha;
+			constraint.mixScaleY = data.mixScaleY + (scaleY - data.mixScaleY) * alpha;
+			constraint.mixShearY = data.mixShearY + (shearY - data.mixShearY) * alpha;
+		} else {
+			constraint.mixRotate += (rotate - constraint.mixRotate) * alpha;
+			constraint.mixX += (x - constraint.mixX) * alpha;
+			constraint.mixY += (y - constraint.mixY) * alpha;
+			constraint.mixScaleX += (scaleX - constraint.mixScaleX) * alpha;
+			constraint.mixScaleY += (scaleY - constraint.mixScaleY) * alpha;
+			constraint.mixShearY += (shearY - constraint.mixShearY) * alpha;
+		}
+	}
+}
+
+/** Changes a path constraint's {@link PathConstraint#position}. */
+export class PathConstraintPositionTimeline extends CurveTimeline1 {
+	/** The index of the path constraint slot in {@link Skeleton#pathConstraints} that will be changed. */
+	pathConstraintIndex: number;
+
+	constructor(frameCount: number, bezierCount: number, pathConstraintIndex: number) {
+		super(frameCount, bezierCount, Property.pathConstraintPosition + "|" + pathConstraintIndex);
+		this.pathConstraintIndex = pathConstraintIndex;
+	}
+
+	apply(skeleton: Skeleton, lastTime: number, time: number, firedEvents: Array<Event>, alpha: number, blend: MixBlend, direction: MixDirection) {
+		let constraint: PathConstraint = skeleton.pathConstraints[this.pathConstraintIndex];
+		if (!constraint.active) return;
+
+		let frames = this.frames;
+		if (time < frames[0]) {
+			switch (blend) {
+				case MixBlend.setup:
+					constraint.position = constraint.data.position;
+					return;
+				case MixBlend.first:
+					constraint.position += (constraint.data.position - constraint.position) * alpha;
+			}
+			return;
+		}
+
+		let position = this.getCurveValue(time);
+
+		if (blend == MixBlend.setup)
+			constraint.position = constraint.data.position + (position - constraint.data.position) * alpha;
+		else
+			constraint.position += (position - constraint.position) * alpha;
+	}
+}
+
+/** Changes a path constraint's {@link PathConstraint#spacing}. */
+export class PathConstraintSpacingTimeline extends CurveTimeline1 {
+	/** The index of the path constraint slot in {@link Skeleton#getPathConstraints()} that will be changed. */
+	pathConstraintIndex = 0;
+
+	constructor(frameCount: number, bezierCount: number, pathConstraintIndex: number) {
+		super(frameCount, bezierCount, Property.pathConstraintSpacing + "|" + pathConstraintIndex);
+		this.pathConstraintIndex = pathConstraintIndex;
+	}
+
+	apply(skeleton: Skeleton, lastTime: number, time: number, firedEvents: Array<Event>, alpha: number, blend: MixBlend, direction: MixDirection) {
+		let constraint: PathConstraint = skeleton.pathConstraints[this.pathConstraintIndex];
+		if (!constraint.active) return;
+
+		let frames = this.frames;
+		if (time < frames[0]) {
+			switch (blend) {
+				case MixBlend.setup:
+					constraint.spacing = constraint.data.spacing;
+					return;
+				case MixBlend.first:
+					constraint.spacing += (constraint.data.spacing - constraint.spacing) * alpha;
+			}
+			return;
+		}
+
+		let spacing = this.getCurveValue(time);
+
+		if (blend == MixBlend.setup)
+			constraint.spacing = constraint.data.spacing + (spacing - constraint.data.spacing) * alpha;
+		else
+			constraint.spacing += (spacing - constraint.spacing) * alpha;
+	}
+}
+
+/** Changes a transform constraint's {@link PathConstraint#getMixRotate()}, {@link PathConstraint#getMixX()}, and
+ * {@link PathConstraint#getMixY()}. */
+export class PathConstraintMixTimeline extends CurveTimeline {
+	/** The index of the path constraint slot in {@link Skeleton#getPathConstraints()} that will be changed. */
+	pathConstraintIndex = 0;
+
+	constructor(frameCount: number, bezierCount: number, pathConstraintIndex: number) {
+		super(frameCount, bezierCount, [
+			Property.pathConstraintMix + "|" + pathConstraintIndex
+		]);
+		this.pathConstraintIndex = pathConstraintIndex;
+	}
+
+	getFrameEntries() {
+		return 4/*ENTRIES*/;
+	}
+
+	setFrame(frame: number, time: number, mixRotate: number, mixX: number, mixY: number) {
+		let frames = this.frames;
+		frame <<= 2;
+		frames[frame] = time;
+		frames[frame + 1/*ROTATE*/] = mixRotate;
+		frames[frame + 2/*X*/] = mixX;
+		frames[frame + 3/*Y*/] = mixY;
+	}
+
+	apply(skeleton: Skeleton, lastTime: number, time: number, firedEvents: Array<Event>, alpha: number, blend: MixBlend, direction: MixDirection) {
+		let constraint: PathConstraint = skeleton.pathConstraints[this.pathConstraintIndex];
+		if (!constraint.active) return;
+
+		let frames = this.frames;
+		if (time < frames[0]) {
+			switch (blend) {
+				case MixBlend.setup:
+					constraint.mixRotate = constraint.data.mixRotate;
+					constraint.mixX = constraint.data.mixX;
+					constraint.mixY = constraint.data.mixY;
+					return;
+				case MixBlend.first:
+					constraint.mixRotate += (constraint.data.mixRotate - constraint.mixRotate) * alpha;
+					constraint.mixX += (constraint.data.mixX - constraint.mixX) * alpha;
+					constraint.mixY += (constraint.data.mixY - constraint.mixY) * alpha;
+			}
+			return;
+		}
+
+		let rotate, x, y;
+		let i = Timeline.search(frames, time, 4/*ENTRIES*/);
+		let curveType = this.curves[i >> 2];
+		switch (curveType) {
+			case 0/*LINEAR*/:
+				let before = frames[i];
+				rotate = frames[i + 1/*ROTATE*/];
+				x = frames[i + 2/*X*/];
+				y = frames[i + 3/*Y*/];
+				let t = (time - before) / (frames[i + 4/*ENTRIES*/] - before);
+				rotate += (frames[i + 4/*ENTRIES*/ + 1/*ROTATE*/] - rotate) * t;
+				x += (frames[i + 4/*ENTRIES*/ + 2/*X*/] - x) * t;
+				y += (frames[i + 4/*ENTRIES*/ + 3/*Y*/] - y) * t;
+				break;
+			case 1/*STEPPED*/:
+				rotate = frames[i + 1/*ROTATE*/];
+				x = frames[i + 2/*X*/];
+				y = frames[i + 3/*Y*/];
+				break;
+			default:
+				rotate = this.getBezierValue(time, i, 1/*ROTATE*/, curveType - 2/*BEZIER*/);
+				x = this.getBezierValue(time, i, 2/*X*/, curveType + 18/*BEZIER_SIZE*/ - 2/*BEZIER*/);
+				y = this.getBezierValue(time, i, 3/*Y*/, curveType + 18/*BEZIER_SIZE*/ * 2 - 2/*BEZIER*/);
+		}
+
+		if (blend == MixBlend.setup) {
+			let data = constraint.data;
+			constraint.mixRotate = data.mixRotate + (rotate - data.mixRotate) * alpha;
+			constraint.mixX = data.mixX + (x - data.mixX) * alpha;
+			constraint.mixY = data.mixY + (y - data.mixY) * alpha;
+		} else {
+			constraint.mixRotate += (rotate - constraint.mixRotate) * alpha;
+			constraint.mixX += (x - constraint.mixX) * alpha;
+			constraint.mixY += (y - constraint.mixY) * alpha;
+		}
+	}
+}

+ 1176 - 0
spine-ts/spine-core/src/AnimationState.ts

@@ -0,0 +1,1176 @@
+/******************************************************************************
+ * Spine Runtimes License Agreement
+ * Last updated January 1, 2020. Replaces all prior versions.
+ *
+ * Copyright (c) 2013-2020, Esoteric Software LLC
+ *
+ * Integration of the Spine Runtimes into software or otherwise creating
+ * derivative works of the Spine Runtimes is permitted under the terms and
+ * conditions of Section 2 of the Spine Editor License Agreement:
+ * http://esotericsoftware.com/spine-editor-license
+ *
+ * Otherwise, it is permitted to integrate the Spine Runtimes into software
+ * or otherwise create derivative works of the Spine Runtimes (collectively,
+ * "Products"), provided that each user of the Products must obtain their own
+ * Spine Editor license and redistribution of the Products in any form must
+ * include this license and copyright notice.
+ *
+ * THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "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 LLC 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
+ * THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *****************************************************************************/
+
+import { Animation, MixBlend, AttachmentTimeline, MixDirection, RotateTimeline, DrawOrderTimeline, Timeline, EventTimeline } from "./Animation";
+import { AnimationStateData } from "./AnimationStateData";
+import { Skeleton } from "./Skeleton";
+import { Slot } from "./Slot";
+import { StringSet, Pool, Utils, MathUtils } from "./Utils";
+import { Event } from "./Event";
+
+
+/** Applies animations over time, queues animations for later playback, mixes (crossfading) between animations, and applies
+ * multiple animations on top of each other (layering).
+ *
+ * See [Applying Animations](http://esotericsoftware.com/spine-applying-animations/) in the Spine Runtimes Guide. */
+export class AnimationState {
+	private static emptyAnimation(): Animation {
+		if (!_emptyAnimation) _emptyAnimation = new Animation("<empty>", [], 0);
+		return _emptyAnimation;
+	}
+
+	/** The AnimationStateData to look up mix durations. */
+	data: AnimationStateData;
+
+	/** The list of tracks that currently have animations, which may contain null entries. */
+	tracks = new Array<TrackEntry>();
+
+	/** Multiplier for the delta time when the animation state is updated, causing time for all animations and mixes to play slower
+	 * or faster. Defaults to 1.
+	 *
+	 * See TrackEntry {@link TrackEntry#timeScale} for affecting a single animation. */
+	timeScale = 1;
+	unkeyedState = 0;
+
+	events = new Array<Event>();
+	listeners = new Array<AnimationStateListener>();
+	queue = new EventQueue(this);
+	propertyIDs = new StringSet();
+	animationsChanged = false;
+
+	trackEntryPool = new Pool<TrackEntry>(() => new TrackEntry());
+
+	constructor(data: AnimationStateData) {
+		this.data = data;
+	}
+
+	/** Increments each track entry {@link TrackEntry#trackTime()}, setting queued animations as current if needed. */
+	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) 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) {
+				// 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 += current.timeScale == 0 ? 0 : (nextTime / current.timeScale + delta) * next.timeScale;
+					current.trackTime += currentDelta;
+					this.setCurrent(i, next, true);
+					while (next.mixingFrom) {
+						next.mixTime += delta;
+						next = next.mixingFrom;
+					}
+					continue;
+				}
+			} else if (current.trackLast >= current.trackEnd && !current.mixingFrom) {
+				tracks[i] = null;
+				this.queue.end(current);
+				this.clearNext(current);
+				continue;
+			}
+			if (current.mixingFrom && this.updateMixingFrom(current, delta)) {
+				// End mixing from entries once all have completed.
+				let from = current.mixingFrom;
+				current.mixingFrom = null;
+				if (from) from.mixingTo = null;
+				while (from) {
+					this.queue.end(from);
+					from = from.mixingFrom;
+				}
+			}
+
+			current.trackTime += currentDelta;
+		}
+
+		this.queue.drain();
+	}
+
+	/** Returns true when all mixing from entries are complete. */
+	updateMixingFrom(to: TrackEntry, delta: number): boolean {
+		let from = to.mixingFrom;
+		if (!from) return true;
+
+		let finished = this.updateMixingFrom(from, delta);
+
+		from.animationLast = from.nextAnimationLast;
+		from.trackLast = from.nextTrackLast;
+
+		// Require mixTime > 0 to ensure the mixing from entry was applied at least once.
+		if (to.mixTime > 0 && to.mixTime >= to.mixDuration) {
+			// Require totalAlpha == 0 to ensure mixing is complete, unless mixDuration == 0 (the transition is a single frame).
+			if (from.totalAlpha == 0 || to.mixDuration == 0) {
+				to.mixingFrom = from.mixingFrom;
+				if (from.mixingFrom) from.mixingFrom.mixingTo = to;
+				to.interruptAlpha = from.interruptAlpha;
+				this.queue.end(from);
+			}
+			return finished;
+		}
+
+		from.trackTime += delta * from.timeScale;
+		to.mixTime += delta;
+		return false;
+	}
+
+	/** Poses the skeleton using the track entry animations. There are no side effects other than invoking listeners, so the
+	 * animation state can be applied to multiple skeletons to pose them identically.
+	 * @returns True if any animations were applied. */
+	apply(skeleton: Skeleton): boolean {
+		if (!skeleton) 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 || current.delay > 0) continue;
+			applied = true;
+			let blend: MixBlend = i == 0 ? MixBlend.first : current.mixBlend;
+
+			// Apply mixing from entries first.
+			let mix = current.alpha;
+			if (current.mixingFrom)
+				mix *= this.applyMixingFrom(current, skeleton, blend);
+			else if (current.trackTime >= current.trackEnd && !current.next)
+				mix = 0;
+
+			// Apply current entry.
+			let animationLast = current.animationLast, animationTime = current.getAnimationTime(), applyTime = animationTime;
+			let applyEvents = events;
+			if (current.reverse) {
+				applyTime = current.animation.duration - applyTime;
+				applyEvents = null;
+			}
+			let timelines = current.animation.timelines;
+			let timelineCount = timelines.length;
+			if ((i == 0 && mix == 1) || blend == MixBlend.add) {
+				for (let ii = 0; ii < timelineCount; ii++) {
+					// Fixes issue #302 on IOS9 where mix, blend sometimes became undefined and caused assets
+					// to sometimes stop rendering when using color correction, as their RGBA values become NaN.
+					// (https://github.com/pixijs/pixi-spine/issues/302)
+					Utils.webkit602BugfixHelper(mix, blend);
+					var timeline = timelines[ii];
+					if (timeline instanceof AttachmentTimeline)
+						this.applyAttachmentTimeline(timeline, skeleton, applyTime, blend, true);
+					else
+						timeline.apply(skeleton, animationLast, applyTime, applyEvents, mix, blend, MixDirection.mixIn);
+				}
+			} else {
+				let timelineMode = current.timelineMode;
+
+				let firstFrame = current.timelinesRotation.length != timelineCount << 1;
+				if (firstFrame) current.timelinesRotation.length = timelineCount << 1;
+
+				for (let ii = 0; ii < timelineCount; ii++) {
+					let timeline = timelines[ii];
+					let timelineBlend = timelineMode[ii] == SUBSEQUENT ? blend : MixBlend.setup;
+					if (timeline instanceof RotateTimeline) {
+						this.applyRotateTimeline(timeline, skeleton, applyTime, mix, timelineBlend, current.timelinesRotation, ii << 1, firstFrame);
+					} else if (timeline instanceof AttachmentTimeline) {
+						this.applyAttachmentTimeline(timeline, skeleton, applyTime, blend, true);
+					} else {
+						// This fixes the WebKit 602 specific issue described at http://esotericsoftware.com/forum/iOS-10-disappearing-graphics-10109
+						Utils.webkit602BugfixHelper(mix, blend);
+						timeline.apply(skeleton, animationLast, applyTime, applyEvents, mix, timelineBlend, MixDirection.mixIn);
+					}
+				}
+			}
+			this.queueEvents(current, animationTime);
+			events.length = 0;
+			current.nextAnimationLast = animationTime;
+			current.nextTrackLast = current.trackTime;
+		}
+
+		// Set slots attachments to the setup pose, if needed. This occurs if an animation that is mixing out sets attachments so
+		// subsequent timelines see any deform, but the subsequent timelines don't set an attachment (eg they are also mixing out or
+		// the time is before the first key).
+		var setupState = this.unkeyedState + SETUP;
+		var slots = skeleton.slots;
+		for (var i = 0, n = skeleton.slots.length; i < n; i++) {
+			var slot = slots[i];
+			if (slot.attachmentState == setupState) {
+				var attachmentName = slot.data.attachmentName;
+				slot.setAttachment(!attachmentName ? null : skeleton.getAttachment(slot.data.index, attachmentName));
+			}
+		}
+		this.unkeyedState += 2; // Increasing after each use avoids the need to reset attachmentState for every slot.
+
+		this.queue.drain();
+		return applied;
+	}
+
+	applyMixingFrom(to: TrackEntry, skeleton: Skeleton, blend: MixBlend) {
+		let from = to.mixingFrom;
+		if (from.mixingFrom) this.applyMixingFrom(from, skeleton, blend);
+
+		let mix = 0;
+		if (to.mixDuration == 0) { // Single frame mix to undo mixingFrom changes.
+			mix = 1;
+			if (blend == MixBlend.first) blend = MixBlend.setup;
+		} else {
+			mix = to.mixTime / to.mixDuration;
+			if (mix > 1) mix = 1;
+			if (blend != MixBlend.first) blend = from.mixBlend;
+		}
+
+		let attachments = mix < from.attachmentThreshold, drawOrder = mix < from.drawOrderThreshold;
+		let timelines = from.animation.timelines;
+		let timelineCount = timelines.length;
+		let alphaHold = from.alpha * to.interruptAlpha, alphaMix = alphaHold * (1 - mix);
+		let animationLast = from.animationLast, animationTime = from.getAnimationTime(), applyTime = animationTime;
+		let events = null;
+		if (from.reverse)
+			applyTime = from.animation.duration - applyTime;
+		else if (mix < from.eventThreshold)
+			events = this.events;
+
+		if (blend == MixBlend.add) {
+			for (let i = 0; i < timelineCount; i++)
+				timelines[i].apply(skeleton, animationLast, applyTime, events, alphaMix, blend, MixDirection.mixOut);
+		} else {
+			let timelineMode = from.timelineMode;
+			let timelineHoldMix = from.timelineHoldMix;
+
+			let firstFrame = from.timelinesRotation.length != timelineCount << 1;
+			if (firstFrame) from.timelinesRotation.length = timelineCount << 1;
+
+			from.totalAlpha = 0;
+			for (let i = 0; i < timelineCount; i++) {
+				let timeline = timelines[i];
+				let direction = MixDirection.mixOut;
+				let timelineBlend: MixBlend;
+				let alpha = 0;
+				switch (timelineMode[i]) {
+					case SUBSEQUENT:
+						if (!drawOrder && timeline instanceof DrawOrderTimeline) continue;
+						timelineBlend = blend;
+						alpha = alphaMix;
+						break;
+					case FIRST:
+						timelineBlend = MixBlend.setup;
+						alpha = alphaMix;
+						break;
+					case HOLD_SUBSEQUENT:
+						timelineBlend = blend;
+						alpha = alphaHold;
+						break;
+					case HOLD_FIRST:
+						timelineBlend = MixBlend.setup;
+						alpha = alphaHold;
+						break;
+					default:
+						timelineBlend = MixBlend.setup;
+						let holdMix = timelineHoldMix[i];
+						alpha = alphaHold * Math.max(0, 1 - holdMix.mixTime / holdMix.mixDuration);
+						break;
+				}
+				from.totalAlpha += alpha;
+
+				if (timeline instanceof RotateTimeline)
+					this.applyRotateTimeline(timeline, skeleton, applyTime, alpha, timelineBlend, from.timelinesRotation, i << 1, firstFrame);
+				else if (timeline instanceof AttachmentTimeline)
+					this.applyAttachmentTimeline(timeline, skeleton, applyTime, timelineBlend, attachments);
+				else {
+					// This fixes the WebKit 602 specific issue described at http://esotericsoftware.com/forum/iOS-10-disappearing-graphics-10109
+					Utils.webkit602BugfixHelper(alpha, blend);
+					if (drawOrder && timeline instanceof DrawOrderTimeline && timelineBlend == MixBlend.setup)
+						direction = MixDirection.mixIn;
+					timeline.apply(skeleton, animationLast, applyTime, events, alpha, timelineBlend, direction);
+				}
+			}
+		}
+
+		if (to.mixDuration > 0) this.queueEvents(from, animationTime);
+		this.events.length = 0;
+		from.nextAnimationLast = animationTime;
+		from.nextTrackLast = from.trackTime;
+
+		return mix;
+	}
+
+	applyAttachmentTimeline(timeline: AttachmentTimeline, skeleton: Skeleton, time: number, blend: MixBlend, attachments: boolean) {
+		var slot = skeleton.slots[timeline.slotIndex];
+		if (!slot.bone.active) return;
+
+		if (time < timeline.frames[0]) { // Time is before first frame.
+			if (blend == MixBlend.setup || blend == MixBlend.first)
+				this.setAttachment(skeleton, slot, slot.data.attachmentName, attachments);
+		} else
+			this.setAttachment(skeleton, slot, timeline.attachmentNames[Timeline.search1(timeline.frames, time)], attachments);
+
+		// If an attachment wasn't set (ie before the first frame or attachments is false), set the setup attachment later.
+		if (slot.attachmentState <= this.unkeyedState) slot.attachmentState = this.unkeyedState + SETUP;
+	}
+
+	setAttachment(skeleton: Skeleton, slot: Slot, attachmentName: string, attachments: boolean) {
+		slot.setAttachment(!attachmentName ? null : skeleton.getAttachment(slot.data.index, attachmentName));
+		if (attachments) slot.attachmentState = this.unkeyedState + CURRENT;
+	}
+
+	applyRotateTimeline(timeline: RotateTimeline, skeleton: Skeleton, time: number, alpha: number, blend: MixBlend,
+		timelinesRotation: Array<number>, i: number, firstFrame: boolean) {
+
+		if (firstFrame) timelinesRotation[i] = 0;
+
+		if (alpha == 1) {
+			timeline.apply(skeleton, 0, time, null, 1, blend, MixDirection.mixIn);
+			return;
+		}
+
+		let bone = skeleton.bones[timeline.boneIndex];
+		if (!bone.active) return;
+		let frames = timeline.frames;
+		let r1 = 0, r2 = 0;
+		if (time < frames[0]) {
+			switch (blend) {
+				case MixBlend.setup:
+					bone.rotation = bone.data.rotation;
+				default:
+					return;
+				case MixBlend.first:
+					r1 = bone.rotation;
+					r2 = bone.data.rotation;
+			}
+		} else {
+			r1 = blend == MixBlend.setup ? bone.data.rotation : bone.rotation;
+			r2 = bone.data.rotation + timeline.getCurveValue(time);
+		}
+
+		// Mix between rotations using the direction of the shortest route on the first frame while detecting crosses.
+		let total = 0, diff = r2 - r1;
+		diff -= (16384 - ((16384.499999999996 - diff / 360) | 0)) * 360;
+		if (diff == 0) {
+			total = timelinesRotation[i];
+		} else {
+			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;
+		bone.rotation = r1 + total * alpha;
+	}
+
+	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.
+		let complete = false;
+		if (entry.loop)
+			complete = duration == 0 || trackLastWrapped > entry.trackTime % duration;
+		else
+			complete = animationTime >= animationEnd && entry.animationLast < animationEnd;
+		if (complete) 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, event);
+		}
+	}
+
+	/** Removes all animations from all tracks, leaving skeletons in their current pose.
+	 *
+	 * It may be desired to use {@link AnimationState#setEmptyAnimation()} to mix the skeletons back to the setup pose,
+	 * rather than leaving them in their current pose. */
+	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();
+	}
+
+	/** Removes all animations from the track, leaving skeletons in their current pose.
+	 *
+	 * It may be desired to use {@link AnimationState#setEmptyAnimation()} to mix the skeletons back to the setup pose,
+	 * rather than leaving them in their current pose. */
+	clearTrack(trackIndex: number) {
+		if (trackIndex >= this.tracks.length) return;
+		let current = this.tracks[trackIndex];
+		if (!current) return;
+
+		this.queue.end(current);
+
+		this.clearNext(current);
+
+		let entry = current;
+		while (true) {
+			let from = entry.mixingFrom;
+			if (!from) break;
+			this.queue.end(from);
+			entry.mixingFrom = null;
+			entry.mixingTo = 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;
+		current.previous = null;
+
+		if (from) {
+			if (interrupt) this.queue.interrupt(from);
+			current.mixingFrom = from;
+			from.mixingTo = current;
+			current.mixTime = 0;
+
+			// Store the interrupted mix percentage.
+			if (from.mixingFrom && 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);
+	}
+
+	/** Sets an animation by name.
+	  *
+	  * See {@link #setAnimationWith()}. */
+	setAnimation(trackIndex: number, animationName: string, loop: boolean = false) {
+		let animation = this.data.skeletonData.findAnimation(animationName);
+		if (!animation) throw new Error("Animation not found: " + animationName);
+		return this.setAnimationWith(trackIndex, animation, loop);
+	}
+
+	/** Sets the current animation for a track, discarding any queued animations. If the formerly current track entry was never
+	 * applied to a skeleton, it is replaced (not mixed from).
+	 * @param loop If true, the animation will repeat. If false it will not, instead its last frame is applied if played beyond its
+	 *           duration. In either case {@link TrackEntry#trackEnd} determines when the track is cleared.
+	 * @returns A track entry to allow further customization of animation playback. References to the track entry must not be kept
+	 *         after the {@link AnimationStateListener#dispose()} event occurs. */
+	setAnimationWith(trackIndex: number, animation: Animation, loop: boolean = false) {
+		if (!animation) throw new Error("animation cannot be null.");
+		let interrupt = true;
+		let current = this.expandToIndex(trackIndex);
+		if (current) {
+			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.clearNext(current);
+				current = current.mixingFrom;
+				interrupt = false;
+			} else
+				this.clearNext(current);
+		}
+		let entry = this.trackEntry(trackIndex, animation, loop, current);
+		this.setCurrent(trackIndex, entry, interrupt);
+		this.queue.drain();
+		return entry;
+	}
+
+	/** Queues an animation by name.
+	 *
+	 * See {@link #addAnimationWith()}. */
+	addAnimation(trackIndex: number, animationName: string, loop: boolean = false, delay: number = 0) {
+		let animation = this.data.skeletonData.findAnimation(animationName);
+		if (!animation) throw new Error("Animation not found: " + animationName);
+		return this.addAnimationWith(trackIndex, animation, loop, delay);
+	}
+
+	/** Adds an animation to be played after the current or last queued animation for a track. If the track is empty, it is
+	 * equivalent to calling {@link #setAnimationWith()}.
+	 * @param delay If > 0, sets {@link TrackEntry#delay}. If <= 0, the delay set is the duration of the previous track entry
+	 *           minus any mix duration (from the {@link AnimationStateData}) plus the specified `delay` (ie the mix
+	 *           ends at (`delay` = 0) or before (`delay` < 0) the previous track entry duration). If the
+	 *           previous entry is looping, its next loop completion is used instead of its duration.
+	 * @returns A track entry to allow further customization of animation playback. References to the track entry must not be kept
+	 *         after the {@link AnimationStateListener#dispose()} event occurs. */
+	addAnimationWith(trackIndex: number, animation: Animation, loop: boolean = false, delay: number = 0) {
+		if (!animation) throw new Error("animation cannot be null.");
+
+		let last = this.expandToIndex(trackIndex);
+		if (last) {
+			while (last.next)
+				last = last.next;
+		}
+
+		let entry = this.trackEntry(trackIndex, animation, loop, last);
+
+		if (!last) {
+			this.setCurrent(trackIndex, entry, true);
+			this.queue.drain();
+		} else {
+			last.next = entry;
+			entry.previous = last;
+			if (delay <= 0) delay += last.getTrackComplete() - entry.mixDuration;
+		}
+
+		entry.delay = delay;
+		return entry;
+	}
+
+	/** Sets an empty animation for a track, discarding any queued animations, and sets the track entry's
+	 * {@link TrackEntry#mixduration}. An empty animation has no timelines and serves as a placeholder for mixing in or out.
+	 *
+	 * Mixing out is done by setting an empty animation with a mix duration using either {@link #setEmptyAnimation()},
+	 * {@link #setEmptyAnimations()}, or {@link #addEmptyAnimation()}. Mixing to an empty animation causes
+	 * the previous animation to be applied less and less over the mix duration. Properties keyed in the previous animation
+	 * transition to the value from lower tracks or to the setup pose value if no lower tracks key the property. A mix duration of
+	 * 0 still mixes out over one frame.
+	 *
+	 * Mixing in is done by first setting an empty animation, then adding an animation using
+	 * {@link #addAnimation()} and on the returned track entry, set the
+	 * {@link TrackEntry#setMixDuration()}. Mixing from an empty animation causes the new animation to be applied more and
+	 * more over the mix duration. Properties keyed in the new animation transition from the value from lower tracks or from the
+	 * setup pose value if no lower tracks key the property to the value keyed in the new animation. */
+	setEmptyAnimation(trackIndex: number, mixDuration: number = 0) {
+		let entry = this.setAnimationWith(trackIndex, AnimationState.emptyAnimation(), false);
+		entry.mixDuration = mixDuration;
+		entry.trackEnd = mixDuration;
+		return entry;
+	}
+
+	/** Adds an empty animation to be played after the current or last queued animation for a track, and sets the track entry's
+	 * {@link TrackEntry#mixDuration}. If the track is empty, it is equivalent to calling
+	 * {@link #setEmptyAnimation()}.
+	 *
+	 * See {@link #setEmptyAnimation()}.
+	 * @param delay If > 0, sets {@link TrackEntry#delay}. If <= 0, the delay set is the duration of the previous track entry
+	 *           minus any mix duration plus the specified `delay` (ie the mix ends at (`delay` = 0) or
+	 *           before (`delay` < 0) the previous track entry duration). If the previous entry is looping, its next
+	 *           loop completion is used instead of its duration.
+	 * @return A track entry to allow further customization of animation playback. References to the track entry must not be kept
+	 *         after the {@link AnimationStateListener#dispose()} event occurs. */
+	addEmptyAnimation(trackIndex: number, mixDuration: number = 0, delay: number = 0) {
+		let entry = this.addAnimationWith(trackIndex, AnimationState.emptyAnimation(), false, delay);
+		if (delay <= 0) entry.delay += entry.mixDuration - mixDuration;
+		entry.mixDuration = mixDuration;
+		entry.trackEnd = mixDuration;
+		return entry;
+	}
+
+	/** Sets an empty animation for every track, discarding any queued animations, and mixes to it over the specified mix
+	  * duration. */
+	setEmptyAnimations(mixDuration: number = 0) {
+		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) 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 + 1, null);
+		this.tracks.length = index + 1;
+		return null;
+	}
+
+	/** @param last May be 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.holdPrevious = false;
+
+		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 ? 0 : this.data.getMix(last.animation, animation);
+		entry.mixBlend = MixBlend.replace;
+		return entry;
+	}
+
+	/** Removes the {@link TrackEntry#getNext() next entry} and all entries after it for the specified entry. */
+	clearNext(entry: TrackEntry) {
+		let next = entry.next;
+		while (next) {
+			this.queue.dispose(next);
+			next = next.next;
+		}
+		entry.next = null;
+	}
+
+	_animationsChanged() {
+		this.animationsChanged = false;
+
+		this.propertyIDs.clear();
+		let tracks = this.tracks;
+		for (let i = 0, n = tracks.length; i < n; i++) {
+			let entry = tracks[i];
+			if (!entry) continue;
+			while (entry.mixingFrom)
+				entry = entry.mixingFrom;
+			do {
+				if (!entry.mixingTo || entry.mixBlend != MixBlend.add) this.computeHold(entry);
+				entry = entry.mixingTo;
+			} while (entry);
+		}
+	}
+
+	computeHold(entry: TrackEntry) {
+		let to = entry.mixingTo;
+		let timelines = entry.animation.timelines;
+		let timelinesCount = entry.animation.timelines.length;
+		let timelineMode = entry.timelineMode;
+		timelineMode.length = timelinesCount;
+		let timelineHoldMix = entry.timelineHoldMix;
+		timelineHoldMix.length = 0;
+		let propertyIDs = this.propertyIDs;
+
+		if (to && to.holdPrevious) {
+			for (let i = 0; i < timelinesCount; i++)
+				timelineMode[i] = propertyIDs.addAll(timelines[i].getPropertyIds()) ? HOLD_FIRST : HOLD_SUBSEQUENT;
+			return;
+		}
+
+		outer:
+		for (let i = 0; i < timelinesCount; i++) {
+			let timeline = timelines[i];
+			let ids = timeline.getPropertyIds();
+			if (!propertyIDs.addAll(ids))
+				timelineMode[i] = SUBSEQUENT;
+			else if (!to || timeline instanceof AttachmentTimeline || timeline instanceof DrawOrderTimeline
+				|| timeline instanceof EventTimeline || !to.animation.hasTimeline(ids)) {
+				timelineMode[i] = FIRST;
+			} else {
+				for (let next = to.mixingTo; next; next = next.mixingTo) {
+					if (next.animation.hasTimeline(ids)) continue;
+					if (entry.mixDuration > 0) {
+						timelineMode[i] = HOLD_MIX;
+						timelineHoldMix[i] = next;
+						continue outer;
+					}
+					break;
+				}
+				timelineMode[i] = HOLD_FIRST;
+			}
+		}
+	}
+
+	/** Returns the track entry for the animation currently playing on the track, or null if no animation is currently playing. */
+	getCurrent(trackIndex: number) {
+		if (trackIndex >= this.tracks.length) return null;
+		return this.tracks[trackIndex];
+	}
+
+	/** Adds a listener to receive events for all track entries. */
+	addListener(listener: AnimationStateListener) {
+		if (!listener) throw new Error("listener cannot be null.");
+		this.listeners.push(listener);
+	}
+
+	/** Removes the listener added with {@link #addListener()}. */
+	removeListener(listener: AnimationStateListener) {
+		let index = this.listeners.indexOf(listener);
+		if (index >= 0) this.listeners.splice(index, 1);
+	}
+
+	/** Removes all listeners added with {@link #addListener()}. */
+	clearListeners() {
+		this.listeners.length = 0;
+	}
+
+	/** Discards all listener notifications that have not yet been delivered. This can be useful to call from an
+	 * {@link AnimationStateListener} when it is known that further notifications that may have been already queued for delivery
+	 * are not wanted because new animations are being set. */
+	clearListenerNotifications() {
+		this.queue.clear();
+	}
+}
+
+/** Stores settings and other state for the playback of an animation on an {@link AnimationState} track.
+ *
+ * References to a track entry must not be kept after the {@link AnimationStateListener#dispose()} event occurs. */
+export class TrackEntry {
+	/** The animation to apply for this track entry. */
+	animation: Animation;
+
+	previous: TrackEntry;
+
+	/** The animation queued to start after this animation, or null. `next` makes up a linked list. */
+	next: TrackEntry;
+
+	/** The track entry for the previous animation when mixing from the previous animation to this animation, or null if no
+	 * mixing is currently occuring. When mixing from multiple animations, `mixingFrom` makes up a linked list. */
+	mixingFrom: TrackEntry;
+
+	/** The track entry for the next animation when mixing from this animation to the next animation, or null if no mixing is
+	 * currently occuring. When mixing to multiple animations, `mixingTo` makes up a linked list. */
+	mixingTo: TrackEntry;
+
+	/** The listener for events generated by this track entry, or null.
+	 *
+	 * A track entry returned from {@link AnimationState#setAnimation()} is already the current animation
+	 * for the track, so the track entry listener {@link AnimationStateListener#start()} will not be called. */
+	listener: AnimationStateListener;
+
+	/** The index of the track where this track entry is either current or queued.
+	 *
+	 * See {@link AnimationState#getCurrent()}. */
+	trackIndex: number;
+
+	/** If true, the animation will repeat. If false it will not, instead its last frame is applied if played beyond its
+	 * duration. */
+	loop: boolean;
+
+	/** If true, when mixing from the previous animation to this animation, the previous animation is applied as normal instead
+	 * of being mixed out.
+	 *
+	 * When mixing between animations that key the same property, if a lower track also keys that property then the value will
+	 * briefly dip toward the lower track value during the mix. This happens because the first animation mixes from 100% to 0%
+	 * while the second animation mixes from 0% to 100%. Setting `holdPrevious` to true applies the first animation
+	 * at 100% during the mix so the lower track value is overwritten. Such dipping does not occur on the lowest track which
+	 * keys the property, only when a higher track also keys the property.
+	 *
+	 * Snapping will occur if `holdPrevious` is true and this animation does not key all the same properties as the
+	 * previous animation. */
+	holdPrevious: boolean;
+
+	reverse: boolean;
+
+	/** When the mix percentage ({@link #mixTime} / {@link #mixDuration}) is less than the
+	 * `eventThreshold`, event timelines are applied while this animation is being mixed out. Defaults to 0, so event
+	 * timelines are not applied while this animation is being mixed out. */
+	eventThreshold: number;
+
+	/** When the mix percentage ({@link #mixtime} / {@link #mixDuration}) is less than the
+	 * `attachmentThreshold`, attachment timelines are applied while this animation is being mixed out. Defaults to
+	 * 0, so attachment timelines are not applied while this animation is being mixed out. */
+	attachmentThreshold: number;
+
+	/** When the mix percentage ({@link #mixTime} / {@link #mixDuration}) is less than the
+	 * `drawOrderThreshold`, draw order timelines are applied while this animation is being mixed out. Defaults to 0,
+	 * so draw order timelines are not applied while this animation is being mixed out. */
+	drawOrderThreshold: number;
+
+	/** Seconds when this animation starts, both initially and after looping. Defaults to 0.
+	 *
+	 * When changing the `animationStart` time, it often makes sense to set {@link #animationLast} to the same
+	 * value to prevent timeline keys before the start time from triggering. */
+	animationStart: number;
+
+	/** Seconds for the last frame of this animation. Non-looping animations won't play past this time. Looping animations will
+	 * loop back to {@link #animationStart} at this time. Defaults to the animation {@link Animation#duration}. */
+	animationEnd: number;
+
+
+	/** The time in seconds this animation was last applied. Some timelines use this for one-time triggers. Eg, when this
+	 * animation is applied, event timelines will fire all events between the `animationLast` time (exclusive) and
+	 * `animationTime` (inclusive). Defaults to -1 to ensure triggers on frame 0 happen the first time this animation
+	 * is applied. */
+	animationLast: number;
+
+	nextAnimationLast: number;
+
+	/** Seconds to postpone playing the animation. When this track entry is the current track entry, `delay`
+	 * postpones incrementing the {@link #trackTime}. When this track entry is queued, `delay` is the time from
+	 * the start of the previous animation to when this track entry will become the current track entry (ie when the previous
+	 * track entry {@link TrackEntry#trackTime} >= this track entry's `delay`).
+	 *
+	 * {@link #timeScale} affects the delay. */
+	delay: number;
+
+	/** Current time in seconds this track entry has been the current track entry. The track time determines
+	 * {@link #animationTime}. The track time can be set to start the animation at a time other than 0, without affecting
+	 * looping. */
+	trackTime: number;
+
+	trackLast: number; nextTrackLast: number;
+
+	/** The track time in seconds when this animation will be removed from the track. Defaults to the highest possible float
+	 * value, meaning the animation will be applied until a new animation is set or the track is cleared. If the track end time
+	 * is reached, no other animations are queued for playback, and mixing from any previous animations is complete, then the
+	 * properties keyed by the animation are set to the setup pose and the track is cleared.
+	 *
+	 * It may be desired to use {@link AnimationState#addEmptyAnimation()} rather than have the animation
+	 * abruptly cease being applied. */
+	trackEnd: number;
+
+	/** Multiplier for the delta time when this track entry is updated, causing time for this animation to pass slower or
+	 * faster. Defaults to 1.
+	 *
+	 * {@link #mixTime} is not affected by track entry time scale, so {@link #mixDuration} may need to be adjusted to
+	 * match the animation speed.
+	 *
+	 * When using {@link AnimationState#addAnimation()} with a `delay` <= 0, note the
+	 * {@link #delay} is set using the mix duration from the {@link AnimationStateData}, assuming time scale to be 1. If
+	 * the time scale is not 1, the delay may need to be adjusted.
+	 *
+	 * See AnimationState {@link AnimationState#timeScale} for affecting all animations. */
+	timeScale: number;
+
+	/** Values < 1 mix this animation with the skeleton's current pose (usually the pose resulting from lower tracks). Defaults
+	 * to 1, which overwrites the skeleton's current pose with this animation.
+	 *
+	 * Typically track 0 is used to completely pose the skeleton, then alpha is used on higher tracks. It doesn't make sense to
+	 * use alpha on track 0 if the skeleton pose is from the last frame render. */
+	alpha: number;
+
+	/** Seconds from 0 to the {@link #getMixDuration()} when mixing from the previous animation to this animation. May be
+	 * slightly more than `mixDuration` when the mix is complete. */
+	mixTime: number;
+
+	/** Seconds for mixing from the previous animation to this animation. Defaults to the value provided by AnimationStateData
+	 * {@link AnimationStateData#getMix()} based on the animation before this animation (if any).
+	 *
+	 * A mix duration of 0 still mixes out over one frame to provide the track entry being mixed out a chance to revert the
+	 * properties it was animating.
+	 *
+	 * The `mixDuration` can be set manually rather than use the value from
+	 * {@link AnimationStateData#getMix()}. In that case, the `mixDuration` can be set for a new
+	 * track entry only before {@link AnimationState#update(float)} is first called.
+	 *
+	 * When using {@link AnimationState#addAnimation()} with a `delay` <= 0, note the
+	 * {@link #delay} is set using the mix duration from the {@link AnimationStateData}, not a mix duration set
+	 * afterward. */
+	mixDuration: number; interruptAlpha: number; totalAlpha: number;
+
+	/** Controls how properties keyed in the animation are mixed with lower tracks. Defaults to {@link MixBlend#replace}, which
+	 * replaces the values from the lower tracks with the animation values. {@link MixBlend#add} adds the animation values to
+	 * the values from the lower tracks.
+	 *
+	 * The `mixBlend` can be set for a new track entry only before {@link AnimationState#apply()} is first
+	 * called. */
+	mixBlend = MixBlend.replace;
+	timelineMode = new Array<number>();
+	timelineHoldMix = new Array<TrackEntry>();
+	timelinesRotation = new Array<number>();
+
+	reset() {
+		this.next = null;
+		this.previous = null;
+		this.mixingFrom = null;
+		this.mixingTo = null;
+		this.animation = null;
+		this.listener = null;
+		this.timelineMode.length = 0;
+		this.timelineHoldMix.length = 0;
+		this.timelinesRotation.length = 0;
+	}
+
+	/** Uses {@link #trackTime} to compute the `animationTime`, which is between {@link #animationStart}
+	 * and {@link #animationEnd}. When the `trackTime` is 0, the `animationTime` is equal to the
+	 * `animationStart` time. */
+	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;
+	}
+
+	/** Returns true if at least one loop has been completed.
+	 *
+	 * See {@link AnimationStateListener#complete()}. */
+	isComplete() {
+		return this.trackTime >= this.animationEnd - this.animationStart;
+	}
+
+	/** Resets the rotation directions for mixing this entry's rotate timelines. This can be useful to avoid bones rotating the
+	 * long way around when using {@link #alpha} and starting animations on other tracks.
+	 *
+	 * Mixing with {@link MixBlend#replace} involves finding a rotation between two others, which has two possible solutions:
+	 * the short way or the long way around. The two rotations likely change over time, so which direction is the short or long
+	 * way also changes. If the short way was always chosen, bones would flip to the other side when that direction became the
+	 * long way. TrackEntry chooses the short way the first time it is applied and remembers that direction. */
+	resetRotationDirections() {
+		this.timelinesRotation.length = 0;
+	}
+
+	getTrackComplete() {
+		let duration = this.animationEnd - this.animationStart;
+		if (duration != 0) {
+			if (this.loop) return duration * (1 + ((this.trackTime / duration) | 0)); // Completion of next loop.
+			if (this.trackTime < duration) return duration; // Before duration.
+		}
+		return this.trackTime; // Next update.
+	}
+}
+
+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);
+	}
+
+	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 && entry.listener.start) entry.listener.start(entry);
+					for (let ii = 0; ii < listeners.length; ii++)
+						if (listeners[ii].start) listeners[ii].start(entry);
+					break;
+				case EventType.interrupt:
+					if (entry.listener && 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 && entry.listener.end) entry.listener.end(entry);
+					for (let ii = 0; ii < listeners.length; ii++)
+						if (listeners[ii].end) listeners[ii].end(entry);
+				// Fall through.
+				case EventType.dispose:
+					if (entry.listener && 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 && entry.listener.complete) entry.listener.complete(entry);
+					for (let ii = 0; ii < listeners.length; ii++)
+						if (listeners[ii].complete) listeners[ii].complete(entry);
+					break;
+				case EventType.event:
+					let event = objects[i++ + 2] as Event;
+					if (entry.listener && 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);
+					break;
+			}
+		}
+		this.clear();
+
+		this.drainDisabled = false;
+	}
+
+	clear() {
+		this.objects.length = 0;
+	}
+}
+
+export enum EventType {
+	start, interrupt, end, dispose, complete, event
+}
+
+/** The interface to implement for receiving TrackEntry events. It is always safe to call AnimationState methods when receiving
+ * events.
+ *
+ * See TrackEntry {@link TrackEntry#listener} and AnimationState
+ * {@link AnimationState#addListener()}. */
+export interface AnimationStateListener {
+	/** 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 AnimationStateAdapter implements AnimationStateListener {
+	start(entry: TrackEntry) {
+	}
+
+	interrupt(entry: TrackEntry) {
+	}
+
+	end(entry: TrackEntry) {
+	}
+
+	dispose(entry: TrackEntry) {
+	}
+
+	complete(entry: TrackEntry) {
+	}
+
+	event(entry: TrackEntry, event: Event) {
+	}
+}
+
+/** 1. A previously applied timeline has set this property.
+ *
+ * Result: Mix from the current pose to the timeline pose. */
+export const SUBSEQUENT = 0;
+/** 1. This is the first timeline to set this property.
+ * 2. The next track entry applied after this one does not have a timeline to set this property.
+ *
+ * Result: Mix from the setup pose to the timeline pose. */
+export const FIRST = 1;
+/** 1) A previously applied timeline has set this property.<br>
+ * 2) The next track entry to be applied does have a timeline to set this property.<br>
+ * 3) The next track entry after that one does not have a timeline to set this property.<br>
+ * Result: Mix from the current pose to the timeline pose, but do not mix out. This avoids "dipping" when crossfading
+ * animations that key the same property. A subsequent timeline will set this property using a mix. */
+export const HOLD_SUBSEQUENT = 2;
+/** 1) This is the first timeline to set this property.<br>
+ * 2) The next track entry to be applied does have a timeline to set this property.<br>
+ * 3) The next track entry after that one does not have a timeline to set this property.<br>
+ * Result: Mix from the setup pose to the timeline pose, but do not mix out. This avoids "dipping" when crossfading animations
+ * that key the same property. A subsequent timeline will set this property using a mix. */
+export const HOLD_FIRST = 3;
+/** 1. This is the first timeline to set this property.
+ * 2. The next track entry to be applied does have a timeline to set this property.
+ * 3. The next track entry after that one does have a timeline to set this property.
+ * 4. timelineHoldMix stores the first subsequent track entry that does not have a timeline to set this property.
+ *
+ * Result: The same as HOLD except the mix percentage from the timelineHoldMix track entry is used. This handles when more than
+ * 2 track entries in a row have a timeline that sets the same property.
+ *
+ * Eg, A -> B -> C -> D where A, B, and C have a timeline setting same property, but D does not. When A is applied, to avoid
+ * "dipping" A is not mixed out, however D (the first entry that doesn't set the property) mixing in is used to mix out A
+ * (which affects B and C). Without using D to mix out, A would be applied fully until mixing completes, then snap into
+ * place. */
+export const HOLD_MIX = 4;
+
+export const SETUP = 1;
+export const CURRENT = 2;
+
+let _emptyAnimation: Animation = null;

+ 78 - 0
spine-ts/spine-core/src/AnimationStateData.ts

@@ -0,0 +1,78 @@
+/******************************************************************************
+ * Spine Runtimes License Agreement
+ * Last updated January 1, 2020. Replaces all prior versions.
+ *
+ * Copyright (c) 2013-2020, Esoteric Software LLC
+ *
+ * Integration of the Spine Runtimes into software or otherwise creating
+ * derivative works of the Spine Runtimes is permitted under the terms and
+ * conditions of Section 2 of the Spine Editor License Agreement:
+ * http://esotericsoftware.com/spine-editor-license
+ *
+ * Otherwise, it is permitted to integrate the Spine Runtimes into software
+ * or otherwise create derivative works of the Spine Runtimes (collectively,
+ * "Products"), provided that each user of the Products must obtain their own
+ * Spine Editor license and redistribution of the Products in any form must
+ * include this license and copyright notice.
+ *
+ * THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "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 LLC 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
+ * THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *****************************************************************************/
+
+import { Animation } from "./Animation";
+import { SkeletonData } from "./SkeletonData";
+import { StringMap } from "./Utils";
+
+
+/** Stores mix (crossfade) durations to be applied when {@link AnimationState} animations are changed. */
+export class AnimationStateData {
+	/** The SkeletonData to look up animations when they are specified by name. */
+	skeletonData: SkeletonData;
+
+	animationToMixTime: StringMap<number> = {};
+
+	/** The mix duration to use when no mix duration has been defined between two animations. */
+	defaultMix = 0;
+
+	constructor(skeletonData: SkeletonData) {
+		if (!skeletonData) throw new Error("skeletonData cannot be null.");
+		this.skeletonData = skeletonData;
+	}
+
+	/** Sets a mix duration by animation name.
+	 *
+	 * See {@link #setMixWith()}. */
+	setMix(fromName: string, toName: string, duration: number) {
+		let from = this.skeletonData.findAnimation(fromName);
+		if (!from) throw new Error("Animation not found: " + fromName);
+		let to = this.skeletonData.findAnimation(toName);
+		if (!to) throw new Error("Animation not found: " + toName);
+		this.setMixWith(from, to, duration);
+	}
+
+	/** Sets the mix duration when changing from the specified animation to the other.
+	 *
+	 * See {@link TrackEntry#mixDuration}. */
+	setMixWith(from: Animation, to: Animation, duration: number) {
+		if (!from) throw new Error("from cannot be null.");
+		if (!to) throw new Error("to cannot be null.");
+		let key = from.name + "." + to.name;
+		this.animationToMixTime[key] = duration;
+	}
+
+	/** Returns the mix duration to use when changing from the specified animation to the other, or the {@link #defaultMix} if
+	  * no mix duration has been set. */
+	getMix(from: Animation, to: Animation) {
+		let key = from.name + "." + to.name;
+		let value = this.animationToMixTime[key];
+		return value === undefined ? this.defaultMix : value;
+	}
+}

+ 286 - 0
spine-ts/spine-core/src/AssetManager.ts

@@ -0,0 +1,286 @@
+/******************************************************************************
+ * Spine Runtimes License Agreement
+ * Last updated January 1, 2020. Replaces all prior versions.
+ *
+ * Copyright (c) 2013-2020, Esoteric Software LLC
+ *
+ * Integration of the Spine Runtimes into software or otherwise creating
+ * derivative works of the Spine Runtimes is permitted under the terms and
+ * conditions of Section 2 of the Spine Editor License Agreement:
+ * http://esotericsoftware.com/spine-editor-license
+ *
+ * Otherwise, it is permitted to integrate the Spine Runtimes into software
+ * or otherwise create derivative works of the Spine Runtimes (collectively,
+ * "Products"), provided that each user of the Products must obtain their own
+ * Spine Editor license and redistribution of the Products in any form must
+ * include this license and copyright notice.
+ *
+ * THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "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 LLC 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
+ * THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *****************************************************************************/
+
+import { Texture } from "./Texture";
+import { TextureAtlas } from "./TextureAtlas";
+import { Disposable, StringMap } from "./Utils";
+
+export class AssetManager implements Disposable {
+	private pathPrefix: string;
+	private textureLoader: (image: HTMLImageElement | ImageBitmap) => Texture;
+	private downloader: Downloader;
+	private assets: StringMap<any> = {};
+	private errors: StringMap<string> = {};
+	private toLoad = 0;
+	private loaded = 0;
+
+	constructor(textureLoader: (image: HTMLImageElement | ImageBitmap) => Texture, pathPrefix: string = "", downloader: Downloader = null) {
+		this.textureLoader = textureLoader;
+		this.pathPrefix = pathPrefix;
+		this.downloader = downloader || new Downloader();
+	}
+
+	private start(path: string): string {
+		this.toLoad++;
+		return this.pathPrefix + path;
+	}
+
+	private success(callback: (path: string, data: any) => void, path: string, asset: any) {
+		this.toLoad--;
+		this.loaded++;
+		this.assets[path] = asset;
+		if (callback) callback(path, asset);
+	}
+
+	private error(callback: (path: string, message: string) => void, path: string, message: string) {
+		this.toLoad--;
+		this.loaded++;
+		this.errors[path] = message;
+		if (callback) callback(path, message);
+	}
+
+	setRawDataURI(path: string, data: string) {
+		this.downloader.rawDataUris[this.pathPrefix + path] = data;
+	}
+
+	loadBinary(path: string,
+		success: (path: string, binary: Uint8Array) => void = null,
+		error: (path: string, message: string) => void = null) {
+		path = this.start(path);
+
+		this.downloader.downloadBinary(path, (data: Uint8Array): void => {
+			this.success(success, path, data);
+		}, (status: number, responseText: string): void => {
+			this.error(error, path, `Couldn't load binary ${path}: status ${status}, ${responseText}`);
+		});
+	}
+
+	loadText(path: string,
+		success: (path: string, text: string) => void = null,
+		error: (path: string, message: string) => void = null) {
+		path = this.start(path);
+
+		this.downloader.downloadText(path, (data: string): void => {
+			this.success(success, path, data);
+		}, (status: number, responseText: string): void => {
+			this.error(error, path, `Couldn't load text ${path}: status ${status}, ${responseText}`);
+		});
+	}
+
+	loadJson(path: string,
+		success: (path: string, object: object) => void = null,
+		error: (path: string, message: string) => void = null) {
+		path = this.start(path);
+
+		this.downloader.downloadJson(path, (data: object): void => {
+			this.success(success, path, data);
+		}, (status: number, responseText: string): void => {
+			this.error(error, path, `Couldn't load JSON ${path}: status ${status}, ${responseText}`);
+		});
+	}
+
+	loadTexture(path: string,
+		success: (path: string, texture: Texture) => void = null,
+		error: (path: string, message: string) => void = null) {
+		path = this.start(path);
+
+		let isBrowser = !!(typeof window !== 'undefined' && typeof navigator !== 'undefined' && window.document);
+		let isWebWorker = !isBrowser && typeof importScripts !== 'undefined';
+		if (isWebWorker) {
+			fetch(path, { mode: <RequestMode>"cors" }).then((response) => {
+				if (response.ok) return response.blob();
+				this.error(error, path, `Couldn't load image: ${path}`);
+				return null;
+			}).then((blob) => {
+				return blob ? createImageBitmap(blob, { premultiplyAlpha: "none", colorSpaceConversion: "none" }) : null;
+			}).then((bitmap) => {
+				if (bitmap) this.success(success, path, this.textureLoader(bitmap));
+			});
+		} else {
+			let image = new Image();
+			image.crossOrigin = "anonymous";
+			image.onload = () => {
+				this.success(success, path, this.textureLoader(image));
+			};
+			image.onerror = () => {
+				this.error(error, path, `Couldn't load image: ${path}`);
+			};
+			if (this.downloader.rawDataUris[path]) path = this.downloader.rawDataUris[path];
+			image.src = path;
+		}
+	}
+
+	loadTextureAtlas(path: string,
+		success: (path: string, atlas: TextureAtlas) => void = null,
+		error: (path: string, message: string) => void = null
+	) {
+		let index = path.lastIndexOf("/");
+		let parent = index >= 0 ? path.substring(0, index + 1) : "";
+		path = this.start(path);
+
+		this.downloader.downloadText(path, (atlasText: string): void => {
+			try {
+				let atlas = new TextureAtlas(atlasText);
+				let toLoad = atlas.pages.length, abort = false;
+				for (let page of atlas.pages) {
+					this.loadTexture(parent + page.name,
+						(imagePath: string, texture: Texture) => {
+							if (!abort) {
+								page.setTexture(texture);
+								if (--toLoad == 0) this.success(success, path, atlas);
+							}
+						},
+						(imagePath: string, message: string) => {
+							if (!abort) this.error(error, path, `Couldn't load texture atlas ${path} page image: ${imagePath}`);
+							abort = true;
+						}
+					);
+				}
+			} catch (e) {
+				this.error(error, path, `Couldn't parse texture atlas ${path}: ${e.message}`);
+			}
+		}, (status: number, responseText: string): void => {
+			this.error(error, path, `Couldn't load texture atlas ${path}: status ${status}, ${responseText}`);
+		});
+	}
+
+	get(path: string) {
+		return this.assets[this.pathPrefix + path];
+	}
+
+	require(path: string) {
+		path = this.pathPrefix + path;
+		let asset = this.assets[path];
+		if (asset) return asset;
+		let error = this.errors[path];
+		throw Error("Asset not found: " + path + (error ? "\n" + error : ""));
+	}
+
+	remove(path: string) {
+		path = this.pathPrefix + path;
+		let asset = this.assets[path];
+		if ((<any>asset).dispose) (<any>asset).dispose();
+		delete this.assets[path];
+		return asset;
+	}
+
+	removeAll() {
+		for (let key in this.assets) {
+			let asset = this.assets[key];
+			if ((<any>asset).dispose) (<any>asset).dispose();
+		}
+		this.assets = {};
+	}
+
+	isLoadingComplete(): boolean {
+		return this.toLoad == 0;
+	}
+
+	getToLoad(): number {
+		return this.toLoad;
+	}
+
+	getLoaded(): number {
+		return this.loaded;
+	}
+
+	dispose() {
+		this.removeAll();
+	}
+
+	hasErrors() {
+		return Object.keys(this.errors).length > 0;
+	}
+
+	getErrors() {
+		return this.errors;
+	}
+}
+
+export class Downloader {
+	private callbacks: StringMap<Array<Function>> = {};
+	rawDataUris: StringMap<string> = {};
+
+	downloadText(url: string, success: (data: string) => void, error: (status: number, responseText: string) => void) {
+		if (this.rawDataUris[url]) url = this.rawDataUris[url];
+		if (this.start(url, success, error)) return;
+		let request = new XMLHttpRequest();
+		request.overrideMimeType("text/html");
+		request.open("GET", url, true);
+		let done = () => {
+			this.finish(url, request.status, request.responseText);
+		};
+		request.onload = done;
+		request.onerror = done;
+		request.send();
+	}
+
+	downloadJson(url: string, success: (data: object) => void, error: (status: number, responseText: string) => void) {
+		this.downloadText(url, (data: string): void => {
+			success(JSON.parse(data));
+		}, error);
+	}
+
+	downloadBinary(url: string, success: (data: Uint8Array) => void, error: (status: number, responseText: string) => void) {
+		if (this.rawDataUris[url]) url = this.rawDataUris[url];
+		if (this.start(url, success, error)) return;
+		let request = new XMLHttpRequest();
+		request.open("GET", url, true);
+		request.responseType = "arraybuffer";
+		let onerror = () => {
+			this.finish(url, request.status, request.responseText);
+		};
+		request.onload = () => {
+			if (request.status == 200)
+				this.finish(url, 200, new Uint8Array(request.response as ArrayBuffer));
+			else
+				onerror();
+		};
+		request.onerror = onerror;
+		request.send();
+	}
+
+	private start(url: string, success: any, error: any) {
+		let callbacks = this.callbacks[url];
+		try {
+			if (callbacks) return true;
+			this.callbacks[url] = callbacks = [];
+		} finally {
+			callbacks.push(success, error);
+		}
+	}
+
+	private finish(url: string, status: number, data: any) {
+		let callbacks = this.callbacks[url];
+		delete this.callbacks[url];
+		let args = status == 200 ? [data] : [status, data];
+		for (let i = args.length - 1, n = callbacks.length; i < n; i += 2)
+			callbacks[i].apply(null, args);
+	}
+}

+ 84 - 0
spine-ts/spine-core/src/AtlasAttachmentLoader.ts

@@ -0,0 +1,84 @@
+/******************************************************************************
+ * Spine Runtimes License Agreement
+ * Last updated January 1, 2020. Replaces all prior versions.
+ *
+ * Copyright (c) 2013-2020, Esoteric Software LLC
+ *
+ * Integration of the Spine Runtimes into software or otherwise creating
+ * derivative works of the Spine Runtimes is permitted under the terms and
+ * conditions of Section 2 of the Spine Editor License Agreement:
+ * http://esotericsoftware.com/spine-editor-license
+ *
+ * Otherwise, it is permitted to integrate the Spine Runtimes into software
+ * or otherwise create derivative works of the Spine Runtimes (collectively,
+ * "Products"), provided that each user of the Products must obtain their own
+ * Spine Editor license and redistribution of the Products in any form must
+ * include this license and copyright notice.
+ *
+ * THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "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 LLC 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
+ * THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *****************************************************************************/
+
+import { AttachmentLoader } from "./attachments/AttachmentLoader";
+import { BoundingBoxAttachment } from "./attachments/BoundingBoxAttachment";
+import { ClippingAttachment } from "./attachments/ClippingAttachment";
+import { MeshAttachment } from "./attachments/MeshAttachment";
+import { PathAttachment } from "./attachments/PathAttachment";
+import { PointAttachment } from "./attachments/PointAttachment";
+import { RegionAttachment } from "./attachments/RegionAttachment";
+import { Skin } from "./Skin";
+import { TextureAtlas } from "./TextureAtlas";
+
+/** An {@link AttachmentLoader} that configures attachments using texture regions from an {@link TextureAtlas}.
+ *
+ * See [Loading skeleton data](http://esotericsoftware.com/spine-loading-skeleton-data#JSON-and-binary-data) in the
+ * Spine Runtimes Guide. */
+export class AtlasAttachmentLoader implements AttachmentLoader {
+	atlas: TextureAtlas;
+
+	constructor(atlas: TextureAtlas) {
+		this.atlas = atlas;
+	}
+
+	newRegionAttachment(skin: Skin, name: string, path: string): RegionAttachment {
+		let region = this.atlas.findRegion(path);
+		if (!region) throw new Error("Region not found in atlas: " + path + " (region attachment: " + name + ")");
+		region.renderObject = region;
+		let attachment = new RegionAttachment(name);
+		attachment.setRegion(region);
+		return attachment;
+	}
+
+	newMeshAttachment(skin: Skin, name: string, path: string): MeshAttachment {
+		let region = this.atlas.findRegion(path);
+		if (!region) throw new Error("Region not found in atlas: " + path + " (mesh attachment: " + name + ")");
+		region.renderObject = region;
+		let attachment = new MeshAttachment(name);
+		attachment.region = region;
+		return attachment;
+	}
+
+	newBoundingBoxAttachment(skin: Skin, name: string): BoundingBoxAttachment {
+		return new BoundingBoxAttachment(name);
+	}
+
+	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);
+	}
+}

+ 380 - 0
spine-ts/spine-core/src/Bone.ts

@@ -0,0 +1,380 @@
+/******************************************************************************
+ * Spine Runtimes License Agreement
+ * Last updated January 1, 2020. Replaces all prior versions.
+ *
+ * Copyright (c) 2013-2020, Esoteric Software LLC
+ *
+ * Integration of the Spine Runtimes into software or otherwise creating
+ * derivative works of the Spine Runtimes is permitted under the terms and
+ * conditions of Section 2 of the Spine Editor License Agreement:
+ * http://esotericsoftware.com/spine-editor-license
+ *
+ * Otherwise, it is permitted to integrate the Spine Runtimes into software
+ * or otherwise create derivative works of the Spine Runtimes (collectively,
+ * "Products"), provided that each user of the Products must obtain their own
+ * Spine Editor license and redistribution of the Products in any form must
+ * include this license and copyright notice.
+ *
+ * THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "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 LLC 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
+ * THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *****************************************************************************/
+
+import { BoneData, TransformMode } from "./BoneData";
+import { Skeleton } from "./Skeleton";
+import { Updatable } from "./Updatable";
+import { MathUtils, Vector2 } from "./Utils";
+
+/** Stores a bone's current pose.
+ *
+ * A bone has a local transform which is used to compute its world transform. A bone also has an applied transform, which is a
+ * local transform that can be applied to compute the world transform. The local transform and applied transform may differ if a
+ * constraint or application code modifies the world transform after it was computed from the local transform. */
+export class Bone implements Updatable {
+	/** The bone's setup pose data. */
+	data: BoneData;
+
+	/** The skeleton this bone belongs to. */
+	skeleton: Skeleton;
+
+	/** The parent bone, or null if this is the root bone. */
+	parent: Bone;
+
+	/** The immediate children of this bone. */
+	children = new Array<Bone>();
+
+	/** The local x translation. */
+	x = 0;
+
+	/** The local y translation. */
+	y = 0;
+
+	/** The local rotation in degrees, counter clockwise. */
+	rotation = 0;
+
+	/** The local scaleX. */
+	scaleX = 0;
+
+	/** The local scaleY. */
+	scaleY = 0;
+
+	/** The local shearX. */
+	shearX = 0;
+
+	/** The local shearY. */
+	shearY = 0;
+
+	/** The applied local x translation. */
+	ax = 0;
+
+	/** The applied local y translation. */
+	ay = 0;
+
+	/** The applied local rotation in degrees, counter clockwise. */
+	arotation = 0;
+
+	/** The applied local scaleX. */
+	ascaleX = 0;
+
+	/** The applied local scaleY. */
+	ascaleY = 0;
+
+	/** The applied local shearX. */
+	ashearX = 0;
+
+	/** The applied local shearY. */
+	ashearY = 0;
+
+	/** Part of the world transform matrix for the X axis. If changed, {@link #updateAppliedTransform()} should be called. */
+	a = 0;
+
+	/** Part of the world transform matrix for the Y axis. If changed, {@link #updateAppliedTransform()} should be called. */
+	b = 0;
+
+	/** Part of the world transform matrix for the X axis. If changed, {@link #updateAppliedTransform()} should be called. */
+	c = 0;
+
+	/** Part of the world transform matrix for the Y axis. If changed, {@link #updateAppliedTransform()} should be called. */
+	d = 0;
+
+	/** The world X position. If changed, {@link #updateAppliedTransform()} should be called. */
+	worldY = 0;
+
+	/** The world Y position. If changed, {@link #updateAppliedTransform()} should be called. */
+	worldX = 0;
+
+	sorted = false;
+	active = false;
+
+	/** @param parent May be null. */
+	constructor(data: BoneData, skeleton: Skeleton, parent: Bone) {
+		if (!data) throw new Error("data cannot be null.");
+		if (!skeleton) throw new Error("skeleton cannot be null.");
+		this.data = data;
+		this.skeleton = skeleton;
+		this.parent = parent;
+		this.setToSetupPose();
+	}
+
+	/** Returns false when the bone has not been computed because {@link BoneData#skinRequired} is true and the
+	  * {@link Skeleton#skin active skin} does not {@link Skin#bones contain} this bone. */
+	isActive() {
+		return this.active;
+	}
+
+	/** Computes the world transform using the parent bone and this bone's local applied transform. */
+	update() {
+		this.updateWorldTransformWith(this.ax, this.ay, this.arotation, this.ascaleX, this.ascaleY, this.ashearX, this.ashearY);
+	}
+
+	/** Computes the world transform using the parent bone and this bone's local transform.
+	 *
+	 * See {@link #updateWorldTransformWith()}. */
+	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. The applied transform is set to the
+	 * specified local transform. Child bones are not updated.
+	 *
+	 * See [World transforms](http://esotericsoftware.com/spine-runtime-skeletons#World-transforms) in the Spine
+	 * Runtimes Guide. */
+	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;
+
+		let parent = this.parent;
+		if (!parent) { // Root bone.
+			let skeleton = this.skeleton;
+			let rotationY = rotation + 90 + shearY;
+			let sx = skeleton.scaleX;
+			let sy = skeleton.scaleY;
+			this.a = MathUtils.cosDeg(rotation + shearX) * scaleX * sx;
+			this.b = MathUtils.cosDeg(rotationY) * scaleY * sx;
+			this.c = MathUtils.sinDeg(rotation + shearX) * scaleX * sy;
+			this.d = MathUtils.sinDeg(rotationY) * scaleY * sy;
+			this.worldX = x * sx + skeleton.x;
+			this.worldY = y * sy + skeleton.y;
+			return;
+		}
+
+		let pa = parent.a, pb = parent.b, pc = parent.c, pd = parent.d;
+		this.worldX = pa * x + pb * y + parent.worldX;
+		this.worldY = pc * x + pd * y + parent.worldY;
+
+		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;
+				this.a = pa * la + pb * lc;
+				this.b = pa * lb + pb * ld;
+				this.c = pc * la + pd * lc;
+				this.d = pc * lb + pd * ld;
+				return;
+			}
+			case TransformMode.OnlyTranslation: {
+				let rotationY = rotation + 90 + shearY;
+				this.a = MathUtils.cosDeg(rotation + shearX) * scaleX;
+				this.b = MathUtils.cosDeg(rotationY) * scaleY;
+				this.c = MathUtils.sinDeg(rotation + shearX) * scaleX;
+				this.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;
+					pa /= this.skeleton.scaleX;
+					pc /= this.skeleton.scaleY;
+					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;
+				this.a = pa * la - pb * lc;
+				this.b = pa * lb - pb * ld;
+				this.c = pc * la + pd * lc;
+				this.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) / this.skeleton.scaleX;
+				let zc = (pc * cos + pd * sin) / this.skeleton.scaleY;
+				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);
+				if (this.data.transformMode == TransformMode.NoScale
+					&& (pa * pd - pb * pc < 0) != (this.skeleton.scaleX < 0 != this.skeleton.scaleY < 0)) s = -s;
+				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;
+				this.a = za * la + zb * lc;
+				this.b = za * lb + zb * ld;
+				this.c = zc * la + zd * lc;
+				this.d = zc * lb + zd * ld;
+				break;
+			}
+		}
+		this.a *= this.skeleton.scaleX;
+		this.b *= this.skeleton.scaleX;
+		this.c *= this.skeleton.scaleY;
+		this.d *= this.skeleton.scaleY;
+	}
+
+	/** Sets this bone's local transform to the setup pose. */
+	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;
+	}
+
+	/** The world rotation for the X axis, calculated using {@link #a} and {@link #c}. */
+	getWorldRotationX() {
+		return Math.atan2(this.c, this.a) * MathUtils.radDeg;
+	}
+
+	/** The world rotation for the Y axis, calculated using {@link #b} and {@link #d}. */
+	getWorldRotationY() {
+		return Math.atan2(this.d, this.b) * MathUtils.radDeg;
+	}
+
+	/** The magnitude (always positive) of the world scale X, calculated using {@link #a} and {@link #c}. */
+	getWorldScaleX() {
+		return Math.sqrt(this.a * this.a + this.c * this.c);
+	}
+
+	/** The magnitude (always positive) of the world scale Y, calculated using {@link #b} and {@link #d}. */
+	getWorldScaleY() {
+		return Math.sqrt(this.b * this.b + this.d * this.d);
+	}
+
+	/** Computes the applied transform values from the world transform.
+	 *
+	 * If the world transform is modified (by a constraint, {@link #rotateWorld(float)}, etc) then this method should be called so
+	 * the applied transform matches the world transform. The applied transform may be needed by other code (eg to apply other
+	 * constraints).
+	 *
+	 * Some information is ambiguous in the world transform, such as -1,-1 scale versus 180 rotation. The applied transform after
+	 * calling this method is equivalent to the local transform used to compute the world transform, but may not be identical. */
+	updateAppliedTransform() {
+		let parent = this.parent;
+		if (!parent) {
+			this.ax = this.worldX;
+			this.ay = this.worldY;
+			this.arotation = Math.atan2(this.c, this.a) * MathUtils.radDeg;
+			this.ascaleX = Math.sqrt(this.a * this.a + this.c * this.c);
+			this.ascaleY = Math.sqrt(this.b * this.b + this.d * this.d);
+			this.ashearX = 0;
+			this.ashearY = Math.atan2(this.a * this.b + this.c * this.d, this.a * this.d - this.b * this.c) * MathUtils.radDeg;
+			return;
+		}
+		let pa = parent.a, pb = parent.b, pc = parent.c, pd = parent.d;
+		let pid = 1 / (pa * pd - pb * pc);
+		let dx = this.worldX - parent.worldX, dy = this.worldY - parent.worldY;
+		this.ax = (dx * pd * pid - dy * pb * pid);
+		this.ay = (dy * pa * pid - dx * pc * pid);
+		let ia = pid * pd;
+		let id = pid * pa;
+		let ib = pid * pb;
+		let ic = pid * pc;
+		let ra = ia * this.a - ib * this.c;
+		let rb = ia * this.b - ib * this.d;
+		let rc = id * this.c - ic * this.a;
+		let rd = id * this.d - ic * this.b;
+		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;
+		}
+	}
+
+	/** Transforms a point from world coordinates to the bone's local coordinates. */
+	worldToLocal(world: Vector2) {
+		let invDet = 1 / (this.a * this.d - this.b * this.c);
+		let x = world.x - this.worldX, y = world.y - this.worldY;
+		world.x = x * this.d * invDet - y * this.b * invDet;
+		world.y = y * this.a * invDet - x * this.c * invDet;
+		return world;
+	}
+
+	/** Transforms a point from the bone's local coordinates to world coordinates. */
+	localToWorld(local: Vector2) {
+		let x = local.x, y = local.y;
+		local.x = x * this.a + y * this.b + this.worldX;
+		local.y = x * this.c + y * this.d + this.worldY;
+		return local;
+	}
+
+	/** Transforms a world rotation to a local rotation. */
+	worldToLocalRotation(worldRotation: number) {
+		let sin = MathUtils.sinDeg(worldRotation), cos = MathUtils.cosDeg(worldRotation);
+		return Math.atan2(this.a * sin - this.c * cos, this.d * cos - this.b * sin) * MathUtils.radDeg + this.rotation - this.shearX;
+	}
+
+	/** Transforms a local rotation to a world rotation. */
+	localToWorldRotation(localRotation: number) {
+		localRotation -= this.rotation - this.shearX;
+		let sin = MathUtils.sinDeg(localRotation), cos = MathUtils.cosDeg(localRotation);
+		return Math.atan2(cos * this.c + sin * this.d, cos * this.a + sin * this.b) * MathUtils.radDeg;
+	}
+
+	/** Rotates the world transform the specified amount.
+	 * <p>
+	 * After changes are made to the world transform, {@link #updateAppliedTransform()} should be called and {@link #update()} will
+	 * need to be called on any child bones, recursively. */
+	rotateWorld(degrees: number) {
+		let a = this.a, b = this.b, c = this.c, d = this.d;
+		let cos = MathUtils.cosDeg(degrees), sin = MathUtils.sinDeg(degrees);
+		this.a = cos * a - sin * c;
+		this.b = cos * b - sin * d;
+		this.c = sin * a + cos * c;
+		this.d = sin * b + cos * d;
+	}
+}

+ 89 - 0
spine-ts/spine-core/src/BoneData.ts

@@ -0,0 +1,89 @@
+/******************************************************************************
+ * Spine Runtimes License Agreement
+ * Last updated January 1, 2020. Replaces all prior versions.
+ *
+ * Copyright (c) 2013-2020, Esoteric Software LLC
+ *
+ * Integration of the Spine Runtimes into software or otherwise creating
+ * derivative works of the Spine Runtimes is permitted under the terms and
+ * conditions of Section 2 of the Spine Editor License Agreement:
+ * http://esotericsoftware.com/spine-editor-license
+ *
+ * Otherwise, it is permitted to integrate the Spine Runtimes into software
+ * or otherwise create derivative works of the Spine Runtimes (collectively,
+ * "Products"), provided that each user of the Products must obtain their own
+ * Spine Editor license and redistribution of the Products in any form must
+ * include this license and copyright notice.
+ *
+ * THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "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 LLC 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
+ * THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *****************************************************************************/
+
+import { Color } from "./Utils";
+
+/** Stores the setup pose for a {@link Bone}. */
+export class BoneData {
+	/** The index of the bone in {@link Skeleton#getBones()}. */
+	index: number;
+
+	/** The name of the bone, which is unique across all bones in the skeleton. */
+	name: string;
+
+	/** @returns May be null. */
+	parent: BoneData;
+
+	/** The bone's length. */
+	length: number;
+
+	/** The local x translation. */
+	x = 0;
+
+	/** The local y translation. */
+	y = 0;
+
+	/** The local rotation. */
+	rotation = 0;
+
+	/** The local scaleX. */
+	scaleX = 1;
+
+	/** The local scaleY. */
+	scaleY = 1;
+
+	/** The local shearX. */
+	shearX = 0;
+
+	/** The local shearX. */
+	shearY = 0;
+
+	/** The transform mode for how parent world transforms affect this bone. */
+	transformMode = TransformMode.Normal;
+
+	/** When true, {@link Skeleton#updateWorldTransform()} only updates this bone if the {@link Skeleton#skin} contains this
+	  * bone.
+	  * @see Skin#bones */
+	skinRequired = false;
+
+	/** The color of the bone as it was in Spine. Available only when nonessential data was exported. Bones are not usually
+	 * rendered at runtime. */
+	color = new Color();
+
+	constructor(index: number, name: string, parent: BoneData) {
+		if (index < 0) throw new Error("index must be >= 0.");
+		if (!name) throw new Error("name cannot be null.");
+		this.index = index;
+		this.name = name;
+		this.parent = parent;
+	}
+}
+
+/** Determines how a bone inherits world transforms from parent bones. */
+export enum TransformMode { Normal, OnlyTranslation, NoRotationOrReflection, NoScale, NoScaleOrReflection }

+ 3 - 5
spine-ts/core/src/ConstraintData.ts → spine-ts/spine-core/src/ConstraintData.ts

@@ -27,9 +27,7 @@
  * THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  *****************************************************************************/
 
-module spine {
-	/** The base class for all constraint datas. */
-	export abstract class ConstraintData {
-		constructor (public name: string, public order: number, public skinRequired: boolean) { }
-	}
+/** The base class for all constraint datas. */
+export abstract class ConstraintData {
+	constructor(public name: string, public order: number, public skinRequired: boolean) { }
 }

+ 18 - 19
spine-ts/core/src/Event.ts → spine-ts/spine-core/src/Event.ts

@@ -27,26 +27,25 @@
  * THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  *****************************************************************************/
 
-module spine {
+import { EventData } from "./EventData";
 
-	/** Stores the current pose values for an {@link Event}.
-	 *
-	 * See Timeline {@link Timeline#apply()},
-	 * AnimationStateListener {@link AnimationStateListener#event()}, and
-	 * [Events](http://esotericsoftware.com/spine-events) in the Spine User Guide. */
-	export class Event {
-		data: EventData;
-		intValue: number;
-		floatValue: number;
-		stringValue: string;
-		time: number;
-		volume: number;
-		balance: number;
+/** Stores the current pose values for an {@link Event}.
+ *
+ * See Timeline {@link Timeline#apply()},
+ * AnimationStateListener {@link AnimationStateListener#event()}, and
+ * [Events](http://esotericsoftware.com/spine-events) in the Spine User Guide. */
+export class Event {
+	data: EventData;
+	intValue: number;
+	floatValue: number;
+	stringValue: string;
+	time: number;
+	volume: number;
+	balance: number;
 
-		constructor (time: number, data: EventData) {
-			if (!data) throw new Error("data cannot be null.");
-			this.time = time;
-			this.data = data;
-		}
+	constructor(time: number, data: EventData) {
+		if (!data) throw new Error("data cannot be null.");
+		this.time = time;
+		this.data = data;
 	}
 }

+ 13 - 15
spine-ts/core/src/EventData.ts → spine-ts/spine-core/src/EventData.ts

@@ -27,21 +27,19 @@
  * THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  *****************************************************************************/
 
-module spine {
-	/** Stores the setup pose values for an {@link Event}.
-	 *
-	 * See [Events](http://esotericsoftware.com/spine-events) in the Spine User Guide. */
-	export class EventData {
-		name: string;
-		intValue: number;
-		floatValue: number;
-		stringValue: string;
-		audioPath: string;
-		volume: number;
-		balance: number;
+/** Stores the setup pose values for an {@link Event}.
+ *
+ * See [Events](http://esotericsoftware.com/spine-events) in the Spine User Guide. */
+export class EventData {
+	name: string;
+	intValue: number;
+	floatValue: number;
+	stringValue: string;
+	audioPath: string;
+	volume: number;
+	balance: number;
 
-		constructor (name: string) {
-			this.name = name;
-		}
+	constructor(name: string) {
+		this.name = name;
 	}
 }

+ 298 - 0
spine-ts/spine-core/src/IkConstraint.ts

@@ -0,0 +1,298 @@
+/******************************************************************************
+ * Spine Runtimes License Agreement
+ * Last updated January 1, 2020. Replaces all prior versions.
+ *
+ * Copyright (c) 2013-2020, Esoteric Software LLC
+ *
+ * Integration of the Spine Runtimes into software or otherwise creating
+ * derivative works of the Spine Runtimes is permitted under the terms and
+ * conditions of Section 2 of the Spine Editor License Agreement:
+ * http://esotericsoftware.com/spine-editor-license
+ *
+ * Otherwise, it is permitted to integrate the Spine Runtimes into software
+ * or otherwise create derivative works of the Spine Runtimes (collectively,
+ * "Products"), provided that each user of the Products must obtain their own
+ * Spine Editor license and redistribution of the Products in any form must
+ * include this license and copyright notice.
+ *
+ * THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "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 LLC 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
+ * THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *****************************************************************************/
+
+import { Bone } from "./Bone";
+import { TransformMode } from "./BoneData";
+import { IkConstraintData } from "./IkConstraintData";
+import { Skeleton } from "./Skeleton";
+import { Updatable } from "./Updatable";
+import { MathUtils } from "./Utils";
+
+/** Stores the current pose for an IK constraint. An IK constraint adjusts the rotation of 1 or 2 constrained bones so the tip of
+ * the last bone is as close to the target bone as possible.
+ *
+ * See [IK constraints](http://esotericsoftware.com/spine-ik-constraints) in the Spine User Guide. */
+export class IkConstraint implements Updatable {
+	/** The IK constraint's setup pose data. */
+	data: IkConstraintData;
+
+	/** The bones that will be modified by this IK constraint. */
+	bones: Array<Bone>;
+
+	/** The bone that is the IK target. */
+	target: Bone;
+
+	/** Controls the bend direction of the IK bones, either 1 or -1. */
+	bendDirection = 0;
+
+	/** When true and only a single bone is being constrained, if the target is too close, the bone is scaled to reach it. */
+	compress = false;
+
+	/** When true, if the target is out of range, the parent bone is scaled to reach it. If more than one bone is being constrained
+	 * and the parent bone has local nonuniform scale, stretch is not applied. */
+	stretch = false;
+
+	/** A percentage (0-1) that controls the mix between the constrained and unconstrained rotations. */
+	mix = 1;
+
+	/** For two bone IK, the distance from the maximum reach of the bones that rotation will slow. */
+	softness = 0;
+	active = false;
+
+	constructor(data: IkConstraintData, skeleton: Skeleton) {
+		if (!data) throw new Error("data cannot be null.");
+		if (!skeleton) throw new Error("skeleton cannot be null.");
+		this.data = data;
+		this.mix = data.mix;
+		this.softness = data.softness;
+		this.bendDirection = data.bendDirection;
+		this.compress = data.compress;
+		this.stretch = data.stretch;
+
+		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);
+	}
+
+	isActive() {
+		return this.active;
+	}
+
+	update() {
+		if (this.mix == 0) return;
+		let target = this.target;
+		let bones = this.bones;
+		switch (bones.length) {
+			case 1:
+				this.apply1(bones[0], target.worldX, target.worldY, this.compress, this.stretch, this.data.uniform, this.mix);
+				break;
+			case 2:
+				this.apply2(bones[0], bones[1], target.worldX, target.worldY, this.bendDirection, this.stretch, this.data.uniform, this.softness, this.mix);
+				break;
+		}
+	}
+
+	/** Applies 1 bone IK. The target is specified in the world coordinate system. */
+	apply1(bone: Bone, targetX: number, targetY: number, compress: boolean, stretch: boolean, uniform: boolean, alpha: number) {
+		let p = bone.parent;
+		let pa = p.a, pb = p.b, pc = p.c, pd = p.d;
+		let rotationIK = -bone.ashearX - bone.arotation, tx = 0, ty = 0;
+
+		switch (bone.data.transformMode) {
+			case TransformMode.OnlyTranslation:
+				tx = targetX - bone.worldX;
+				ty = targetY - bone.worldY;
+				break;
+			case TransformMode.NoRotationOrReflection:
+				let s = Math.abs(pa * pd - pb * pc) / (pa * pa + pc * pc);
+				let sa = pa / bone.skeleton.scaleX;
+				let sc = pc / bone.skeleton.scaleY;
+				pb = -sc * s * bone.skeleton.scaleX;
+				pd = sa * s * bone.skeleton.scaleY;
+				rotationIK += Math.atan2(sc, sa) * MathUtils.radDeg;
+			// Fall through
+			default:
+				let x = targetX - p.worldX, y = targetY - p.worldY;
+				let d = pa * pd - pb * pc;
+				tx = (x * pd - y * pb) / d - bone.ax;
+				ty = (y * pa - x * pc) / d - bone.ay;
+		}
+		rotationIK += Math.atan2(ty, tx) * MathUtils.radDeg;
+		if (bone.ascaleX < 0) rotationIK += 180;
+		if (rotationIK > 180)
+			rotationIK -= 360;
+		else if (rotationIK < -180)
+			rotationIK += 360;
+		let sx = bone.ascaleX, sy = bone.ascaleY;
+		if (compress || stretch) {
+			switch (bone.data.transformMode) {
+				case TransformMode.NoScale:
+				case TransformMode.NoScaleOrReflection:
+					tx = targetX - bone.worldX;
+					ty = targetY - bone.worldY;
+			}
+			let b = bone.data.length * sx, dd = Math.sqrt(tx * tx + ty * ty);
+			if ((compress && dd < b) || (stretch && dd > b) && b > 0.0001) {
+				let s = (dd / b - 1) * alpha + 1;
+				sx *= s;
+				if (uniform) sy *= s;
+			}
+		}
+		bone.updateWorldTransformWith(bone.ax, bone.ay, bone.arotation + rotationIK * alpha, sx, sy, bone.ashearX,
+			bone.ashearY);
+	}
+
+	/** Applies 2 bone IK. 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, stretch: boolean, uniform: boolean, softness: number, alpha: number) {
+		let px = parent.ax, py = parent.ay, psx = parent.ascaleX, psy = parent.ascaleY, sx = psx, sy = psy, csx = child.ascaleX;
+		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 = parent.a, b = parent.b, c = parent.c, d = parent.d;
+		let u = Math.abs(psx - psy) <= 0.0001;
+		if (!u || stretch) {
+			cy = 0;
+			cwx = a * cx + parent.worldX;
+			cwy = c * cx + parent.worldY;
+		} else {
+			cy = child.ay;
+			cwx = a * cx + b * cy + parent.worldX;
+			cwy = c * cx + d * cy + parent.worldY;
+		}
+		let pp = parent.parent;
+		a = pp.a;
+		b = pp.b;
+		c = pp.c;
+		d = pp.d;
+		let id = 1 / (a * d - b * c), x = cwx - pp.worldX, y = cwy - pp.worldY;
+		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, a2;
+		if (l1 < 0.0001) {
+			this.apply1(parent, targetX, targetY, false, stretch, false, alpha);
+			child.updateWorldTransformWith(cx, cy, 0, child.ascaleX, child.ascaleY, child.ashearX, child.ashearY);
+			return;
+		}
+		x = targetX - pp.worldX;
+		y = targetY - pp.worldY;
+		let tx = (x * d - y * b) * id - px, ty = (y * a - x * c) * id - py;
+		let dd = tx * tx + ty * ty;
+		if (softness != 0) {
+			softness *= psx * (csx + 1) * 0.5;
+			let td = Math.sqrt(dd), sd = td - l1 - l2 * psx + softness;
+			if (sd > 0) {
+				let p = Math.min(1, sd / (softness * 2)) - 1;
+				p = (sd - softness * (1 - p * p)) / td;
+				tx -= p * tx;
+				ty -= p * ty;
+				dd = tx * tx + ty * ty;
+			}
+		}
+		outer:
+		if (u) {
+			l2 *= psx;
+			let cos = (dd - l1 * l1 - l2 * l2) / (2 * l1 * l2);
+			if (cos < -1) {
+				cos = -1;
+				a2 = Math.PI * bendDir;
+			} else if (cos > 1) {
+				cos = 1;
+				a2 = 0;
+				if (stretch) {
+					a = (Math.sqrt(dd) / (l1 + l2) - 1) * alpha + 1;
+					sx *= a;
+					if (uniform) sy *= a;
+				}
+			} else
+				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, 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) * 0.5;
+				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) * 0.5) {
+				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, sx, sy, 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);
+	}
+}

+ 27 - 26
spine-ts/core/src/IkConstraintData.ts → spine-ts/spine-core/src/IkConstraintData.ts

@@ -27,40 +27,41 @@
  * THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  *****************************************************************************/
 
-module spine {
+import { BoneData } from "./BoneData";
+import { ConstraintData } from "./ConstraintData";
 
-	/** Stores the setup pose for an {@link IkConstraint}.
-	 * <p>
-	 * See [IK constraints](http://esotericsoftware.com/spine-ik-constraints) in the Spine User Guide. */
-	export class IkConstraintData extends ConstraintData {
-		/** The bones that are constrained by this IK constraint. */
-		bones = new Array<BoneData>();
 
-		/** The bone that is the IK target. */
-		target: BoneData;
+/** Stores the setup pose for an {@link IkConstraint}.
+ * <p>
+ * See [IK constraints](http://esotericsoftware.com/spine-ik-constraints) in the Spine User Guide. */
+export class IkConstraintData extends ConstraintData {
+	/** The bones that are constrained by this IK constraint. */
+	bones = new Array<BoneData>();
 
-		/** Controls the bend direction of the IK bones, either 1 or -1. */
-		bendDirection = 1;
+	/** The bone that is the IK target. */
+	target: BoneData;
 
-		/** When true and only a single bone is being constrained, if the target is too close, the bone is scaled to reach it. */
-		compress = false;
+	/** Controls the bend direction of the IK bones, either 1 or -1. */
+	bendDirection = 1;
 
-		/** When true, if the target is out of range, the parent bone is scaled to reach it. If more than one bone is being constrained
-		 * and the parent bone has local nonuniform scale, stretch is not applied. */
-		stretch = false;
+	/** When true and only a single bone is being constrained, if the target is too close, the bone is scaled to reach it. */
+	compress = false;
 
-		/** When true, only a single bone is being constrained, and {@link #getCompress()} or {@link #getStretch()} is used, the bone
-		 * is scaled on both the X and Y axes. */
-		uniform = false;
+	/** When true, if the target is out of range, the parent bone is scaled to reach it. If more than one bone is being constrained
+	 * and the parent bone has local nonuniform scale, stretch is not applied. */
+	stretch = false;
 
-		/** A percentage (0-1) that controls the mix between the constrained and unconstrained rotations. */
-		mix = 1;
+	/** When true, only a single bone is being constrained, and {@link #getCompress()} or {@link #getStretch()} is used, the bone
+	 * is scaled on both the X and Y axes. */
+	uniform = false;
 
-		/** For two bone IK, the distance from the maximum reach of the bones that rotation will slow. */
-		softness = 0;
+	/** A percentage (0-1) that controls the mix between the constrained and unconstrained rotations. */
+	mix = 1;
 
-		constructor (name: string) {
-			super(name, 0, false);
-		}
+	/** For two bone IK, the distance from the maximum reach of the bones that rotation will slow. */
+	softness = 0;
+
+	constructor(name: string) {
+		super(name, 0, false);
 	}
 }

+ 489 - 0
spine-ts/spine-core/src/PathConstraint.ts

@@ -0,0 +1,489 @@
+/******************************************************************************
+ * Spine Runtimes License Agreement
+ * Last updated January 1, 2020. Replaces all prior versions.
+ *
+ * Copyright (c) 2013-2020, Esoteric Software LLC
+ *
+ * Integration of the Spine Runtimes into software or otherwise creating
+ * derivative works of the Spine Runtimes is permitted under the terms and
+ * conditions of Section 2 of the Spine Editor License Agreement:
+ * http://esotericsoftware.com/spine-editor-license
+ *
+ * Otherwise, it is permitted to integrate the Spine Runtimes into software
+ * or otherwise create derivative works of the Spine Runtimes (collectively,
+ * "Products"), provided that each user of the Products must obtain their own
+ * Spine Editor license and redistribution of the Products in any form must
+ * include this license and copyright notice.
+ *
+ * THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "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 LLC 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
+ * THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *****************************************************************************/
+
+import { PathAttachment } from "./attachments/PathAttachment";
+import { Bone } from "./Bone";
+import { PathConstraintData, RotateMode, SpacingMode, PositionMode } from "./PathConstraintData";
+import { Skeleton } from "./Skeleton";
+import { Slot } from "./Slot";
+import { Updatable } from "./Updatable";
+import { Utils, MathUtils } from "./Utils";
+
+
+/** Stores the current pose for a path constraint. A path constraint adjusts the rotation, translation, and scale of the
+ * constrained bones so they follow a {@link PathAttachment}.
+ *
+ * See [Path constraints](http://esotericsoftware.com/spine-path-constraints) in the Spine User Guide. */
+export class PathConstraint implements Updatable {
+	static NONE = -1; static BEFORE = -2; static AFTER = -3;
+	static epsilon = 0.00001;
+
+	/** The path constraint's setup pose data. */
+	data: PathConstraintData;
+
+	/** The bones that will be modified by this path constraint. */
+	bones: Array<Bone>;
+
+	/** The slot whose path attachment will be used to constrained the bones. */
+	target: Slot;
+
+	/** The position along the path. */
+	position = 0;
+
+	/** The spacing between bones. */
+	spacing = 0;
+
+	mixRotate = 0;
+
+	mixX = 0;
+
+	mixY = 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>();
+
+	active = false;
+
+	constructor(data: PathConstraintData, skeleton: Skeleton) {
+		if (!data) throw new Error("data cannot be null.");
+		if (!skeleton) 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.mixRotate = data.mixRotate;
+		this.mixX = data.mixX;
+		this.mixY = data.mixY;
+	}
+
+	isActive() {
+		return this.active;
+	}
+
+	update() {
+		let attachment = this.target.getAttachment();
+		if (!(attachment instanceof PathAttachment)) return;
+
+		let mixRotate = this.mixRotate, mixX = this.mixX, mixY = this.mixY;
+		if (mixRotate == 0 && mixX == 0 && mixY == 0) return;
+
+		let data = this.data;
+		let tangents = data.rotateMode == RotateMode.Tangent, scale = data.rotateMode == RotateMode.ChainScale;
+
+		let bones = this.bones;
+		let boneCount = bones.length, spacesCount = tangents ? boneCount : boneCount + 1;
+		let spaces = Utils.setArraySize(this.spaces, spacesCount), lengths: Array<number> = scale ? this.lengths = Utils.setArraySize(this.lengths, boneCount) : null;
+		let spacing = this.spacing;
+
+		switch (data.spacingMode) {
+			case SpacingMode.Percent:
+				if (scale) {
+					for (let i = 0, n = spacesCount - 1; i < n; i++) {
+						let bone = bones[i];
+						let setupLength = bone.data.length;
+						if (setupLength < PathConstraint.epsilon)
+							lengths[i] = 0;
+						else {
+							let x = setupLength * bone.a, y = setupLength * bone.c;
+							lengths[i] = Math.sqrt(x * x + y * y);
+						}
+					}
+				}
+				Utils.arrayFill(spaces, 1, spacesCount, spacing);
+				break;
+			case SpacingMode.Proportional:
+				let sum = 0;
+				for (let i = 0, n = spacesCount - 1; i < n;) {
+					let bone = bones[i];
+					let setupLength = bone.data.length;
+					if (setupLength < PathConstraint.epsilon) {
+						if (scale) lengths[i] = 0;
+						spaces[++i] = spacing;
+					} else {
+						let x = setupLength * bone.a, y = setupLength * bone.c;
+						let length = Math.sqrt(x * x + y * y);
+						if (scale) lengths[i] = length;
+						spaces[++i] = length;
+						sum += length;
+					}
+				}
+				if (sum > 0) {
+					sum = spacesCount / sum * spacing;
+					for (let i = 1; i < spacesCount; i++)
+						spaces[i] *= sum;
+				}
+				break;
+			default:
+				let lengthSpacing = data.spacingMode == SpacingMode.Length;
+				for (let i = 0, n = spacesCount - 1; i < n;) {
+					let bone = bones[i];
+					let setupLength = bone.data.length;
+					if (setupLength < PathConstraint.epsilon) {
+						if (scale) lengths[i] = 0;
+						spaces[++i] = spacing;
+					} else {
+						let x = setupLength * bone.a, y = setupLength * bone.c;
+						let length = Math.sqrt(x * x + y * y);
+						if (scale) lengths[i] = length;
+						spaces[++i] = (lengthSpacing ? setupLength + spacing : spacing) * length / setupLength;
+					}
+				}
+		}
+
+		let positions = this.computeWorldPositions(<PathAttachment>attachment, spacesCount, tangents);
+		let boneX = positions[0], boneY = positions[1], offsetRotation = data.offsetRotation;
+		let tip = false;
+		if (offsetRotation == 0)
+			tip = data.rotateMode == RotateMode.Chain;
+		else {
+			tip = false;
+			let p = this.target.bone;
+			offsetRotation *= p.a * p.d - p.b * p.c > 0 ? MathUtils.degRad : -MathUtils.degRad;
+		}
+		for (let i = 0, p = 3; i < boneCount; i++, p += 3) {
+			let bone = bones[i];
+			bone.worldX += (boneX - bone.worldX) * mixX;
+			bone.worldY += (boneY - bone.worldY) * mixY;
+			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) * mixRotate + 1;
+					bone.a *= s;
+					bone.c *= s;
+				}
+			}
+			boneX = x;
+			boneY = y;
+			if (mixRotate > 0) {
+				let a = bone.a, b = bone.b, c = bone.c, d = bone.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) * mixRotate;
+					boneY += (length * (sin * a + cos * c) - dy) * mixRotate;
+				} else {
+					r += offsetRotation;
+				}
+				if (r > MathUtils.PI)
+					r -= MathUtils.PI2;
+				else if (r < -MathUtils.PI) //
+					r += MathUtils.PI2;
+				r *= mixRotate;
+				cos = Math.cos(r);
+				sin = Math.sin(r);
+				bone.a = cos * a - sin * c;
+				bone.b = cos * b - sin * d;
+				bone.c = sin * a + cos * c;
+				bone.d = sin * b + cos * d;
+			}
+			bone.updateAppliedTransform();
+		}
+	}
+
+	computeWorldPositions(path: PathAttachment, spacesCount: number, tangents: 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 (this.data.positionMode == PositionMode.Percent) position *= pathLength;
+
+			let multiplier;
+			switch (this.data.spacingMode) {
+				case SpacingMode.Percent:
+					multiplier = pathLength;
+					break;
+				case SpacingMode.Proportional:
+					multiplier = pathLength / spacesCount;
+					break;
+				default:
+					multiplier = 1;
+			}
+			world = Utils.setArraySize(this.world, 8);
+			for (let i = 0, o = 0, curve = 0; i < spacesCount; i++, o += 3) {
+				let space = spaces[i] * multiplier;
+				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 (this.data.positionMode == PositionMode.Percent) position *= pathLength;
+
+		let multiplier;
+		switch (this.data.spacingMode) {
+			case SpacingMode.Percent:
+				multiplier = pathLength;
+				break;
+			case SpacingMode.Proportional:
+				multiplier = pathLength / spacesCount;
+				break;
+			default:
+				multiplier = 1;
+		}
+
+		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] * multiplier;
+			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)) {
+			out[o] = x1;
+			out[o + 1] = y1;
+			out[o + 2] = Math.atan2(cy1 - y1, cx1 - x1);
+			return;
+		}
+		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) {
+			if (p < 0.001)
+				out[o + 2] = Math.atan2(cy1 - y1, cx1 - x1);
+			else
+				out[o + 2] = Math.atan2(y - (y1 * uu + cy1 * ut * 2 + cy2 * tt), x - (x1 * uu + cx1 * ut * 2 + cx2 * tt));
+		}
+	}
+}

+ 86 - 0
spine-ts/spine-core/src/PathConstraintData.ts

@@ -0,0 +1,86 @@
+/******************************************************************************
+ * Spine Runtimes License Agreement
+ * Last updated January 1, 2020. Replaces all prior versions.
+ *
+ * Copyright (c) 2013-2020, Esoteric Software LLC
+ *
+ * Integration of the Spine Runtimes into software or otherwise creating
+ * derivative works of the Spine Runtimes is permitted under the terms and
+ * conditions of Section 2 of the Spine Editor License Agreement:
+ * http://esotericsoftware.com/spine-editor-license
+ *
+ * Otherwise, it is permitted to integrate the Spine Runtimes into software
+ * or otherwise create derivative works of the Spine Runtimes (collectively,
+ * "Products"), provided that each user of the Products must obtain their own
+ * Spine Editor license and redistribution of the Products in any form must
+ * include this license and copyright notice.
+ *
+ * THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "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 LLC 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
+ * THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *****************************************************************************/
+
+import { BoneData } from "./BoneData";
+import { ConstraintData } from "./ConstraintData";
+import { SlotData } from "./SlotData";
+
+
+/** Stores the setup pose for a {@link PathConstraint}.
+ *
+ * See [path constraints](http://esotericsoftware.com/spine-path-constraints) in the Spine User Guide. */
+export class PathConstraintData extends ConstraintData {
+
+	/** The bones that will be modified by this path constraint. */
+	bones = new Array<BoneData>();
+
+	/** The slot whose path attachment will be used to constrained the bones. */
+	target: SlotData;
+
+	/** The mode for positioning the first bone on the path. */
+	positionMode: PositionMode;
+
+	/** The mode for positioning the bones after the first bone on the path. */
+	spacingMode: SpacingMode;
+
+	/** The mode for adjusting the rotation of the bones. */
+	rotateMode: RotateMode;
+
+	/** An offset added to the constrained bone rotation. */
+	offsetRotation: number;
+
+	/** The position along the path. */
+	position: number;
+
+	/** The spacing between bones. */
+	spacing: number;
+
+	mixRotate = 0;
+	mixX = 0;
+	mixY = 0;
+
+	constructor(name: string) {
+		super(name, 0, false);
+	}
+}
+
+/** Controls how the first bone is positioned along the path.
+ *
+ * See [position](http://esotericsoftware.com/spine-path-constraints#Position) in the Spine User Guide. */
+export enum PositionMode { Fixed, Percent }
+
+/** Controls how bones after the first bone are positioned along the path.
+ *
+ * See [spacing](http://esotericsoftware.com/spine-path-constraints#Spacing) in the Spine User Guide. */
+export enum SpacingMode { Length, Fixed, Percent, Proportional }
+
+/** Controls how bones are rotated, translated, and scaled to match the path.
+ *
+ * See [rotate mix](http://esotericsoftware.com/spine-path-constraints#Rotate-mix) in the Spine User Guide. */
+export enum RotateMode { Tangent, Chain, ChainScale }

+ 649 - 0
spine-ts/spine-core/src/Skeleton.ts

@@ -0,0 +1,649 @@
+/******************************************************************************
+ * Spine Runtimes License Agreement
+ * Last updated January 1, 2020. Replaces all prior versions.
+ *
+ * Copyright (c) 2013-2020, Esoteric Software LLC
+ *
+ * Integration of the Spine Runtimes into software or otherwise creating
+ * derivative works of the Spine Runtimes is permitted under the terms and
+ * conditions of Section 2 of the Spine Editor License Agreement:
+ * http://esotericsoftware.com/spine-editor-license
+ *
+ * Otherwise, it is permitted to integrate the Spine Runtimes into software
+ * or otherwise create derivative works of the Spine Runtimes (collectively,
+ * "Products"), provided that each user of the Products must obtain their own
+ * Spine Editor license and redistribution of the Products in any form must
+ * include this license and copyright notice.
+ *
+ * THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "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 LLC 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
+ * THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *****************************************************************************/
+
+import { Attachment } from "./attachments/Attachment";
+import { MeshAttachment } from "./attachments/MeshAttachment";
+import { PathAttachment } from "./attachments/PathAttachment";
+import { RegionAttachment } from "./attachments/RegionAttachment";
+import { Bone } from "./Bone";
+import { IkConstraint } from "./IkConstraint";
+import { PathConstraint } from "./PathConstraint";
+import { SkeletonData } from "./SkeletonData";
+import { Skin } from "./Skin";
+import { Slot } from "./Slot";
+import { TransformConstraint } from "./TransformConstraint";
+import { Updatable } from "./Updatable";
+import { Color, Utils, MathUtils, Vector2, NumberArrayLike } from "./Utils";
+
+/** Stores the current pose for a skeleton.
+ *
+ * See [Instance objects](http://esotericsoftware.com/spine-runtime-architecture#Instance-objects) in the Spine Runtimes Guide. */
+export class Skeleton {
+	/** The skeleton's setup pose data. */
+	data: SkeletonData;
+
+	/** The skeleton's bones, sorted parent first. The root bone is always the first bone. */
+	bones: Array<Bone>;
+
+	/** The skeleton's slots. */
+	slots: Array<Slot>;
+
+	/** The skeleton's slots in the order they should be drawn. The returned array may be modified to change the draw order. */
+	drawOrder: Array<Slot>;
+
+	/** The skeleton's IK constraints. */
+	ikConstraints: Array<IkConstraint>;
+
+	/** The skeleton's transform constraints. */
+	transformConstraints: Array<TransformConstraint>;
+
+	/** The skeleton's path constraints. */
+	pathConstraints: Array<PathConstraint>;
+
+	/** The list of bones and constraints, sorted in the order they should be updated, as computed by {@link #updateCache()}. */
+	_updateCache = new Array<Updatable>();
+
+	/** The skeleton's current skin. May be null. */
+	skin: Skin;
+
+	/** The color to tint all the skeleton's attachments. */
+	color: Color;
+
+	/** Returns the skeleton's time. This can be used for tracking, such as with Slot {@link Slot#attachmentTime}.
+	 * <p>
+	 * See {@link #update()}. */
+	time = 0;
+
+	/** Scales the entire skeleton on the X axis. This affects all bones, even if the bone's transform mode disallows scale
+	  * inheritance. */
+	scaleX = 1;
+
+	/** Scales the entire skeleton on the Y axis. This affects all bones, even if the bone's transform mode disallows scale
+	  * inheritance. */
+	scaleY = 1;
+
+	/** Sets the skeleton X position, which is added to the root bone worldX position. */
+	x = 0;
+
+	/** Sets the skeleton Y position, which is added to the root bone worldY position. */
+	y = 0;
+
+	constructor(data: SkeletonData) {
+		if (!data) 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)
+				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();
+	}
+
+	/** Caches information about bones and constraints. Must be called if the {@link #getSkin()} is modified or if bones,
+	 * constraints, or weighted path attachments are added or removed. */
+	updateCache() {
+		let updateCache = this._updateCache;
+		updateCache.length = 0;
+
+		let bones = this.bones;
+		for (let i = 0, n = bones.length; i < n; i++) {
+			let bone = bones[i];
+			bone.sorted = bone.data.skinRequired;
+			bone.active = !bone.sorted;
+		}
+
+		if (this.skin) {
+			let skinBones = this.skin.bones;
+			for (let i = 0, n = this.skin.bones.length; i < n; i++) {
+				let bone = this.bones[skinBones[i].index];
+				do {
+					bone.sorted = false;
+					bone.active = true;
+					bone = bone.parent;
+				} while (bone);
+			}
+		}
+
+		// 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) {
+		constraint.active = constraint.target.isActive() && (!constraint.data.skinRequired || (this.skin && Utils.contains(this.skin.constraints, constraint.data, true)));
+		if (!constraint.active) return;
+
+		let target = constraint.target;
+		this.sortBone(target);
+
+		let constrained = constraint.bones;
+		let parent = constrained[0];
+		this.sortBone(parent);
+
+		if (constrained.length == 1) {
+			this._updateCache.push(constraint);
+			this.sortReset(parent.children);
+		} else {
+			let child = constrained[constrained.length - 1];
+			this.sortBone(child);
+
+			this._updateCache.push(constraint);
+
+			this.sortReset(parent.children);
+			child.sorted = true;
+		}
+	}
+
+	sortPathConstraint(constraint: PathConstraint) {
+		constraint.active = constraint.target.bone.isActive() && (!constraint.data.skinRequired || (this.skin && Utils.contains(this.skin.constraints, constraint.data, true)));
+		if (!constraint.active) return;
+
+		let slot = constraint.target;
+		let slotIndex = slot.data.index;
+		let slotBone = slot.bone;
+		if (this.skin) this.sortPathConstraintAttachment(this.skin, slotIndex, slotBone);
+		if (this.data.defaultSkin && 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) {
+		constraint.active = constraint.target.isActive() && (!constraint.data.skinRequired || (this.skin && Utils.contains(this.skin.constraints, constraint.data, true)));
+		if (!constraint.active) return;
+
+		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);
+				this.sortBone(child);
+			}
+		} else {
+			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;
+	}
+
+	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)
+			this.sortBone(slotBone);
+		else {
+			let bones = this.bones;
+			for (let i = 0, n = pathBones.length; i < n;) {
+				let nn = pathBones[i++];
+				nn += i;
+				while (i < nn)
+					this.sortBone(bones[pathBones[i++]]);
+			}
+		}
+	}
+
+	sortBone(bone: Bone) {
+		if (bone.sorted) return;
+		let parent = bone.parent;
+		if (parent) 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.active) continue;
+			if (bone.sorted) this.sortReset(bone.children);
+			bone.sorted = false;
+		}
+	}
+
+	/** Updates the world transform for each bone and applies all constraints.
+	 *
+	 * See [World transforms](http://esotericsoftware.com/spine-runtime-skeletons#World-transforms) in the Spine
+	 * Runtimes Guide. */
+	updateWorldTransform() {
+		let bones = this.bones;
+		for (let i = 0, n = bones.length; i < n; i++) {
+			let bone = bones[i];
+			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;
+		}
+
+		let updateCache = this._updateCache;
+		for (let i = 0, n = updateCache.length; i < n; i++)
+			updateCache[i].update();
+	}
+
+	updateWorldTransformWith(parent: Bone) {
+		// Apply the parent bone transform to the root bone. The root bone always inherits scale, rotation and reflection.
+		let rootBone = this.getRootBone();
+		let pa = parent.a, pb = parent.b, pc = parent.c, pd = parent.d;
+		rootBone.worldX = pa * this.x + pb * this.y + parent.worldX;
+		rootBone.worldY = pc * this.x + pd * this.y + parent.worldY;
+
+		let rotationY = rootBone.rotation + 90 + rootBone.shearY;
+		let la = MathUtils.cosDeg(rootBone.rotation + rootBone.shearX) * rootBone.scaleX;
+		let lb = MathUtils.cosDeg(rotationY) * rootBone.scaleY;
+		let lc = MathUtils.sinDeg(rootBone.rotation + rootBone.shearX) * rootBone.scaleX;
+		let ld = MathUtils.sinDeg(rotationY) * rootBone.scaleY;
+		rootBone.a = (pa * la + pb * lc) * this.scaleX;
+		rootBone.b = (pa * lb + pb * ld) * this.scaleX;
+		rootBone.c = (pc * la + pd * lc) * this.scaleY;
+		rootBone.d = (pc * lb + pd * ld) * this.scaleY;
+
+		// Update everything except root bone.
+		let updateCache = this._updateCache;
+		for (let i = 0, n = updateCache.length; i < n; i++) {
+			let updatable = updateCache[i];
+			if (updatable != rootBone) updatable.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.mix = constraint.data.mix;
+			constraint.softness = constraint.data.softness;
+			constraint.bendDirection = constraint.data.bendDirection;
+			constraint.compress = constraint.data.compress;
+			constraint.stretch = constraint.data.stretch;
+		}
+
+		let transformConstraints = this.transformConstraints;
+		for (let i = 0, n = transformConstraints.length; i < n; i++) {
+			let constraint = transformConstraints[i];
+			let data = constraint.data;
+			constraint.mixRotate = data.mixRotate;
+			constraint.mixX = data.mixX;
+			constraint.mixY = data.mixY;
+			constraint.mixScaleX = data.mixScaleX;
+			constraint.mixScaleY = data.mixScaleY;
+			constraint.mixShearY = data.mixShearY;
+		}
+
+		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.mixRotate = data.mixRotate;
+			constraint.mixX = data.mixX;
+			constraint.mixY = data.mixY;
+		}
+	}
+
+	/** Sets the slots and draw order to their setup pose values. */
+	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();
+	}
+
+	/** @returns May return null. */
+	getRootBone() {
+		if (this.bones.length == 0) return null;
+		return this.bones[0];
+	}
+
+	/** @returns May be null. */
+	findBone(boneName: string) {
+		if (!boneName) 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;
+	}
+
+	/** @returns -1 if the bone was not found. */
+	findBoneIndex(boneName: string) {
+		if (!boneName) 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;
+	}
+
+	/** Finds a slot by comparing each slot's name. It is more efficient to cache the results of this method than to call it
+	 * repeatedly.
+	 * @returns May be null. */
+	findSlot(slotName: string) {
+		if (!slotName) 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;
+	}
+
+	/** @returns -1 if the bone was not found. */
+	findSlotIndex(slotName: string) {
+		if (!slotName) 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 {@link #setSkin()}. */
+	setSkinByName(skinName: string) {
+		let skin = this.data.findSkin(skinName);
+		if (!skin) throw new Error("Skin not found: " + skinName);
+		this.setSkin(skin);
+	}
+
+	/** Sets the skin used to look up attachments before looking in the {@link SkeletonData#defaultSkin default skin}. If the
+	 * skin is changed, {@link #updateCache()} is called.
+	 *
+	 * 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.
+	 *
+	 * After changing the skin, the visible attachments can be reset to those attached in the setup pose by calling
+	 * {@link #setSlotsToSetupPose()}. Also, often {@link AnimationState#apply()} is called before the next time the
+	 * skeleton is rendered to allow any attachment keys in the current animation(s) to hide or show attachments from the new skin.
+	 * @param newSkin May be null. */
+	setSkin(newSkin: Skin) {
+		if (newSkin == this.skin) return;
+		if (newSkin) {
+			if (this.skin)
+				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) {
+						let attachment: Attachment = newSkin.getAttachment(i, name);
+						if (attachment) slot.setAttachment(attachment);
+					}
+				}
+			}
+		}
+		this.skin = newSkin;
+		this.updateCache();
+	}
+
+
+	/** Finds an attachment by looking in the {@link #skin} and {@link SkeletonData#defaultSkin} using the slot name and attachment
+	 * name.
+	 *
+	 * See {@link #getAttachment()}.
+	 * @returns May be null. */
+	getAttachmentByName(slotName: string, attachmentName: string): Attachment {
+		return this.getAttachment(this.data.findSlotIndex(slotName), attachmentName);
+	}
+
+	/** Finds an attachment by looking in the {@link #skin} and {@link SkeletonData#defaultSkin} using the slot index and
+	 * attachment name. First the skin is checked and if the attachment was not found, the default skin is checked.
+	 *
+	 * See [Runtime skins](http://esotericsoftware.com/spine-runtime-skins) in the Spine Runtimes Guide.
+	 * @returns May be null. */
+	getAttachment(slotIndex: number, attachmentName: string): Attachment {
+		if (!attachmentName) throw new Error("attachmentName cannot be null.");
+		if (this.skin) {
+			let attachment: Attachment = this.skin.getAttachment(slotIndex, attachmentName);
+			if (attachment) return attachment;
+		}
+		if (this.data.defaultSkin) return this.data.defaultSkin.getAttachment(slotIndex, attachmentName);
+		return null;
+	}
+
+	/** A convenience method to set an attachment by finding the slot with {@link #findSlot()}, finding the attachment with
+	 * {@link #getAttachment()}, then setting the slot's {@link Slot#attachment}.
+	 * @param attachmentName May be null to clear the slot's attachment. */
+	setAttachment(slotName: string, attachmentName: string) {
+		if (!slotName) 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) {
+					attachment = this.getAttachment(i, attachmentName);
+					if (!attachment) throw new Error("Attachment not found: " + attachmentName + ", for slot: " + slotName);
+				}
+				slot.setAttachment(attachment);
+				return;
+			}
+		}
+		throw new Error("Slot not found: " + slotName);
+	}
+
+
+	/** Finds an IK constraint by comparing each IK constraint's name. It is more efficient to cache the results of this method
+	 * than to call it repeatedly.
+	 * @return May be null. */
+	findIkConstraint(constraintName: string) {
+		if (!constraintName) 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;
+	}
+
+	/** Finds a transform constraint by comparing each transform constraint's name. It is more efficient to cache the results of
+	 * this method than to call it repeatedly.
+	 * @return May be null. */
+	findTransformConstraint(constraintName: string) {
+		if (!constraintName) 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;
+	}
+
+	/** Finds a path constraint by comparing each path constraint's name. It is more efficient to cache the results of this method
+	 * than to call it repeatedly.
+	 * @return May be null. */
+	findPathConstraint(constraintName: string) {
+		if (!constraintName) 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 An output value, the distance from the skeleton origin to the bottom left corner of the AABB.
+	 * @param size An output value, the width and height of the AABB.
+	 * @param temp Working memory to temporarily store attachments' computed world vertices. */
+	getBounds(offset: Vector2, size: Vector2, temp: Array<number> = new Array<number>(2)) {
+		if (!offset) throw new Error("offset cannot be null.");
+		if (!size) 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];
+			if (!slot.bone.active) continue;
+			let verticesLength = 0;
+			let vertices: NumberArrayLike = 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) {
+				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);
+	}
+
+	/** Increments the skeleton's {@link #time}. */
+	update(delta: number) {
+		this.time += delta;
+	}
+}

+ 1145 - 0
spine-ts/spine-core/src/SkeletonBinary.ts

@@ -0,0 +1,1145 @@
+/******************************************************************************
+ * Spine Runtimes License Agreement
+ * Last updated January 1, 2020. Replaces all prior versions.
+ *
+ * Copyright (c) 2013-2020, Esoteric Software LLC
+ *
+ * Integration of the Spine Runtimes into software or otherwise creating
+ * derivative works of the Spine Runtimes is permitted under the terms and
+ * conditions of Section 2 of the Spine Editor License Agreement:
+ * http://esotericsoftware.com/spine-editor-license
+ *
+ * Otherwise, it is permitted to integrate the Spine Runtimes into software
+ * or otherwise create derivative works of the Spine Runtimes (collectively,
+ * "Products"), provided that each user of the Products must obtain their own
+ * Spine Editor license and redistribution of the Products in any form must
+ * include this license and copyright notice.
+ *
+ * THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "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 LLC 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
+ * THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *****************************************************************************/
+
+import { Animation, Timeline, AttachmentTimeline, RGBATimeline, RGBTimeline, RGBA2Timeline, RGB2Timeline, AlphaTimeline, RotateTimeline, TranslateTimeline, TranslateXTimeline, TranslateYTimeline, ScaleTimeline, ScaleXTimeline, ScaleYTimeline, ShearTimeline, ShearXTimeline, ShearYTimeline, IkConstraintTimeline, TransformConstraintTimeline, PathConstraintPositionTimeline, PathConstraintSpacingTimeline, PathConstraintMixTimeline, DeformTimeline, DrawOrderTimeline, EventTimeline, CurveTimeline1, CurveTimeline2, CurveTimeline } from "./Animation";
+import { VertexAttachment, Attachment } from "./attachments/Attachment";
+import { AttachmentLoader } from "./attachments/AttachmentLoader";
+import { MeshAttachment } from "./attachments/MeshAttachment";
+import { BoneData } from "./BoneData";
+import { Event } from "./Event";
+import { EventData } from "./EventData";
+import { IkConstraintData } from "./IkConstraintData";
+import { PathConstraintData, PositionMode, SpacingMode } from "./PathConstraintData";
+import { SkeletonData } from "./SkeletonData";
+import { Skin } from "./Skin";
+import { SlotData } from "./SlotData";
+import { TransformConstraintData } from "./TransformConstraintData";
+import { Color, Utils } from "./Utils";
+
+/** Loads skeleton data in the Spine binary format.
+ *
+ * See [Spine binary format](http://esotericsoftware.com/spine-binary-format) and
+ * [JSON and binary data](http://esotericsoftware.com/spine-loading-skeleton-data#JSON-and-binary-data) in the Spine
+ * Runtimes Guide. */
+export class SkeletonBinary {
+	/** Scales bone positions, image sizes, and translations as they are loaded. This allows different size images to be used at
+	 * runtime than were used in Spine.
+	 *
+	 * See [Scaling](http://esotericsoftware.com/spine-loading-skeleton-data#Scaling) in the Spine Runtimes Guide. */
+	scale = 1;
+
+	attachmentLoader: AttachmentLoader;
+	private linkedMeshes = new Array<LinkedMesh>();
+
+	constructor(attachmentLoader: AttachmentLoader) {
+		this.attachmentLoader = attachmentLoader;
+	}
+
+	readSkeletonData(binary: Uint8Array): SkeletonData {
+		let scale = this.scale;
+
+		let skeletonData = new SkeletonData();
+		skeletonData.name = ""; // BOZO
+
+		let input = new BinaryInput(binary);
+
+		let lowHash = input.readInt32();
+		let highHash = input.readInt32();
+		skeletonData.hash = highHash == 0 && lowHash == 0 ? null : highHash.toString(16) + lowHash.toString(16);
+		skeletonData.version = input.readString();
+		skeletonData.x = input.readFloat();
+		skeletonData.y = input.readFloat();
+		skeletonData.width = input.readFloat();
+		skeletonData.height = input.readFloat();
+
+		let nonessential = input.readBoolean();
+		if (nonessential) {
+			skeletonData.fps = input.readFloat();
+
+			skeletonData.imagesPath = input.readString();
+			skeletonData.audioPath = input.readString();
+		}
+
+		let n = 0;
+		// Strings.
+		n = input.readInt(true)
+		for (let i = 0; i < n; i++)
+			input.strings.push(input.readString());
+
+		// Bones.
+		n = input.readInt(true)
+		for (let i = 0; i < n; i++) {
+			let name = input.readString();
+			let parent = i == 0 ? null : skeletonData.bones[input.readInt(true)];
+			let data = new BoneData(i, name, parent);
+			data.rotation = input.readFloat();
+			data.x = input.readFloat() * scale;
+			data.y = input.readFloat() * scale;
+			data.scaleX = input.readFloat();
+			data.scaleY = input.readFloat();
+			data.shearX = input.readFloat();
+			data.shearY = input.readFloat();
+			data.length = input.readFloat() * scale;
+			data.transformMode = input.readInt(true);
+			data.skinRequired = input.readBoolean();
+			if (nonessential) Color.rgba8888ToColor(data.color, input.readInt32());
+			skeletonData.bones.push(data);
+		}
+
+		// Slots.
+		n = input.readInt(true);
+		for (let i = 0; i < n; i++) {
+			let slotName = input.readString();
+			let boneData = skeletonData.bones[input.readInt(true)];
+			let data = new SlotData(i, slotName, boneData);
+			Color.rgba8888ToColor(data.color, input.readInt32());
+
+			let darkColor = input.readInt32();
+			if (darkColor != -1) Color.rgb888ToColor(data.darkColor = new Color(), darkColor);
+
+			data.attachmentName = input.readStringRef();
+			data.blendMode = input.readInt(true);
+			skeletonData.slots.push(data);
+		}
+
+		// IK constraints.
+		n = input.readInt(true);
+		for (let i = 0, nn; i < n; i++) {
+			let data = new IkConstraintData(input.readString());
+			data.order = input.readInt(true);
+			data.skinRequired = input.readBoolean();
+			nn = input.readInt(true);
+			for (let ii = 0; ii < nn; ii++)
+				data.bones.push(skeletonData.bones[input.readInt(true)]);
+			data.target = skeletonData.bones[input.readInt(true)];
+			data.mix = input.readFloat();
+			data.softness = input.readFloat() * scale;
+			data.bendDirection = input.readByte();
+			data.compress = input.readBoolean();
+			data.stretch = input.readBoolean();
+			data.uniform = input.readBoolean();
+			skeletonData.ikConstraints.push(data);
+		}
+
+		// Transform constraints.
+		n = input.readInt(true);
+		for (let i = 0, nn; i < n; i++) {
+			let data = new TransformConstraintData(input.readString());
+			data.order = input.readInt(true);
+			data.skinRequired = input.readBoolean();
+			nn = input.readInt(true);
+			for (let ii = 0; ii < nn; ii++)
+				data.bones.push(skeletonData.bones[input.readInt(true)]);
+			data.target = skeletonData.bones[input.readInt(true)];
+			data.local = input.readBoolean();
+			data.relative = input.readBoolean();
+			data.offsetRotation = input.readFloat();
+			data.offsetX = input.readFloat() * scale;
+			data.offsetY = input.readFloat() * scale;
+			data.offsetScaleX = input.readFloat();
+			data.offsetScaleY = input.readFloat();
+			data.offsetShearY = input.readFloat();
+			data.mixRotate = input.readFloat();
+			data.mixX = input.readFloat();
+			data.mixY = input.readFloat();
+			data.mixScaleX = input.readFloat();
+			data.mixScaleY = input.readFloat();
+			data.mixShearY = input.readFloat();
+			skeletonData.transformConstraints.push(data);
+		}
+
+		// Path constraints.
+		n = input.readInt(true);
+		for (let i = 0, nn; i < n; i++) {
+			let data = new PathConstraintData(input.readString());
+			data.order = input.readInt(true);
+			data.skinRequired = input.readBoolean();
+			nn = input.readInt(true);
+			for (let ii = 0; ii < nn; ii++)
+				data.bones.push(skeletonData.bones[input.readInt(true)]);
+			data.target = skeletonData.slots[input.readInt(true)];
+			data.positionMode = input.readInt(true);
+			data.spacingMode = input.readInt(true);
+			data.rotateMode = input.readInt(true);
+			data.offsetRotation = input.readFloat();
+			data.position = input.readFloat();
+			if (data.positionMode == PositionMode.Fixed) data.position *= scale;
+			data.spacing = input.readFloat();
+			if (data.spacingMode == SpacingMode.Length || data.spacingMode == SpacingMode.Fixed) data.spacing *= scale;
+			data.mixRotate = input.readFloat();
+			data.mixX = input.readFloat();
+			data.mixY = input.readFloat();
+			skeletonData.pathConstraints.push(data);
+		}
+
+		// Default skin.
+		let defaultSkin = this.readSkin(input, skeletonData, true, nonessential);
+		if (defaultSkin) {
+			skeletonData.defaultSkin = defaultSkin;
+			skeletonData.skins.push(defaultSkin);
+		}
+
+		// Skins.
+		{
+			let i = skeletonData.skins.length;
+			Utils.setArraySize(skeletonData.skins, n = i + input.readInt(true));
+			for (; i < n; i++)
+				skeletonData.skins[i] = this.readSkin(input, skeletonData, false, nonessential);
+		}
+
+		// Linked meshes.
+		n = this.linkedMeshes.length;
+		for (let i = 0; i < n; i++) {
+			let linkedMesh = this.linkedMeshes[i];
+			let skin = !linkedMesh.skin ? skeletonData.defaultSkin : skeletonData.findSkin(linkedMesh.skin);
+			let parent = skin.getAttachment(linkedMesh.slotIndex, linkedMesh.parent);
+			linkedMesh.mesh.deformAttachment = linkedMesh.inheritDeform ? parent as VertexAttachment : linkedMesh.mesh;
+			linkedMesh.mesh.setParentMesh(parent as MeshAttachment);
+			linkedMesh.mesh.updateUVs();
+		}
+		this.linkedMeshes.length = 0;
+
+		// Events.
+		n = input.readInt(true);
+		for (let i = 0; i < n; i++) {
+			let data = new EventData(input.readStringRef());
+			data.intValue = input.readInt(false);
+			data.floatValue = input.readFloat();
+			data.stringValue = input.readString();
+			data.audioPath = input.readString();
+			if (data.audioPath) {
+				data.volume = input.readFloat();
+				data.balance = input.readFloat();
+			}
+			skeletonData.events.push(data);
+		}
+
+		// Animations.
+		n = input.readInt(true);
+		for (let i = 0; i < n; i++)
+			skeletonData.animations.push(this.readAnimation(input, input.readString(), skeletonData));
+		return skeletonData;
+	}
+
+	private readSkin(input: BinaryInput, skeletonData: SkeletonData, defaultSkin: boolean, nonessential: boolean): Skin {
+		let skin = null;
+		let slotCount = 0;
+
+		if (defaultSkin) {
+			slotCount = input.readInt(true)
+			if (slotCount == 0) return null;
+			skin = new Skin("default");
+		} else {
+			skin = new Skin(input.readStringRef());
+			skin.bones.length = input.readInt(true);
+			for (let i = 0, n = skin.bones.length; i < n; i++)
+				skin.bones[i] = skeletonData.bones[input.readInt(true)];
+
+			for (let i = 0, n = input.readInt(true); i < n; i++)
+				skin.constraints.push(skeletonData.ikConstraints[input.readInt(true)]);
+			for (let i = 0, n = input.readInt(true); i < n; i++)
+				skin.constraints.push(skeletonData.transformConstraints[input.readInt(true)]);
+			for (let i = 0, n = input.readInt(true); i < n; i++)
+				skin.constraints.push(skeletonData.pathConstraints[input.readInt(true)]);
+
+			slotCount = input.readInt(true);
+		}
+
+		for (let i = 0; i < slotCount; i++) {
+			let slotIndex = input.readInt(true);
+			for (let ii = 0, nn = input.readInt(true); ii < nn; ii++) {
+				let name = input.readStringRef();
+				let attachment = this.readAttachment(input, skeletonData, skin, slotIndex, name, nonessential);
+				if (attachment) skin.setAttachment(slotIndex, name, attachment);
+			}
+		}
+		return skin;
+	}
+
+	private readAttachment(input: BinaryInput, skeletonData: SkeletonData, skin: Skin, slotIndex: number, attachmentName: string, nonessential: boolean): Attachment {
+		let scale = this.scale;
+
+		let name = input.readStringRef();
+		if (!name) name = attachmentName;
+
+		switch (input.readByte()) {
+			case AttachmentType.Region: {
+				let path = input.readStringRef();
+				let rotation = input.readFloat();
+				let x = input.readFloat();
+				let y = input.readFloat();
+				let scaleX = input.readFloat();
+				let scaleY = input.readFloat();
+				let width = input.readFloat();
+				let height = input.readFloat();
+				let color = input.readInt32();
+
+				if (!path) path = name;
+				let region = this.attachmentLoader.newRegionAttachment(skin, name, path);
+				if (!region) return null;
+				region.path = path;
+				region.x = x * scale;
+				region.y = y * scale;
+				region.scaleX = scaleX;
+				region.scaleY = scaleY;
+				region.rotation = rotation;
+				region.width = width * scale;
+				region.height = height * scale;
+				Color.rgba8888ToColor(region.color, color);
+				region.updateOffset();
+				return region;
+			}
+			case AttachmentType.BoundingBox: {
+				let vertexCount = input.readInt(true);
+				let vertices = this.readVertices(input, vertexCount);
+				let color = nonessential ? input.readInt32() : 0;
+
+				let box = this.attachmentLoader.newBoundingBoxAttachment(skin, name);
+				if (!box) return null;
+				box.worldVerticesLength = vertexCount << 1;
+				box.vertices = vertices.vertices;
+				box.bones = vertices.bones;
+				if (nonessential) Color.rgba8888ToColor(box.color, color);
+				return box;
+			}
+			case AttachmentType.Mesh: {
+				let path = input.readStringRef();
+				let color = input.readInt32();
+				let vertexCount = input.readInt(true);
+				let uvs = this.readFloatArray(input, vertexCount << 1, 1);
+				let triangles = this.readShortArray(input);
+				let vertices = this.readVertices(input, vertexCount);
+				let hullLength = input.readInt(true);
+				let edges = null;
+				let width = 0, height = 0;
+				if (nonessential) {
+					edges = this.readShortArray(input);
+					width = input.readFloat();
+					height = input.readFloat();
+				}
+
+				if (!path) path = name;
+				let mesh = this.attachmentLoader.newMeshAttachment(skin, name, path);
+				if (!mesh) return null;
+				mesh.path = path;
+				Color.rgba8888ToColor(mesh.color, color);
+				mesh.bones = vertices.bones;
+				mesh.vertices = vertices.vertices;
+				mesh.worldVerticesLength = vertexCount << 1;
+				mesh.triangles = triangles;
+				mesh.regionUVs = uvs;
+				mesh.updateUVs();
+				mesh.hullLength = hullLength << 1;
+				if (nonessential) {
+					mesh.edges = edges;
+					mesh.width = width * scale;
+					mesh.height = height * scale;
+				}
+				return mesh;
+			}
+			case AttachmentType.LinkedMesh: {
+				let path = input.readStringRef();
+				let color = input.readInt32();
+				let skinName = input.readStringRef();
+				let parent = input.readStringRef();
+				let inheritDeform = input.readBoolean();
+				let width = 0, height = 0;
+				if (nonessential) {
+					width = input.readFloat();
+					height = input.readFloat();
+				}
+
+				if (!path) path = name;
+				let mesh = this.attachmentLoader.newMeshAttachment(skin, name, path);
+				if (!mesh) return null;
+				mesh.path = path;
+				Color.rgba8888ToColor(mesh.color, color);
+				if (nonessential) {
+					mesh.width = width * scale;
+					mesh.height = height * scale;
+				}
+				this.linkedMeshes.push(new LinkedMesh(mesh, skinName, slotIndex, parent, inheritDeform));
+				return mesh;
+			}
+			case AttachmentType.Path: {
+				let closed = input.readBoolean();
+				let constantSpeed = input.readBoolean();
+				let vertexCount = input.readInt(true);
+				let vertices = this.readVertices(input, vertexCount);
+				let lengths = Utils.newArray(vertexCount / 3, 0);
+				for (let i = 0, n = lengths.length; i < n; i++)
+					lengths[i] = input.readFloat() * scale;
+				let color = nonessential ? input.readInt32() : 0;
+
+				let path = this.attachmentLoader.newPathAttachment(skin, name);
+				if (!path) return null;
+				path.closed = closed;
+				path.constantSpeed = constantSpeed;
+				path.worldVerticesLength = vertexCount << 1;
+				path.vertices = vertices.vertices;
+				path.bones = vertices.bones;
+				path.lengths = lengths;
+				if (nonessential) Color.rgba8888ToColor(path.color, color);
+				return path;
+			}
+			case AttachmentType.Point: {
+				let rotation = input.readFloat();
+				let x = input.readFloat();
+				let y = input.readFloat();
+				let color = nonessential ? input.readInt32() : 0;
+
+				let point = this.attachmentLoader.newPointAttachment(skin, name);
+				if (!point) return null;
+				point.x = x * scale;
+				point.y = y * scale;
+				point.rotation = rotation;
+				if (nonessential) Color.rgba8888ToColor(point.color, color);
+				return point;
+			}
+			case AttachmentType.Clipping: {
+				let endSlotIndex = input.readInt(true);
+				let vertexCount = input.readInt(true);
+				let vertices = this.readVertices(input, vertexCount);
+				let color = nonessential ? input.readInt32() : 0;
+
+				let clip = this.attachmentLoader.newClippingAttachment(skin, name);
+				if (!clip) return null;
+				clip.endSlot = skeletonData.slots[endSlotIndex];
+				clip.worldVerticesLength = vertexCount << 1;
+				clip.vertices = vertices.vertices;
+				clip.bones = vertices.bones;
+				if (nonessential) Color.rgba8888ToColor(clip.color, color);
+				return clip;
+			}
+		}
+		return null;
+	}
+
+	private readVertices(input: BinaryInput, vertexCount: number): Vertices {
+		let scale = this.scale;
+		let verticesLength = vertexCount << 1;
+		let vertices = new Vertices();
+		if (!input.readBoolean()) {
+			vertices.vertices = this.readFloatArray(input, verticesLength, scale);
+			return vertices;
+		}
+		let weights = new Array<number>();
+		let bonesArray = new Array<number>();
+		for (let i = 0; i < vertexCount; i++) {
+			let boneCount = input.readInt(true);
+			bonesArray.push(boneCount);
+			for (let ii = 0; ii < boneCount; ii++) {
+				bonesArray.push(input.readInt(true));
+				weights.push(input.readFloat() * scale);
+				weights.push(input.readFloat() * scale);
+				weights.push(input.readFloat());
+			}
+		}
+		vertices.vertices = Utils.toFloatArray(weights);
+		vertices.bones = bonesArray;
+		return vertices;
+	}
+
+	private readFloatArray(input: BinaryInput, n: number, scale: number): number[] {
+		let array = new Array<number>(n);
+		if (scale == 1) {
+			for (let i = 0; i < n; i++)
+				array[i] = input.readFloat();
+		} else {
+			for (let i = 0; i < n; i++)
+				array[i] = input.readFloat() * scale;
+		}
+		return array;
+	}
+
+	private readShortArray(input: BinaryInput): number[] {
+		let n = input.readInt(true);
+		let array = new Array<number>(n);
+		for (let i = 0; i < n; i++)
+			array[i] = input.readShort();
+		return array;
+	}
+
+	private readAnimation(input: BinaryInput, name: string, skeletonData: SkeletonData): Animation {
+		input.readInt(true); // Number of timelines.
+		let timelines = new Array<Timeline>();
+		let scale = this.scale;
+		let tempColor1 = new Color();
+		let tempColor2 = new Color();
+
+		// Slot timelines.
+		for (let i = 0, n = input.readInt(true); i < n; i++) {
+			let slotIndex = input.readInt(true);
+			for (let ii = 0, nn = input.readInt(true); ii < nn; ii++) {
+				let timelineType = input.readByte();
+				let frameCount = input.readInt(true);
+				let frameLast = frameCount - 1;
+				switch (timelineType) {
+					case SLOT_ATTACHMENT: {
+						let timeline = new AttachmentTimeline(frameCount, slotIndex);
+						for (let frame = 0; frame < frameCount; frame++)
+							timeline.setFrame(frame, input.readFloat(), input.readStringRef());
+						timelines.push(timeline);
+						break;
+					}
+					case SLOT_RGBA: {
+						let bezierCount = input.readInt(true);
+						let timeline = new RGBATimeline(frameCount, bezierCount, slotIndex);
+
+						let time = input.readFloat();
+						let r = input.readUnsignedByte() / 255.0;
+						let g = input.readUnsignedByte() / 255.0;
+						let b = input.readUnsignedByte() / 255.0;
+						let a = input.readUnsignedByte() / 255.0;
+
+						for (let frame = 0, bezier = 0; ; frame++) {
+							timeline.setFrame(frame, time, r, g, b, a);
+							if (frame == frameLast) break;
+
+							let time2 = input.readFloat();
+							let r2 = input.readUnsignedByte() / 255.0;
+							let g2 = input.readUnsignedByte() / 255.0;
+							let b2 = input.readUnsignedByte() / 255.0;
+							let a2 = input.readUnsignedByte() / 255.0;
+
+							switch (input.readByte()) {
+								case CURVE_STEPPED:
+									timeline.setStepped(frame);
+									break;
+								case CURVE_BEZIER:
+									setBezier(input, timeline, bezier++, frame, 0, time, time2, r, r2, 1);
+									setBezier(input, timeline, bezier++, frame, 1, time, time2, g, g2, 1);
+									setBezier(input, timeline, bezier++, frame, 2, time, time2, b, b2, 1);
+									setBezier(input, timeline, bezier++, frame, 3, time, time2, a, a2, 1);
+							}
+							time = time2;
+							r = r2;
+							g = g2;
+							b = b2;
+							a = a2;
+						}
+						timelines.push(timeline);
+						break;
+					}
+					case SLOT_RGB: {
+						let bezierCount = input.readInt(true);
+						let timeline = new RGBTimeline(frameCount, bezierCount, slotIndex);
+
+						let time = input.readFloat();
+						let r = input.readUnsignedByte() / 255.0;
+						let g = input.readUnsignedByte() / 255.0;
+						let b = input.readUnsignedByte() / 255.0;
+
+						for (let frame = 0, bezier = 0; ; frame++) {
+							timeline.setFrame(frame, time, r, g, b);
+							if (frame == frameLast) break;
+
+							let time2 = input.readFloat();
+							let r2 = input.readUnsignedByte() / 255.0;
+							let g2 = input.readUnsignedByte() / 255.0;
+							let b2 = input.readUnsignedByte() / 255.0;
+
+							switch (input.readByte()) {
+								case CURVE_STEPPED:
+									timeline.setStepped(frame);
+									break;
+								case CURVE_BEZIER:
+									setBezier(input, timeline, bezier++, frame, 0, time, time2, r, r2, 1);
+									setBezier(input, timeline, bezier++, frame, 1, time, time2, g, g2, 1);
+									setBezier(input, timeline, bezier++, frame, 2, time, time2, b, b2, 1);
+							}
+							time = time2;
+							r = r2;
+							g = g2;
+							b = b2;
+						}
+						timelines.push(timeline);
+						break;
+					}
+					case SLOT_RGBA2: {
+						let bezierCount = input.readInt(true);
+						let timeline = new RGBA2Timeline(frameCount, bezierCount, slotIndex);
+
+						let time = input.readFloat();
+						let r = input.readUnsignedByte() / 255.0;
+						let g = input.readUnsignedByte() / 255.0;
+						let b = input.readUnsignedByte() / 255.0;
+						let a = input.readUnsignedByte() / 255.0;
+						let r2 = input.readUnsignedByte() / 255.0;
+						let g2 = input.readUnsignedByte() / 255.0;
+						let b2 = input.readUnsignedByte() / 255.0;
+
+						for (let frame = 0, bezier = 0; ; frame++) {
+							timeline.setFrame(frame, time, r, g, b, a, r2, g2, b2);
+							if (frame == frameLast) break;
+							let time2 = input.readFloat();
+							let nr = input.readUnsignedByte() / 255.0;
+							let ng = input.readUnsignedByte() / 255.0;
+							let nb = input.readUnsignedByte() / 255.0;
+							let na = input.readUnsignedByte() / 255.0;
+							let nr2 = input.readUnsignedByte() / 255.0;
+							let ng2 = input.readUnsignedByte() / 255.0;
+							let nb2 = input.readUnsignedByte() / 255.0;
+
+							switch (input.readByte()) {
+								case CURVE_STEPPED:
+									timeline.setStepped(frame);
+									break;
+								case CURVE_BEZIER:
+									setBezier(input, timeline, bezier++, frame, 0, time, time2, r, nr, 1);
+									setBezier(input, timeline, bezier++, frame, 1, time, time2, g, ng, 1);
+									setBezier(input, timeline, bezier++, frame, 2, time, time2, b, nb, 1);
+									setBezier(input, timeline, bezier++, frame, 3, time, time2, a, na, 1);
+									setBezier(input, timeline, bezier++, frame, 4, time, time2, r2, nr2, 1);
+									setBezier(input, timeline, bezier++, frame, 5, time, time2, g2, ng2, 1);
+									setBezier(input, timeline, bezier++, frame, 6, time, time2, b2, nb2, 1);
+							}
+							time = time2;
+							r = nr;
+							g = ng;
+							b = nb;
+							a = na;
+							r2 = nr2;
+							g2 = ng2;
+							b2 = nb2;
+						}
+						timelines.push(timeline);
+						break;
+					}
+					case SLOT_RGB2: {
+						let bezierCount = input.readInt(true);
+						let timeline = new RGB2Timeline(frameCount, bezierCount, slotIndex);
+
+						let time = input.readFloat();
+						let r = input.readUnsignedByte() / 255.0;
+						let g = input.readUnsignedByte() / 255.0;
+						let b = input.readUnsignedByte() / 255.0;
+						let r2 = input.readUnsignedByte() / 255.0;
+						let g2 = input.readUnsignedByte() / 255.0;
+						let b2 = input.readUnsignedByte() / 255.0;
+
+						for (let frame = 0, bezier = 0; ; frame++) {
+							timeline.setFrame(frame, time, r, g, b, r2, g2, b2);
+							if (frame == frameLast) break;
+							let time2 = input.readFloat();
+							let nr = input.readUnsignedByte() / 255.0;
+							let ng = input.readUnsignedByte() / 255.0;
+							let nb = input.readUnsignedByte() / 255.0;
+							let nr2 = input.readUnsignedByte() / 255.0;
+							let ng2 = input.readUnsignedByte() / 255.0;
+							let nb2 = input.readUnsignedByte() / 255.0;
+
+							switch (input.readByte()) {
+								case CURVE_STEPPED:
+									timeline.setStepped(frame);
+									break;
+								case CURVE_BEZIER:
+									setBezier(input, timeline, bezier++, frame, 0, time, time2, r, nr, 1);
+									setBezier(input, timeline, bezier++, frame, 1, time, time2, g, ng, 1);
+									setBezier(input, timeline, bezier++, frame, 2, time, time2, b, nb, 1);
+									setBezier(input, timeline, bezier++, frame, 3, time, time2, r2, nr2, 1);
+									setBezier(input, timeline, bezier++, frame, 4, time, time2, g2, ng2, 1);
+									setBezier(input, timeline, bezier++, frame, 5, time, time2, b2, nb2, 1);
+							}
+							time = time2;
+							r = nr;
+							g = ng;
+							b = nb;
+							r2 = nr2;
+							g2 = ng2;
+							b2 = nb2;
+						}
+						timelines.push(timeline);
+						break;
+					}
+					case SLOT_ALPHA: {
+						let timeline = new AlphaTimeline(frameCount, input.readInt(true), slotIndex);
+						let time = input.readFloat(), a = input.readUnsignedByte() / 255;
+						for (let frame = 0, bezier = 0; ; frame++) {
+							timeline.setFrame(frame, time, a);
+							if (frame == frameLast) break;
+							let time2 = input.readFloat();
+							let a2 = input.readUnsignedByte() / 255;
+							switch (input.readByte()) {
+								case CURVE_STEPPED:
+									timeline.setStepped(frame);
+									break;
+								case CURVE_BEZIER:
+									setBezier(input, timeline, bezier++, frame, 0, time, time2, a, a2, 1);
+							}
+							time = time2;
+							a = a2;
+						}
+						timelines.push(timeline);
+						break;
+					}
+				}
+			}
+		}
+
+		// Bone timelines.
+		for (let i = 0, n = input.readInt(true); i < n; i++) {
+			let boneIndex = input.readInt(true);
+			for (let ii = 0, nn = input.readInt(true); ii < nn; ii++) {
+				let type = input.readByte(), frameCount = input.readInt(true), bezierCount = input.readInt(true);
+				switch (type) {
+					case BONE_ROTATE:
+						timelines.push(readTimeline1(input, new RotateTimeline(frameCount, bezierCount, boneIndex), 1));
+						break;
+					case BONE_TRANSLATE:
+						timelines.push(readTimeline2(input, new TranslateTimeline(frameCount, bezierCount, boneIndex), scale));
+						break;
+					case BONE_TRANSLATEX:
+						timelines.push(readTimeline1(input, new TranslateXTimeline(frameCount, bezierCount, boneIndex), scale));
+						break;
+					case BONE_TRANSLATEY:
+						timelines.push(readTimeline1(input, new TranslateYTimeline(frameCount, bezierCount, boneIndex), scale));
+						break;
+					case BONE_SCALE:
+						timelines.push(readTimeline2(input, new ScaleTimeline(frameCount, bezierCount, boneIndex), 1));
+						break;
+					case BONE_SCALEX:
+						timelines.push(readTimeline1(input, new ScaleXTimeline(frameCount, bezierCount, boneIndex), 1));
+						break;
+					case BONE_SCALEY:
+						timelines.push(readTimeline1(input, new ScaleYTimeline(frameCount, bezierCount, boneIndex), 1));
+						break;
+					case BONE_SHEAR:
+						timelines.push(readTimeline2(input, new ShearTimeline(frameCount, bezierCount, boneIndex), 1));
+						break;
+					case BONE_SHEARX:
+						timelines.push(readTimeline1(input, new ShearXTimeline(frameCount, bezierCount, boneIndex), 1));
+						break;
+					case BONE_SHEARY:
+						timelines.push(readTimeline1(input, new ShearYTimeline(frameCount, bezierCount, boneIndex), 1));
+				}
+			}
+		}
+
+		// IK constraint timelines.
+		for (let i = 0, n = input.readInt(true); i < n; i++) {
+			let index = input.readInt(true), frameCount = input.readInt(true), frameLast = frameCount - 1;
+			let timeline = new IkConstraintTimeline(frameCount, input.readInt(true), index);
+			let time = input.readFloat(), mix = input.readFloat(), softness = input.readFloat() * scale;
+			for (let frame = 0, bezier = 0; ; frame++) {
+				timeline.setFrame(frame, time, mix, softness, input.readByte(), input.readBoolean(), input.readBoolean());
+				if (frame == frameLast) break;
+				let time2 = input.readFloat(), mix2 = input.readFloat(), softness2 = input.readFloat() * scale;
+				switch (input.readByte()) {
+					case CURVE_STEPPED:
+						timeline.setStepped(frame);
+						break;
+					case CURVE_BEZIER:
+						setBezier(input, timeline, bezier++, frame, 0, time, time2, mix, mix2, 1);
+						setBezier(input, timeline, bezier++, frame, 1, time, time2, softness, softness2, scale);
+				}
+				time = time2;
+				mix = mix2;
+				softness = softness2;
+			}
+			timelines.push(timeline);
+		}
+
+		// Transform constraint timelines.
+		for (let i = 0, n = input.readInt(true); i < n; i++) {
+			let index = input.readInt(true), frameCount = input.readInt(true), frameLast = frameCount - 1;
+			let timeline = new TransformConstraintTimeline(frameCount, input.readInt(true), index);
+			let time = input.readFloat(), mixRotate = input.readFloat(), mixX = input.readFloat(), mixY = input.readFloat(),
+				mixScaleX = input.readFloat(), mixScaleY = input.readFloat(), mixShearY = input.readFloat();
+			for (let frame = 0, bezier = 0; ; frame++) {
+				timeline.setFrame(frame, time, mixRotate, mixX, mixY, mixScaleX, mixScaleY, mixShearY);
+				if (frame == frameLast) break;
+				let time2 = input.readFloat(), mixRotate2 = input.readFloat(), mixX2 = input.readFloat(), mixY2 = input.readFloat(),
+					mixScaleX2 = input.readFloat(), mixScaleY2 = input.readFloat(), mixShearY2 = input.readFloat();
+				switch (input.readByte()) {
+					case CURVE_STEPPED:
+						timeline.setStepped(frame);
+						break;
+					case CURVE_BEZIER:
+						setBezier(input, timeline, bezier++, frame, 0, time, time2, mixRotate, mixRotate2, 1);
+						setBezier(input, timeline, bezier++, frame, 1, time, time2, mixX, mixX2, 1);
+						setBezier(input, timeline, bezier++, frame, 2, time, time2, mixY, mixY2, 1);
+						setBezier(input, timeline, bezier++, frame, 3, time, time2, mixScaleX, mixScaleX2, 1);
+						setBezier(input, timeline, bezier++, frame, 4, time, time2, mixScaleY, mixScaleY2, 1);
+						setBezier(input, timeline, bezier++, frame, 5, time, time2, mixShearY, mixShearY2, 1);
+				}
+				time = time2;
+				mixRotate = mixRotate2;
+				mixX = mixX2;
+				mixY = mixY2;
+				mixScaleX = mixScaleX2;
+				mixScaleY = mixScaleY2;
+				mixShearY = mixShearY2;
+			}
+			timelines.push(timeline);
+		}
+
+		// Path constraint timelines.
+		for (let i = 0, n = input.readInt(true); i < n; i++) {
+			let index = input.readInt(true);
+			let data = skeletonData.pathConstraints[index];
+			for (let ii = 0, nn = input.readInt(true); ii < nn; ii++) {
+				switch (input.readByte()) {
+					case PATH_POSITION:
+						timelines
+							.push(readTimeline1(input, new PathConstraintPositionTimeline(input.readInt(true), input.readInt(true), index),
+								data.positionMode == PositionMode.Fixed ? scale : 1));
+						break;
+					case PATH_SPACING:
+						timelines
+							.push(readTimeline1(input, new PathConstraintSpacingTimeline(input.readInt(true), input.readInt(true), index),
+								data.spacingMode == SpacingMode.Length || data.spacingMode == SpacingMode.Fixed ? scale : 1));
+						break;
+					case PATH_MIX:
+						let timeline = new PathConstraintMixTimeline(input.readInt(true), input.readInt(true), index);
+						let time = input.readFloat(), mixRotate = input.readFloat(), mixX = input.readFloat(), mixY = input.readFloat();
+						for (let frame = 0, bezier = 0, frameLast = timeline.getFrameCount() - 1; ; frame++) {
+							timeline.setFrame(frame, time, mixRotate, mixX, mixY);
+							if (frame == frameLast) break;
+							let time2 = input.readFloat(), mixRotate2 = input.readFloat(), mixX2 = input.readFloat(),
+								mixY2 = input.readFloat();
+							switch (input.readByte()) {
+								case CURVE_STEPPED:
+									timeline.setStepped(frame);
+									break;
+								case CURVE_BEZIER:
+									setBezier(input, timeline, bezier++, frame, 0, time, time2, mixRotate, mixRotate2, 1);
+									setBezier(input, timeline, bezier++, frame, 1, time, time2, mixX, mixX2, 1);
+									setBezier(input, timeline, bezier++, frame, 2, time, time2, mixY, mixY2, 1);
+							}
+							time = time2;
+							mixRotate = mixRotate2;
+							mixX = mixX2;
+							mixY = mixY2;
+						}
+						timelines.push(timeline);
+				}
+			}
+		}
+
+		// Deform timelines.
+		for (let i = 0, n = input.readInt(true); i < n; i++) {
+			let skin = skeletonData.skins[input.readInt(true)];
+			for (let ii = 0, nn = input.readInt(true); ii < nn; ii++) {
+				let slotIndex = input.readInt(true);
+				for (let iii = 0, nnn = input.readInt(true); iii < nnn; iii++) {
+					let attachmentName = input.readStringRef();
+					let attachment = skin.getAttachment(slotIndex, attachmentName) as VertexAttachment;
+					let weighted = attachment.bones;
+					let vertices = attachment.vertices;
+					let deformLength = weighted ? vertices.length / 3 * 2 : vertices.length;
+
+					let frameCount = input.readInt(true);
+					let frameLast = frameCount - 1;
+					let bezierCount = input.readInt(true);
+					let timeline = new DeformTimeline(frameCount, bezierCount, slotIndex, attachment);
+
+					let time = input.readFloat();
+					for (let frame = 0, bezier = 0; ; frame++) {
+						let deform;
+						let end = input.readInt(true);
+						if (end == 0)
+							deform = weighted ? Utils.newFloatArray(deformLength) : vertices;
+						else {
+							deform = Utils.newFloatArray(deformLength);
+							let start = input.readInt(true);
+							end += start;
+							if (scale == 1) {
+								for (let v = start; v < end; v++)
+									deform[v] = input.readFloat();
+							} else {
+								for (let v = start; v < end; v++)
+									deform[v] = input.readFloat() * scale;
+							}
+							if (!weighted) {
+								for (let v = 0, vn = deform.length; v < vn; v++)
+									deform[v] += vertices[v];
+							}
+						}
+
+						timeline.setFrame(frame, time, deform);
+						if (frame == frameLast) break;
+						let time2 = input.readFloat();
+						switch (input.readByte()) {
+							case CURVE_STEPPED:
+								timeline.setStepped(frame);
+								break;
+							case CURVE_BEZIER:
+								setBezier(input, timeline, bezier++, frame, 0, time, time2, 0, 1, 1);
+						}
+						time = time2;
+					}
+					timelines.push(timeline);
+				}
+			}
+		}
+
+		// Draw order timeline.
+		let drawOrderCount = input.readInt(true);
+		if (drawOrderCount > 0) {
+			let timeline = new DrawOrderTimeline(drawOrderCount);
+			let slotCount = skeletonData.slots.length;
+			for (let i = 0; i < drawOrderCount; i++) {
+				let time = input.readFloat();
+				let offsetCount = input.readInt(true);
+				let drawOrder = Utils.newArray(slotCount, 0);
+				for (let ii = slotCount - 1; ii >= 0; ii--)
+					drawOrder[ii] = -1;
+				let unchanged = Utils.newArray(slotCount - offsetCount, 0);
+				let originalIndex = 0, unchangedIndex = 0;
+				for (let ii = 0; ii < offsetCount; ii++) {
+					let slotIndex = input.readInt(true);
+					// Collect unchanged items.
+					while (originalIndex != slotIndex)
+						unchanged[unchangedIndex++] = originalIndex++;
+					// Set changed items.
+					drawOrder[originalIndex + input.readInt(true)] = originalIndex++;
+				}
+				// Collect remaining unchanged items.
+				while (originalIndex < slotCount)
+					unchanged[unchangedIndex++] = originalIndex++;
+				// Fill in unchanged items.
+				for (let ii = slotCount - 1; ii >= 0; ii--)
+					if (drawOrder[ii] == -1) drawOrder[ii] = unchanged[--unchangedIndex];
+				timeline.setFrame(i, time, drawOrder);
+			}
+			timelines.push(timeline);
+		}
+
+		// Event timeline.
+		let eventCount = input.readInt(true);
+		if (eventCount > 0) {
+			let timeline = new EventTimeline(eventCount);
+			for (let i = 0; i < eventCount; i++) {
+				let time = input.readFloat();
+				let eventData = skeletonData.events[input.readInt(true)];
+				let event = new Event(time, eventData);
+				event.intValue = input.readInt(false);
+				event.floatValue = input.readFloat();
+				event.stringValue = input.readBoolean() ? input.readString() : eventData.stringValue;
+				if (event.data.audioPath) {
+					event.volume = input.readFloat();
+					event.balance = input.readFloat();
+				}
+				timeline.setFrame(i, event);
+			}
+			timelines.push(timeline);
+		}
+
+		let duration = 0;
+		for (let i = 0, n = timelines.length; i < n; i++)
+			duration = Math.max(duration, timelines[i].getDuration());
+		return new Animation(name, timelines, duration);
+	}
+}
+
+export class BinaryInput {
+	constructor(data: Uint8Array, public strings = new Array<string>(), private index: number = 0, private buffer = new DataView(data.buffer)) {
+	}
+
+	readByte(): number {
+		return this.buffer.getInt8(this.index++);
+	}
+
+	readUnsignedByte(): number {
+		return this.buffer.getUint8(this.index++);
+	}
+
+	readShort(): number {
+		let value = this.buffer.getInt16(this.index);
+		this.index += 2;
+		return value;
+	}
+
+	readInt32(): number {
+		let value = this.buffer.getInt32(this.index)
+		this.index += 4;
+		return value;
+	}
+
+	readInt(optimizePositive: boolean) {
+		let b = this.readByte();
+		let result = b & 0x7F;
+		if ((b & 0x80) != 0) {
+			b = this.readByte();
+			result |= (b & 0x7F) << 7;
+			if ((b & 0x80) != 0) {
+				b = this.readByte();
+				result |= (b & 0x7F) << 14;
+				if ((b & 0x80) != 0) {
+					b = this.readByte();
+					result |= (b & 0x7F) << 21;
+					if ((b & 0x80) != 0) {
+						b = this.readByte();
+						result |= (b & 0x7F) << 28;
+					}
+				}
+			}
+		}
+		return optimizePositive ? result : ((result >>> 1) ^ -(result & 1));
+	}
+
+	readStringRef(): string {
+		let index = this.readInt(true);
+		return index == 0 ? null : this.strings[index - 1];
+	}
+
+	readString(): string {
+		let byteCount = this.readInt(true);
+		switch (byteCount) {
+			case 0:
+				return null;
+			case 1:
+				return "";
+		}
+		byteCount--;
+		let chars = "";
+		let charCount = 0;
+		for (let i = 0; i < byteCount;) {
+			let b = this.readByte();
+			switch (b >> 4) {
+				case 12:
+				case 13:
+					chars += String.fromCharCode(((b & 0x1F) << 6 | this.readByte() & 0x3F));
+					i += 2;
+					break;
+				case 14:
+					chars += String.fromCharCode(((b & 0x0F) << 12 | (this.readByte() & 0x3F) << 6 | this.readByte() & 0x3F));
+					i += 3;
+					break;
+				default:
+					chars += String.fromCharCode(b);
+					i++;
+			}
+		}
+		return chars;
+	}
+
+	readFloat(): number {
+		let value = this.buffer.getFloat32(this.index);
+		this.index += 4;
+		return value;
+	}
+
+	readBoolean(): boolean {
+		return this.readByte() != 0;
+	}
+}
+
+class LinkedMesh {
+	parent: string; skin: string;
+	slotIndex: number;
+	mesh: MeshAttachment;
+	inheritDeform: boolean;
+
+	constructor(mesh: MeshAttachment, skin: string, slotIndex: number, parent: string, inheritDeform: boolean) {
+		this.mesh = mesh;
+		this.skin = skin;
+		this.slotIndex = slotIndex;
+		this.parent = parent;
+		this.inheritDeform = inheritDeform;
+	}
+}
+
+class Vertices {
+	constructor(public bones: Array<number> = null, public vertices: Array<number> | Float32Array = null) { }
+}
+
+enum AttachmentType { Region, BoundingBox, Mesh, LinkedMesh, Path, Point, Clipping }
+
+function readTimeline1(input: BinaryInput, timeline: CurveTimeline1, scale: number): CurveTimeline1 {
+	let time = input.readFloat(), value = input.readFloat() * scale;
+	for (let frame = 0, bezier = 0, frameLast = timeline.getFrameCount() - 1; ; frame++) {
+		timeline.setFrame(frame, time, value);
+		if (frame == frameLast) break;
+		let time2 = input.readFloat(), value2 = input.readFloat() * scale;
+		switch (input.readByte()) {
+			case CURVE_STEPPED:
+				timeline.setStepped(frame);
+				break;
+			case CURVE_BEZIER:
+				setBezier(input, timeline, bezier++, frame, 0, time, time2, value, value2, scale);
+		}
+		time = time2;
+		value = value2;
+	}
+	return timeline;
+}
+
+function readTimeline2(input: BinaryInput, timeline: CurveTimeline2, scale: number): CurveTimeline2 {
+	let time = input.readFloat(), value1 = input.readFloat() * scale, value2 = input.readFloat() * scale;
+	for (let frame = 0, bezier = 0, frameLast = timeline.getFrameCount() - 1; ; frame++) {
+		timeline.setFrame(frame, time, value1, value2);
+		if (frame == frameLast) break;
+		let time2 = input.readFloat(), nvalue1 = input.readFloat() * scale, nvalue2 = input.readFloat() * scale;
+		switch (input.readByte()) {
+			case CURVE_STEPPED:
+				timeline.setStepped(frame);
+				break;
+			case CURVE_BEZIER:
+				setBezier(input, timeline, bezier++, frame, 0, time, time2, value1, nvalue1, scale);
+				setBezier(input, timeline, bezier++, frame, 1, time, time2, value2, nvalue2, scale);
+		}
+		time = time2;
+		value1 = nvalue1;
+		value2 = nvalue2;
+	}
+	return timeline;
+}
+
+function setBezier(input: BinaryInput, timeline: CurveTimeline, bezier: number, frame: number, value: number,
+	time1: number, time2: number, value1: number, value2: number, scale: number) {
+	timeline.setBezier(bezier, frame, value, time1, value1, input.readFloat(), input.readFloat() * scale, input.readFloat(), input.readFloat() * scale, time2, value2);
+}
+
+const BONE_ROTATE = 0;
+const BONE_TRANSLATE = 1;
+const BONE_TRANSLATEX = 2;
+const BONE_TRANSLATEY = 3;
+const BONE_SCALE = 4;
+const BONE_SCALEX = 5;
+const BONE_SCALEY = 6;
+const BONE_SHEAR = 7;
+const BONE_SHEARX = 8;
+const BONE_SHEARY = 9;
+
+const SLOT_ATTACHMENT = 0;
+const SLOT_RGBA = 1;
+const SLOT_RGB = 2;
+const SLOT_RGBA2 = 3;
+const SLOT_RGB2 = 4;
+const SLOT_ALPHA = 5;
+
+const PATH_POSITION = 0;
+const PATH_SPACING = 1;
+const PATH_MIX = 2;
+
+const CURVE_LINEAR = 0;
+const CURVE_STEPPED = 1;
+const CURVE_BEZIER = 2;

+ 232 - 0
spine-ts/spine-core/src/SkeletonBounds.ts

@@ -0,0 +1,232 @@
+/******************************************************************************
+ * Spine Runtimes License Agreement
+ * Last updated January 1, 2020. Replaces all prior versions.
+ *
+ * Copyright (c) 2013-2020, Esoteric Software LLC
+ *
+ * Integration of the Spine Runtimes into software or otherwise creating
+ * derivative works of the Spine Runtimes is permitted under the terms and
+ * conditions of Section 2 of the Spine Editor License Agreement:
+ * http://esotericsoftware.com/spine-editor-license
+ *
+ * Otherwise, it is permitted to integrate the Spine Runtimes into software
+ * or otherwise create derivative works of the Spine Runtimes (collectively,
+ * "Products"), provided that each user of the Products must obtain their own
+ * Spine Editor license and redistribution of the Products in any form must
+ * include this license and copyright notice.
+ *
+ * THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "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 LLC 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
+ * THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *****************************************************************************/
+
+import { BoundingBoxAttachment } from "./attachments/BoundingBoxAttachment";
+import { Skeleton } from "./Skeleton";
+import { NumberArrayLike, Pool, Utils } from "./Utils";
+
+/** Collects each visible {@link BoundingBoxAttachment} and computes the world vertices for its polygon. The polygon vertices are
+ * provided along with convenience methods for doing hit detection. */
+export class SkeletonBounds {
+
+	/** The left edge of the axis aligned bounding box. */
+	minX = 0;
+
+	/** The bottom edge of the axis aligned bounding box. */
+	minY = 0;
+
+	/** The right edge of the axis aligned bounding box. */
+	maxX = 0;
+
+	/** The top edge of the axis aligned bounding box. */
+	maxY = 0;
+
+	/** The visible bounding boxes. */
+	boundingBoxes = new Array<BoundingBoxAttachment>();
+
+	/** The world vertices for the bounding box polygons. */
+	polygons = new Array<NumberArrayLike>();
+
+	private polygonPool = new Pool<NumberArrayLike>(() => {
+		return Utils.newFloatArray(16);
+	});
+
+	/** Clears any previous polygons, finds all visible bounding box attachments, and computes the world vertices for each bounding
+	 * box's polygon.
+	 * @param updateAabb If true, the axis aligned bounding box containing all the polygons is computed. If false, the
+	 *           SkeletonBounds AABB methods will always return true. */
+	update(skeleton: Skeleton, updateAabb: boolean) {
+		if (!skeleton) 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];
+			if (!slot.bone.active) continue;
+			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: NumberArrayLike, 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()} 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: NumberArrayLike, 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) throw new Error("boundingBox cannot be null.");
+		let index = this.boundingBoxes.indexOf(boundingBox);
+		return index == -1 ? null : this.polygons[index];
+	}
+
+	/** The width of the axis aligned bounding box. */
+	getWidth() {
+		return this.maxX - this.minX;
+	}
+
+	/** The height of the axis aligned bounding box. */
+	getHeight() {
+		return this.maxY - this.minY;
+	}
+}

+ 350 - 0
spine-ts/spine-core/src/SkeletonClipping.ts

@@ -0,0 +1,350 @@
+/******************************************************************************
+ * Spine Runtimes License Agreement
+ * Last updated January 1, 2020. Replaces all prior versions.
+ *
+ * Copyright (c) 2013-2020, Esoteric Software LLC
+ *
+ * Integration of the Spine Runtimes into software or otherwise creating
+ * derivative works of the Spine Runtimes is permitted under the terms and
+ * conditions of Section 2 of the Spine Editor License Agreement:
+ * http://esotericsoftware.com/spine-editor-license
+ *
+ * Otherwise, it is permitted to integrate the Spine Runtimes into software
+ * or otherwise create derivative works of the Spine Runtimes (collectively,
+ * "Products"), provided that each user of the Products must obtain their own
+ * Spine Editor license and redistribution of the Products in any form must
+ * include this license and copyright notice.
+ *
+ * THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "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 LLC 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
+ * THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *****************************************************************************/
+
+import { ClippingAttachment } from "./attachments/ClippingAttachment";
+import { Slot } from "./Slot";
+import { Triangulator } from "./Triangulator";
+import { Utils, Color, NumberArrayLike } from "./Utils";
+
+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) 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 && this.clipAttachment.endSlot == slot.data) this.clipEnd();
+	}
+
+	clipEnd() {
+		if (!this.clipAttachment) 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: NumberArrayLike, verticesLength: number, triangles: NumberArrayLike, trianglesLength: number, uvs: NumberArrayLike,
+		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 s = c0 * (edgeX2 - edgeX) - c2 * (edgeY2 - edgeY);
+					if (Math.abs(s) > 0.000001) {
+						let ua = (c2 * (edgeY - inputY) - c0 * (edgeX - inputX)) / s;
+						output.push(edgeX + (edgeX2 - edgeX) * ua);
+						output.push(edgeY + (edgeY2 - edgeY) * ua);
+					} else {
+						output.push(edgeX);
+						output.push(edgeY);
+					}
+				} else if (side2) { // v1 outside, v2 inside
+					let c0 = inputY2 - inputY, c2 = inputX2 - inputX;
+					let s = c0 * (edgeX2 - edgeX) - c2 * (edgeY2 - edgeY);
+					if (Math.abs(s) > 0.000001) {
+						let ua = (c2 * (edgeY - inputY) - c0 * (edgeX - inputX)) / s;
+						output.push(edgeX + (edgeX2 - edgeX) * ua);
+						output.push(edgeY + (edgeY2 - edgeY) * ua);
+					} else {
+						output.push(edgeX);
+						output.push(edgeY);
+					}
+					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: NumberArrayLike) {
+		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;
+		}
+	}
+}

+ 223 - 0
spine-ts/spine-core/src/SkeletonData.ts

@@ -0,0 +1,223 @@
+/******************************************************************************
+ * Spine Runtimes License Agreement
+ * Last updated January 1, 2020. Replaces all prior versions.
+ *
+ * Copyright (c) 2013-2020, Esoteric Software LLC
+ *
+ * Integration of the Spine Runtimes into software or otherwise creating
+ * derivative works of the Spine Runtimes is permitted under the terms and
+ * conditions of Section 2 of the Spine Editor License Agreement:
+ * http://esotericsoftware.com/spine-editor-license
+ *
+ * Otherwise, it is permitted to integrate the Spine Runtimes into software
+ * or otherwise create derivative works of the Spine Runtimes (collectively,
+ * "Products"), provided that each user of the Products must obtain their own
+ * Spine Editor license and redistribution of the Products in any form must
+ * include this license and copyright notice.
+ *
+ * THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "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 LLC 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
+ * THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *****************************************************************************/
+
+import { Animation } from "./Animation"
+import { BoneData } from "./BoneData";
+import { EventData } from "./EventData";
+import { IkConstraintData } from "./IkConstraintData";
+import { PathConstraintData } from "./PathConstraintData";
+import { Skin } from "./Skin";
+import { SlotData } from "./SlotData";
+import { TransformConstraintData } from "./TransformConstraintData";
+
+/** Stores the setup pose and all of the stateless data for a skeleton.
+ *
+ * See [Data objects](http://esotericsoftware.com/spine-runtime-architecture#Data-objects) in the Spine Runtimes
+ * Guide. */
+export class SkeletonData {
+
+	/** The skeleton's name, which by default is the name of the skeleton data file, if possible. May be null. */
+	name: string;
+
+	/** The skeleton's bones, sorted parent first. The root bone is always the first bone. */
+	bones = new Array<BoneData>(); // Ordered parents first.
+
+	/** The skeleton's slots. */
+	slots = new Array<SlotData>(); // Setup pose draw order.
+	skins = new Array<Skin>();
+
+	/** The skeleton's default skin. By default this skin contains all attachments that were not in a skin in Spine.
+	 *
+	 * See {@link Skeleton#getAttachmentByName()}.
+	 * May be null. */
+	defaultSkin: Skin;
+
+	/** The skeleton's events. */
+	events = new Array<EventData>();
+
+	/** The skeleton's animations. */
+	animations = new Array<Animation>();
+
+	/** The skeleton's IK constraints. */
+	ikConstraints = new Array<IkConstraintData>();
+
+	/** The skeleton's transform constraints. */
+	transformConstraints = new Array<TransformConstraintData>();
+
+	/** The skeleton's path constraints. */
+	pathConstraints = new Array<PathConstraintData>();
+
+	/** The X coordinate of the skeleton's axis aligned bounding box in the setup pose. */
+	x: number;
+
+	/** The Y coordinate of the skeleton's axis aligned bounding box in the setup pose. */
+	y: number;
+
+	/** The width of the skeleton's axis aligned bounding box in the setup pose. */
+	width: number;
+
+	/** The height of the skeleton's axis aligned bounding box in the setup pose. */
+	height: number;
+
+	/** The Spine version used to export the skeleton data, or null. */
+	version: string;
+
+	/** The skeleton data hash. This value will change if any of the skeleton data has changed. May be null. */
+	hash: string;
+
+	// Nonessential
+	/** The dopesheet FPS in Spine. Available only when nonessential data was exported. */
+	fps = 0;
+
+	/** The path to the images directory as defined in Spine. Available only when nonessential data was exported. May be null. */
+	imagesPath: string;
+
+	/** The path to the audio directory as defined in Spine. Available only when nonessential data was exported. May be null. */
+	audioPath: string;
+
+	/** Finds a bone by comparing each bone's name. It is more efficient to cache the results of this method than to call it
+	 * multiple times.
+	 * @returns May be null. */
+	findBone(boneName: string) {
+		if (!boneName) 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) 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;
+	}
+
+	/** Finds a slot by comparing each slot's name. It is more efficient to cache the results of this method than to call it
+	 * multiple times.
+	 * @returns May be null. */
+	findSlot(slotName: string) {
+		if (!slotName) 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) 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;
+	}
+
+	/** Finds a skin by comparing each skin's name. It is more efficient to cache the results of this method than to call it
+	 * multiple times.
+	 * @returns May be null. */
+	findSkin(skinName: string) {
+		if (!skinName) 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;
+	}
+
+	/** Finds an event by comparing each events's name. It is more efficient to cache the results of this method than to call it
+	 * multiple times.
+	 * @returns May be null. */
+	findEvent(eventDataName: string) {
+		if (!eventDataName) 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;
+	}
+
+	/** Finds an animation by comparing each animation's name. It is more efficient to cache the results of this method than to
+	 * call it multiple times.
+	 * @returns May be null. */
+	findAnimation(animationName: string) {
+		if (!animationName) 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;
+	}
+
+	/** Finds an IK constraint by comparing each IK constraint's name. It is more efficient to cache the results of this method
+	 * than to call it multiple times.
+	 * @return May be null. */
+	findIkConstraint(constraintName: string) {
+		if (!constraintName) 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;
+	}
+
+	/** Finds a transform constraint by comparing each transform constraint's name. It is more efficient to cache the results of
+	 * this method than to call it multiple times.
+	 * @return May be null. */
+	findTransformConstraint(constraintName: string) {
+		if (!constraintName) 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;
+	}
+
+	/** Finds a path constraint by comparing each path constraint's name. It is more efficient to cache the results of this method
+	 * than to call it multiple times.
+	 * @return May be null. */
+	findPathConstraint(constraintName: string) {
+		if (!constraintName) 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;
+	}
+}

+ 977 - 0
spine-ts/spine-core/src/SkeletonJson.ts

@@ -0,0 +1,977 @@
+/******************************************************************************
+ * Spine Runtimes License Agreement
+ * Last updated January 1, 2020. Replaces all prior versions.
+ *
+ * Copyright (c) 2013-2020, Esoteric Software LLC
+ *
+ * Integration of the Spine Runtimes into software or otherwise creating
+ * derivative works of the Spine Runtimes is permitted under the terms and
+ * conditions of Section 2 of the Spine Editor License Agreement:
+ * http://esotericsoftware.com/spine-editor-license
+ *
+ * Otherwise, it is permitted to integrate the Spine Runtimes into software
+ * or otherwise create derivative works of the Spine Runtimes (collectively,
+ * "Products"), provided that each user of the Products must obtain their own
+ * Spine Editor license and redistribution of the Products in any form must
+ * include this license and copyright notice.
+ *
+ * THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "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 LLC 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
+ * THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *****************************************************************************/
+
+import { Animation, Timeline, AttachmentTimeline, RGBATimeline, RGBTimeline, AlphaTimeline, RGBA2Timeline, RGB2Timeline, RotateTimeline, TranslateTimeline, TranslateXTimeline, TranslateYTimeline, ScaleTimeline, ScaleXTimeline, ScaleYTimeline, ShearTimeline, ShearXTimeline, ShearYTimeline, IkConstraintTimeline, TransformConstraintTimeline, PathConstraintPositionTimeline, PathConstraintSpacingTimeline, PathConstraintMixTimeline, DeformTimeline, DrawOrderTimeline, EventTimeline, CurveTimeline1, CurveTimeline2, CurveTimeline } from "./Animation";
+import { VertexAttachment, Attachment } from "./attachments/Attachment";
+import { AttachmentLoader } from "./attachments/AttachmentLoader";
+import { MeshAttachment } from "./attachments/MeshAttachment";
+import { BoneData, TransformMode } from "./BoneData";
+import { EventData } from "./EventData";
+import { Event } from "./Event";
+import { IkConstraintData } from "./IkConstraintData";
+import { PathConstraintData, PositionMode, SpacingMode, RotateMode } from "./PathConstraintData";
+import { SkeletonData } from "./SkeletonData";
+import { Skin } from "./Skin";
+import { SlotData, BlendMode } from "./SlotData";
+import { TransformConstraintData } from "./TransformConstraintData";
+import { Utils, Color, NumberArrayLike } from "./Utils";
+
+/** Loads skeleton data in the Spine JSON format.
+ *
+ * See [Spine JSON format](http://esotericsoftware.com/spine-json-format) and
+ * [JSON and binary data](http://esotericsoftware.com/spine-loading-skeleton-data#JSON-and-binary-data) in the Spine
+ * Runtimes Guide. */
+export class SkeletonJson {
+	attachmentLoader: AttachmentLoader;
+
+	/** Scales bone positions, image sizes, and translations as they are loaded. This allows different size images to be used at
+	 * runtime than were used in Spine.
+	 *
+	 * See [Scaling](http://esotericsoftware.com/spine-loading-skeleton-data#Scaling) in the Spine Runtimes Guide. */
+	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) {
+			skeletonData.hash = skeletonMap.hash;
+			skeletonData.version = skeletonMap.spine;
+			skeletonData.x = skeletonMap.x;
+			skeletonData.y = skeletonMap.y;
+			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 = getValue(boneMap, "parent", null);
+				if (parentName) parent = skeletonData.findBone(parentName);
+				let data = new BoneData(skeletonData.bones.length, boneMap.name, parent);
+				data.length = getValue(boneMap, "length", 0) * scale;
+				data.x = getValue(boneMap, "x", 0) * scale;
+				data.y = getValue(boneMap, "y", 0) * scale;
+				data.rotation = getValue(boneMap, "rotation", 0);
+				data.scaleX = getValue(boneMap, "scaleX", 1);
+				data.scaleY = getValue(boneMap, "scaleY", 1);
+				data.shearX = getValue(boneMap, "shearX", 0);
+				data.shearY = getValue(boneMap, "shearY", 0);
+				data.transformMode = Utils.enumValue(TransformMode, getValue(boneMap, "transform", "Normal"));
+				data.skinRequired = getValue(boneMap, "skin", false);
+
+				let color = getValue(boneMap, "color", null);
+				if (color) data.color.setFromString(color);
+
+				skeletonData.bones.push(data);
+			}
+		}
+
+		// Slots.
+		if (root.slots) {
+			for (let i = 0; i < root.slots.length; i++) {
+				let slotMap = root.slots[i];
+				let boneData = skeletonData.findBone(slotMap.bone);
+				let data = new SlotData(skeletonData.slots.length, slotMap.name, boneData);
+
+				let color: string = getValue(slotMap, "color", null);
+				if (color) data.color.setFromString(color);
+
+				let dark: string = getValue(slotMap, "dark", null);
+				if (dark) data.darkColor = Color.fromString(dark);
+
+				data.attachmentName = getValue(slotMap, "attachment", null);
+				data.blendMode = Utils.enumValue(BlendMode, 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 = getValue(constraintMap, "order", 0);
+				data.skinRequired = getValue(constraintMap, "skin", false);
+
+				for (let ii = 0; ii < constraintMap.bones.length; ii++)
+					data.bones.push(skeletonData.findBone(constraintMap.bones[ii]));
+
+				data.target = skeletonData.findBone(constraintMap.target);
+
+				data.mix = getValue(constraintMap, "mix", 1);
+				data.softness = getValue(constraintMap, "softness", 0) * scale;
+				data.bendDirection = getValue(constraintMap, "bendPositive", true) ? 1 : -1;
+				data.compress = getValue(constraintMap, "compress", false);
+				data.stretch = getValue(constraintMap, "stretch", false);
+				data.uniform = getValue(constraintMap, "uniform", false);
+
+				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 = getValue(constraintMap, "order", 0);
+				data.skinRequired = getValue(constraintMap, "skin", false);
+
+				for (let ii = 0; ii < constraintMap.bones.length; ii++)
+					data.bones.push(skeletonData.findBone(constraintMap.bones[ii]));
+
+				let targetName: string = constraintMap.target;
+				data.target = skeletonData.findBone(targetName);
+
+				data.local = getValue(constraintMap, "local", false);
+				data.relative = getValue(constraintMap, "relative", false);
+				data.offsetRotation = getValue(constraintMap, "rotation", 0);
+				data.offsetX = getValue(constraintMap, "x", 0) * scale;
+				data.offsetY = getValue(constraintMap, "y", 0) * scale;
+				data.offsetScaleX = getValue(constraintMap, "scaleX", 0);
+				data.offsetScaleY = getValue(constraintMap, "scaleY", 0);
+				data.offsetShearY = getValue(constraintMap, "shearY", 0);
+
+				data.mixRotate = getValue(constraintMap, "mixRotate", 1);
+				data.mixX = getValue(constraintMap, "mixX", 1);
+				data.mixY = getValue(constraintMap, "mixY", data.mixX);
+				data.mixScaleX = getValue(constraintMap, "mixScaleX", 1);
+				data.mixScaleY = getValue(constraintMap, "mixScaleY", data.mixScaleX);
+				data.mixShearY = getValue(constraintMap, "mixShearY", 1);
+
+				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 = getValue(constraintMap, "order", 0);
+				data.skinRequired = getValue(constraintMap, "skin", false);
+
+				for (let ii = 0; ii < constraintMap.bones.length; ii++)
+					data.bones.push(skeletonData.findBone(constraintMap.bones[ii]));
+
+				let targetName: string = constraintMap.target;
+				data.target = skeletonData.findSlot(targetName);
+
+				data.positionMode = Utils.enumValue(PositionMode, getValue(constraintMap, "positionMode", "Percent"));
+				data.spacingMode = Utils.enumValue(SpacingMode, getValue(constraintMap, "spacingMode", "Length"));
+				data.rotateMode = Utils.enumValue(RotateMode, getValue(constraintMap, "rotateMode", "Tangent"));
+				data.offsetRotation = getValue(constraintMap, "rotation", 0);
+				data.position = getValue(constraintMap, "position", 0);
+				if (data.positionMode == PositionMode.Fixed) data.position *= scale;
+				data.spacing = getValue(constraintMap, "spacing", 0);
+				if (data.spacingMode == SpacingMode.Length || data.spacingMode == SpacingMode.Fixed) data.spacing *= scale;
+				data.mixRotate = getValue(constraintMap, "mixRotate", 1);
+				data.mixX = getValue(constraintMap, "mixX", 1);
+				data.mixY = getValue(constraintMap, "mixY", data.mixX);
+
+				skeletonData.pathConstraints.push(data);
+			}
+		}
+
+		// Skins.
+		if (root.skins) {
+			for (let i = 0; i < root.skins.length; i++) {
+				let skinMap = root.skins[i]
+				let skin = new Skin(skinMap.name);
+
+				if (skinMap.bones) {
+					for (let ii = 0; ii < skinMap.bones.length; ii++)
+						skin.bones.push(skeletonData.findBone(skinMap.bones[ii]));
+				}
+
+				if (skinMap.ik) {
+					for (let ii = 0; ii < skinMap.ik.length; ii++)
+						skin.constraints.push(skeletonData.findIkConstraint(skinMap.ik[ii]));
+				}
+
+				if (skinMap.transform) {
+					for (let ii = 0; ii < skinMap.transform.length; ii++)
+						skin.constraints.push(skeletonData.findTransformConstraint(skinMap.transform[ii]));
+				}
+
+				if (skinMap.path) {
+					for (let ii = 0; ii < skinMap.path.length; ii++)
+						skin.constraints.push(skeletonData.findPathConstraint(skinMap.path[ii]));
+				}
+
+				for (let slotName in skinMap.attachments) {
+					let slot = skeletonData.findSlot(slotName);
+					let slotMap = skinMap.attachments[slotName];
+					for (let entryName in slotMap) {
+						let attachment = this.readAttachment(slotMap[entryName], skin, slot.index, entryName, skeletonData);
+						if (attachment) skin.setAttachment(slot.index, 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 ? skeletonData.defaultSkin : skeletonData.findSkin(linkedMesh.skin);
+			let parent = skin.getAttachment(linkedMesh.slotIndex, linkedMesh.parent);
+			linkedMesh.mesh.deformAttachment = linkedMesh.inheritDeform ? <VertexAttachment>parent : <VertexAttachment>linkedMesh.mesh;
+			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 = getValue(eventMap, "int", 0);
+				data.floatValue = getValue(eventMap, "float", 0);
+				data.stringValue = getValue(eventMap, "string", "");
+				data.audioPath = getValue(eventMap, "audio", null);
+				if (data.audioPath) {
+					data.volume = getValue(eventMap, "volume", 1);
+					data.balance = getValue(eventMap, "balance", 0);
+				}
+				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 = getValue(map, "name", name);
+
+		switch (getValue(map, "type", "region")) {
+			case "region": {
+				let path = getValue(map, "path", name);
+				let region = this.attachmentLoader.newRegionAttachment(skin, name, path);
+				if (!region) return null;
+				region.path = path;
+				region.x = getValue(map, "x", 0) * scale;
+				region.y = getValue(map, "y", 0) * scale;
+				region.scaleX = getValue(map, "scaleX", 1);
+				region.scaleY = getValue(map, "scaleY", 1);
+				region.rotation = getValue(map, "rotation", 0);
+				region.width = map.width * scale;
+				region.height = map.height * scale;
+
+				let color: string = getValue(map, "color", null);
+				if (color) region.color.setFromString(color);
+
+				region.updateOffset();
+				return region;
+			}
+			case "boundingbox": {
+				let box = this.attachmentLoader.newBoundingBoxAttachment(skin, name);
+				if (!box) return null;
+				this.readVertices(map, box, map.vertexCount << 1);
+				let color: string = getValue(map, "color", null);
+				if (color) box.color.setFromString(color);
+				return box;
+			}
+			case "mesh":
+			case "linkedmesh": {
+				let path = getValue(map, "path", name);
+				let mesh = this.attachmentLoader.newMeshAttachment(skin, name, path);
+				if (!mesh) return null;
+				mesh.path = path;
+
+				let color = getValue(map, "color", null);
+				if (color) mesh.color.setFromString(color);
+
+				mesh.width = getValue(map, "width", 0) * scale;
+				mesh.height = getValue(map, "height", 0) * scale;
+
+				let parent: string = getValue(map, "parent", null);
+				if (parent) {
+					this.linkedMeshes.push(new LinkedMesh(mesh, <string>getValue(map, "skin", null), slotIndex, parent, getValue(map, "deform", true)));
+					return mesh;
+				}
+
+				let uvs: Array<number> = map.uvs;
+				this.readVertices(map, mesh, uvs.length);
+				mesh.triangles = map.triangles;
+				mesh.regionUVs = uvs;
+				mesh.updateUVs();
+
+				mesh.edges = getValue(map, "edges", null);
+				mesh.hullLength = getValue(map, "hull", 0) * 2;
+				return mesh;
+			}
+			case "path": {
+				let path = this.attachmentLoader.newPathAttachment(skin, name);
+				if (!path) return null;
+				path.closed = getValue(map, "closed", false);
+				path.constantSpeed = 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 = getValue(map, "color", null);
+				if (color) path.color.setFromString(color);
+				return path;
+			}
+			case "point": {
+				let point = this.attachmentLoader.newPointAttachment(skin, name);
+				if (!point) return null;
+				point.x = getValue(map, "x", 0) * scale;
+				point.y = getValue(map, "y", 0) * scale;
+				point.rotation = getValue(map, "rotation", 0);
+
+				let color = getValue(map, "color", null);
+				if (color) point.color.setFromString(color);
+				return point;
+			}
+			case "clipping": {
+				let clip = this.attachmentLoader.newClippingAttachment(skin, name);
+				if (!clip) return null;
+
+				let end = getValue(map, "end", null);
+				if (end) clip.endSlot = skeletonData.findSlot(end);
+
+				let vertexCount = map.vertexCount;
+				this.readVertices(map, clip, vertexCount << 1);
+
+				let color: string = getValue(map, "color", null);
+				if (color) 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>();
+
+		// Slot timelines.
+		if (map.slots) {
+			for (let slotName in map.slots) {
+				let slotMap = map.slots[slotName];
+				let slotIndex = skeletonData.findSlotIndex(slotName);
+				for (let timelineName in slotMap) {
+					let timelineMap = slotMap[timelineName];
+					if (!timelineMap) continue;
+					if (timelineName == "attachment") {
+						let timeline = new AttachmentTimeline(timelineMap.length, slotIndex);
+						for (let frame = 0; frame < timelineMap.length; frame++) {
+							let keyMap = timelineMap[frame];
+							timeline.setFrame(frame, getValue(keyMap, "time", 0), keyMap.name);
+						}
+						timelines.push(timeline);
+
+					} else if (timelineName == "rgba") {
+						let timeline = new RGBATimeline(timelineMap.length, timelineMap.length << 2, slotIndex);
+						let keyMap = timelineMap[0];
+						let time = getValue(keyMap, "time", 0);
+						let color = Color.fromString(keyMap.color);
+
+						for (let frame = 0, bezier = 0; ; frame++) {
+							timeline.setFrame(frame, time, color.r, color.g, color.b, color.a);
+							let nextMap = timelineMap[frame + 1];
+							if (!nextMap) {
+								timeline.shrink(bezier);
+								break;
+							}
+							let time2 = getValue(nextMap, "time", 0);
+							let newColor = Color.fromString(nextMap.color);
+							let curve = keyMap.curve;
+							if (curve) {
+								bezier = readCurve(curve, timeline, bezier, frame, 0, time, time2, color.r, newColor.r, 1);
+								bezier = readCurve(curve, timeline, bezier, frame, 1, time, time2, color.g, newColor.g, 1);
+								bezier = readCurve(curve, timeline, bezier, frame, 2, time, time2, color.b, newColor.b, 1);
+								bezier = readCurve(curve, timeline, bezier, frame, 3, time, time2, color.a, newColor.a, 1);
+							}
+							time = time2;
+							color = newColor;
+							keyMap = nextMap;
+						}
+
+						timelines.push(timeline);
+
+					} else if (timelineName == "rgb") {
+						let timeline = new RGBTimeline(timelineMap.length, timelineMap.length * 3, slotIndex);
+						let keyMap = timelineMap[0];
+						let time = getValue(keyMap, "time", 0);
+						let color = Color.fromString(keyMap.color);
+
+						for (let frame = 0, bezier = 0; ; frame++) {
+							timeline.setFrame(frame, time, color.r, color.g, color.b);
+							let nextMap = timelineMap[frame + 1];
+							if (!nextMap) {
+								timeline.shrink(bezier);
+								break;
+							}
+							let time2 = getValue(nextMap, "time", 0);
+							let newColor = Color.fromString(nextMap.color);
+							let curve = keyMap.curve;
+							if (curve) {
+								bezier = readCurve(curve, timeline, bezier, frame, 0, time, time2, color.r, newColor.r, 1);
+								bezier = readCurve(curve, timeline, bezier, frame, 1, time, time2, color.g, newColor.g, 1);
+								bezier = readCurve(curve, timeline, bezier, frame, 2, time, time2, color.b, newColor.b, 1);
+							}
+							time = time2;
+							color = newColor;
+							keyMap = nextMap;
+						}
+
+						timelines.push(timeline);
+
+					} else if (timelineName == "alpha") {
+						timelines.push(readTimeline1(timelineMap, new AlphaTimeline(timelineMap.length, timelineMap.length, slotIndex), 0, 1));
+					} else if (timelineName == "rgba2") {
+						let timeline = new RGBA2Timeline(timelineMap.length, timelineMap.length * 7, slotIndex);
+
+						let keyMap = timelineMap[0];
+						let time = getValue(keyMap, "time", 0);
+						let color = Color.fromString(keyMap.light);
+						let color2 = Color.fromString(keyMap.dark);
+
+						for (let frame = 0, bezier = 0; ; frame++) {
+							timeline.setFrame(frame, time, color.r, color.g, color.b, color.a, color2.r, color2.g, color2.b);
+							let nextMap = timelineMap[frame + 1];
+							if (!nextMap) {
+								timeline.shrink(bezier);
+								break;
+							}
+							let time2 = getValue(nextMap, "time", 0);
+							let newColor = Color.fromString(nextMap.light);
+							let newColor2 = Color.fromString(nextMap.dark);
+							let curve = keyMap.curve;
+							if (curve) {
+								bezier = readCurve(curve, timeline, bezier, frame, 0, time, time2, color.r, newColor.r, 1);
+								bezier = readCurve(curve, timeline, bezier, frame, 1, time, time2, color.g, newColor.g, 1);
+								bezier = readCurve(curve, timeline, bezier, frame, 2, time, time2, color.b, newColor.b, 1);
+								bezier = readCurve(curve, timeline, bezier, frame, 3, time, time2, color.a, newColor.a, 1);
+								bezier = readCurve(curve, timeline, bezier, frame, 4, time, time2, color2.r, newColor2.r, 1);
+								bezier = readCurve(curve, timeline, bezier, frame, 5, time, time2, color2.g, newColor2.g, 1);
+								bezier = readCurve(curve, timeline, bezier, frame, 6, time, time2, color2.b, newColor2.b, 1);
+							}
+							time = time2;
+							color = newColor;
+							color2 = newColor2;
+							keyMap = nextMap;
+						}
+
+						timelines.push(timeline);
+
+					} else if (timelineName == "rgb2") {
+						let timeline = new RGB2Timeline(timelineMap.length, timelineMap.length * 6, slotIndex);
+
+						let keyMap = timelineMap[0];
+						let time = getValue(keyMap, "time", 0);
+						let color = Color.fromString(keyMap.light);
+						let color2 = Color.fromString(keyMap.dark);
+
+						for (let frame = 0, bezier = 0; ; frame++) {
+							timeline.setFrame(frame, time, color.r, color.g, color.b, color2.r, color2.g, color2.b);
+							let nextMap = timelineMap[frame + 1];
+							if (!nextMap) {
+								timeline.shrink(bezier);
+								break;
+							}
+							let time2 = getValue(nextMap, "time", 0);
+							let newColor = Color.fromString(nextMap.light);
+							let newColor2 = Color.fromString(nextMap.dark);
+							let curve = keyMap.curve;
+							if (curve) {
+								bezier = readCurve(curve, timeline, bezier, frame, 0, time, time2, color.r, newColor.r, 1);
+								bezier = readCurve(curve, timeline, bezier, frame, 1, time, time2, color.g, newColor.g, 1);
+								bezier = readCurve(curve, timeline, bezier, frame, 2, time, time2, color.b, newColor.b, 1);
+								bezier = readCurve(curve, timeline, bezier, frame, 3, time, time2, color2.r, newColor2.r, 1);
+								bezier = readCurve(curve, timeline, bezier, frame, 4, time, time2, color2.g, newColor2.g, 1);
+								bezier = readCurve(curve, timeline, bezier, frame, 5, time, time2, color2.b, newColor2.b, 1);
+							}
+							time = time2;
+							color = newColor;
+							color2 = newColor2;
+							keyMap = nextMap;
+						}
+
+						timelines.push(timeline);
+					}
+				}
+			}
+		}
+
+		// Bone timelines.
+		if (map.bones) {
+			for (let boneName in map.bones) {
+				let boneMap = map.bones[boneName];
+				let boneIndex = skeletonData.findBoneIndex(boneName);
+				for (let timelineName in boneMap) {
+					let timelineMap = boneMap[timelineName];
+					if (timelineMap.length == 0) continue;
+
+					if (timelineName === "rotate") {
+						timelines.push(readTimeline1(timelineMap, new RotateTimeline(timelineMap.length, timelineMap.length, boneIndex), 0, 1));
+					} else if (timelineName === "translate") {
+						let timeline = new TranslateTimeline(timelineMap.length, timelineMap.length << 1, boneIndex);
+						timelines.push(readTimeline2(timelineMap, timeline, "x", "y", 0, scale));
+					} else if (timelineName === "translatex") {
+						let timeline = new TranslateXTimeline(timelineMap.length, timelineMap.length, boneIndex);
+						timelines.push(readTimeline1(timelineMap, timeline, 0, scale));
+					} else if (timelineName === "translatey") {
+						let timeline = new TranslateYTimeline(timelineMap.length, timelineMap.length, boneIndex);
+						timelines.push(readTimeline1(timelineMap, timeline, 0, scale));
+					} else if (timelineName === "scale") {
+						let timeline = new ScaleTimeline(timelineMap.length, timelineMap.length << 1, boneIndex);
+						timelines.push(readTimeline2(timelineMap, timeline, "x", "y", 1, 1));
+					} else if (timelineName === "scalex") {
+						let timeline = new ScaleXTimeline(timelineMap.length, timelineMap.length, boneIndex);
+						timelines.push(readTimeline1(timelineMap, timeline, 1, 1));
+					} else if (timelineName === "scaley") {
+						let timeline = new ScaleYTimeline(timelineMap.length, timelineMap.length, boneIndex);
+						timelines.push(readTimeline1(timelineMap, timeline, 1, 1));
+					} else if (timelineName === "shear") {
+						let timeline = new ShearTimeline(timelineMap.length, timelineMap.length << 1, boneIndex);
+						timelines.push(readTimeline2(timelineMap, timeline, "x", "y", 0, 1));
+					} else if (timelineName === "shearx") {
+						let timeline = new ShearXTimeline(timelineMap.length, timelineMap.length, boneIndex);
+						timelines.push(readTimeline1(timelineMap, timeline, 0, 1));
+					} else if (timelineName === "sheary") {
+						let timeline = new ShearYTimeline(timelineMap.length, timelineMap.length, boneIndex);
+						timelines.push(readTimeline1(timelineMap, timeline, 0, 1));
+					}
+				}
+			}
+		}
+
+		// IK constraint timelines.
+		if (map.ik) {
+			for (let constraintName in map.ik) {
+				let constraintMap = map.ik[constraintName];
+				let keyMap = constraintMap[0];
+				if (!keyMap) continue;
+
+				let constraint = skeletonData.findIkConstraint(constraintName);
+				let constraintIndex = skeletonData.ikConstraints.indexOf(constraint);
+				let timeline = new IkConstraintTimeline(constraintMap.length, constraintMap.length << 1, constraintIndex);
+
+				let time = getValue(keyMap, "time", 0);
+				let mix = getValue(keyMap, "mix", 1);
+				let softness = getValue(keyMap, "softness", 0) * scale;
+
+				for (let frame = 0, bezier = 0; ; frame++) {
+					timeline.setFrame(frame, time, mix, softness, getValue(keyMap, "bendPositive", true) ? 1 : -1, getValue(keyMap, "compress", false), getValue(keyMap, "stretch", false));
+					let nextMap = constraintMap[frame + 1];
+					if (!nextMap) {
+						timeline.shrink(bezier);
+						break;
+					}
+
+					let time2 = getValue(nextMap, "time", 0);
+					let mix2 = getValue(nextMap, "mix", 1);
+					let softness2 = getValue(nextMap, "softness", 0) * scale;
+					let curve = keyMap.curve;
+					if (curve) {
+						bezier = readCurve(curve, timeline, bezier, frame, 0, time, time2, mix, mix2, 1);
+						bezier = readCurve(curve, timeline, bezier, frame, 1, time, time2, softness, softness2, scale);
+					}
+
+					time = time2;
+					mix = mix2;
+					softness = softness2;
+					keyMap = nextMap;
+				}
+				timelines.push(timeline);
+			}
+		}
+
+		// Transform constraint timelines.
+		if (map.transform) {
+			for (let constraintName in map.transform) {
+				let timelineMap = map.transform[constraintName];
+				let keyMap = timelineMap[0];
+				if (!keyMap) continue;
+
+				let constraint = skeletonData.findTransformConstraint(constraintName);
+				let constraintIndex = skeletonData.transformConstraints.indexOf(constraint);
+				let timeline = new TransformConstraintTimeline(timelineMap.length, timelineMap.length << 2, constraintIndex);
+
+				let time = getValue(keyMap, "time", 0);
+				let mixRotate = getValue(keyMap, "mixRotate", 1);
+				let mixX = getValue(keyMap, "mixX", 1);
+				let mixY = getValue(keyMap, "mixY", mixX);
+				let mixScaleX = getValue(keyMap, "mixScaleX", 1);
+				let mixScaleY = getValue(keyMap, "mixScaleY", mixScaleX);
+				let mixShearY = getValue(keyMap, "mixShearY", 1);
+
+				for (let frame = 0, bezier = 0; ; frame++) {
+					timeline.setFrame(frame, time, mixRotate, mixX, mixY, mixScaleX, mixScaleY, mixShearY);
+					let nextMap = timelineMap[frame + 1];
+					if (!nextMap) {
+						timeline.shrink(bezier);
+						break;
+					}
+
+					let time2 = getValue(nextMap, "time", 0);
+					let mixRotate2 = getValue(nextMap, "mixRotate", 1);
+					let mixX2 = getValue(nextMap, "mixX", 1);
+					let mixY2 = getValue(nextMap, "mixY", mixX2);
+					let mixScaleX2 = getValue(nextMap, "mixScaleX", 1);
+					let mixScaleY2 = getValue(nextMap, "mixScaleY", mixScaleX2);
+					let mixShearY2 = getValue(nextMap, "mixShearY", 1);
+					let curve = keyMap.curve;
+					if (curve) {
+						bezier = readCurve(curve, timeline, bezier, frame, 0, time, time2, mixRotate, mixRotate2, 1);
+						bezier = readCurve(curve, timeline, bezier, frame, 1, time, time2, mixX, mixX2, 1);
+						bezier = readCurve(curve, timeline, bezier, frame, 2, time, time2, mixY, mixY2, 1);
+						bezier = readCurve(curve, timeline, bezier, frame, 3, time, time2, mixScaleX, mixScaleX2, 1);
+						bezier = readCurve(curve, timeline, bezier, frame, 4, time, time2, mixScaleY, mixScaleY2, 1);
+						bezier = readCurve(curve, timeline, bezier, frame, 5, time, time2, mixShearY, mixShearY2, 1);
+					}
+
+					time = time2;
+					mixRotate = mixRotate2;
+					mixX = mixX2;
+					mixY = mixY2;
+					mixScaleX = mixScaleX2;
+					mixScaleY = mixScaleY2;
+					mixScaleX = mixScaleX2;
+					keyMap = nextMap;
+				}
+				timelines.push(timeline);
+			}
+		}
+
+		// Path constraint timelines.
+		if (map.path) {
+			for (let constraintName in map.path) {
+				let constraintMap = map.path[constraintName];
+				let constraint = skeletonData.findPathConstraint(constraintName);
+				let constraintIndex = skeletonData.pathConstraints.indexOf(constraint);
+				for (let timelineName in constraintMap) {
+					let timelineMap = constraintMap[timelineName];
+					let keyMap = timelineMap[0];
+					if (!keyMap) continue;
+
+					if (timelineName === "position") {
+						let timeline = new PathConstraintPositionTimeline(timelineMap.length, timelineMap.length, constraintIndex);
+						timelines.push(readTimeline1(timelineMap, timeline, 0, constraint.positionMode == PositionMode.Fixed ? scale : 1));
+					} else if (timelineName === "spacing") {
+						let timeline = new PathConstraintSpacingTimeline(timelineMap.length, timelineMap.length, constraintIndex);
+						timelines.push(readTimeline1(timelineMap, timeline, 0, constraint.spacingMode == SpacingMode.Length || constraint.spacingMode == SpacingMode.Fixed ? scale : 1));
+					} else if (timelineName === "mix") {
+						let timeline = new PathConstraintMixTimeline(timelineMap.size, timelineMap.size * 3, constraintIndex);
+						let time = getValue(keyMap, "time", 0);
+						let mixRotate = getValue(keyMap, "mixRotate", 1);
+						let mixX = getValue(keyMap, "mixX", 1);
+						let mixY = getValue(keyMap, "mixY", mixX);
+						for (let frame = 0, bezier = 0; ; frame++) {
+							timeline.setFrame(frame, time, mixRotate, mixX, mixY);
+							let nextMap = timelineMap[frame + 1];
+							if (!nextMap) {
+								timeline.shrink(bezier);
+								break;
+							}
+							let time2 = getValue(nextMap, "time", 0);
+							let mixRotate2 = getValue(nextMap, "mixRotate", 1);
+							let mixX2 = getValue(nextMap, "mixX", 1);
+							let mixY2 = getValue(nextMap, "mixY", mixX2);
+							let curve = keyMap.curve;
+							if (curve) {
+								bezier = readCurve(curve, timeline, bezier, frame, 0, time, time2, mixRotate, mixRotate2, 1);
+								bezier = readCurve(curve, timeline, bezier, frame, 1, time, time2, mixX, mixX2, 1);
+								bezier = readCurve(curve, timeline, bezier, frame, 2, time, time2, mixY, mixY2, 1);
+							}
+							time = time2;
+							mixRotate = mixRotate2;
+							mixX = mixX2;
+							mixY = mixY2;
+							keyMap = nextMap;
+						}
+						timelines.push(timeline);
+					}
+				}
+			}
+		}
+
+		// Deform timelines.
+		if (map.deform) {
+			for (let deformName in map.deform) {
+				let deformMap = map.deform[deformName];
+				let skin = skeletonData.findSkin(deformName);
+				for (let slotName in deformMap) {
+					let slotMap = deformMap[slotName];
+					let slotIndex = skeletonData.findSlotIndex(slotName);
+					for (let timelineName in slotMap) {
+						let timelineMap = slotMap[timelineName];
+						let keyMap = timelineMap[0];
+						if (!keyMap) continue;
+
+						let attachment = <VertexAttachment>skin.getAttachment(slotIndex, timelineName);
+						let weighted = attachment.bones;
+						let vertices = attachment.vertices;
+						let deformLength = weighted ? vertices.length / 3 * 2 : vertices.length;
+
+						let timeline = new DeformTimeline(timelineMap.length, timelineMap.length, slotIndex, attachment);
+						let time = getValue(keyMap, "time", 0);
+						for (let frame = 0, bezier = 0; ; frame++) {
+							let deform: NumberArrayLike;
+							let verticesValue: Array<Number> = getValue(keyMap, "vertices", null);
+							if (!verticesValue)
+								deform = weighted ? Utils.newFloatArray(deformLength) : vertices;
+							else {
+								deform = Utils.newFloatArray(deformLength);
+								let start = <number>getValue(keyMap, "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(frame, time, deform);
+							let nextMap = timelineMap[frame + 1];
+							if (!nextMap) {
+								timeline.shrink(bezier);
+								break;
+							}
+							let time2 = getValue(nextMap, "time", 0);
+							let curve = keyMap.curve;
+							if (curve) bezier = readCurve(curve, timeline, bezier, frame, 0, time, time2, 0, 1, 1);
+							time = time2;
+							keyMap = nextMap;
+						}
+						timelines.push(timeline);
+					}
+				}
+			}
+		}
+
+		// Draw order timelines.
+		if (map.drawOrder) {
+			let timeline = new DrawOrderTimeline(map.drawOrder.length);
+			let slotCount = skeletonData.slots.length;
+			let frame = 0;
+			for (let i = 0; i < map.drawOrder.length; i++, frame++) {
+				let drawOrderMap = map.drawOrder[i];
+				let drawOrder: Array<number> = null;
+				let offsets = getValue(drawOrderMap, "offsets", null);
+				if (offsets) {
+					drawOrder = Utils.newArray<number>(slotCount, -1);
+					let unchanged = Utils.newArray<number>(slotCount - offsets.length, 0);
+					let originalIndex = 0, unchangedIndex = 0;
+					for (let ii = 0; ii < offsets.length; ii++) {
+						let offsetMap = offsets[ii];
+						let slotIndex = skeletonData.findSlotIndex(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 ii = slotCount - 1; ii >= 0; ii--)
+						if (drawOrder[ii] == -1) drawOrder[ii] = unchanged[--unchangedIndex];
+				}
+				timeline.setFrame(frame, getValue(drawOrderMap, "time", 0), drawOrder);
+			}
+			timelines.push(timeline);
+		}
+
+		// Event timelines.
+		if (map.events) {
+			let timeline = new EventTimeline(map.events.length);
+			let frame = 0;
+			for (let i = 0; i < map.events.length; i++, frame++) {
+				let eventMap = map.events[i];
+				let eventData = skeletonData.findEvent(eventMap.name);
+				let event = new Event(Utils.toSinglePrecision(getValue(eventMap, "time", 0)), eventData);
+				event.intValue = getValue(eventMap, "int", eventData.intValue);
+				event.floatValue = getValue(eventMap, "float", eventData.floatValue);
+				event.stringValue = getValue(eventMap, "string", eventData.stringValue);
+				if (event.data.audioPath) {
+					event.volume = getValue(eventMap, "volume", 1);
+					event.balance = getValue(eventMap, "balance", 0);
+				}
+				timeline.setFrame(frame, event);
+			}
+			timelines.push(timeline);
+		}
+
+		let duration = 0;
+		for (let i = 0, n = timelines.length; i < n; i++)
+			duration = Math.max(duration, timelines[i].getDuration());
+		skeletonData.animations.push(new Animation(name, timelines, duration));
+	}
+}
+
+class LinkedMesh {
+	parent: string; skin: string;
+	slotIndex: number;
+	mesh: MeshAttachment;
+	inheritDeform: boolean;
+
+	constructor(mesh: MeshAttachment, skin: string, slotIndex: number, parent: string, inheritDeform: boolean) {
+		this.mesh = mesh;
+		this.skin = skin;
+		this.slotIndex = slotIndex;
+		this.parent = parent;
+		this.inheritDeform = inheritDeform;
+	}
+}
+
+function readTimeline1(keys: any[], timeline: CurveTimeline1, defaultValue: number, scale: number) {
+	let keyMap = keys[0];
+	let time = getValue(keyMap, "time", 0);
+	let value = getValue(keyMap, "value", defaultValue) * scale;
+	let bezier = 0;
+	for (let frame = 0; ; frame++) {
+		timeline.setFrame(frame, time, value);
+		let nextMap = keys[frame + 1];
+		if (!nextMap) {
+			timeline.shrink(bezier);
+			return timeline;
+		}
+		let time2 = getValue(nextMap, "time", 0);
+		let value2 = getValue(nextMap, "value", defaultValue) * scale;
+		if (keyMap.curve) bezier = readCurve(keyMap.curve, timeline, bezier, frame, 0, time, time2, value, value2, scale);
+		time = time2;
+		value = value2;
+		keyMap = nextMap;
+	}
+}
+
+function readTimeline2(keys: any[], timeline: CurveTimeline2, name1: string, name2: string, defaultValue: number, scale: number) {
+	let keyMap = keys[0];
+	let time = getValue(keyMap, "time", 0);
+	let value1 = getValue(keyMap, name1, defaultValue) * scale;
+	let value2 = getValue(keyMap, name2, defaultValue) * scale;
+	let bezier = 0;
+	for (let frame = 0; ; frame++) {
+		timeline.setFrame(frame, time, value1, value2);
+		let nextMap = keys[frame + 1];
+		if (!nextMap) {
+			timeline.shrink(bezier);
+			return timeline;
+		}
+		let time2 = getValue(nextMap, "time", 0);
+		let nvalue1 = getValue(nextMap, name1, defaultValue) * scale;
+		let nvalue2 = getValue(nextMap, name2, defaultValue) * scale;
+		let curve = keyMap.curve;
+		if (curve) {
+			bezier = readCurve(curve, timeline, bezier, frame, 0, time, time2, value1, nvalue1, scale);
+			bezier = readCurve(curve, timeline, bezier, frame, 1, time, time2, value2, nvalue2, scale);
+		}
+		time = time2;
+		value1 = nvalue1;
+		value2 = nvalue2;
+		keyMap = nextMap;
+	}
+}
+
+function readCurve(curve: any, timeline: CurveTimeline, bezier: number, frame: number, value: number, time1: number, time2: number,
+	value1: number, value2: number, scale: number) {
+	if (curve == "stepped") {
+		timeline.setStepped(frame);
+		return bezier;
+	}
+	let i = value << 2;
+	let cx1 = curve[i];
+	let cy1 = curve[i + 1] * scale;
+	let cx2 = curve[i + 2];
+	let cy2 = curve[i + 3] * scale;
+	timeline.setBezier(bezier, frame, value, time1, value1, cx1, cy1, cx2, cy2, time2, value2);
+	return bezier + 1;
+}
+
+function getValue(map: any, property: string, defaultValue: any) {
+	return map[property] !== undefined ? map[property] : defaultValue;
+}

+ 207 - 0
spine-ts/spine-core/src/Skin.ts

@@ -0,0 +1,207 @@
+/******************************************************************************
+ * Spine Runtimes License Agreement
+ * Last updated January 1, 2020. Replaces all prior versions.
+ *
+ * Copyright (c) 2013-2020, Esoteric Software LLC
+ *
+ * Integration of the Spine Runtimes into software or otherwise creating
+ * derivative works of the Spine Runtimes is permitted under the terms and
+ * conditions of Section 2 of the Spine Editor License Agreement:
+ * http://esotericsoftware.com/spine-editor-license
+ *
+ * Otherwise, it is permitted to integrate the Spine Runtimes into software
+ * or otherwise create derivative works of the Spine Runtimes (collectively,
+ * "Products"), provided that each user of the Products must obtain their own
+ * Spine Editor license and redistribution of the Products in any form must
+ * include this license and copyright notice.
+ *
+ * THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "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 LLC 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
+ * THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *****************************************************************************/
+
+import { Attachment } from "./attachments/Attachment";
+import { MeshAttachment } from "./attachments/MeshAttachment";
+import { BoneData } from "./BoneData";
+import { ConstraintData } from "./ConstraintData";
+import { Skeleton } from "./Skeleton";
+import { StringMap } from "./Utils";
+
+/** Stores an entry in the skin consisting of the slot index, name, and attachment **/
+export class SkinEntry {
+	constructor(public slotIndex: number, public name: string, public attachment: Attachment) { }
+}
+
+/** Stores attachments by slot index and attachment name.
+ *
+ * See SkeletonData {@link SkeletonData#defaultSkin}, Skeleton {@link Skeleton#skin}, and
+ * [Runtime skins](http://esotericsoftware.com/spine-runtime-skins) in the Spine Runtimes Guide. */
+export class Skin {
+	/** The skin's name, which is unique across all skins in the skeleton. */
+	name: string;
+
+	attachments = new Array<StringMap<Attachment>>();
+	bones = Array<BoneData>();
+	constraints = new Array<ConstraintData>();
+
+	constructor(name: string) {
+		if (!name) throw new Error("name cannot be null.");
+		this.name = name;
+	}
+
+	/** Adds an attachment to the skin for the specified slot index and name. */
+	setAttachment(slotIndex: number, name: string, attachment: Attachment) {
+		if (!attachment) 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;
+	}
+
+	/** Adds all attachments, bones, and constraints from the specified skin to this skin. */
+	addSkin(skin: Skin) {
+		for (let i = 0; i < skin.bones.length; i++) {
+			let bone = skin.bones[i];
+			let contained = false;
+			for (let ii = 0; ii < this.bones.length; ii++) {
+				if (this.bones[ii] == bone) {
+					contained = true;
+					break;
+				}
+			}
+			if (!contained) this.bones.push(bone);
+		}
+
+		for (let i = 0; i < skin.constraints.length; i++) {
+			let constraint = skin.constraints[i];
+			let contained = false;
+			for (let ii = 0; ii < this.constraints.length; ii++) {
+				if (this.constraints[ii] == constraint) {
+					contained = true;
+					break;
+				}
+			}
+			if (!contained) this.constraints.push(constraint);
+		}
+
+		let attachments = skin.getAttachments();
+		for (let i = 0; i < attachments.length; i++) {
+			var attachment = attachments[i];
+			this.setAttachment(attachment.slotIndex, attachment.name, attachment.attachment);
+		}
+	}
+
+	/** Adds all bones and constraints and copies of all attachments from the specified skin to this skin. Mesh attachments are not
+	 * copied, instead a new linked mesh is created. The attachment copies can be modified without affecting the originals. */
+	copySkin(skin: Skin) {
+		for (let i = 0; i < skin.bones.length; i++) {
+			let bone = skin.bones[i];
+			let contained = false;
+			for (let ii = 0; ii < this.bones.length; ii++) {
+				if (this.bones[ii] == bone) {
+					contained = true;
+					break;
+				}
+			}
+			if (!contained) this.bones.push(bone);
+		}
+
+		for (let i = 0; i < skin.constraints.length; i++) {
+			let constraint = skin.constraints[i];
+			let contained = false;
+			for (let ii = 0; ii < this.constraints.length; ii++) {
+				if (this.constraints[ii] == constraint) {
+					contained = true;
+					break;
+				}
+			}
+			if (!contained) this.constraints.push(constraint);
+		}
+
+		let attachments = skin.getAttachments();
+		for (let i = 0; i < attachments.length; i++) {
+			var attachment = attachments[i];
+			if (!attachment.attachment) continue;
+			if (attachment.attachment instanceof MeshAttachment) {
+				attachment.attachment = attachment.attachment.newLinkedMesh();
+				this.setAttachment(attachment.slotIndex, attachment.name, attachment.attachment);
+			} else {
+				attachment.attachment = attachment.attachment.copy();
+				this.setAttachment(attachment.slotIndex, attachment.name, attachment.attachment);
+			}
+		}
+	}
+
+	/** Returns the attachment for the specified slot index and name, or null. */
+	getAttachment(slotIndex: number, name: string): Attachment {
+		let dictionary = this.attachments[slotIndex];
+		return dictionary ? dictionary[name] : null;
+	}
+
+	/** Removes the attachment in the skin for the specified slot index and name, if any. */
+	removeAttachment(slotIndex: number, name: string) {
+		let dictionary = this.attachments[slotIndex];
+		if (dictionary) dictionary[name] = null;
+	}
+
+	/** Returns all attachments in this skin. */
+	getAttachments(): Array<SkinEntry> {
+		let entries = new Array<SkinEntry>();
+		for (var i = 0; i < this.attachments.length; i++) {
+			let slotAttachments = this.attachments[i];
+			if (slotAttachments) {
+				for (let name in slotAttachments) {
+					let attachment = slotAttachments[name];
+					if (attachment) entries.push(new SkinEntry(i, name, attachment));
+				}
+			}
+		}
+		return entries;
+	}
+
+	/** Returns all attachments in this skin for the specified slot index. */
+	getAttachmentsForSlot(slotIndex: number, attachments: Array<SkinEntry>) {
+		let slotAttachments = this.attachments[slotIndex];
+		if (slotAttachments) {
+			for (let name in slotAttachments) {
+				let attachment = slotAttachments[name];
+				if (attachment) attachments.push(new SkinEntry(slotIndex, name, attachment));
+			}
+		}
+	}
+
+	/** Clears all attachments, bones, and constraints. */
+	clear() {
+		this.attachments.length = 0;
+		this.bones.length = 0;
+		this.constraints.length = 0;
+	}
+
+	/** 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, key);
+						if (attachment) slot.setAttachment(attachment);
+						break;
+					}
+				}
+			}
+			slotIndex++;
+		}
+	}
+}

+ 121 - 0
spine-ts/spine-core/src/Slot.ts

@@ -0,0 +1,121 @@
+/******************************************************************************
+ * Spine Runtimes License Agreement
+ * Last updated January 1, 2020. Replaces all prior versions.
+ *
+ * Copyright (c) 2013-2020, Esoteric Software LLC
+ *
+ * Integration of the Spine Runtimes into software or otherwise creating
+ * derivative works of the Spine Runtimes is permitted under the terms and
+ * conditions of Section 2 of the Spine Editor License Agreement:
+ * http://esotericsoftware.com/spine-editor-license
+ *
+ * Otherwise, it is permitted to integrate the Spine Runtimes into software
+ * or otherwise create derivative works of the Spine Runtimes (collectively,
+ * "Products"), provided that each user of the Products must obtain their own
+ * Spine Editor license and redistribution of the Products in any form must
+ * include this license and copyright notice.
+ *
+ * THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "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 LLC 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
+ * THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *****************************************************************************/
+
+import { Attachment, VertexAttachment } from "./attachments/Attachment";
+import { Bone } from "./Bone";
+import { Skeleton } from "./Skeleton";
+import { SlotData } from "./SlotData";
+import { Color } from "./Utils";
+
+/** Stores a slot's current pose. Slots organize attachments for {@link Skeleton#drawOrder} purposes and provide a place to store
+ * state for an attachment. State cannot be stored in an attachment itself because attachments are stateless and may be shared
+ * across multiple skeletons. */
+export class Slot {
+	/** The slot's setup pose data. */
+	data: SlotData;
+
+	/** The bone this slot belongs to. */
+	bone: Bone;
+
+	/** The color used to tint the slot's attachment. If {@link #getDarkColor()} is set, this is used as the light color for two
+	 * color tinting. */
+	color: Color;
+
+	/** The dark color used to tint the slot's attachment for two color tinting, or null if two color tinting is not used. The dark
+	 * color's alpha is not used. */
+	darkColor: Color;
+
+	attachment: Attachment;
+
+	private attachmentTime: number;
+
+	attachmentState: number;
+
+	/** Values to deform the slot's attachment. For an unweighted mesh, the entries are local positions for each vertex. For a
+	 * weighted mesh, the entries are an offset for each vertex which will be added to the mesh's local vertex positions.
+	 *
+	 * See {@link VertexAttachment#computeWorldVertices()} and {@link DeformTimeline}. */
+	deform = new Array<number>();
+
+	constructor(data: SlotData, bone: Bone) {
+		if (!data) throw new Error("data cannot be null.");
+		if (!bone) throw new Error("bone cannot be null.");
+		this.data = data;
+		this.bone = bone;
+		this.color = new Color();
+		this.darkColor = !data.darkColor ? null : new Color();
+		this.setToSetupPose();
+	}
+
+	/** The skeleton this slot belongs to. */
+	getSkeleton(): Skeleton {
+		return this.bone.skeleton;
+	}
+
+	/** The current attachment for the slot, or null if the slot has no attachment. */
+	getAttachment(): Attachment {
+		return this.attachment;
+	}
+
+	/** Sets the slot's attachment and, if the attachment changed, resets {@link #attachmentTime} and clears the {@link #deform}.
+	 * The deform is not cleared if the old attachment has the same {@link VertexAttachment#getDeformAttachment()} as the specified
+	 * attachment.
+	 * @param attachment May be null. */
+	setAttachment(attachment: Attachment) {
+		if (this.attachment == attachment) return;
+		if (!(attachment instanceof VertexAttachment) || !(this.attachment instanceof VertexAttachment)
+			|| (<VertexAttachment>attachment).deformAttachment != (<VertexAttachment>this.attachment).deformAttachment) {
+			this.deform.length = 0;
+		}
+		this.attachment = attachment;
+		this.attachmentTime = this.bone.skeleton.time;
+	}
+
+	setAttachmentTime(time: number) {
+		this.attachmentTime = this.bone.skeleton.time - time;
+	}
+
+	/** The time that has elapsed since the last time the attachment was set or cleared. Relies on Skeleton
+	 * {@link Skeleton#time}. */
+	getAttachmentTime(): number {
+		return this.bone.skeleton.time - this.attachmentTime;
+	}
+
+	/** Sets this slot to the setup pose. */
+	setToSetupPose() {
+		this.color.setFromColor(this.data.color);
+		if (this.darkColor) this.darkColor.setFromColor(this.data.darkColor);
+		if (!this.data.attachmentName)
+			this.attachment = null;
+		else {
+			this.attachment = null;
+			this.setAttachment(this.bone.skeleton.getAttachment(this.data.index, this.data.attachmentName));
+		}
+	}
+}

+ 69 - 0
spine-ts/spine-core/src/SlotData.ts

@@ -0,0 +1,69 @@
+/******************************************************************************
+ * Spine Runtimes License Agreement
+ * Last updated January 1, 2020. Replaces all prior versions.
+ *
+ * Copyright (c) 2013-2020, Esoteric Software LLC
+ *
+ * Integration of the Spine Runtimes into software or otherwise creating
+ * derivative works of the Spine Runtimes is permitted under the terms and
+ * conditions of Section 2 of the Spine Editor License Agreement:
+ * http://esotericsoftware.com/spine-editor-license
+ *
+ * Otherwise, it is permitted to integrate the Spine Runtimes into software
+ * or otherwise create derivative works of the Spine Runtimes (collectively,
+ * "Products"), provided that each user of the Products must obtain their own
+ * Spine Editor license and redistribution of the Products in any form must
+ * include this license and copyright notice.
+ *
+ * THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "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 LLC 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
+ * THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *****************************************************************************/
+
+import { BoneData } from "./BoneData";
+import { Color } from "./Utils";
+
+/** Stores the setup pose for a {@link Slot}. */
+export class SlotData {
+	/** The index of the slot in {@link Skeleton#getSlots()}. */
+	index: number;
+
+	/** The name of the slot, which is unique across all slots in the skeleton. */
+	name: string;
+
+	/** The bone this slot belongs to. */
+	boneData: BoneData;
+
+	/** The color used to tint the slot's attachment. If {@link #getDarkColor()} is set, this is used as the light color for two
+	 * color tinting. */
+	color = new Color(1, 1, 1, 1);
+
+	/** The dark color used to tint the slot's attachment for two color tinting, or null if two color tinting is not used. The dark
+	 * color's alpha is not used. */
+	darkColor: Color;
+
+	/** The name of the attachment that is visible for this slot in the setup pose, or null if no attachment is visible. */
+	attachmentName: string;
+
+	/** The blend mode for drawing the slot's attachment. */
+	blendMode: BlendMode;
+
+	constructor(index: number, name: string, boneData: BoneData) {
+		if (index < 0) throw new Error("index must be >= 0.");
+		if (!name) throw new Error("name cannot be null.");
+		if (!boneData) throw new Error("boneData cannot be null.");
+		this.index = index;
+		this.name = name;
+		this.boneData = boneData;
+	}
+}
+
+/** Determines how images are blended with existing pixels when drawn. */
+export enum BlendMode { Normal, Additive, Multiply, Screen }

+ 40 - 19
spine-ts/core/src/attachments/AttachmentLoader.ts → spine-ts/spine-core/src/Texture.ts

@@ -27,29 +27,50 @@
  * THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  *****************************************************************************/
 
-module spine {
+export abstract class Texture {
+	protected _image: HTMLImageElement | ImageBitmap;
 
-	/** The interface which can be implemented to customize creating and populating attachments.
-	 *
-	 * See [Loading skeleton data](http://esotericsoftware.com/spine-loading-skeleton-data#AttachmentLoader) in the Spine
-	 * Runtimes Guide. */
-	export interface AttachmentLoader {
-		/** @return May be null to not load an attachment. */
-		newRegionAttachment (skin: Skin, name: string, path: string): RegionAttachment;
+	constructor(image: HTMLImageElement | ImageBitmap) {
+		this._image = image;
+	}
+
+	getImage(): HTMLImageElement | ImageBitmap {
+		return this._image;
+	}
 
-		/** @return May be null to not load an attachment. */
-		newMeshAttachment (skin: Skin, name: string, path: string): MeshAttachment;
+	abstract setFilters(minFilter: TextureFilter, magFilter: TextureFilter): void;
+	abstract setWraps(uWrap: TextureWrap, vWrap: TextureWrap): void;
+	abstract dispose(): void;
+}
 
-		/** @return May be null to not load an attachment. */
-		newBoundingBoxAttachment (skin: Skin, name: string): BoundingBoxAttachment;
+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
+}
 
-		/** @return May be null to not load an attachment */
-		newPathAttachment (skin: Skin, name: string): PathAttachment;
+export enum TextureWrap {
+	MirroredRepeat = 33648, // WebGLRenderingContext.MIRRORED_REPEAT
+	ClampToEdge = 33071, // WebGLRenderingContext.CLAMP_TO_EDGE
+	Repeat = 10497 // WebGLRenderingContext.REPEAT
+}
 
-		/** @return May be null to not load an attachment */
-		newPointAttachment (skin: Skin, name: string): PointAttachment;
+export class TextureRegion {
+	renderObject: any;
+	u = 0; v = 0;
+	u2 = 0; v2 = 0;
+	width = 0; height = 0;
+	degrees = 0;
+	offsetX = 0; offsetY = 0;
+	originalWidth = 0; originalHeight = 0;
+}
 
-		/** @return May be null to not load an attachment */
-		newClippingAttachment (skin: Skin, name: string): ClippingAttachment;
-	}
+export class FakeTexture extends Texture {
+	setFilters(minFilter: TextureFilter, magFilter: TextureFilter) { }
+	setWraps(uWrap: TextureWrap, vWrap: TextureWrap) { }
+	dispose() { }
 }

+ 266 - 0
spine-ts/spine-core/src/TextureAtlas.ts

@@ -0,0 +1,266 @@
+/******************************************************************************
+ * Spine Runtimes License Agreement
+ * Last updated January 1, 2020. Replaces all prior versions.
+ *
+ * Copyright (c) 2013-2020, Esoteric Software LLC
+ *
+ * Integration of the Spine Runtimes into software or otherwise creating
+ * derivative works of the Spine Runtimes is permitted under the terms and
+ * conditions of Section 2 of the Spine Editor License Agreement:
+ * http://esotericsoftware.com/spine-editor-license
+ *
+ * Otherwise, it is permitted to integrate the Spine Runtimes into software
+ * or otherwise create derivative works of the Spine Runtimes (collectively,
+ * "Products"), provided that each user of the Products must obtain their own
+ * Spine Editor license and redistribution of the Products in any form must
+ * include this license and copyright notice.
+ *
+ * THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "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 LLC 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
+ * THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *****************************************************************************/
+
+import { AssetManager } from "./AssetManager";
+import { TextureFilter, TextureWrap, Texture, TextureRegion } from "./Texture";
+import { Disposable, Utils, StringMap } from "./Utils";
+
+export class TextureAtlas implements Disposable {
+	pages = new Array<TextureAtlasPage>();
+	regions = new Array<TextureAtlasRegion>();
+
+	constructor(atlasText: string) {
+		let reader = new TextureAtlasReader(atlasText);
+		let entry = new Array<string>(4);
+		let page: TextureAtlasPage = null;
+		let region: TextureAtlasRegion = null;
+
+		let pageFields: StringMap<Function> = {};
+		pageFields["size"] = () => {
+			page.width = parseInt(entry[1]);
+			page.height = parseInt(entry[2]);
+		};
+		pageFields["format"] = () => {
+			// page.format = Format[tuple[0]]; we don't need format in WebGL
+		};
+		pageFields["filter"] = () => {
+			page.minFilter = Utils.enumValue(TextureFilter, entry[1]);
+			page.magFilter = Utils.enumValue(TextureFilter, entry[2]);
+		};
+		pageFields["repeat"] = () => {
+			if (entry[1].indexOf('x') != -1) page.uWrap = TextureWrap.Repeat;
+			if (entry[1].indexOf('y') != -1) page.vWrap = TextureWrap.Repeat;
+		};
+		pageFields["pma"] = () => {
+			page.pma = entry[1] == "true";
+		};
+
+		var regionFields: StringMap<Function> = {};
+		regionFields["xy"] = () => { // Deprecated, use bounds.
+			region.x = parseInt(entry[1]);
+			region.y = parseInt(entry[2]);
+		};
+		regionFields["size"] = () => { // Deprecated, use bounds.
+			region.width = parseInt(entry[1]);
+			region.height = parseInt(entry[2]);
+		};
+		regionFields["bounds"] = () => {
+			region.x = parseInt(entry[1]);
+			region.y = parseInt(entry[2]);
+			region.width = parseInt(entry[3]);
+			region.height = parseInt(entry[4]);
+		};
+		regionFields["offset"] = () => { // Deprecated, use offsets.
+			region.offsetX = parseInt(entry[1]);
+			region.offsetY = parseInt(entry[2]);
+		};
+		regionFields["orig"] = () => { // Deprecated, use offsets.
+			region.originalWidth = parseInt(entry[1]);
+			region.originalHeight = parseInt(entry[2]);
+		};
+		regionFields["offsets"] = () => {
+			region.offsetX = parseInt(entry[1]);
+			region.offsetY = parseInt(entry[2]);
+			region.originalWidth = parseInt(entry[3]);
+			region.originalHeight = parseInt(entry[4]);
+		};
+		regionFields["rotate"] = () => {
+			let value = entry[1];
+			if (value == "true")
+				region.degrees = 90;
+			else if (value != "false")
+				region.degrees = parseInt(value);
+		};
+		regionFields["index"] = () => {
+			region.index = parseInt(entry[1]);
+		};
+
+		let line = reader.readLine();
+		// Ignore empty lines before first entry.
+		while (line && line.trim().length == 0)
+			line = reader.readLine();
+		// Header entries.
+		while (true) {
+			if (!line || line.trim().length == 0) break;
+			if (reader.readEntry(entry, line) == 0) break; // Silently ignore all header fields.
+			line = reader.readLine();
+		}
+
+		// Page and region entries.
+		let names: string[] = null;
+		let values: number[][] = null;
+		while (true) {
+			if (line === null) break;
+			if (line.trim().length == 0) {
+				page = null;
+				line = reader.readLine();
+			} else if (!page) {
+				page = new TextureAtlasPage();
+				page.name = line.trim();
+				while (true) {
+					if (reader.readEntry(entry, line = reader.readLine()) == 0) break;
+					let field: Function = pageFields[entry[0]];
+					if (field) field();
+				}
+				this.pages.push(page);
+			} else {
+				region = new TextureAtlasRegion();
+
+				region.page = page;
+				region.name = line;
+				while (true) {
+					let count = reader.readEntry(entry, line = reader.readLine());
+					if (count == 0) break;
+					let field: Function = regionFields[entry[0]];
+					if (field)
+						field();
+					else {
+						if (!names) {
+							names = [];
+							values = [];
+						}
+						names.push(entry[0]);
+						let entryValues: number[] = [];
+						for (let i = 0; i < count; i++)
+							entryValues.push(parseInt(entry[i + 1]));
+						values.push(entryValues);
+					}
+				}
+				if (region.originalWidth == 0 && region.originalHeight == 0) {
+					region.originalWidth = region.width;
+					region.originalHeight = region.height;
+				}
+				if (names && names.length > 0) {
+					region.names = names;
+					region.values = values;
+					names = null;
+					values = null;
+				}
+				region.u = region.x / page.width;
+				region.v = region.y / page.height;
+				if (region.degrees == 90) {
+					region.u2 = (region.x + region.height) / page.width;
+					region.v2 = (region.y + region.width) / page.height;
+				} else {
+					region.u2 = (region.x + region.width) / page.width;
+					region.v2 = (region.y + region.height) / page.height;
+				}
+				this.regions.push(region);
+			}
+		}
+	}
+
+	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;
+	}
+
+	setTextures(assetManager: AssetManager, pathPrefix: string = "") {
+		for (let page of this.pages)
+			page.setTexture(assetManager.get(pathPrefix + page.name));
+	}
+
+	dispose() {
+		for (let i = 0; i < this.pages.length; i++) {
+			this.pages[i].texture.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++];
+	}
+
+	readEntry(entry: string[], line: string): number {
+		if (!line) return 0;
+		line = line.trim();
+		if (line.length == 0) return 0;
+
+		let colon = line.indexOf(':');
+		if (colon == -1) return 0;
+		entry[0] = line.substr(0, colon).trim();
+		for (let i = 1, lastMatch = colon + 1; ; i++) {
+			let comma = line.indexOf(',', lastMatch);
+			if (comma == -1) {
+				entry[i] = line.substr(lastMatch).trim();
+				return i;
+			}
+			entry[i] = line.substr(lastMatch, comma - lastMatch).trim();
+			lastMatch = comma + 1;
+			if (i == 4) return 4;
+		}
+	}
+}
+
+export class TextureAtlasPage {
+	name: string;
+	minFilter: TextureFilter = TextureFilter.Nearest;
+	magFilter: TextureFilter = TextureFilter.Nearest;
+	uWrap: TextureWrap = TextureWrap.ClampToEdge;
+	vWrap: TextureWrap = TextureWrap.ClampToEdge;
+	texture: Texture;
+	width: number;
+	height: number;
+	pma: boolean;
+
+	setTexture(texture: Texture) {
+		this.texture = texture;
+		texture.setFilters(this.minFilter, this.magFilter);
+		texture.setWraps(this.uWrap, this.vWrap);
+	}
+}
+
+export class TextureAtlasRegion extends TextureRegion {
+	page: TextureAtlasPage;
+	name: string;
+	x: number;
+	y: number;
+	offsetX: number;
+	offsetY: number;
+	originalWidth: number;
+	originalHeight: number;
+	index: number;
+	degrees: number;
+	names: string[];
+	values: number[][];
+}

+ 283 - 0
spine-ts/spine-core/src/TransformConstraint.ts

@@ -0,0 +1,283 @@
+/******************************************************************************
+ * Spine Runtimes License Agreement
+ * Last updated January 1, 2020. Replaces all prior versions.
+ *
+ * Copyright (c) 2013-2020, Esoteric Software LLC
+ *
+ * Integration of the Spine Runtimes into software or otherwise creating
+ * derivative works of the Spine Runtimes is permitted under the terms and
+ * conditions of Section 2 of the Spine Editor License Agreement:
+ * http://esotericsoftware.com/spine-editor-license
+ *
+ * Otherwise, it is permitted to integrate the Spine Runtimes into software
+ * or otherwise create derivative works of the Spine Runtimes (collectively,
+ * "Products"), provided that each user of the Products must obtain their own
+ * Spine Editor license and redistribution of the Products in any form must
+ * include this license and copyright notice.
+ *
+ * THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "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 LLC 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
+ * THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *****************************************************************************/
+
+import { Bone } from "./Bone";
+import { Skeleton } from "./Skeleton";
+import { TransformConstraintData } from "./TransformConstraintData";
+import { Updatable } from "./Updatable";
+import { Vector2, MathUtils } from "./Utils";
+
+
+/** Stores the current pose for a transform constraint. A transform constraint adjusts the world transform of the constrained
+ * bones to match that of the target bone.
+ *
+ * See [Transform constraints](http://esotericsoftware.com/spine-transform-constraints) in the Spine User Guide. */
+export class TransformConstraint implements Updatable {
+
+	/** The transform constraint's setup pose data. */
+	data: TransformConstraintData;
+
+	/** The bones that will be modified by this transform constraint. */
+	bones: Array<Bone>;
+
+	/** The target bone whose world transform will be copied to the constrained bones. */
+	target: Bone;
+
+	mixRotate = 0; mixX = 0; mixY = 0; mixScaleX = 0; mixScaleY = 0; mixShearY = 0;
+
+	temp = new Vector2();
+	active = false;
+
+	constructor(data: TransformConstraintData, skeleton: Skeleton) {
+		if (!data) throw new Error("data cannot be null.");
+		if (!skeleton) throw new Error("skeleton cannot be null.");
+		this.data = data;
+		this.mixRotate = data.mixRotate;
+		this.mixX = data.mixX;
+		this.mixY = data.mixY;
+		this.mixScaleX = data.mixScaleX;
+		this.mixScaleY = data.mixScaleY;
+		this.mixShearY = data.mixShearY;
+		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);
+	}
+
+	isActive() {
+		return this.active;
+	}
+
+	update() {
+		if (this.mixRotate == 0 && this.mixX == 0 && this.mixY == 0 && this.mixScaleX == 0 && this.mixScaleX == 0 && this.mixShearY == 0) return;
+
+		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 mixRotate = this.mixRotate, mixX = this.mixX, mixY = this.mixY, mixScaleX = this.mixScaleX,
+			mixScaleY = this.mixScaleY, mixShearY = this.mixShearY;
+		let translate = mixX != 0 || mixY != 0;
+
+		let target = this.target;
+		let ta = target.a, tb = target.b, tc = target.c, td = target.d;
+		let degRadReflect = ta * td - tb * tc > 0 ? MathUtils.degRad : -MathUtils.degRad;
+		let offsetRotation = this.data.offsetRotation * degRadReflect;
+		let offsetShearY = this.data.offsetShearY * degRadReflect;
+
+		let bones = this.bones;
+		for (let i = 0, n = bones.length; i < n; i++) {
+			let bone = bones[i];
+
+			if (mixRotate != 0) {
+				let a = bone.a, b = bone.b, c = bone.c, d = bone.d;
+				let r = Math.atan2(tc, ta) - Math.atan2(c, a) + offsetRotation;
+				if (r > MathUtils.PI)
+					r -= MathUtils.PI2;
+				else if (r < -MathUtils.PI) //
+					r += MathUtils.PI2;
+				r *= mixRotate;
+				let cos = Math.cos(r), sin = Math.sin(r);
+				bone.a = cos * a - sin * c;
+				bone.b = cos * b - sin * d;
+				bone.c = sin * a + cos * c;
+				bone.d = sin * b + cos * d;
+			}
+
+			if (translate) {
+				let temp = this.temp;
+				target.localToWorld(temp.set(this.data.offsetX, this.data.offsetY));
+				bone.worldX += (temp.x - bone.worldX) * mixX;
+				bone.worldY += (temp.y - bone.worldY) * mixY;
+			}
+
+			if (mixScaleX != 0) {
+				let s = Math.sqrt(bone.a * bone.a + bone.c * bone.c);
+				if (s != 0) s = (s + (Math.sqrt(ta * ta + tc * tc) - s + this.data.offsetScaleX) * mixScaleX) / s;
+				bone.a *= s;
+				bone.c *= s;
+			}
+			if (mixScaleY != 0) {
+				let s = Math.sqrt(bone.b * bone.b + bone.d * bone.d);
+				if (s != 0) s = (s + (Math.sqrt(tb * tb + td * td) - s + this.data.offsetScaleY) * mixScaleY) / s;
+				bone.b *= s;
+				bone.d *= s;
+			}
+
+			if (mixShearY > 0) {
+				let b = bone.b, d = bone.d;
+				let by = Math.atan2(d, b);
+				let r = Math.atan2(td, tb) - Math.atan2(tc, ta) - (by - Math.atan2(bone.c, bone.a));
+				if (r > MathUtils.PI)
+					r -= MathUtils.PI2;
+				else if (r < -MathUtils.PI) //
+					r += MathUtils.PI2;
+				r = by + (r + offsetShearY) * mixShearY;
+				let s = Math.sqrt(b * b + d * d);
+				bone.b = Math.cos(r) * s;
+				bone.d = Math.sin(r) * s;
+			}
+
+			bone.updateAppliedTransform();
+		}
+	}
+
+	applyRelativeWorld() {
+		let mixRotate = this.mixRotate, mixX = this.mixX, mixY = this.mixY, mixScaleX = this.mixScaleX,
+			mixScaleY = this.mixScaleY, mixShearY = this.mixShearY;
+		let translate = mixX != 0 || mixY != 0;
+
+		let target = this.target;
+		let ta = target.a, tb = target.b, tc = target.c, td = target.d;
+		let degRadReflect = ta * td - tb * tc > 0 ? MathUtils.degRad : -MathUtils.degRad;
+		let offsetRotation = this.data.offsetRotation * degRadReflect, offsetShearY = this.data.offsetShearY * degRadReflect;
+
+		let bones = this.bones;
+		for (let i = 0, n = bones.length; i < n; i++) {
+			let bone = bones[i];
+
+			if (mixRotate != 0) {
+				let a = bone.a, b = bone.b, c = bone.c, d = bone.d;
+				let r = Math.atan2(tc, ta) + offsetRotation;
+				if (r > MathUtils.PI)
+					r -= MathUtils.PI2;
+				else if (r < -MathUtils.PI) //
+					r += MathUtils.PI2;
+				r *= mixRotate;
+				let cos = Math.cos(r), sin = Math.sin(r);
+				bone.a = cos * a - sin * c;
+				bone.b = cos * b - sin * d;
+				bone.c = sin * a + cos * c;
+				bone.d = sin * b + cos * d;
+			}
+
+			if (translate) {
+				let temp = this.temp;
+				target.localToWorld(temp.set(this.data.offsetX, this.data.offsetY));
+				bone.worldX += temp.x * mixX;
+				bone.worldY += temp.y * mixY;
+			}
+
+			if (mixScaleX != 0) {
+				let s = (Math.sqrt(ta * ta + tc * tc) - 1 + this.data.offsetScaleX) * mixScaleX + 1;
+				bone.a *= s;
+				bone.c *= s;
+			}
+			if (mixScaleY != 0) {
+				let s = (Math.sqrt(tb * tb + td * td) - 1 + this.data.offsetScaleY) * mixScaleY + 1;
+				bone.b *= s;
+				bone.d *= s;
+			}
+
+			if (mixShearY > 0) {
+				let r = Math.atan2(td, tb) - Math.atan2(tc, ta);
+				if (r > MathUtils.PI)
+					r -= MathUtils.PI2;
+				else if (r < -MathUtils.PI) //
+					r += MathUtils.PI2;
+				let b = bone.b, d = bone.d;
+				r = Math.atan2(d, b) + (r - MathUtils.PI / 2 + offsetShearY) * mixShearY;
+				let s = Math.sqrt(b * b + d * d);
+				bone.b = Math.cos(r) * s;
+				bone.d = Math.sin(r) * s;
+			}
+
+			bone.updateAppliedTransform();
+		}
+	}
+
+	applyAbsoluteLocal() {
+		let mixRotate = this.mixRotate, mixX = this.mixX, mixY = this.mixY, mixScaleX = this.mixScaleX,
+			mixScaleY = this.mixScaleY, mixShearY = this.mixShearY;
+
+		let target = this.target;
+
+		let bones = this.bones;
+		for (let i = 0, n = bones.length; i < n; i++) {
+			let bone = bones[i];
+
+			let rotation = bone.arotation;
+			if (mixRotate != 0) {
+				let r = target.arotation - rotation + this.data.offsetRotation;
+				r -= (16384 - ((16384.499999999996 - r / 360) | 0)) * 360;
+				rotation += r * mixRotate;
+			}
+
+			let x = bone.ax, y = bone.ay;
+			x += (target.ax - x + this.data.offsetX) * mixX;
+			y += (target.ay - y + this.data.offsetY) * mixY;
+
+			let scaleX = bone.ascaleX, scaleY = bone.ascaleY;
+			if (mixScaleX != 0 && scaleX != 0)
+				scaleX = (scaleX + (target.ascaleX - scaleX + this.data.offsetScaleX) * mixScaleX) / scaleX;
+			if (mixScaleY != 0 && scaleY != 0)
+				scaleY = (scaleY + (target.ascaleY - scaleY + this.data.offsetScaleY) * mixScaleY) / scaleY;
+
+			let shearY = bone.ashearY;
+			if (mixShearY != 0) {
+				let r = target.ashearY - shearY + this.data.offsetShearY;
+				r -= (16384 - ((16384.499999999996 - r / 360) | 0)) * 360;
+				shearY += r * mixShearY;
+			}
+
+			bone.updateWorldTransformWith(x, y, rotation, scaleX, scaleY, bone.ashearX, shearY);
+		}
+	}
+
+	applyRelativeLocal() {
+		let mixRotate = this.mixRotate, mixX = this.mixX, mixY = this.mixY, mixScaleX = this.mixScaleX,
+			mixScaleY = this.mixScaleY, mixShearY = this.mixShearY;
+
+		let target = this.target;
+
+		let bones = this.bones;
+		for (let i = 0, n = bones.length; i < n; i++) {
+			let bone = bones[i];
+
+			let rotation = bone.arotation + (target.arotation + this.data.offsetRotation) * mixRotate;
+			let x = bone.ax + (target.ax + this.data.offsetX) * mixX;
+			let y = bone.ay + (target.ay + this.data.offsetY) * mixY;
+			let scaleX = (bone.ascaleX * ((target.ascaleX - 1 + this.data.offsetScaleX) * mixScaleX) + 1);
+			let scaleY = (bone.ascaleY * ((target.ascaleY - 1 + this.data.offsetScaleY) * mixScaleY) + 1);
+			let shearY = bone.ashearY + (target.ashearY + this.data.offsetShearY) * mixShearY;
+
+			bone.updateWorldTransformWith(x, y, rotation, scaleX, scaleY, bone.ashearX, shearY);
+		}
+	}
+}

+ 32 - 32
spine-ts/core/src/TransformConstraintData.ts → spine-ts/spine-core/src/TransformConstraintData.ts

@@ -27,49 +27,49 @@
  * THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  *****************************************************************************/
 
-module spine {
+import { ConstraintData } from "./ConstraintData";
+import { BoneData } from "./BoneData";
 
-	/** Stores the setup pose for a {@link TransformConstraint}.
-	 *
-	 * See [Transform constraints](http://esotericsoftware.com/spine-transform-constraints) in the Spine User Guide. */
-	export class TransformConstraintData extends ConstraintData {
+/** Stores the setup pose for a {@link TransformConstraint}.
+ *
+ * See [Transform constraints](http://esotericsoftware.com/spine-transform-constraints) in the Spine User Guide. */
+export class TransformConstraintData extends ConstraintData {
 
-		/** The bones that will be modified by this transform constraint. */
-		bones = new Array<BoneData>();
+	/** The bones that will be modified by this transform constraint. */
+	bones = new Array<BoneData>();
 
-		/** The target bone whose world transform will be copied to the constrained bones. */
-		target: BoneData;
+	/** The target bone whose world transform will be copied to the constrained bones. */
+	target: BoneData;
 
-		mixRotate = 0;
-		mixX = 0;
-		mixY = 0;
-		mixScaleX = 0;
-		mixScaleY = 0;
-		mixShearY = 0;
+	mixRotate = 0;
+	mixX = 0;
+	mixY = 0;
+	mixScaleX = 0;
+	mixScaleY = 0;
+	mixShearY = 0;
 
-		/** An offset added to the constrained bone rotation. */
-		offsetRotation = 0;
+	/** An offset added to the constrained bone rotation. */
+	offsetRotation = 0;
 
-		/** An offset added to the constrained bone X translation. */
-		offsetX = 0;
+	/** An offset added to the constrained bone X translation. */
+	offsetX = 0;
 
-		/** An offset added to the constrained bone Y translation. */
-		offsetY = 0;
+	/** An offset added to the constrained bone Y translation. */
+	offsetY = 0;
 
-		/** An offset added to the constrained bone scaleX. */
-		offsetScaleX = 0;
+	/** An offset added to the constrained bone scaleX. */
+	offsetScaleX = 0;
 
-		/** An offset added to the constrained bone scaleY. */
-		offsetScaleY = 0;
+	/** An offset added to the constrained bone scaleY. */
+	offsetScaleY = 0;
 
-		/** An offset added to the constrained bone shearY. */
-		offsetShearY = 0;
+	/** An offset added to the constrained bone shearY. */
+	offsetShearY = 0;
 
-		relative = false;
-		local = false;
+	relative = false;
+	local = false;
 
-		constructor (name: string) {
-			super(name, 0, false);
-		}
+	constructor(name: string) {
+		super(name, 0, false);
 	}
 }

+ 269 - 0
spine-ts/spine-core/src/Triangulator.ts

@@ -0,0 +1,269 @@
+/******************************************************************************
+ * Spine Runtimes License Agreement
+ * Last updated January 1, 2020. Replaces all prior versions.
+ *
+ * Copyright (c) 2013-2020, Esoteric Software LLC
+ *
+ * Integration of the Spine Runtimes into software or otherwise creating
+ * derivative works of the Spine Runtimes is permitted under the terms and
+ * conditions of Section 2 of the Spine Editor License Agreement:
+ * http://esotericsoftware.com/spine-editor-license
+ *
+ * Otherwise, it is permitted to integrate the Spine Runtimes into software
+ * or otherwise create derivative works of the Spine Runtimes (collectively,
+ * "Products"), provided that each user of the Products must obtain their own
+ * Spine Editor license and redistribution of the Products in any form must
+ * include this license and copyright notice.
+ *
+ * THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "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 LLC 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
+ * THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *****************************************************************************/
+
+import { NumberArrayLike, Pool } from "./Utils";
+
+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: NumberArrayLike): 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: NumberArrayLike, indices: NumberArrayLike): 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;
+	}
+}

+ 8 - 11
spine-ts/core/src/Updatable.ts → spine-ts/spine-core/src/Updatable.ts

@@ -27,16 +27,13 @@
  * THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  *****************************************************************************/
 
-module spine {
+/** The interface for items updated by {@link Skeleton#updateWorldTransform()}. */
+export interface Updatable {
+	update(): void;
 
-	/** The interface for items updated by {@link Skeleton#updateWorldTransform()}. */
-	export interface Updatable {
-		update (): void;
-
-		/** Returns false when this item has not been updated because a skin is required and the {@link Skeleton#skin active skin}
-		 * does not contain this item.
-		 * @see Skin#getBones()
-		 * @see Skin#getConstraints() */
-		isActive (): boolean;
-	}
+	/** Returns false when this item has not been updated because a skin is required and the {@link Skeleton#skin active skin}
+	 * does not contain this item.
+	 * @see Skin#getBones()
+	 * @see Skin#getConstraints() */
+	isActive(): boolean;
 }

+ 468 - 0
spine-ts/spine-core/src/Utils.ts

@@ -0,0 +1,468 @@
+/******************************************************************************
+ * Spine Runtimes License Agreement
+ * Last updated January 1, 2020. Replaces all prior versions.
+ *
+ * Copyright (c) 2013-2020, Esoteric Software LLC
+ *
+ * Integration of the Spine Runtimes into software or otherwise creating
+ * derivative works of the Spine Runtimes is permitted under the terms and
+ * conditions of Section 2 of the Spine Editor License Agreement:
+ * http://esotericsoftware.com/spine-editor-license
+ *
+ * Otherwise, it is permitted to integrate the Spine Runtimes into software
+ * or otherwise create derivative works of the Spine Runtimes (collectively,
+ * "Products"), provided that each user of the Products must obtain their own
+ * Spine Editor license and redistribution of the Products in any form must
+ * include this license and copyright notice.
+ *
+ * THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "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 LLC 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
+ * THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *****************************************************************************/
+
+import { Skeleton } from "./Skeleton";
+import { MixBlend } from "./Animation";
+
+export interface StringMap<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 class StringSet {
+	entries: StringMap<boolean> = {};
+	size = 0;
+
+	add(value: string): boolean {
+		let contains = this.entries[value];
+		this.entries[value] = true;
+		if (!contains) {
+			this.size++;
+			return true;
+		}
+		return false;
+	}
+
+	addAll(values: string[]): boolean {
+		let oldSize = this.size;
+		for (var i = 0, n = values.length; i < n; i++)
+			this.add(values[i]);
+		return oldSize != this.size;
+	}
+
+	contains(value: string) {
+		return this.entries[value];
+	}
+
+	clear() {
+		this.entries = {};
+		this.size = 0;
+	}
+}
+
+export interface NumberArrayLike {
+	readonly length: number;
+	[n: number]: number;
+}
+
+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;
+		return this.clamp();
+	}
+
+	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;
+		this.g = parseInt(hex.substr(2, 2), 16) / 255;
+		this.b = parseInt(hex.substr(4, 2), 16) / 255;
+		this.a = hex.length != 8 ? 1 : parseInt(hex.substr(6, 2), 16) / 255;
+		return this;
+	}
+
+	add(r: number, g: number, b: number, a: number) {
+		this.r += r;
+		this.g += g;
+		this.b += b;
+		this.a += a;
+		return this.clamp();
+	}
+
+	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;
+	}
+
+	static rgba8888ToColor(color: Color, value: number) {
+		color.r = ((value & 0xff000000) >>> 24) / 255;
+		color.g = ((value & 0x00ff0000) >>> 16) / 255;
+		color.b = ((value & 0x0000ff00) >>> 8) / 255;
+		color.a = ((value & 0x000000ff)) / 255;
+	}
+
+	static rgb888ToColor(color: Color, value: number) {
+		color.r = ((value & 0x00ff0000) >>> 16) / 255;
+		color.g = ((value & 0x0000ff00) >>> 8) / 255;
+		color.b = ((value & 0x000000ff)) / 255;
+	}
+
+	static fromString(hex: string): Color {
+		return new Color().setFromString(hex);
+	}
+}
+
+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));
+	}
+
+	static isPowerOfTwo(value: number) {
+		return value && (value & (value - 1)) === 0;
+	}
+}
+
+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 arrayFill<T>(array: ArrayLike<T>, fromIndex: number, toIndex: number, value: T) {
+		for (let i = fromIndex; i < toIndex; i++)
+			array[i] = value;
+	}
+
+	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): NumberArrayLike {
+		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): NumberArrayLike {
+		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;
+	}
+
+	// This function is used to fix WebKit 602 specific issue described at http://esotericsoftware.com/forum/iOS-10-disappearing-graphics-10109
+	static webkit602BugfixHelper(alpha: number, blend: MixBlend) {
+	}
+
+	static contains<T>(array: Array<T>, element: T, identity = true) {
+		for (var i = 0; i < array.length; i++)
+			if (array[i] == element) return true;
+		return false;
+	}
+
+	static enumValue(type: any, name: string) {
+		return type[name[0].toUpperCase() + name.slice(1)];
+	}
+}
+
+export class DebugUtils {
+	static logBones(skeleton: Skeleton) {
+		for (let i = 0; i < skeleton.bones.length; i++) {
+			let bone = skeleton.bones[i];
+			console.log(bone.data.name + ", " + bone.a + ", " + bone.b + ", " + bone.c + ", " + bone.d + ", " + bone.worldX + ", " + bone.worldY);
+		}
+	}
+}
+
+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++)
+			this.free(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() {
+		let 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;
+		}
+		return 0;
+	}
+}

+ 7 - 6
spine-ts/core/src/VertexEffect.ts → spine-ts/spine-core/src/VertexEffect.ts

@@ -27,10 +27,11 @@
  * THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  *****************************************************************************/
 
-module spine {
-	export interface VertexEffect {
-		begin (skeleton: Skeleton): void;
-		transform (position: Vector2, uv: Vector2, light: Color, dark: Color): void;
-		end (): void;
-	}
+import { Skeleton } from "./Skeleton";
+import { Vector2, Color } from "./Utils";
+
+export interface VertexEffect {
+	begin(skeleton: Skeleton): void;
+	transform(position: Vector2, uv: Vector2, light: Color, dark: Color): void;
+	end(): void;
 }

+ 160 - 0
spine-ts/spine-core/src/attachments/Attachment.ts

@@ -0,0 +1,160 @@
+/******************************************************************************
+ * Spine Runtimes License Agreement
+ * Last updated January 1, 2020. Replaces all prior versions.
+ *
+ * Copyright (c) 2013-2020, Esoteric Software LLC
+ *
+ * Integration of the Spine Runtimes into software or otherwise creating
+ * derivative works of the Spine Runtimes is permitted under the terms and
+ * conditions of Section 2 of the Spine Editor License Agreement:
+ * http://esotericsoftware.com/spine-editor-license
+ *
+ * Otherwise, it is permitted to integrate the Spine Runtimes into software
+ * or otherwise create derivative works of the Spine Runtimes (collectively,
+ * "Products"), provided that each user of the Products must obtain their own
+ * Spine Editor license and redistribution of the Products in any form must
+ * include this license and copyright notice.
+ *
+ * THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "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 LLC 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
+ * THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *****************************************************************************/
+
+import { Slot } from "../Slot";
+import { NumberArrayLike, Utils } from "../Utils";
+
+/** The base class for all attachments. */
+export abstract class Attachment {
+	name: string;
+
+	constructor(name: string) {
+		if (!name) throw new Error("name cannot be null.");
+		this.name = name;
+	}
+
+	abstract copy(): Attachment;
+}
+
+/** Base class for an attachment with vertices that are transformed by one or more bones and can be deformed by a slot's
+ * {@link Slot#deform}. */
+export abstract class VertexAttachment extends Attachment {
+	private static nextID = 0;
+
+	/** The unique ID for this attachment. */
+	id = VertexAttachment.nextID++;
+
+	/** The bones which affect the {@link #getVertices()}. The array entries are, for each vertex, the number of bones affecting
+	 * the vertex followed by that many bone indices, which is the index of the bone in {@link Skeleton#bones}. Will be null
+	 * if this attachment has no weights. */
+	bones: Array<number>;
+
+	/** The vertex positions in the bone's coordinate system. For a non-weighted attachment, the values are `x,y`
+	 * entries for each vertex. For a weighted attachment, the values are `x,y,weight` entries for each bone affecting
+	 * each vertex. */
+	vertices: NumberArrayLike;
+
+	/** The maximum number of world vertex values that can be output by
+	 * {@link #computeWorldVertices()} using the `count` parameter. */
+	worldVerticesLength = 0;
+
+	/** Deform keys for the deform attachment are also applied to this attachment. May be null if no deform keys should be applied. */
+	deformAttachment: VertexAttachment = this;
+
+	constructor(name: string) {
+		super(name);
+	}
+
+	/** Transforms the attachment's local {@link #vertices} to world coordinates. If the slot's {@link Slot#deform} is
+	 * not empty, it is used to deform the vertices.
+	 *
+	 * See [World transforms](http://esotericsoftware.com/spine-runtime-skeletons#World-transforms) in the Spine
+	 * Runtimes Guide.
+	 * @param start The index of the first {@link #vertices} value to transform. Each vertex has 2 values, x and y.
+	 * @param count The number of world vertex values to output. Must be <= {@link #worldVerticesLength} - `start`.
+	 * @param worldVertices The output world vertices. Must have a length >= `offset` + `count` *
+	 *           `stride` / 2.
+	 * @param offset The `worldVertices` index to begin writing values.
+	 * @param stride The number of `worldVertices` entries between the value pairs written. */
+	computeWorldVertices(slot: Slot, start: number, count: number, worldVertices: NumberArrayLike, offset: number, stride: number) {
+		count = offset + (count >> 1) * stride;
+		let skeleton = slot.bone.skeleton;
+		let deformArray = slot.deform;
+		let vertices = this.vertices;
+		let bones = this.bones;
+		if (!bones) {
+			if (deformArray.length > 0) vertices = deformArray;
+			let bone = slot.bone;
+			let x = bone.worldX;
+			let y = bone.worldY;
+			let a = bone.a, b = bone.b, c = bone.c, d = bone.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 bone = skeletonBones[bones[v]];
+					let vx = vertices[b], vy = vertices[b + 1], weight = vertices[b + 2];
+					wx += (vx * bone.a + vy * bone.b + bone.worldX) * weight;
+					wy += (vx * bone.c + vy * bone.d + bone.worldY) * 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 bone = skeletonBones[bones[v]];
+					let vx = vertices[b] + deform[f], vy = vertices[b + 1] + deform[f + 1], weight = vertices[b + 2];
+					wx += (vx * bone.a + vy * bone.b + bone.worldX) * weight;
+					wy += (vx * bone.c + vy * bone.d + bone.worldY) * weight;
+				}
+				worldVertices[w] = wx;
+				worldVertices[w + 1] = wy;
+			}
+		}
+	}
+
+	/** Does not copy id (generated) or name (set on construction). **/
+	copyTo(attachment: VertexAttachment) {
+		if (this.bones) {
+			attachment.bones = new Array<number>(this.bones.length);
+			Utils.arrayCopy(this.bones, 0, attachment.bones, 0, this.bones.length);
+		} else
+			attachment.bones = null;
+
+		if (this.vertices) {
+			attachment.vertices = Utils.newFloatArray(this.vertices.length);
+			Utils.arrayCopy(this.vertices, 0, attachment.vertices, 0, this.vertices.length);
+		} else
+			attachment.vertices = null;
+
+		attachment.worldVerticesLength = this.worldVerticesLength;
+		attachment.deformAttachment = this.deformAttachment;
+	}
+}

+ 25 - 34
spine-ts/core/src/SlotData.ts → spine-ts/spine-core/src/attachments/AttachmentLoader.ts

@@ -27,43 +27,34 @@
  * THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  *****************************************************************************/
 
-module spine {
-
-	/** Stores the setup pose for a {@link Slot}. */
-	export class SlotData {
-		/** The index of the slot in {@link Skeleton#getSlots()}. */
-		index: number;
-
-		/** The name of the slot, which is unique across all slots in the skeleton. */
-		name: string;
-
-		/** The bone this slot belongs to. */
-		boneData: BoneData;
-
-		/** The color used to tint the slot's attachment. If {@link #getDarkColor()} is set, this is used as the light color for two
-		 * color tinting. */
-		color = new Color(1, 1, 1, 1);
+import { Skin } from "../Skin";
+import { BoundingBoxAttachment } from "./BoundingBoxAttachment";
+import { ClippingAttachment } from "./ClippingAttachment";
+import { MeshAttachment } from "./MeshAttachment";
+import { PathAttachment } from "./PathAttachment";
+import { PointAttachment } from "./PointAttachment";
+import { RegionAttachment } from "./RegionAttachment";
+
+/** The interface which can be implemented to customize creating and populating attachments.
+ *
+ * See [Loading skeleton data](http://esotericsoftware.com/spine-loading-skeleton-data#AttachmentLoader) in the Spine
+ * Runtimes Guide. */
+export interface AttachmentLoader {
+	/** @return May be null to not load an attachment. */
+	newRegionAttachment(skin: Skin, name: string, path: string): RegionAttachment;
 
-		/** The dark color used to tint the slot's attachment for two color tinting, or null if two color tinting is not used. The dark
-		 * color's alpha is not used. */
-		darkColor: Color;
+	/** @return May be null to not load an attachment. */
+	newMeshAttachment(skin: Skin, name: string, path: string): MeshAttachment;
 
-		/** The name of the attachment that is visible for this slot in the setup pose, or null if no attachment is visible. */
-		attachmentName: string;
+	/** @return May be null to not load an attachment. */
+	newBoundingBoxAttachment(skin: Skin, name: string): BoundingBoxAttachment;
 
-		/** The blend mode for drawing the slot's attachment. */
-		blendMode: BlendMode;
+	/** @return May be null to not load an attachment */
+	newPathAttachment(skin: Skin, name: string): PathAttachment;
 
-		constructor (index: number, name: string, boneData: BoneData) {
-			if (index < 0) throw new Error("index must be >= 0.");
-			if (!name) throw new Error("name cannot be null.");
-			if (!boneData) throw new Error("boneData cannot be null.");
-			this.index = index;
-			this.name = name;
-			this.boneData = boneData;
-		}
-	}
+	/** @return May be null to not load an attachment */
+	newPointAttachment(skin: Skin, name: string): PointAttachment;
 
-	/** Determines how images are blended with existing pixels when drawn. */
-	export enum BlendMode { Normal, Additive, Multiply, Screen }
+	/** @return May be null to not load an attachment */
+	newClippingAttachment(skin: Skin, name: string): ClippingAttachment;
 }

+ 18 - 17
spine-ts/core/src/attachments/BoundingBoxAttachment.ts → spine-ts/spine-core/src/attachments/BoundingBoxAttachment.ts

@@ -27,24 +27,25 @@
  * THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  *****************************************************************************/
 
-module spine {
-	/** An attachment with vertices that make up a polygon. Can be used for hit detection, creating physics bodies, spawning particle
-	 * effects, and more.
-	 *
-	 * See {@link SkeletonBounds} and [Bounding Boxes](http://esotericsoftware.com/spine-bounding-boxes) in the Spine User
-	 * Guide. */
-	export class BoundingBoxAttachment extends VertexAttachment {
-		color = new Color(1, 1, 1, 1);
+import { Color } from "../Utils";
+import { VertexAttachment, Attachment } from "./Attachment";
 
-		constructor (name: string) {
-			super(name);
-		}
+/** An attachment with vertices that make up a polygon. Can be used for hit detection, creating physics bodies, spawning particle
+ * effects, and more.
+ *
+ * See {@link SkeletonBounds} and [Bounding Boxes](http://esotericsoftware.com/spine-bounding-boxes) in the Spine User
+ * Guide. */
+export class BoundingBoxAttachment extends VertexAttachment {
+	color = new Color(1, 1, 1, 1);
+
+	constructor(name: string) {
+		super(name);
+	}
 
-		copy (): Attachment {
-			let copy = new BoundingBoxAttachment(this.name);
-			this.copyTo(copy);
-			copy.color.setFromColor(this.color);
-			return copy;
-		}
+	copy(): Attachment {
+		let copy = new BoundingBoxAttachment(this.name);
+		this.copyTo(copy);
+		copy.color.setFromColor(this.color);
+		return copy;
 	}
 }

+ 22 - 20
spine-ts/core/src/attachments/ClippingAttachment.ts → spine-ts/spine-core/src/attachments/ClippingAttachment.ts

@@ -27,28 +27,30 @@
  * THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  *****************************************************************************/
 
-module spine {
-	/** An attachment with vertices that make up a polygon used for clipping the rendering of other attachments. */
-	export class ClippingAttachment extends VertexAttachment {
-		/** Clipping is performed between the clipping polygon's slot and the end slot. Returns null if clipping is done until the end of
-		 * the skeleton's rendering. */
-		endSlot: SlotData;
+import { SlotData } from "../SlotData";
+import { Color } from "../Utils";
+import { VertexAttachment, Attachment } from "./Attachment";
 
-		// Nonessential.
-		/** The color of the clipping polygon as it was in Spine. Available only when nonessential data was exported. Clipping polygons
-		 * are not usually rendered at runtime. */
-		color = new Color(0.2275, 0.2275, 0.8078, 1); // ce3a3aff
+/** An attachment with vertices that make up a polygon used for clipping the rendering of other attachments. */
+export class ClippingAttachment extends VertexAttachment {
+	/** Clipping is performed between the clipping polygon's slot and the end slot. Returns null if clipping is done until the end of
+	 * the skeleton's rendering. */
+	endSlot: SlotData;
 
-		constructor (name: string) {
-			super(name);
-		}
+	// Nonessential.
+	/** The color of the clipping polygon as it was in Spine. Available only when nonessential data was exported. Clipping polygons
+	 * are not usually rendered at runtime. */
+	color = new Color(0.2275, 0.2275, 0.8078, 1); // ce3a3aff
 
-		copy (): Attachment {
-			let copy = new ClippingAttachment(this.name);
-			this.copyTo(copy);
-			copy.endSlot = this.endSlot;
-			copy.color.setFromColor(this.color);
-			return copy;
-		}
+	constructor(name: string) {
+		super(name);
+	}
+
+	copy(): Attachment {
+		let copy = new ClippingAttachment(this.name);
+		this.copyTo(copy);
+		copy.endSlot = this.endSlot;
+		copy.color.setFromColor(this.color);
+		return copy;
 	}
 }

+ 200 - 0
spine-ts/spine-core/src/attachments/MeshAttachment.ts

@@ -0,0 +1,200 @@
+/******************************************************************************
+ * Spine Runtimes License Agreement
+ * Last updated January 1, 2020. Replaces all prior versions.
+ *
+ * Copyright (c) 2013-2020, Esoteric Software LLC
+ *
+ * Integration of the Spine Runtimes into software or otherwise creating
+ * derivative works of the Spine Runtimes is permitted under the terms and
+ * conditions of Section 2 of the Spine Editor License Agreement:
+ * http://esotericsoftware.com/spine-editor-license
+ *
+ * Otherwise, it is permitted to integrate the Spine Runtimes into software
+ * or otherwise create derivative works of the Spine Runtimes (collectively,
+ * "Products"), provided that each user of the Products must obtain their own
+ * Spine Editor license and redistribution of the Products in any form must
+ * include this license and copyright notice.
+ *
+ * THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "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 LLC 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
+ * THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *****************************************************************************/
+
+import { TextureRegion } from "../Texture";
+import { TextureAtlasRegion } from "../TextureAtlas";
+import { Color, NumberArrayLike, Utils } from "../Utils";
+import { VertexAttachment, Attachment } from "./Attachment";
+
+/** An attachment that displays a textured mesh. A mesh has hull vertices and internal vertices within the hull. Holes are not
+ * supported. Each vertex has UVs (texture coordinates) and triangles are used to map an image on to the mesh.
+ *
+ * See [Mesh attachments](http://esotericsoftware.com/spine-meshes) in the Spine User Guide. */
+export class MeshAttachment extends VertexAttachment {
+	region: TextureRegion;
+
+	/** The name of the texture region for this attachment. */
+	path: string;
+
+	/** The UV pair for each vertex, normalized within the texture region. */
+	regionUVs: NumberArrayLike;
+
+	/** The UV pair for each vertex, normalized within the entire texture.
+	 *
+	 * See {@link #updateUVs}. */
+	uvs: NumberArrayLike;
+
+	/** Triplets of vertex indices which describe the mesh's triangulation. */
+	triangles: Array<number>;
+
+	/** The color to tint the mesh. */
+	color = new Color(1, 1, 1, 1);
+
+	/** The width of the mesh's image. Available only when nonessential data was exported. */
+	width: number;
+
+	/** The height of the mesh's image. Available only when nonessential data was exported. */
+	height: number;
+
+	/** The number of entries at the beginning of {@link #vertices} that make up the mesh hull. */
+	hullLength: number;
+
+	/** Vertex index pairs describing edges for controling triangulation. Mesh triangles will never cross edges. Only available if
+	 * nonessential data was exported. Triangulation is not performed at runtime. */
+	edges: Array<number>;
+
+	private parentMesh: MeshAttachment;
+	tempColor = new Color(0, 0, 0, 0);
+
+	constructor(name: string) {
+		super(name);
+	}
+
+	/** Calculates {@link #uvs} using {@link #regionUVs} and the {@link #region}. Must be called after changing the region UVs or
+	 * region. */
+	updateUVs() {
+		let regionUVs = this.regionUVs;
+		if (!this.uvs || this.uvs.length != regionUVs.length) this.uvs = Utils.newFloatArray(regionUVs.length);
+		let uvs = this.uvs;
+		let n = this.uvs.length;
+		let u = this.region.u, v = this.region.v, width = 0, height = 0;
+		if (this.region instanceof TextureAtlasRegion) {
+			let region = this.region, image = region.page.texture.getImage();
+			let textureWidth = image.width, textureHeight = image.height;
+			switch (region.degrees) {
+				case 90:
+					u -= (region.originalHeight - region.offsetY - region.height) / textureWidth;
+					v -= (region.originalWidth - region.offsetX - region.width) / textureHeight;
+					width = region.originalHeight / textureWidth;
+					height = region.originalWidth / textureHeight;
+					for (let i = 0; i < n; i += 2) {
+						uvs[i] = u + regionUVs[i + 1] * width;
+						uvs[i + 1] = v + (1 - regionUVs[i]) * height;
+					}
+					return;
+				case 180:
+					u -= (region.originalWidth - region.offsetX - region.width) / textureWidth;
+					v -= region.offsetY / textureHeight;
+					width = region.originalWidth / textureWidth;
+					height = region.originalHeight / textureHeight;
+					for (let i = 0; i < n; i += 2) {
+						uvs[i] = u + (1 - regionUVs[i]) * width;
+						uvs[i + 1] = v + (1 - regionUVs[i + 1]) * height;
+					}
+					return;
+				case 270:
+					u -= region.offsetY / textureWidth;
+					v -= region.offsetX / textureHeight;
+					width = region.originalHeight / textureWidth;
+					height = region.originalWidth / textureHeight;
+					for (let i = 0; i < n; i += 2) {
+						uvs[i] = u + (1 - regionUVs[i + 1]) * width;
+						uvs[i + 1] = v + regionUVs[i] * height;
+					}
+					return;
+			}
+			u -= region.offsetX / textureWidth;
+			v -= (region.originalHeight - region.offsetY - region.height) / textureHeight;
+			width = region.originalWidth / textureWidth;
+			height = region.originalHeight / textureHeight;
+		} else if (!this.region) {
+			u = v = 0;
+			width = height = 1;
+		} else {
+			width = this.region.u2 - u;
+			height = this.region.v2 - v;
+		}
+
+		for (let i = 0; i < n; i += 2) {
+			uvs[i] = u + regionUVs[i] * width;
+			uvs[i + 1] = v + regionUVs[i + 1] * height;
+		}
+	}
+
+	/** The parent mesh if this is a linked mesh, else null. A linked mesh shares the {@link #bones}, {@link #vertices},
+	 * {@link #regionUVs}, {@link #triangles}, {@link #hullLength}, {@link #edges}, {@link #width}, and {@link #height} with the
+	 * parent mesh, but may have a different {@link #name} or {@link #path} (and therefore a different texture). */
+	getParentMesh() {
+		return this.parentMesh;
+	}
+
+	/** @param parentMesh May be null. */
+	setParentMesh(parentMesh: MeshAttachment) {
+		this.parentMesh = parentMesh;
+		if (parentMesh) {
+			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
+		}
+	}
+
+	copy(): Attachment {
+		if (this.parentMesh) return this.newLinkedMesh();
+
+		let copy = new MeshAttachment(this.name);
+		copy.region = this.region;
+		copy.path = this.path;
+		copy.color.setFromColor(this.color);
+
+		this.copyTo(copy);
+		copy.regionUVs = new Array<number>(this.regionUVs.length);
+		Utils.arrayCopy(this.regionUVs, 0, copy.regionUVs, 0, this.regionUVs.length);
+		copy.uvs = new Array<number>(this.uvs.length);
+		Utils.arrayCopy(this.uvs, 0, copy.uvs, 0, this.uvs.length);
+		copy.triangles = new Array<number>(this.triangles.length);
+		Utils.arrayCopy(this.triangles, 0, copy.triangles, 0, this.triangles.length);
+		copy.hullLength = this.hullLength;
+
+		// Nonessential.
+		if (this.edges) {
+			copy.edges = new Array<number>(this.edges.length);
+			Utils.arrayCopy(this.edges, 0, copy.edges, 0, this.edges.length);
+		}
+		copy.width = this.width;
+		copy.height = this.height;
+
+		return copy;
+	}
+
+	/** Returns a new mesh with the {@link #parentMesh} set to this mesh's parent mesh, if any, else to this mesh. **/
+	newLinkedMesh(): MeshAttachment {
+		let copy = new MeshAttachment(this.name);
+		copy.region = this.region;
+		copy.path = this.path;
+		copy.color.setFromColor(this.color);
+		copy.deformAttachment = this.deformAttachment;
+		copy.setParentMesh(this.parentMesh ? this.parentMesh : this);
+		copy.updateUVs();
+		return copy;
+	}
+}

+ 29 - 29
spine-ts/core/src/attachments/PathAttachment.ts → spine-ts/spine-core/src/attachments/PathAttachment.ts

@@ -27,40 +27,40 @@
  * THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  *****************************************************************************/
 
-module spine {
+import { Color, Utils } from "../Utils";
+import { VertexAttachment, Attachment } from "./Attachment";
 
-	/** An attachment whose vertices make up a composite Bezier curve.
-	 *
-	 * See {@link PathConstraint} and [Paths](http://esotericsoftware.com/spine-paths) in the Spine User Guide. */
-	export class PathAttachment extends VertexAttachment {
+/** An attachment whose vertices make up a composite Bezier curve.
+ *
+ * See {@link PathConstraint} and [Paths](http://esotericsoftware.com/spine-paths) in the Spine User Guide. */
+export class PathAttachment extends VertexAttachment {
 
-		/** The lengths along the path in the setup pose from the start of the path to the end of each Bezier curve. */
-		lengths: Array<number>;
+	/** The lengths along the path in the setup pose from the start of the path to the end of each Bezier curve. */
+	lengths: Array<number>;
 
-		/** If true, the start and end knots are connected. */
-		closed = false;
+	/** If true, the start and end knots are connected. */
+	closed = false;
 
-		/** If true, additional calculations are performed to make calculating positions along the path more accurate. If false, fewer
-		 * calculations are performed but calculating positions along the path is less accurate. */
-		constantSpeed = false;
+	/** If true, additional calculations are performed to make calculating positions along the path more accurate. If false, fewer
+	 * calculations are performed but calculating positions along the path is less accurate. */
+	constantSpeed = false;
 
-		/** The color of the path as it was in Spine. Available only when nonessential data was exported. Paths are not usually
-		 * rendered at runtime. */
-		color = new Color(1, 1, 1, 1);
+	/** The color of the path as it was in Spine. Available only when nonessential data was exported. Paths are not usually
+	 * rendered at runtime. */
+	color = new Color(1, 1, 1, 1);
 
-		constructor (name: string) {
-			super(name);
-		}
+	constructor(name: string) {
+		super(name);
+	}
 
-		copy (): Attachment {
-			let copy = new PathAttachment(this.name);
-			this.copyTo(copy);
-			copy.lengths = new Array<number>(this.lengths.length);
-			Utils.arrayCopy(this.lengths, 0, copy.lengths, 0, this.lengths.length);
-			copy.closed = closed;
-			copy.constantSpeed = this.constantSpeed;
-			copy.color.setFromColor(this.color);
-			return copy;
-		}
+	copy(): Attachment {
+		let copy = new PathAttachment(this.name);
+		this.copyTo(copy);
+		copy.lengths = new Array<number>(this.lengths.length);
+		Utils.arrayCopy(this.lengths, 0, copy.lengths, 0, this.lengths.length);
+		copy.closed = closed;
+		copy.constantSpeed = this.constantSpeed;
+		copy.color.setFromColor(this.color);
+		return copy;
 	}
-}
+}

+ 71 - 0
spine-ts/spine-core/src/attachments/PointAttachment.ts

@@ -0,0 +1,71 @@
+/******************************************************************************
+ * Spine Runtimes License Agreement
+ * Last updated January 1, 2020. Replaces all prior versions.
+ *
+ * Copyright (c) 2013-2020, Esoteric Software LLC
+ *
+ * Integration of the Spine Runtimes into software or otherwise creating
+ * derivative works of the Spine Runtimes is permitted under the terms and
+ * conditions of Section 2 of the Spine Editor License Agreement:
+ * http://esotericsoftware.com/spine-editor-license
+ *
+ * Otherwise, it is permitted to integrate the Spine Runtimes into software
+ * or otherwise create derivative works of the Spine Runtimes (collectively,
+ * "Products"), provided that each user of the Products must obtain their own
+ * Spine Editor license and redistribution of the Products in any form must
+ * include this license and copyright notice.
+ *
+ * THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "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 LLC 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
+ * THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *****************************************************************************/
+
+import { Bone } from "../Bone";
+import { Color, Vector2, MathUtils } from "../Utils";
+import { VertexAttachment, Attachment } from "./Attachment";
+
+/** An attachment which is a single point and a rotation. This can be used to spawn projectiles, particles, etc. A bone can be
+ * used in similar ways, but a PointAttachment is slightly less expensive to compute and can be hidden, shown, and placed in a
+ * skin.
+ *
+ * See [Point Attachments](http://esotericsoftware.com/spine-point-attachments) in the Spine User Guide. */
+export class PointAttachment extends VertexAttachment {
+	x: number; y: number; rotation: number;
+
+	/** The color of the point attachment as it was in Spine. Available only when nonessential data was exported. Point attachments
+	 * are not usually rendered at runtime. */
+	color = new Color(0.38, 0.94, 0, 1);
+
+	constructor(name: string) {
+		super(name);
+	}
+
+	computeWorldPosition(bone: Bone, point: Vector2) {
+		point.x = this.x * bone.a + this.y * bone.b + bone.worldX;
+		point.y = this.x * bone.c + this.y * bone.d + bone.worldY;
+		return point;
+	}
+
+	computeWorldRotation(bone: Bone) {
+		let cos = MathUtils.cosDeg(this.rotation), sin = MathUtils.sinDeg(this.rotation);
+		let x = cos * bone.a + sin * bone.b;
+		let y = cos * bone.c + sin * bone.d;
+		return Math.atan2(y, x) * MathUtils.radDeg;
+	}
+
+	copy(): Attachment {
+		let copy = new PointAttachment(this.name);
+		copy.x = this.x;
+		copy.y = this.y;
+		copy.rotation = this.rotation;
+		copy.color.setFromColor(this.color);
+		return copy;
+	}
+}

+ 228 - 0
spine-ts/spine-core/src/attachments/RegionAttachment.ts

@@ -0,0 +1,228 @@
+/******************************************************************************
+ * Spine Runtimes License Agreement
+ * Last updated January 1, 2020. Replaces all prior versions.
+ *
+ * Copyright (c) 2013-2020, Esoteric Software LLC
+ *
+ * Integration of the Spine Runtimes into software or otherwise creating
+ * derivative works of the Spine Runtimes is permitted under the terms and
+ * conditions of Section 2 of the Spine Editor License Agreement:
+ * http://esotericsoftware.com/spine-editor-license
+ *
+ * Otherwise, it is permitted to integrate the Spine Runtimes into software
+ * or otherwise create derivative works of the Spine Runtimes (collectively,
+ * "Products"), provided that each user of the Products must obtain their own
+ * Spine Editor license and redistribution of the Products in any form must
+ * include this license and copyright notice.
+ *
+ * THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "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 LLC 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
+ * THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *****************************************************************************/
+
+import { Bone } from "../Bone";
+import { TextureRegion } from "../Texture";
+import { Color, NumberArrayLike, Utils } from "../Utils";
+import { Attachment } from "./Attachment";
+
+/** An attachment that displays a textured quadrilateral.
+ *
+ * See [Region attachments](http://esotericsoftware.com/spine-regions) in the Spine User Guide. */
+export class RegionAttachment extends Attachment {
+	/** The local x translation. */
+	x = 0;
+
+	/** The local y translation. */
+	y = 0;
+
+	/** The local scaleX. */
+	scaleX = 1;
+
+	/** The local scaleY. */
+	scaleY = 1;
+
+	/** The local rotation. */
+	rotation = 0;
+
+	/** The width of the region attachment in Spine. */
+	width = 0;
+
+	/** The height of the region attachment in Spine. */
+	height = 0;
+
+	/** The color to tint the region attachment. */
+	color = new Color(1, 1, 1, 1);
+
+	/** The name of the texture region for this attachment. */
+	path: string;
+
+	rendererObject: any;
+	region: TextureRegion;
+
+	/** For each of the 4 vertices, a pair of <code>x,y</code> values that is the local position of the vertex.
+	 *
+	 * See {@link #updateOffset()}. */
+	offset = Utils.newFloatArray(8);
+
+	uvs = Utils.newFloatArray(8);
+
+	tempColor = new Color(1, 1, 1, 1);
+
+	constructor(name: string) {
+		super(name);
+	}
+
+	/** Calculates the {@link #offset} using the region settings. Must be called after changing region settings. */
+	updateOffset(): void {
+		let region = this.region;
+		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 x = this.x, y = this.y;
+		let localXCos = localX * cos + x;
+		let localXSin = localX * sin;
+		let localYCos = localY * cos + y;
+		let localYSin = localY * sin;
+		let localX2Cos = localX2 * cos + x;
+		let localX2Sin = localX2 * sin;
+		let localY2Cos = localY2 * cos + y;
+		let localY2Sin = localY2 * sin;
+		let offset = this.offset;
+		offset[0] = localXCos - localYSin;
+		offset[1] = localYCos + localXSin;
+		offset[2] = localXCos - localY2Sin;
+		offset[3] = localY2Cos + localXSin;
+		offset[4] = localX2Cos - localY2Sin;
+		offset[5] = localY2Cos + localX2Sin;
+		offset[6] = localX2Cos - localYSin;
+		offset[7] = localYCos + localX2Sin;
+	}
+
+	setRegion(region: TextureRegion): void {
+		this.region = region;
+		let uvs = this.uvs;
+		if (region.degrees == 90) {
+			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;
+		}
+	}
+
+	/** Transforms the attachment's four vertices to world coordinates.
+	 *
+	 * See [World transforms](http://esotericsoftware.com/spine-runtime-skeletons#World-transforms) in the Spine
+	 * Runtimes Guide.
+	 * @param worldVertices The output world vertices. Must have a length >= `offset` + 8.
+	 * @param offset The `worldVertices` index to begin writing values.
+	 * @param stride The number of `worldVertices` entries between the value pairs written. */
+	computeWorldVertices(bone: Bone, worldVertices: NumberArrayLike, offset: number, stride: number) {
+		let vertexOffset = this.offset;
+		let x = bone.worldX, y = bone.worldY;
+		let a = bone.a, b = bone.b, c = bone.c, d = bone.d;
+		let offsetX = 0, offsetY = 0;
+
+		offsetX = vertexOffset[0];
+		offsetY = vertexOffset[1];
+		worldVertices[offset] = offsetX * a + offsetY * b + x; // br
+		worldVertices[offset + 1] = offsetX * c + offsetY * d + y;
+		offset += stride;
+
+		offsetX = vertexOffset[2];
+		offsetY = vertexOffset[3];
+		worldVertices[offset] = offsetX * a + offsetY * b + x; // bl
+		worldVertices[offset + 1] = offsetX * c + offsetY * d + y;
+		offset += stride;
+
+		offsetX = vertexOffset[4];
+		offsetY = vertexOffset[5];
+		worldVertices[offset] = offsetX * a + offsetY * b + x; // ul
+		worldVertices[offset + 1] = offsetX * c + offsetY * d + y;
+		offset += stride;
+
+		offsetX = vertexOffset[6];
+		offsetY = vertexOffset[7];
+		worldVertices[offset] = offsetX * a + offsetY * b + x; // ur
+		worldVertices[offset + 1] = offsetX * c + offsetY * d + y;
+	}
+
+	copy(): Attachment {
+		let copy = new RegionAttachment(this.name);
+		copy.region = this.region;
+		copy.rendererObject = this.rendererObject;
+		copy.path = this.path;
+		copy.x = this.x;
+		copy.y = this.y;
+		copy.scaleX = this.scaleX;
+		copy.scaleY = this.scaleY;
+		copy.rotation = this.rotation;
+		copy.width = this.width;
+		copy.height = this.height;
+		Utils.arrayCopy(this.uvs, 0, copy.uvs, 0, 8);
+		Utils.arrayCopy(this.offset, 0, copy.offset, 0, 8);
+		copy.color.setFromColor(this.color);
+		return copy;
+	}
+
+	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;
+}

+ 1 - 1
spine-ts/core/src/polyfills.ts → spine-ts/spine-core/src/polyfills.ts

@@ -28,7 +28,7 @@
  *****************************************************************************/
 
 interface Math {
-	fround (n: number): number;
+	fround(n: number): number;
 }
 
 (() => {

+ 18 - 16
spine-ts/core/src/vertexeffects/JitterEffect.ts → spine-ts/spine-core/src/vertexeffects/JitterEffect.ts

@@ -27,25 +27,27 @@
  * THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  *****************************************************************************/
 
-module spine {
-	export class JitterEffect implements VertexEffect {
-		jitterX = 0;
-		jitterY = 0;
+import { Skeleton } from "../Skeleton";
+import { Vector2, Color, MathUtils } from "../Utils";
+import { VertexEffect } from "../VertexEffect";
 
-		constructor (jitterX: number, jitterY: number) {
-			this.jitterX = jitterX;
-			this.jitterY = jitterY;
-		}
+export class JitterEffect implements VertexEffect {
+	jitterX = 0;
+	jitterY = 0;
 
-		begin (skeleton: Skeleton): void {
-		}
+	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);
-		}
+	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 {
-		}
+	end(): void {
 	}
 }

+ 32 - 30
spine-ts/core/src/vertexeffects/SwirlEffect.ts → spine-ts/spine-core/src/vertexeffects/SwirlEffect.ts

@@ -27,40 +27,42 @@
  * THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  *****************************************************************************/
 
-module spine {
-	export class SwirlEffect implements VertexEffect {
-		static interpolation = new PowOut(2);
-		centerX = 0;
-		centerY = 0;
-		radius = 0;
-		angle = 0;
-		private worldX = 0;
-		private worldY = 0;
+import { Skeleton } from "../Skeleton";
+import { PowOut, Vector2, Color, MathUtils } from "../Utils";
+import { VertexEffect } from "../VertexEffect";
 
-		constructor (radius: number) {
-			this.radius = radius;
-		}
+export class SwirlEffect implements VertexEffect {
+	static interpolation = new PowOut(2);
+	centerX = 0;
+	centerY = 0;
+	radius = 0;
+	angle = 0;
+	private worldX = 0;
+	private worldY = 0;
 
-		begin (skeleton: Skeleton): void {
-			this.worldX = skeleton.x + this.centerX;
-			this.worldY = skeleton.y + this.centerY;
-		}
+	constructor(radius: number) {
+		this.radius = radius;
+	}
 
-		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;
-			}
-		}
+	begin(skeleton: Skeleton): void {
+		this.worldX = skeleton.x + this.centerX;
+		this.worldY = skeleton.y + this.centerY;
+	}
 
-		end (): void {
+	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 {
+	}
 }

+ 7 - 0
spine-ts/spine-core/tsconfig.json

@@ -0,0 +1,7 @@
+{
+	"extends": "../tsconfig.json",
+	"compilerOptions": {
+		"rootDir": "src",
+		"outDir": "build"
+	}
+}

File diff suppressed because it is too large
+ 0 - 0
spine-ts/spine-core/tsconfig.tsbuildinfo


File diff suppressed because it is too large
+ 0 - 268
spine-ts/spine-player/css/spine-player.css


+ 0 - 0
spine-ts/player/example/assets/raptor-pma.atlas → spine-ts/spine-player/example/assets/raptor-pma.atlas


+ 0 - 0
spine-ts/player/example/assets/raptor-pma.png → spine-ts/spine-player/example/assets/raptor-pma.png


+ 0 - 0
spine-ts/player/example/assets/raptor-pro.json → spine-ts/spine-player/example/assets/raptor-pro.json


+ 0 - 0
spine-ts/player/example/assets/spineboy-pma.atlas → spine-ts/spine-player/example/assets/spineboy-pma.atlas


+ 0 - 0
spine-ts/player/example/assets/spineboy-pma.png → spine-ts/spine-player/example/assets/spineboy-pma.png


+ 0 - 0
spine-ts/player/example/assets/spineboy-pro.skel → spine-ts/spine-player/example/assets/spineboy-pro.skel


+ 0 - 0
spine-ts/player/example/editor.html → spine-ts/spine-player/example/editor.html


+ 0 - 0
spine-ts/player/example/embedding-binary-example.html → spine-ts/spine-player/example/embedding-binary-example.html


+ 0 - 0
spine-ts/player/example/embedding-json-example.html → spine-ts/spine-player/example/embedding-json-example.html


+ 0 - 0
spine-ts/player/example/example.html → spine-ts/spine-player/example/example.html


+ 0 - 0
spine-ts/player/example/external/codemirror.css → spine-ts/spine-player/example/external/codemirror.css


+ 0 - 0
spine-ts/player/example/external/codemirror.js → spine-ts/spine-player/example/external/codemirror.js


+ 0 - 0
spine-ts/player/example/generator/embedding-generator.html → spine-ts/spine-player/example/generator/embedding-generator.html


+ 0 - 0
spine-ts/player/example/generator/embedding-generator.js → spine-ts/spine-player/example/generator/embedding-generator.js


+ 0 - 0
spine-ts/player/example/generator/generator.html → spine-ts/spine-player/example/generator/generator.html


+ 0 - 0
spine-ts/player/example/generator/jscolor.js → spine-ts/spine-player/example/generator/jscolor.js


+ 0 - 0
spine-ts/player/example/generator/loader.js → spine-ts/spine-player/example/generator/loader.js


Some files were not shown because too many files changed in this diff