This commit is contained in:
JA
2026-06-27 03:36:46 +08:00
parent aec4e97d27
commit 7a8d4a5d83
1215 changed files with 48271 additions and 146052 deletions

View File

@@ -1,224 +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.
*****************************************************************************/
using System;
namespace Spine {
/// <summary>Attachment that displays a texture region using a mesh.</summary>
public class MeshAttachment : VertexAttachment, IHasRendererObject {
internal float regionOffsetX, regionOffsetY, regionWidth, regionHeight, regionOriginalWidth, regionOriginalHeight;
private MeshAttachment parentMesh;
internal float[] uvs, regionUVs;
internal int[] triangles;
internal float r = 1, g = 1, b = 1, a = 1;
internal int hulllength;
public int HullLength { get { return hulllength; } set { hulllength = value; } }
public float[] RegionUVs { get { return regionUVs; } set { regionUVs = value; } }
/// <summary>The UV pair for each vertex, normalized within the entire texture. <seealso cref="MeshAttachment.UpdateUVs"/></summary>
public float[] UVs { get { return uvs; } set { uvs = value; } }
public int[] Triangles { get { return triangles; } set { triangles = value; } }
public float R { get { return r; } set { r = value; } }
public float G { get { return g; } set { g = value; } }
public float B { get { return b; } set { b = value; } }
public float A { get { return a; } set { a = value; } }
public string Path { get; set; }
public object RendererObject { get; set; }
public float RegionU { get; set; }
public float RegionV { get; set; }
public float RegionU2 { get; set; }
public float RegionV2 { get; set; }
public bool RegionRotate { get; set; }
public int RegionDegrees { get; set; }
public float RegionOffsetX { get { return regionOffsetX; } set { regionOffsetX = value; } }
public float RegionOffsetY { get { return regionOffsetY; } set { regionOffsetY = value; } } // Pixels stripped from the bottom left, unrotated.
public float RegionWidth { get { return regionWidth; } set { regionWidth = value; } }
public float RegionHeight { get { return regionHeight; } set { regionHeight = value; } } // Unrotated, stripped size.
public float RegionOriginalWidth { get { return regionOriginalWidth; } set { regionOriginalWidth = value; } }
public float RegionOriginalHeight { get { return regionOriginalHeight; } set { regionOriginalHeight = value; } } // Unrotated, unstripped size.
public MeshAttachment ParentMesh {
get { return parentMesh; }
set {
parentMesh = value;
if (value != null) {
bones = value.bones;
vertices = value.vertices;
worldVerticesLength = value.worldVerticesLength;
regionUVs = value.regionUVs;
triangles = value.triangles;
HullLength = value.HullLength;
Edges = value.Edges;
Width = value.Width;
Height = value.Height;
}
}
}
// Nonessential.
public int[] Edges { get; set; }
public float Width { get; set; }
public float Height { get; set; }
public MeshAttachment (string name)
: base(name) {
}
public void UpdateUVs () {
float[] regionUVs = this.regionUVs;
if (this.uvs == null || this.uvs.Length != regionUVs.Length) this.uvs = new float[regionUVs.Length];
float[] uvs = this.uvs;
float u = RegionU, v = RegionV, width = 0, height = 0;
if (RegionDegrees == 90) {
float textureHeight = this.regionWidth / (RegionV2 - RegionV);
float textureWidth = this.regionHeight / (RegionU2 - RegionU);
u -= (RegionOriginalHeight - RegionOffsetY - RegionHeight) / textureWidth;
v -= (RegionOriginalWidth - RegionOffsetX - RegionWidth) / textureHeight;
width = RegionOriginalHeight / textureWidth;
height = RegionOriginalWidth / textureHeight;
for (int i = 0, n = uvs.Length; i < n; i += 2) {
uvs[i] = u + regionUVs[i + 1] * width;
uvs[i + 1] = v + (1 - regionUVs[i]) * height;
}
} else if (RegionDegrees == 180) {
float textureWidth = this.regionWidth / (RegionU2 - RegionU);
float textureHeight = this.regionHeight / (RegionV2 - RegionV);
u -= (RegionOriginalWidth - RegionOffsetX - RegionWidth) / textureWidth;
v -= RegionOffsetY / textureHeight;
width = RegionOriginalWidth / textureWidth;
height = RegionOriginalHeight / textureHeight;
for (int i = 0, n = uvs.Length; i < n; i += 2) {
uvs[i] = u + (1 - regionUVs[i]) * width;
uvs[i + 1] = v + (1 - regionUVs[i + 1]) * height;
}
} else if (RegionDegrees == 270) {
float textureWidth = this.regionWidth / (RegionU2 - RegionU);
float textureHeight = this.regionHeight / (RegionV2 - RegionV);
u -= RegionOffsetY / textureWidth;
v -= RegionOffsetX / textureHeight;
width = RegionOriginalHeight / textureWidth;
height = RegionOriginalWidth / textureHeight;
for (int i = 0, n = uvs.Length; i<n; i += 2) {
uvs[i] = u + (1 - regionUVs[i + 1]) * width;
uvs[i + 1] = v + regionUVs[i] * height;
}
} else {
float textureWidth = this.regionWidth / (RegionU2 - RegionU);
float textureHeight = this.regionHeight / (RegionV2 - RegionV);
u -= RegionOffsetX / textureWidth;
v -= (RegionOriginalHeight - RegionOffsetY - RegionHeight) / textureHeight;
width = RegionOriginalWidth / textureWidth;
height = RegionOriginalHeight / textureHeight;
for (int i = 0, n = uvs.Length; i < n; i += 2) {
uvs[i] = u + regionUVs[i] * width;
uvs[i + 1] = v + regionUVs[i + 1] * height;
}
}
}
public override Attachment Copy () {
if (parentMesh != null) return NewLinkedMesh();
MeshAttachment copy = new MeshAttachment(this.Name);
copy.RendererObject = RendererObject;
copy.regionOffsetX = regionOffsetX;
copy.regionOffsetY = regionOffsetY;
copy.regionWidth = regionWidth;
copy.regionHeight = regionHeight;
copy.regionOriginalWidth = regionOriginalWidth;
copy.regionOriginalHeight = regionOriginalHeight;
copy.RegionRotate = RegionRotate;
copy.RegionDegrees = RegionDegrees;
copy.RegionU = RegionU;
copy.RegionV = RegionV;
copy.RegionU2 = RegionU2;
copy.RegionV2 = RegionV2;
copy.Path = Path;
copy.r = r;
copy.g = g;
copy.b = b;
copy.a = a;
CopyTo(copy);
copy.regionUVs = new float[regionUVs.Length];
Array.Copy(regionUVs, 0, copy.regionUVs, 0, regionUVs.Length);
copy.uvs = new float[uvs.Length];
Array.Copy(uvs, 0, copy.uvs, 0, uvs.Length);
copy.triangles = new int[triangles.Length];
Array.Copy(triangles, 0, copy.triangles, 0, triangles.Length);
copy.HullLength = HullLength;
// Nonessential.
if (Edges != null) {
copy.Edges = new int[Edges.Length];
Array.Copy(Edges, 0, copy.Edges, 0, Edges.Length);
}
copy.Width = Width;
copy.Height = Height;
return copy;
}
///<summary>Returns a new mesh with this mesh set as the <see cref="ParentMesh"/>.
public MeshAttachment NewLinkedMesh () {
MeshAttachment mesh = new MeshAttachment(Name);
mesh.RendererObject = RendererObject;
mesh.regionOffsetX = regionOffsetX;
mesh.regionOffsetY = regionOffsetY;
mesh.regionWidth = regionWidth;
mesh.regionHeight = regionHeight;
mesh.regionOriginalWidth = regionOriginalWidth;
mesh.regionOriginalHeight = regionOriginalHeight;
mesh.RegionDegrees = RegionDegrees;
mesh.RegionRotate = RegionRotate;
mesh.RegionU = RegionU;
mesh.RegionV = RegionV;
mesh.RegionU2 = RegionU2;
mesh.RegionV2 = RegionV2;
mesh.Path = Path;
mesh.r = r;
mesh.g = g;
mesh.b = b;
mesh.a = a;
mesh.deformAttachment = deformAttachment;
mesh.ParentMesh = parentMesh != null ? parentMesh : this;
mesh.UpdateUVs();
return mesh;
}
}
}

View File

@@ -1,12 +0,0 @@
fileFormatVersion: 2
guid: b7f7514a003143844b6d01ecc93ed4d5
timeCreated: 1466772712
licenseType: Free
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -1,57 +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.
*****************************************************************************/
using System;
using System.Collections.Generic;
namespace Spine {
public class PathAttachment : VertexAttachment {
internal float[] lengths;
internal bool closed, constantSpeed;
/// <summary>The length in the setup pose from the start of the path to the end of each curve.</summary>
public float[] Lengths { get { return lengths; } set { lengths = value; } }
public bool Closed { get { return closed; } set { closed = value; } }
public bool ConstantSpeed { get { return constantSpeed; } set { constantSpeed = value; } }
public PathAttachment (String name)
: base(name) {
}
public override Attachment Copy () {
PathAttachment copy = new PathAttachment(this.Name);
CopyTo(copy);
copy.lengths = new float[lengths.Length];
Array.Copy(lengths, 0, copy.lengths, 0, lengths.Length);
copy.closed = closed;
copy.constantSpeed = constantSpeed;
return copy;
}
}
}

View File

@@ -1,12 +0,0 @@
fileFormatVersion: 2
guid: c77d9bf384a1e9f41966464e7e3b4870
timeCreated: 1466772712
licenseType: Free
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -1,67 +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.
*****************************************************************************/
namespace Spine {
/// <summary>
/// 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.
/// <p>
/// See <a href="http://esotericsoftware.com/spine-point-attachments">Point Attachments</a> in the Spine User Guide.
/// </summary>
public class PointAttachment : Attachment {
internal float x, y, rotation;
public float X { get { return x; } set { x = value; } }
public float Y { get { return y; } set { y = value; } }
public float Rotation { get { return rotation; } set { rotation = value; } }
public PointAttachment (string name)
: base(name) {
}
public void ComputeWorldPosition (Bone bone, out float ox, out float oy) {
bone.LocalToWorld(this.x, this.y, out ox, out oy);
}
public float ComputeWorldRotation (Bone bone) {
float cos = MathUtils.CosDeg(rotation), sin = MathUtils.SinDeg(rotation);
float ix = cos * bone.a + sin * bone.b;
float iy = cos * bone.c + sin * bone.d;
return MathUtils.Atan2(iy, ix) * MathUtils.RadDeg;
}
public override Attachment Copy () {
PointAttachment copy = new PointAttachment(this.Name);
copy.x = x;
copy.y = y;
copy.rotation = rotation;
return copy;
}
}
}

View File

@@ -1,12 +0,0 @@
fileFormatVersion: 2
guid: 4fdde4cc4df0952468946f4f913dcb36
timeCreated: 1485603478
licenseType: Free
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -1,208 +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.
*****************************************************************************/
using System;
namespace Spine {
/// <summary>Attachment that displays a texture region.</summary>
public class RegionAttachment : Attachment, IHasRendererObject {
public const int BLX = 0;
public const int BLY = 1;
public const int ULX = 2;
public const int ULY = 3;
public const int URX = 4;
public const int URY = 5;
public const int BRX = 6;
public const int BRY = 7;
internal float x, y, rotation, scaleX = 1, scaleY = 1, width, height;
internal float regionOffsetX, regionOffsetY, regionWidth, regionHeight, regionOriginalWidth, regionOriginalHeight;
internal float[] offset = new float[8], uvs = new float[8];
internal float r = 1, g = 1, b = 1, a = 1;
public float X { get { return x; } set { x = value; } }
public float Y { get { return y; } set { y = value; } }
public float Rotation { get { return rotation; } set { rotation = value; } }
public float ScaleX { get { return scaleX; } set { scaleX = value; } }
public float ScaleY { get { return scaleY; } set { scaleY = value; } }
public float Width { get { return width; } set { width = value; } }
public float Height { get { return height; } set { height = value; } }
public float R { get { return r; } set { r = value; } }
public float G { get { return g; } set { g = value; } }
public float B { get { return b; } set { b = value; } }
public float A { get { return a; } set { a = value; } }
public string Path { get; set; }
public object RendererObject { get; set; }
public float RegionOffsetX { get { return regionOffsetX; } set { regionOffsetX = value; } }
public float RegionOffsetY { get { return regionOffsetY; } set { regionOffsetY = value; } } // Pixels stripped from the bottom left, unrotated.
public float RegionWidth { get { return regionWidth; } set { regionWidth = value; } }
public float RegionHeight { get { return regionHeight; } set { regionHeight = value; } } // Unrotated, stripped size.
public float RegionOriginalWidth { get { return regionOriginalWidth; } set { regionOriginalWidth = value; } }
public float RegionOriginalHeight { get { return regionOriginalHeight; } set { regionOriginalHeight = value; } } // Unrotated, unstripped size.
public float[] Offset { get { return offset; } }
public float[] UVs { get { return uvs; } }
public RegionAttachment (string name)
: base(name) {
}
public void UpdateOffset () {
float width = this.width;
float height = this.height;
float localX2 = width * 0.5f;
float localY2 = height * 0.5f;
float localX = -localX2;
float localY = -localY2;
if (regionOriginalWidth != 0) { // if (region != null)
localX += regionOffsetX / regionOriginalWidth * width;
localY += regionOffsetY / regionOriginalHeight * height;
localX2 -= (regionOriginalWidth - regionOffsetX - regionWidth) / regionOriginalWidth * width;
localY2 -= (regionOriginalHeight - regionOffsetY - regionHeight) / regionOriginalHeight * height;
}
float scaleX = this.scaleX;
float scaleY = this.scaleY;
localX *= scaleX;
localY *= scaleY;
localX2 *= scaleX;
localY2 *= scaleY;
float rotation = this.rotation;
float cos = MathUtils.CosDeg(rotation);
float sin = MathUtils.SinDeg(rotation);
float x = this.x;
float y = this.y;
float localXCos = localX * cos + x;
float localXSin = localX * sin;
float localYCos = localY * cos + y;
float localYSin = localY * sin;
float localX2Cos = localX2 * cos + x;
float localX2Sin = localX2 * sin;
float localY2Cos = localY2 * cos + y;
float localY2Sin = localY2 * sin;
float[] offset = this.offset;
offset[BLX] = localXCos - localYSin;
offset[BLY] = localYCos + localXSin;
offset[ULX] = localXCos - localY2Sin;
offset[ULY] = localY2Cos + localXSin;
offset[URX] = localX2Cos - localY2Sin;
offset[URY] = localY2Cos + localX2Sin;
offset[BRX] = localX2Cos - localYSin;
offset[BRY] = localYCos + localX2Sin;
}
public void SetUVs (float u, float v, float u2, float v2, bool rotate) {
float[] uvs = this.uvs;
// UV values differ from RegionAttachment.java
if (rotate) {
uvs[URX] = u;
uvs[URY] = v2;
uvs[BRX] = u;
uvs[BRY] = v;
uvs[BLX] = u2;
uvs[BLY] = v;
uvs[ULX] = u2;
uvs[ULY] = v2;
} else {
uvs[ULX] = u;
uvs[ULY] = v2;
uvs[URX] = u;
uvs[URY] = v;
uvs[BRX] = u2;
uvs[BRY] = v;
uvs[BLX] = u2;
uvs[BLY] = v2;
}
}
/// <summary>Transforms the attachment's four vertices to world coordinates.</summary>
/// <param name="bone">The parent bone.</param>
/// <param name="worldVertices">The output world vertices. Must have a length greater than or equal to offset + 8.</param>
/// <param name="offset">The worldVertices index to begin writing values.</param>
/// <param name="stride">The number of worldVertices entries between the value pairs written.</param>
public void ComputeWorldVertices (Bone bone, float[] worldVertices, int offset, int stride = 2) {
float[] vertexOffset = this.offset;
float bwx = bone.worldX, bwy = bone.worldY;
float a = bone.a, b = bone.b, c = bone.c, d = bone.d;
float offsetX, offsetY;
// Vertex order is different from RegionAttachment.java
offsetX = vertexOffset[BRX]; // 0
offsetY = vertexOffset[BRY]; // 1
worldVertices[offset] = offsetX * a + offsetY * b + bwx; // bl
worldVertices[offset + 1] = offsetX * c + offsetY * d + bwy;
offset += stride;
offsetX = vertexOffset[BLX]; // 2
offsetY = vertexOffset[BLY]; // 3
worldVertices[offset] = offsetX * a + offsetY * b + bwx; // ul
worldVertices[offset + 1] = offsetX * c + offsetY * d + bwy;
offset += stride;
offsetX = vertexOffset[ULX]; // 4
offsetY = vertexOffset[ULY]; // 5
worldVertices[offset] = offsetX * a + offsetY * b + bwx; // ur
worldVertices[offset + 1] = offsetX * c + offsetY * d + bwy;
offset += stride;
offsetX = vertexOffset[URX]; // 6
offsetY = vertexOffset[URY]; // 7
worldVertices[offset] = offsetX * a + offsetY * b + bwx; // br
worldVertices[offset + 1] = offsetX * c + offsetY * d + bwy;
//offset += stride;
}
public override Attachment Copy () {
RegionAttachment copy = new RegionAttachment(this.Name);
copy.RendererObject = RendererObject;
copy.regionOffsetX = regionOffsetX;
copy.regionOffsetY = regionOffsetY;
copy.regionWidth = regionWidth;
copy.regionHeight = regionHeight;
copy.regionOriginalWidth = regionOriginalWidth;
copy.regionOriginalHeight = regionOriginalHeight;
copy.Path = Path;
copy.x = x;
copy.y = y;
copy.scaleX = scaleX;
copy.scaleY = scaleY;
copy.rotation = rotation;
copy.width = width;
copy.height = height;
Array.Copy(uvs, 0, copy.uvs, 0, 8);
Array.Copy(offset, 0, copy.offset, 0, 8);
copy.r = r;
copy.g = g;
copy.b = b;
copy.a = a;
return copy;
}
}
}

View File

@@ -1,12 +0,0 @@
fileFormatVersion: 2
guid: 89cefdd024734a941952a05d2b5dff71
timeCreated: 1466772712
licenseType: Free
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -1,633 +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.
*****************************************************************************/
/******************************************************************************
* Thanks to Travis Parks
* https://github.com/jehugaleahsa/truncon.collections.OrderedDictionary
*****************************************************************************/
using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Linq;
namespace Spine.Collections
{
/// <summary>
/// Represents a dictionary that tracks the order that items were added.
/// </summary>
/// <typeparam name="TKey">The type of the dictionary keys.</typeparam>
/// <typeparam name="TValue">The type of the dictionary values.</typeparam>
/// <remarks>
/// This dictionary makes it possible to get the index of a key and a key based on an index.
/// It can be costly to find the index of a key because it must be searched for linearly.
/// It can be costly to insert a key/value pair because other key's indexes must be adjusted.
/// It can be costly to remove a key/value pair because other keys' indexes must be adjusted.
/// </remarks>
[DebuggerDisplay("Count = {Count}")]
[DebuggerTypeProxy(typeof(OrderedDictionaryDebugView<,>))]
public sealed class OrderedDictionary<TKey, TValue> : IDictionary<TKey, TValue>, IList<KeyValuePair<TKey, TValue>>
{
private readonly Dictionary<TKey, int> dictionary;
private readonly List<TKey> keys;
private readonly List<TValue> values;
private int version;
private const string CollectionModifiedMessage = "Collection was modified; enumeration operation may not execute.";
private const string EditReadOnlyListMessage = "An attempt was made to edit a read-only list.";
private const string IndexOutOfRangeMessage = "The index is negative or outside the bounds of the collection.";
/// <summary>
/// Initializes a new instance of an OrderedDictionary.
/// </summary>
public OrderedDictionary ()
: this(0, null) {
}
/// <summary>
/// Initializes a new instance of an OrderedDictionary.
/// </summary>
/// <param name="capacity">The initial capacity of the dictionary.</param>
/// <exception cref="System.ArgumentOutOfRangeException">The capacity is less than zero.</exception>
public OrderedDictionary (int capacity)
: this(capacity, null) {
}
/// <summary>
/// Initializes a new instance of an OrderedDictionary.
/// </summary>
/// <param name="comparer">The equality comparer to use to compare keys.</param>
public OrderedDictionary (IEqualityComparer<TKey> comparer)
: this(0, comparer) {
}
/// <summary>
/// Initializes a new instance of an OrderedDictionary.
/// </summary>
/// <param name="capacity">The initial capacity of the dictionary.</param>
/// <param name="comparer">The equality comparer to use to compare keys.</param>
public OrderedDictionary (int capacity, IEqualityComparer<TKey> comparer) {
dictionary = new Dictionary<TKey, int>(capacity, comparer ?? EqualityComparer<TKey>.Default);
keys = new List<TKey>(capacity);
values = new List<TValue>(capacity);
}
/// <summary>
/// Gets the equality comparer used to compare keys.
/// </summary>
public IEqualityComparer<TKey> Comparer {
get {
return dictionary.Comparer;
}
}
/// <summary>
/// Adds the given key/value pair to the dictionary.
/// </summary>
/// <param name="key">The key to add to the dictionary.</param>
/// <param name="value">The value to associated with the key.</param>
/// <exception cref="System.ArgumentException">The given key already exists in the dictionary.</exception>
/// <exception cref="System.ArgumentNullException">The key is null.</exception>
public void Add (TKey key, TValue value) {
dictionary.Add(key, values.Count);
keys.Add(key);
values.Add(value);
++version;
}
/// <summary>
/// Inserts the given key/value pair at the specified index.
/// </summary>
/// <param name="index">The index to insert the key/value pair.</param>
/// <param name="key">The key to insert.</param>
/// <param name="value">The value to insert.</param>
/// <exception cref="System.ArgumentException">The given key already exists in the dictionary.</exception>
/// <exception cref="System.ArgumentNullException">The key is null.</exception>
/// <exception cref="System.ArgumentOutOfRangeException">The index is negative -or- larger than the size of the dictionary.</exception>
public void Insert (int index, TKey key, TValue value) {
if (index < 0 || index > values.Count) {
throw new ArgumentOutOfRangeException("index", index, IndexOutOfRangeMessage);
}
dictionary.Add(key, index);
for (int keyIndex = index; keyIndex != keys.Count; ++keyIndex) {
var otherKey = keys[keyIndex];
dictionary[otherKey] += 1;
}
keys.Insert(index, key);
values.Insert(index, value);
++version;
}
/// <summary>
/// Determines whether the given key exists in the dictionary.
/// </summary>
/// <param name="key">The key to look for.</param>
/// <returns>True if the key exists in the dictionary; otherwise, false.</returns>
/// <exception cref="System.ArgumentNullException">The key is null.</exception>
public bool ContainsKey (TKey key) {
return dictionary.ContainsKey(key);
}
/// <summary>
/// Gets the key at the given index.
/// </summary>
/// <param name="index">The index of the key to get.</param>
/// <returns>The key at the given index.</returns>
/// <exception cref="System.ArgumentOutOfRangeException">The index is negative -or- larger than the number of keys.</exception>
public TKey GetKey (int index) {
return keys[index];
}
/// <summary>
/// Gets the index of the given key.
/// </summary>
/// <param name="key">The key to get the index of.</param>
/// <returns>The index of the key in the dictionary -or- -1 if the key is not found.</returns>
/// <remarks>The operation runs in O(n).</remarks>
public int IndexOf (TKey key) {
int index;
if (dictionary.TryGetValue(key, out index)) {
return index;
}
return -1;
}
/// <summary>
/// Gets the keys in the dictionary in the order they were added.
/// </summary>
public KeyCollection Keys {
get {
return new KeyCollection(this.dictionary);
}
}
/// <summary>
/// Removes the key/value pair with the given key from the dictionary.
/// </summary>
/// <param name="key">The key of the pair to remove.</param>
/// <returns>True if the key was found and the pair removed; otherwise, false.</returns>
/// <exception cref="System.ArgumentNullException">The key is null.</exception>
public bool Remove (TKey key) {
int index;
if (dictionary.TryGetValue(key, out index)) {
RemoveAt(index);
return true;
}
return false;
}
/// <summary>
/// Removes the key/value pair at the given index.
/// </summary>
/// <param name="index">The index of the key/value pair to remove.</param>
/// <exception cref="System.ArgumentOutOfRangeException">The index is negative -or- larger than the size of the dictionary.</exception>
public void RemoveAt (int index) {
var key = keys[index];
for (int keyIndex = index + 1; keyIndex < keys.Count; ++keyIndex) {
var otherKey = keys[keyIndex];
dictionary[otherKey] -= 1;
}
dictionary.Remove(key);
keys.RemoveAt(index);
values.RemoveAt(index);
++version;
}
/// <summary>
/// Tries to get the value associated with the given key. If the key is not found,
/// default(TValue) value is stored in the value.
/// </summary>
/// <param name="key">The key to get the value for.</param>
/// <param name="value">The value used to hold the results.</param>
/// <returns>True if the key was found; otherwise, false.</returns>
/// <exception cref="System.ArgumentNullException">The key is null.</exception>
public bool TryGetValue (TKey key, out TValue value) {
int index;
if (dictionary.TryGetValue(key, out index)) {
value = values[index];
return true;
}
value = default(TValue);
return false;
}
/// <summary>
/// Gets the values in the dictionary.
/// </summary>
public ValueCollection Values {
get {
return new ValueCollection(values);
}
}
/// <summary>
/// Gets or sets the value at the given index.
/// </summary>
/// <param name="index">The index of the value to get.</param>
/// <returns>The value at the given index.</returns>
/// <exception cref="System.ArgumentOutOfRangeException">The index is negative -or- beyond the length of the dictionary.</exception>
public TValue this[int index] {
get {
return values[index];
}
set {
values[index] = value;
}
}
/// <summary>
/// Gets or sets the value associated with the given key.
/// </summary>
/// <param name="key">The key to get the associated value by or to associate with the value.</param>
/// <returns>The value associated with the given key.</returns>
/// <exception cref="System.ArgumentNullException">The key is null.</exception>
/// <exception cref="System.Collections.Generic.KeyNotFoundException">The key is not in the dictionary.</exception>
public TValue this[TKey key] {
get {
return values[dictionary[key]];
}
set {
int index;
if (dictionary.TryGetValue(key, out index)) {
keys[index] = key;
values[index] = value;
}
else {
Add(key, value);
}
}
}
/// <summary>
/// Removes all key/value pairs from the dictionary.
/// </summary>
public void Clear () {
dictionary.Clear();
keys.Clear();
values.Clear();
++version;
}
/// <summary>
/// Gets the number of key/value pairs in the dictionary.
/// </summary>
public int Count {
get {
return dictionary.Count;
}
}
/// <summary>
/// Gets the key/value pairs in the dictionary in the order they were added.
/// </summary>
/// <returns>An enumerator over the key/value pairs in the dictionary.</returns>
public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator () {
int startVersion = version;
for (int index = 0; index != keys.Count; ++index) {
var key = keys[index];
var value = values[index];
yield return new KeyValuePair<TKey, TValue>(key, value);
if (version != startVersion) {
throw new InvalidOperationException(CollectionModifiedMessage);
}
}
}
int IList<KeyValuePair<TKey, TValue>>.IndexOf (KeyValuePair<TKey, TValue> item) {
int index;
if (dictionary.TryGetValue(item.Key, out index) && Equals(values[index], item.Value)) {
return index;
}
return -1;
}
void IList<KeyValuePair<TKey, TValue>>.Insert (int index, KeyValuePair<TKey, TValue> item) {
Insert(index, item.Key, item.Value);
}
KeyValuePair<TKey, TValue> IList<KeyValuePair<TKey, TValue>>.this[int index] {
get {
TKey key = keys[index];
TValue value = values[index];
return new KeyValuePair<TKey, TValue>(key, value);
}
set {
TKey key = keys[index];
if (dictionary.Comparer.Equals(key, value.Key)) {
dictionary[value.Key] = index;
}
else {
dictionary.Add(value.Key, index); // will throw if key already exists
dictionary.Remove(key);
}
keys[index] = value.Key;
values[index] = value.Value;
}
}
ICollection<TKey> IDictionary<TKey, TValue>.Keys {
get {
return Keys;
}
}
ICollection<TValue> IDictionary<TKey, TValue>.Values {
get {
return Values;
}
}
void ICollection<KeyValuePair<TKey, TValue>>.Add (KeyValuePair<TKey, TValue> item) {
Add(item.Key, item.Value);
}
bool ICollection<KeyValuePair<TKey, TValue>>.Contains (KeyValuePair<TKey, TValue> item) {
int index;
if (dictionary.TryGetValue(item.Key, out index) && Equals(values[index], item.Value)) {
return true;
}
return false;
}
void ICollection<KeyValuePair<TKey, TValue>>.CopyTo (KeyValuePair<TKey, TValue>[] array, int arrayIndex) {
if (array == null) {
throw new ArgumentNullException("array");
}
if (arrayIndex < 0) {
throw new ArgumentOutOfRangeException("arrayIndex", arrayIndex, IndexOutOfRangeMessage);
}
for (int index = 0; index != keys.Count && arrayIndex < array.Length; ++index, ++arrayIndex) {
var key = keys[index];
var value = values[index];
array[arrayIndex] = new KeyValuePair<TKey, TValue>(key, value);
}
}
bool ICollection<KeyValuePair<TKey, TValue>>.IsReadOnly {
get {
return false;
}
}
bool ICollection<KeyValuePair<TKey, TValue>>.Remove (KeyValuePair<TKey, TValue> item) {
ICollection<KeyValuePair<TKey, TValue>> self = this;
if (self.Contains(item)) {
return Remove(item.Key);
}
return false;
}
IEnumerator IEnumerable.GetEnumerator () {
return GetEnumerator();
}
/// <summary>
/// Wraps the keys in an OrderDictionary.
/// </summary>
public sealed class KeyCollection : ICollection<TKey>
{
private readonly Dictionary<TKey, int> dictionary;
/// <summary>
/// Initializes a new instance of a KeyCollection.
/// </summary>
/// <param name="dictionary">The OrderedDictionary whose keys to wrap.</param>
/// <exception cref="System.ArgumentNullException">The dictionary is null.</exception>
internal KeyCollection (Dictionary<TKey, int> dictionary) {
this.dictionary = dictionary;
}
/// <summary>
/// Copies the keys from the OrderedDictionary to the given array, starting at the given index.
/// </summary>
/// <param name="array">The array to copy the keys to.</param>
/// <param name="arrayIndex">The index into the array to start copying the keys.</param>
/// <exception cref="System.ArgumentNullException">The array is null.</exception>
/// <exception cref="System.ArgumentOutOfRangeException">The arrayIndex is negative.</exception>
/// <exception cref="System.ArgumentException">The array, starting at the given index, is not large enough to contain all the keys.</exception>
public void CopyTo (TKey[] array, int arrayIndex) {
dictionary.Keys.CopyTo(array, arrayIndex);
}
/// <summary>
/// Gets the number of keys in the OrderedDictionary.
/// </summary>
public int Count {
get {
return dictionary.Count;
}
}
/// <summary>
/// Gets an enumerator over the keys in the OrderedDictionary.
/// </summary>
/// <returns>The enumerator.</returns>
public IEnumerator<TKey> GetEnumerator () {
return dictionary.Keys.GetEnumerator();
}
[EditorBrowsable(EditorBrowsableState.Never)]
bool ICollection<TKey>.Contains (TKey item) {
return dictionary.ContainsKey(item);
}
[EditorBrowsable(EditorBrowsableState.Never)]
void ICollection<TKey>.Add (TKey item) {
throw new NotSupportedException(EditReadOnlyListMessage);
}
[EditorBrowsable(EditorBrowsableState.Never)]
void ICollection<TKey>.Clear () {
throw new NotSupportedException(EditReadOnlyListMessage);
}
[EditorBrowsable(EditorBrowsableState.Never)]
bool ICollection<TKey>.IsReadOnly {
get {
return true;
}
}
[EditorBrowsable(EditorBrowsableState.Never)]
bool ICollection<TKey>.Remove (TKey item) {
throw new NotSupportedException(EditReadOnlyListMessage);
}
IEnumerator IEnumerable.GetEnumerator () {
return GetEnumerator();
}
}
/// <summary>
/// Wraps the keys in an OrderDictionary.
/// </summary>
public sealed class ValueCollection : ICollection<TValue>
{
private readonly List<TValue> values;
/// <summary>
/// Initializes a new instance of a ValueCollection.
/// </summary>
/// <param name="values">The OrderedDictionary whose keys to wrap.</param>
/// <exception cref="System.ArgumentNullException">The dictionary is null.</exception>
internal ValueCollection (List<TValue> values) {
this.values = values;
}
/// <summary>
/// Copies the values from the OrderedDictionary to the given array, starting at the given index.
/// </summary>
/// <param name="array">The array to copy the values to.</param>
/// <param name="arrayIndex">The index into the array to start copying the values.</param>
/// <exception cref="System.ArgumentNullException">The array is null.</exception>
/// <exception cref="System.ArgumentOutOfRangeException">The arrayIndex is negative.</exception>
/// <exception cref="System.ArgumentException">The array, starting at the given index, is not large enough to contain all the values.</exception>
public void CopyTo (TValue[] array, int arrayIndex) {
values.CopyTo(array, arrayIndex);
}
/// <summary>
/// Gets the number of values in the OrderedDictionary.
/// </summary>
public int Count {
get {
return values.Count;
}
}
/// <summary>
/// Gets an enumerator over the values in the OrderedDictionary.
/// </summary>
/// <returns>The enumerator.</returns>
public IEnumerator<TValue> GetEnumerator () {
return values.GetEnumerator();
}
[EditorBrowsable(EditorBrowsableState.Never)]
bool ICollection<TValue>.Contains (TValue item) {
return values.Contains(item);
}
[EditorBrowsable(EditorBrowsableState.Never)]
void ICollection<TValue>.Add (TValue item) {
throw new NotSupportedException(EditReadOnlyListMessage);
}
[EditorBrowsable(EditorBrowsableState.Never)]
void ICollection<TValue>.Clear () {
throw new NotSupportedException(EditReadOnlyListMessage);
}
[EditorBrowsable(EditorBrowsableState.Never)]
bool ICollection<TValue>.IsReadOnly {
get {
return true;
}
}
[EditorBrowsable(EditorBrowsableState.Never)]
bool ICollection<TValue>.Remove (TValue item) {
throw new NotSupportedException(EditReadOnlyListMessage);
}
IEnumerator IEnumerable.GetEnumerator () {
return GetEnumerator();
}
}
}
internal class OrderedDictionaryDebugView<TKey, TValue>
{
private readonly OrderedDictionary<TKey, TValue> dictionary;
public OrderedDictionaryDebugView (OrderedDictionary<TKey, TValue> dictionary) {
this.dictionary = dictionary;
}
[DebuggerBrowsable(DebuggerBrowsableState.RootHidden)]
public KeyValuePair<TKey, TValue>[] Items {
get {
return dictionary.ToArray();
}
}
}
/// <summary>
/// Provides extensions methods for constructing instances of <see cref="OrderedDictionary{TKey, TValue}"/>.
/// </summary>
public static class CollectionExtensions
{
#region ToOrderedDictionary
/// <summary>
/// Creates a new OrderedDictionary from the given collection, using the key selector to extract the key.
/// </summary>
/// <typeparam name="TSource">The type of the items in the collection.</typeparam>
/// <typeparam name="TKey">The type of the key.</typeparam>
/// <param name="source">The items to created the OrderedDictionary from.</param>
/// <param name="keySelector">A delegate that can extract a key from an item in the collection.</param>
/// <returns>An OrderedDictionary mapping the extracted keys to their values.</returns>
public static OrderedDictionary<TKey, TSource> ToOrderedDictionary<TSource, TKey> (this IEnumerable<TSource> source, Func<TSource, TKey> keySelector) {
return ToOrderedDictionary(source, keySelector, null);
}
/// <summary>
/// Creates a new OrderedDictionary from the given collection, using the key selector to extract the key.
/// The key comparer is passed to the OrderedDictionary for comparing the extracted keys.
/// </summary>
/// <typeparam name="TSource">The type of the items in the collection.</typeparam>
/// <typeparam name="TKey">The type of the key.</typeparam>
/// <param name="source">The items to created the OrderedDictionary from.</param>
/// <param name="keySelector">A delegate that can extract a key from an item in the collection.</param>
/// <param name="comparer">The key equality comparer to use to compare keys in the dictionary.</param>
/// <returns>An OrderedDictionary mapping the extracted keys to their values.</returns>
public static OrderedDictionary<TKey, TSource> ToOrderedDictionary<TSource, TKey> (
this IEnumerable<TSource> source,
Func<TSource, TKey> keySelector,
IEqualityComparer<TKey> comparer) {
if (source == null) {
throw new ArgumentNullException("source");
}
if (keySelector == null) {
throw new ArgumentNullException("keySelector");
}
var dictionary = new OrderedDictionary<TKey, TSource>(comparer);
foreach (TSource item in source) {
TKey key = keySelector(item);
dictionary.Add(key, item);
}
return dictionary;
}
#endregion
}
}

View File

@@ -1,12 +0,0 @@
fileFormatVersion: 2
guid: ca4b738078ed756469d5d97bcca93e6f
timeCreated: 1636570216
licenseType: Free
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -1,42 +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.
*****************************************************************************/
namespace Spine {
///<summary>The interface for items updated by <see cref="Skeleton.UpdateWorldTransform()"/>.</summary>
public interface IUpdatable {
void Update ();
///<summary>Returns false when this item has not been updated because a skin is required and the <see cref="Skeleton.Skin">active
/// skin</see> does not contain this item.</summary>
/// <seealso cref="Skin.Bones"/>
/// <seealso cref="Skin.Constraints"/>
bool Active { get; }
}
}

View File

@@ -1,12 +0,0 @@
fileFormatVersion: 2
guid: 44a51df5672fe4249b6763960587a017
timeCreated: 1456265154
licenseType: Free
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -1,365 +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.
*****************************************************************************/
using System;
namespace Spine {
/// <summary>
/// <para>
/// 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.</para>
/// <para>
/// See <a href="http://esotericsoftware.com/spine-ik-constraints">IK constraints</a> in the Spine User Guide.</para>
/// </summary>
public class IkConstraint : IUpdatable {
internal IkConstraintData data;
internal ExposedList<Bone> bones = new ExposedList<Bone>();
internal Bone target;
internal int bendDirection;
internal bool compress, stretch;
internal float mix = 1, softness;
internal bool active;
public IkConstraint (IkConstraintData data, Skeleton skeleton) {
if (data == null) throw new ArgumentNullException("data", "data cannot be null.");
if (skeleton == null) throw new ArgumentNullException("skeleton", "skeleton cannot be null.");
this.data = data;
mix = data.mix;
softness = data.softness;
bendDirection = data.bendDirection;
compress = data.compress;
stretch = data.stretch;
bones = new ExposedList<Bone>(data.bones.Count);
foreach (BoneData boneData in data.bones)
bones.Add(skeleton.FindBone(boneData.name));
target = skeleton.FindBone(data.target.name);
}
/// <summary>Copy constructor.</summary>
public IkConstraint (IkConstraint constraint, Skeleton skeleton) {
if (constraint == null) throw new ArgumentNullException("constraint cannot be null.");
if (skeleton == null) throw new ArgumentNullException("skeleton cannot be null.");
data = constraint.data;
bones = new ExposedList<Bone>(constraint.Bones.Count);
foreach (Bone bone in constraint.Bones)
bones.Add(skeleton.Bones.Items[bone.data.index]);
target = skeleton.Bones.Items[constraint.target.data.index];
mix = constraint.mix;
softness = constraint.softness;
bendDirection = constraint.bendDirection;
compress = constraint.compress;
stretch = constraint.stretch;
}
/// <summary>Applies the constraint to the constrained bones.</summary>
public void Apply () {
Update();
}
public void Update () {
Bone target = this.target;
ExposedList<Bone> bones = this.bones;
switch (bones.Count) {
case 1:
Apply(bones.Items[0], target.worldX, target.worldY, compress, stretch, data.uniform, mix);
break;
case 2:
Apply(bones.Items[0], bones.Items[1], target.worldX, target.worldY, bendDirection, stretch, softness, mix);
break;
}
}
/// <summary>The bones that will be modified by this IK constraint.</summary>
public ExposedList<Bone> Bones {
get { return bones; }
}
/// <summary>The bone that is the IK target.</summary>
public Bone Target {
get { return target; }
set { target = value; }
}
/// <summary>A percentage (0-1) that controls the mix between the constrained and unconstrained rotations.</summary>
public float Mix {
get { return mix; }
set { mix = value; }
}
///<summary>For two bone IK, the distance from the maximum reach of the bones that rotation will slow.</summary>
public float Softness {
get { return softness; }
set { softness = value; }
}
/// <summary>Controls the bend direction of the IK bones, either 1 or -1.</summary>
public int BendDirection {
get { return bendDirection; }
set { bendDirection = value; }
}
/// <summary>
/// When true and only a single bone is being constrained, if the target is too close, the bone is scaled to reach it.</summary>
public bool Compress {
get { return compress; }
set { compress = value; }
}
/// <summary>
/// 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.</summary>
public bool Stretch {
get { return stretch; }
set { stretch = value; }
}
public bool Active {
get { return active; }
}
/// <summary>The IK constraint's setup pose data.</summary>
public IkConstraintData Data {
get { return data; }
}
override public string ToString () {
return data.name;
}
/// <summary>Applies 1 bone IK. The target is specified in the world coordinate system.</summary>
static public void Apply (Bone bone, float targetX, float targetY, bool compress, bool stretch, bool uniform,
float alpha) {
if (!bone.appliedValid) bone.UpdateAppliedTransform();
Bone p = bone.parent;
float pa = p.a, pb = p.b, pc = p.c, pd = p.d;
float rotationIK = -bone.ashearX - bone.arotation;
float tx = 0, ty = 0;
switch(bone.data.transformMode) {
case TransformMode.OnlyTranslation:
tx = targetX - bone.worldX;
ty = targetY - bone.worldY;
break;
case TransformMode.NoRotationOrReflection: {
float s = Math.Abs(pa * pd - pb * pc) / (pa * pa + pc * pc);
float sa = pa / bone.skeleton.ScaleX;
float sc = pc / bone.skeleton.ScaleY;
pb = -sc * s * bone.skeleton.ScaleX;
pd = sa * s * bone.skeleton.ScaleY;
rotationIK += (float)Math.Atan2(pc, pa) * MathUtils.RadDeg;
goto default; // Fall through.
}
default: {
float x = targetX - p.worldX, y = targetY - p.worldY;
float d = pa * pd - pb * pc;
tx = (x * pd - y * pb) / d - bone.ax;
ty = (y * pa - x * pc) / d - bone.ay;
break;
}
}
rotationIK += (float)Math.Atan2(ty, tx) * MathUtils.RadDeg;
if (bone.ascaleX < 0) rotationIK += 180;
if (rotationIK > 180)
rotationIK -= 360;
else if (rotationIK < -180) //
rotationIK += 360;
float sx = bone.ascaleX, sy = bone.ascaleY;
if (compress || stretch) {
switch (bone.data.transformMode) {
case TransformMode.NoScale:
tx = targetX - bone.worldX;
ty = targetY - bone.worldY;
break;
case TransformMode.NoScaleOrReflection:
tx = targetX - bone.worldX;
ty = targetY - bone.worldY;
break;
}
float b = bone.data.length * sx, dd = (float)Math.Sqrt(tx * tx + ty * ty);
if ((compress && dd < b) || (stretch && dd > b) && b > 0.0001f) {
float s = (dd / b - 1) * alpha + 1;
sx *= s;
if (uniform) sy *= s;
}
}
bone.UpdateWorldTransform(bone.ax, bone.ay, bone.arotation + rotationIK * alpha, sx, sy, bone.ashearX, bone.ashearY);
}
/// <summary>Applies 2 bone IK. The target is specified in the world coordinate system.</summary>
/// <param name="child">A direct descendant of the parent bone.</param>
static public void Apply (Bone parent, Bone child, float targetX, float targetY, int bendDir, bool stretch, float softness,
float alpha) {
if (alpha == 0) {
child.UpdateWorldTransform();
return;
}
if (!parent.appliedValid) parent.UpdateAppliedTransform();
if (!child.appliedValid) child.UpdateAppliedTransform();
float px = parent.ax, py = parent.ay, psx = parent.ascaleX, sx = psx, psy = parent.ascaleY, csx = child.ascaleX;
int os1, os2, s2;
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;
float cx = child.ax, cy, cwx, cwy, a = parent.a, b = parent.b, c = parent.c, d = parent.d;
bool u = Math.Abs(psx - psy) <= 0.0001f;
if (!u) {
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;
}
Bone pp = parent.parent;
a = pp.a;
b = pp.b;
c = pp.c;
d = pp.d;
float id = 1 / (a * d - b * c), x = cwx - pp.worldX, y = cwy - pp.worldY;
float dx = (x * d - y * b) * id - px, dy = (y * a - x * c) * id - py;
float l1 = (float)Math.Sqrt(dx * dx + dy * dy), l2 = child.data.length * csx, a1, a2;
if (l1 < 0.0001f) {
Apply(parent, targetX, targetY, false, stretch, false, alpha);
child.UpdateWorldTransform(cx, cy, 0, child.ascaleX, child.ascaleY, child.ashearX, child.ashearY);
return;
}
x = targetX - pp.worldX;
y = targetY - pp.worldY;
float tx = (x * d - y * b) * id - px, ty = (y * a - x * c) * id - py;
float dd = tx * tx + ty * ty;
if (softness != 0) {
softness *= psx * (csx + 1) / 2;
float td = (float)Math.Sqrt(dd), sd = td - l1 - l2 * psx + softness;
if (sd > 0) {
float 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;
}
}
if (u) {
l2 *= psx;
float cos = (dd - l1 * l1 - l2 * l2) / (2 * l1 * l2);
if (cos < -1)
cos = -1;
else if (cos > 1) {
cos = 1;
if (stretch) sx *= ((float)Math.Sqrt(dd) / (l1 + l2) - 1) * alpha + 1;
}
a2 = (float)Math.Acos(cos) * bendDir;
a = l1 + l2 * cos;
b = l2 * (float)Math.Sin(a2);
a1 = (float)Math.Atan2(ty * a - tx * b, tx * a + ty * b);
} else {
a = psx * l2;
b = psy * l2;
float aa = a * a, bb = b * b, ta = (float)Math.Atan2(ty, tx);
c = bb * l1 * l1 + aa * dd - aa * bb;
float c1 = -2 * bb * l1, c2 = bb - aa;
d = c1 * c1 - 4 * c2 * c;
if (d >= 0) {
float q = (float)Math.Sqrt(d);
if (c1 < 0) q = -q;
q = -(c1 + q) / 2;
float r0 = q / c2, r1 = c / q;
float r = Math.Abs(r0) < Math.Abs(r1) ? r0 : r1;
if (r * r <= dd) {
y = (float)Math.Sqrt(dd - r * r) * bendDir;
a1 = ta - (float)Math.Atan2(y, r);
a2 = (float)Math.Atan2(y / psy, (r - l1) / psx);
goto break_outer; // break outer;
}
}
float minAngle = MathUtils.PI, minX = l1 - a, minDist = minX * minX, minY = 0;
float maxAngle = 0, maxX = l1 + a, maxDist = maxX * maxX, maxY = 0;
c = -a * l1 / (aa - bb);
if (c >= -1 && c <= 1) {
c = (float)Math.Acos(c);
x = a * (float)Math.Cos(c) + l1;
y = b * (float)Math.Sin(c);
d = x * x + y * y;
if (d < minDist) {
minAngle = c;
minDist = d;
minX = x;
minY = y;
}
if (d > maxDist) {
maxAngle = c;
maxDist = d;
maxX = x;
maxY = y;
}
}
if (dd <= (minDist + maxDist) / 2) {
a1 = ta - (float)Math.Atan2(minY * bendDir, minX);
a2 = minAngle * bendDir;
} else {
a1 = ta - (float)Math.Atan2(maxY * bendDir, maxX);
a2 = maxAngle * bendDir;
}
}
break_outer:
float os = (float)Math.Atan2(cy, cx) * s2;
float rotation = parent.arotation;
a1 = (a1 - os) * MathUtils.RadDeg + os1 - rotation;
if (a1 > 180)
a1 -= 360;
else if (a1 < -180) a1 += 360;
parent.UpdateWorldTransform(px, py, rotation + a1 * alpha, sx, parent.ascaleY, 0, 0);
rotation = child.arotation;
a2 = ((a2 + os) * MathUtils.RadDeg - child.ashearX) * s2 + os2 - rotation;
if (a2 > 180)
a2 -= 360;
else if (a2 < -180) a2 += 360;
child.UpdateWorldTransform(cx, cy, rotation + a2 * alpha, child.ascaleX, child.ascaleY, child.ashearX, child.ashearY);
}
}
}

View File

@@ -1,12 +0,0 @@
fileFormatVersion: 2
guid: 916f8e6534860cc40824adfc2916baa7
timeCreated: 1456265155
licenseType: Free
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -1,99 +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.
*****************************************************************************/
using System;
using System.Collections.Generic;
namespace Spine {
/// <summary>Stores the setup pose for an IkConstraint.</summary>
public class IkConstraintData : ConstraintData {
internal ExposedList<BoneData> bones = new ExposedList<BoneData>();
internal BoneData target;
internal int bendDirection = 1;
internal bool compress, stretch, uniform;
internal float mix = 1, softness;
public IkConstraintData (string name) : base(name) {
}
/// <summary>The bones that are constrained by this IK Constraint.</summary>
public ExposedList<BoneData> Bones {
get { return bones; }
}
/// <summary>The bone that is the IK target.</summary>
public BoneData Target {
get { return target; }
set { target = value; }
}
/// <summary>
/// A percentage (0-1) that controls the mix between the constraint and unconstrained rotations.</summary>
public float Mix {
get { return mix; }
set { mix = value; }
}
///<summary>For two bone IK, the distance from the maximum reach of the bones that rotation will slow.</summary>
public float Softness {
get { return softness; }
set { softness = value; }
}
/// <summary>Controls the bend direction of the IK bones, either 1 or -1.</summary>
public int BendDirection {
get { return bendDirection; }
set { bendDirection = value; }
}
/// <summary>
/// When true, and only a single bone is being constrained,
/// if the target is too close, the bone is scaled to reach it. </summary>
public bool Compress {
get { return compress; }
set { compress = value; }
}
/// <summary>
/// When true, if the target is out of range, the parent bone is scaled on the X axis to reach it.
/// If the bone has local nonuniform scale, stretching is not applied.</summary>
public bool Stretch {
get { return stretch; }
set { stretch = value; }
}
/// <summary>
/// When true, only a single bone is being constrained and Compress or Stretch is used,
/// the bone is scaled both on the X and Y axes.</summary>
public bool Uniform {
get { return uniform; }
set { uniform = value; }
}
}
}

View File

@@ -1,12 +0,0 @@
fileFormatVersion: 2
guid: 94ad1e9256073264785f806086a000ba
timeCreated: 1456265155
licenseType: Free
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -1,531 +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.
*****************************************************************************/
using System;
using System.IO;
using System.Text;
using System.Collections;
using System.Globalization;
using System.Collections.Generic;
namespace Spine {
public static class Json {
public static object Deserialize (TextReader text) {
var parser = new SharpJson.JsonDecoder();
parser.parseNumbersAsFloat = true;
return parser.Decode(text.ReadToEnd());
}
}
}
/**
* Copyright (c) 2016 Adriano Tinoco d'Oliveira Rezende
*
* Based on the JSON parser by Patrick van Bergen
* http://techblog.procurios.nl/k/news/view/14605/14863/how-do-i-write-my-own-parser-(for-json).html
*
* Changes made:
*
* - Optimized parser speed (deserialize roughly near 3x faster than original)
* - Added support to handle lexer/parser error messages with line numbers
* - Added more fine grained control over type conversions during the parsing
* - Refactory API (Separate Lexer code from Parser code and the Encoder from Decoder)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software
* and associated documentation files (the "Software"), to deal in the Software without restriction,
* including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
* The above copyright notice and this permission notice shall be included in all copies or substantial
* portions of the Software.
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT
* LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
* OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
namespace SharpJson
{
class Lexer
{
public enum Token {
None,
Null,
True,
False,
Colon,
Comma,
String,
Number,
CurlyOpen,
CurlyClose,
SquaredOpen,
SquaredClose,
};
public bool hasError {
get {
return !success;
}
}
public int lineNumber {
get;
private set;
}
public bool parseNumbersAsFloat {
get;
set;
}
char[] json;
int index = 0;
bool success = true;
char[] stringBuffer = new char[4096];
public Lexer(string text)
{
Reset();
json = text.ToCharArray();
parseNumbersAsFloat = false;
}
public void Reset()
{
index = 0;
lineNumber = 1;
success = true;
}
public string ParseString()
{
int idx = 0;
StringBuilder builder = null;
SkipWhiteSpaces();
// "
char c = json[index++];
bool failed = false;
bool complete = false;
while (!complete && !failed) {
if (index == json.Length)
break;
c = json[index++];
if (c == '"') {
complete = true;
break;
} else if (c == '\\') {
if (index == json.Length)
break;
c = json[index++];
switch (c) {
case '"':
stringBuffer[idx++] = '"';
break;
case '\\':
stringBuffer[idx++] = '\\';
break;
case '/':
stringBuffer[idx++] = '/';
break;
case 'b':
stringBuffer[idx++] = '\b';
break;
case'f':
stringBuffer[idx++] = '\f';
break;
case 'n':
stringBuffer[idx++] = '\n';
break;
case 'r':
stringBuffer[idx++] = '\r';
break;
case 't':
stringBuffer[idx++] = '\t';
break;
case 'u':
int remainingLength = json.Length - index;
if (remainingLength >= 4) {
var hex = new string(json, index, 4);
// XXX: handle UTF
stringBuffer[idx++] = (char) Convert.ToInt32(hex, 16);
// skip 4 chars
index += 4;
} else {
failed = true;
}
break;
}
} else {
stringBuffer[idx++] = c;
}
if (idx >= stringBuffer.Length) {
if (builder == null)
builder = new StringBuilder();
builder.Append(stringBuffer, 0, idx);
idx = 0;
}
}
if (!complete) {
success = false;
return null;
}
if (builder != null)
return builder.ToString ();
else
return new string (stringBuffer, 0, idx);
}
string GetNumberString()
{
SkipWhiteSpaces();
int lastIndex = GetLastIndexOfNumber(index);
int charLength = (lastIndex - index) + 1;
var result = new string (json, index, charLength);
index = lastIndex + 1;
return result;
}
public float ParseFloatNumber()
{
float number;
var str = GetNumberString ();
if (!float.TryParse (str, NumberStyles.Float, CultureInfo.InvariantCulture, out number))
return 0;
return number;
}
public double ParseDoubleNumber()
{
double number;
var str = GetNumberString ();
if (!double.TryParse(str, NumberStyles.Any, CultureInfo.InvariantCulture, out number))
return 0;
return number;
}
int GetLastIndexOfNumber(int index)
{
int lastIndex;
for (lastIndex = index; lastIndex < json.Length; lastIndex++) {
char ch = json[lastIndex];
if ((ch < '0' || ch > '9') && ch != '+' && ch != '-'
&& ch != '.' && ch != 'e' && ch != 'E')
break;
}
return lastIndex - 1;
}
void SkipWhiteSpaces()
{
for (; index < json.Length; index++) {
char ch = json[index];
if (ch == '\n')
lineNumber++;
if (!char.IsWhiteSpace(json[index]))
break;
}
}
public Token LookAhead()
{
SkipWhiteSpaces();
int savedIndex = index;
return NextToken(json, ref savedIndex);
}
public Token NextToken()
{
SkipWhiteSpaces();
return NextToken(json, ref index);
}
static Token NextToken(char[] json, ref int index)
{
if (index == json.Length)
return Token.None;
char c = json[index++];
switch (c) {
case '{':
return Token.CurlyOpen;
case '}':
return Token.CurlyClose;
case '[':
return Token.SquaredOpen;
case ']':
return Token.SquaredClose;
case ',':
return Token.Comma;
case '"':
return Token.String;
case '0': case '1': case '2': case '3': case '4':
case '5': case '6': case '7': case '8': case '9':
case '-':
return Token.Number;
case ':':
return Token.Colon;
}
index--;
int remainingLength = json.Length - index;
// false
if (remainingLength >= 5) {
if (json[index] == 'f' &&
json[index + 1] == 'a' &&
json[index + 2] == 'l' &&
json[index + 3] == 's' &&
json[index + 4] == 'e') {
index += 5;
return Token.False;
}
}
// true
if (remainingLength >= 4) {
if (json[index] == 't' &&
json[index + 1] == 'r' &&
json[index + 2] == 'u' &&
json[index + 3] == 'e') {
index += 4;
return Token.True;
}
}
// null
if (remainingLength >= 4) {
if (json[index] == 'n' &&
json[index + 1] == 'u' &&
json[index + 2] == 'l' &&
json[index + 3] == 'l') {
index += 4;
return Token.Null;
}
}
return Token.None;
}
}
public class JsonDecoder
{
public string errorMessage {
get;
private set;
}
public bool parseNumbersAsFloat {
get;
set;
}
Lexer lexer;
public JsonDecoder()
{
errorMessage = null;
parseNumbersAsFloat = false;
}
public object Decode(string text)
{
errorMessage = null;
lexer = new Lexer(text);
lexer.parseNumbersAsFloat = parseNumbersAsFloat;
return ParseValue();
}
public static object DecodeText(string text)
{
var builder = new JsonDecoder();
return builder.Decode(text);
}
IDictionary<string, object> ParseObject()
{
var table = new Dictionary<string, object>();
// {
lexer.NextToken();
while (true) {
var token = lexer.LookAhead();
switch (token) {
case Lexer.Token.None:
TriggerError("Invalid token");
return null;
case Lexer.Token.Comma:
lexer.NextToken();
break;
case Lexer.Token.CurlyClose:
lexer.NextToken();
return table;
default:
// name
string name = EvalLexer(lexer.ParseString());
if (errorMessage != null)
return null;
// :
token = lexer.NextToken();
if (token != Lexer.Token.Colon) {
TriggerError("Invalid token; expected ':'");
return null;
}
// value
object value = ParseValue();
if (errorMessage != null)
return null;
table[name] = value;
break;
}
}
//return null; // Unreachable code
}
IList<object> ParseArray()
{
var array = new List<object>();
// [
lexer.NextToken();
while (true) {
var token = lexer.LookAhead();
switch (token) {
case Lexer.Token.None:
TriggerError("Invalid token");
return null;
case Lexer.Token.Comma:
lexer.NextToken();
break;
case Lexer.Token.SquaredClose:
lexer.NextToken();
return array;
default:
object value = ParseValue();
if (errorMessage != null)
return null;
array.Add(value);
break;
}
}
//return null; // Unreachable code
}
object ParseValue()
{
switch (lexer.LookAhead()) {
case Lexer.Token.String:
return EvalLexer(lexer.ParseString());
case Lexer.Token.Number:
if (parseNumbersAsFloat)
return EvalLexer(lexer.ParseFloatNumber());
else
return EvalLexer(lexer.ParseDoubleNumber());
case Lexer.Token.CurlyOpen:
return ParseObject();
case Lexer.Token.SquaredOpen:
return ParseArray();
case Lexer.Token.True:
lexer.NextToken();
return true;
case Lexer.Token.False:
lexer.NextToken();
return false;
case Lexer.Token.Null:
lexer.NextToken();
return null;
case Lexer.Token.None:
break;
}
TriggerError("Unable to parse value");
return null;
}
void TriggerError(string message)
{
errorMessage = string.Format("Error: '{0}' at line {1}",
message, lexer.lineNumber);
}
T EvalLexer<T>(T value)
{
if (lexer.hasError)
TriggerError("Lexical error ocurred");
return value;
}
}
}

View File

@@ -1,12 +0,0 @@
fileFormatVersion: 2
guid: 371f40ecc08b2eb4cbec49585d41e2c3
timeCreated: 1456265153
licenseType: Free
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -1,173 +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.
*****************************************************************************/
//#define USE_FAST_SIN_COS_ATAN2_APPROXIMATIONS
using System;
namespace Spine {
public static class MathUtils {
public const float PI = 3.1415927f;
public const float PI2 = PI * 2;
public const float RadDeg = 180f / PI;
public const float DegRad = PI / 180;
static Random random = new Random();
#if USE_FAST_SIN_COS_ATAN2_APPROXIMATIONS
const int SIN_BITS = 14; // 16KB. Adjust for accuracy.
const int SIN_MASK = ~(-1 << SIN_BITS);
const int SIN_COUNT = SIN_MASK + 1;
const float RadFull = PI * 2;
const float DegFull = 360;
const float RadToIndex = SIN_COUNT / RadFull;
const float DegToIndex = SIN_COUNT / DegFull;
static float[] sin = new float[SIN_COUNT];
static MathUtils () {
for (int i = 0; i < SIN_COUNT; i++)
sin[i] = (float)Math.Sin((i + 0.5f) / SIN_COUNT * RadFull);
for (int i = 0; i < 360; i += 90)
sin[(int)(i * DegToIndex) & SIN_MASK] = (float)Math.Sin(i * DegRad);
}
/// <summary>Returns the sine of a given angle in radians from a lookup table.</summary>
static public float Sin (float radians) {
return sin[(int)(radians * RadToIndex) & SIN_MASK];
}
/// <summary>Returns the cosine of a given angle in radians from a lookup table.</summary>
static public float Cos (float radians) {
return sin[(int)((radians + PI / 2) * RadToIndex) & SIN_MASK];
}
/// <summary>Returns the sine of a given angle in degrees from a lookup table.</summary>
static public float SinDeg (float degrees) {
return sin[(int)(degrees * DegToIndex) & SIN_MASK];
}
/// <summary>Returns the cosine of a given angle in degrees from a lookup table.</summary>
static public float CosDeg (float degrees) {
return sin[(int)((degrees + 90) * DegToIndex) & SIN_MASK];
}
/// <summary>Returns atan2 in radians, faster but less accurate than Math.Atan2. Average error of 0.00231 radians (0.1323
/// degrees), largest error of 0.00488 radians (0.2796 degrees).</summary>
static public float Atan2 (float y, float x) {
if (x == 0f) {
if (y > 0f) return PI / 2;
if (y == 0f) return 0f;
return -PI / 2;
}
float atan, z = y / x;
if (Math.Abs(z) < 1f) {
atan = z / (1f + 0.28f * z * z);
if (x < 0f) return atan + (y < 0f ? -PI : PI);
return atan;
}
atan = PI / 2 - z / (z * z + 0.28f);
return y < 0f ? atan - PI : atan;
}
#else
/// <summary>Returns the sine of a given angle in radians.</summary>
static public float Sin (float radians) {
return (float)Math.Sin(radians);
}
/// <summary>Returns the cosine of a given angle in radians.</summary>
static public float Cos (float radians) {
return (float)Math.Cos(radians);
}
/// <summary>Returns the sine of a given angle in degrees.</summary>
static public float SinDeg (float degrees) {
return (float)Math.Sin(degrees * DegRad);
}
/// <summary>Returns the cosine of a given angle in degrees.</summary>
static public float CosDeg (float degrees) {
return (float)Math.Cos(degrees * DegRad);
}
/// <summary>Returns the atan2 using Math.Atan2.</summary>
static public float Atan2 (float y, float x) {
return (float)Math.Atan2(y, x);
}
#endif
static public float Clamp (float value, float min, float max) {
if (value < min) return min;
if (value > max) return max;
return value;
}
static public float RandomTriangle(float min, float max) {
return RandomTriangle(min, max, (min + max) * 0.5f);
}
static public float RandomTriangle(float min, float max, float mode) {
float u = (float)random.NextDouble();
float d = max - min;
if (u <= (mode - min) / d) return min + (float)Math.Sqrt(u * d * (mode - min));
return max - (float)Math.Sqrt((1 - u) * d * (max - mode));
}
}
public abstract class IInterpolation {
public static IInterpolation Pow2 = new Pow(2);
public static IInterpolation Pow2Out = new PowOut(2);
protected abstract float Apply(float a);
public float Apply(float start, float end, float a) {
return start + (end - start) * Apply(a);
}
}
public class Pow: IInterpolation {
public float Power { get; set; }
public Pow(float power) {
Power = power;
}
protected override float Apply(float a) {
if (a <= 0.5f) return (float)Math.Pow(a * 2, Power) / 2;
return (float)Math.Pow((a - 1) * 2, Power) / (Power % 2 == 0 ? -2 : 2) + 1;
}
}
public class PowOut : Pow {
public PowOut(float power) : base(power) {
}
protected override float Apply(float a) {
return (float)Math.Pow(a - 1, Power) * (Power % 2 == 0 ? -1 : 1) + 1;
}
}
}

View File

@@ -1,12 +0,0 @@
fileFormatVersion: 2
guid: 03b653e54c5403b4191f5003d64c6e18
timeCreated: 1456265153
licenseType: Free
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -1,467 +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.
*****************************************************************************/
using System;
namespace Spine {
/// <summary>
/// <para>
/// 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}.</para>
/// <para>
/// See <a href="http://esotericsoftware.com/spine-path-constraints">Path constraints</a> in the Spine User Guide.</para>
/// </summary>
public class PathConstraint : IUpdatable {
const int NONE = -1, BEFORE = -2, AFTER = -3;
const float Epsilon = 0.00001f;
internal PathConstraintData data;
internal ExposedList<Bone> bones;
internal Slot target;
internal float position, spacing, rotateMix, translateMix;
internal bool active;
internal ExposedList<float> spaces = new ExposedList<float>(), positions = new ExposedList<float>();
internal ExposedList<float> world = new ExposedList<float>(), curves = new ExposedList<float>(), lengths = new ExposedList<float>();
internal float[] segments = new float[10];
public PathConstraint (PathConstraintData data, Skeleton skeleton) {
if (data == null) throw new ArgumentNullException("data", "data cannot be null.");
if (skeleton == null) throw new ArgumentNullException("skeleton", "skeleton cannot be null.");
this.data = data;
bones = new ExposedList<Bone>(data.Bones.Count);
foreach (BoneData boneData in data.bones)
bones.Add(skeleton.FindBone(boneData.name));
target = skeleton.FindSlot(data.target.name);
position = data.position;
spacing = data.spacing;
rotateMix = data.rotateMix;
translateMix = data.translateMix;
}
/// <summary>Copy constructor.</summary>
public PathConstraint (PathConstraint constraint, Skeleton skeleton) {
if (constraint == null) throw new ArgumentNullException("constraint cannot be null.");
if (skeleton == null) throw new ArgumentNullException("skeleton cannot be null.");
data = constraint.data;
bones = new ExposedList<Bone>(constraint.Bones.Count);
foreach (Bone bone in constraint.Bones)
bones.Add(skeleton.Bones.Items[bone.data.index]);
target = skeleton.slots.Items[constraint.target.data.index];
position = constraint.position;
spacing = constraint.spacing;
rotateMix = constraint.rotateMix;
translateMix = constraint.translateMix;
}
/// <summary>Applies the constraint to the constrained bones.</summary>
public void Apply () {
Update();
}
public void Update () {
PathAttachment attachment = target.Attachment as PathAttachment;
if (attachment == null) return;
float rotateMix = this.rotateMix, translateMix = this.translateMix;
bool translate = translateMix > 0, rotate = rotateMix > 0;
if (!translate && !rotate) return;
PathConstraintData data = this.data;
bool percentSpacing = data.spacingMode == SpacingMode.Percent;
RotateMode rotateMode = data.rotateMode;
bool tangents = rotateMode == RotateMode.Tangent, scale = rotateMode == RotateMode.ChainScale;
int boneCount = this.bones.Count, spacesCount = tangents ? boneCount : boneCount + 1;
Bone[] bonesItems = this.bones.Items;
ExposedList<float> spaces = this.spaces.Resize(spacesCount), lengths = null;
float spacing = this.spacing;
if (scale || !percentSpacing) {
if (scale) lengths = this.lengths.Resize(boneCount);
bool lengthSpacing = data.spacingMode == SpacingMode.Length;
for (int i = 0, n = spacesCount - 1; i < n;) {
Bone bone = bonesItems[i];
float setupLength = bone.data.length;
if (setupLength < PathConstraint.Epsilon) {
if (scale) lengths.Items[i] = 0;
spaces.Items[++i] = 0;
} else if (percentSpacing) {
if (scale) {
float x = setupLength * bone.a, y = setupLength * bone.c;
float length = (float)Math.Sqrt(x * x + y * y);
lengths.Items[i] = length;
}
spaces.Items[++i] = spacing;
} else {
float x = setupLength * bone.a, y = setupLength * bone.c;
float length = (float)Math.Sqrt(x * x + y * y);
if (scale) lengths.Items[i] = length;
spaces.Items[++i] = (lengthSpacing ? setupLength + spacing : spacing) * length / setupLength;
}
}
} else {
for (int i = 1; i < spacesCount; i++)
spaces.Items[i] = spacing;
}
float[] positions = ComputeWorldPositions(attachment, spacesCount, tangents,
data.positionMode == PositionMode.Percent, percentSpacing);
float boneX = positions[0], boneY = positions[1], offsetRotation = data.offsetRotation;
bool tip;
if (offsetRotation == 0) {
tip = rotateMode == RotateMode.Chain;
} else {
tip = false;
Bone p = target.bone;
offsetRotation *= p.a * p.d - p.b * p.c > 0 ? MathUtils.DegRad : -MathUtils.DegRad;
}
for (int i = 0, p = 3; i < boneCount; i++, p += 3) {
Bone bone = bonesItems[i];
bone.worldX += (boneX - bone.worldX) * translateMix;
bone.worldY += (boneY - bone.worldY) * translateMix;
float x = positions[p], y = positions[p + 1], dx = x - boneX, dy = y - boneY;
if (scale) {
float length = lengths.Items[i];
if (length >= PathConstraint.Epsilon) {
float s = ((float)Math.Sqrt(dx * dx + dy * dy) / length - 1) * rotateMix + 1;
bone.a *= s;
bone.c *= s;
}
}
boneX = x;
boneY = y;
if (rotate) {
float a = bone.a, b = bone.b, c = bone.c, d = bone.d, r, cos, sin;
if (tangents)
r = positions[p - 1];
else if (spaces.Items[i + 1] < PathConstraint.Epsilon)
r = positions[p + 2];
else
r = MathUtils.Atan2(dy, dx);
r -= MathUtils.Atan2(c, a);
if (tip) {
cos = MathUtils.Cos(r);
sin = MathUtils.Sin(r);
float length = bone.data.length;
boneX += (length * (cos * a - sin * c) - dx) * rotateMix;
boneY += (length * (sin * a + cos * c) - dy) * rotateMix;
} else
r += offsetRotation;
if (r > MathUtils.PI)
r -= MathUtils.PI2;
else if (r < -MathUtils.PI) //
r += MathUtils.PI2;
r *= rotateMix;
cos = MathUtils.Cos(r);
sin = MathUtils.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.appliedValid = false;
}
}
float[] ComputeWorldPositions (PathAttachment path, int spacesCount, bool tangents, bool percentPosition,
bool percentSpacing) {
Slot target = this.target;
float position = this.position;
float[] spacesItems = this.spaces.Items, output = this.positions.Resize(spacesCount * 3 + 2).Items, world;
bool closed = path.Closed;
int verticesLength = path.WorldVerticesLength, curveCount = verticesLength / 6, prevCurve = NONE;
float pathLength = 0;
if (!path.ConstantSpeed) {
float[] lengths = path.Lengths;
curveCount -= closed ? 1 : 2;
pathLength = lengths[curveCount];
if (percentPosition) position *= pathLength;
if (percentSpacing) {
for (int i = 1; i < spacesCount; i++)
spacesItems[i] *= pathLength;
}
world = this.world.Resize(8).Items;
for (int i = 0, o = 0, curve = 0; i < spacesCount; i++, o += 3) {
float space = spacesItems[i];
position += space;
float p = position;
if (closed) {
p %= pathLength;
if (p < 0) p += pathLength;
curve = 0;
} else if (p < 0) {
if (prevCurve != BEFORE) {
prevCurve = BEFORE;
path.ComputeWorldVertices(target, 2, 4, world, 0, 2);
}
AddBeforePosition(p, world, 0, output, o);
continue;
} else if (p > pathLength) {
if (prevCurve != AFTER) {
prevCurve = AFTER;
path.ComputeWorldVertices(target, verticesLength - 6, 4, world, 0, 2);
}
AddAfterPosition(p - pathLength, world, 0, output, o);
continue;
}
// Determine curve containing position.
for (;; curve++) {
float length = lengths[curve];
if (p > length) continue;
if (curve == 0)
p /= length;
else {
float 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);
}
AddCurvePosition(p, world[0], world[1], world[2], world[3], world[4], world[5], world[6], world[7], output, o,
tangents || (i > 0 && space < PathConstraint.Epsilon));
}
return output;
}
// World vertices.
if (closed) {
verticesLength += 2;
world = this.world.Resize(verticesLength).Items;
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 = this.world.Resize(verticesLength).Items;
path.ComputeWorldVertices(target, 2, verticesLength, world, 0, 2);
}
// Curve lengths.
float[] curves = this.curves.Resize(curveCount).Items;
pathLength = 0;
float x1 = world[0], y1 = world[1], cx1 = 0, cy1 = 0, cx2 = 0, cy2 = 0, x2 = 0, y2 = 0;
float tmpx, tmpy, dddfx, dddfy, ddfx, ddfy, dfx, dfy;
for (int 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.1875f;
tmpy = (y1 - cy1 * 2 + cy2) * 0.1875f;
dddfx = ((cx1 - cx2) * 3 - x1 + x2) * 0.09375f;
dddfy = ((cy1 - cy2) * 3 - y1 + y2) * 0.09375f;
ddfx = tmpx * 2 + dddfx;
ddfy = tmpy * 2 + dddfy;
dfx = (cx1 - x1) * 0.75f + tmpx + dddfx * 0.16666667f;
dfy = (cy1 - y1) * 0.75f + tmpy + dddfy * 0.16666667f;
pathLength += (float)Math.Sqrt(dfx * dfx + dfy * dfy);
dfx += ddfx;
dfy += ddfy;
ddfx += dddfx;
ddfy += dddfy;
pathLength += (float)Math.Sqrt(dfx * dfx + dfy * dfy);
dfx += ddfx;
dfy += ddfy;
pathLength += (float)Math.Sqrt(dfx * dfx + dfy * dfy);
dfx += ddfx + dddfx;
dfy += ddfy + dddfy;
pathLength += (float)Math.Sqrt(dfx * dfx + dfy * dfy);
curves[i] = pathLength;
x1 = x2;
y1 = y2;
}
if (percentPosition)
position *= pathLength;
else
position *= pathLength / path.lengths[curveCount - 1];
if (percentSpacing) {
for (int i = 1; i < spacesCount; i++)
spacesItems[i] *= pathLength;
}
float[] segments = this.segments;
float curveLength = 0;
for (int i = 0, o = 0, curve = 0, segment = 0; i < spacesCount; i++, o += 3) {
float space = spacesItems[i];
position += space;
float p = position;
if (closed) {
p %= pathLength;
if (p < 0) p += pathLength;
curve = 0;
} else if (p < 0) {
AddBeforePosition(p, world, 0, output, o);
continue;
} else if (p > pathLength) {
AddAfterPosition(p - pathLength, world, verticesLength - 4, output, o);
continue;
}
// Determine curve containing position.
for (;; curve++) {
float length = curves[curve];
if (p > length) continue;
if (curve == 0)
p /= length;
else {
float prev = curves[curve - 1];
p = (p - prev) / (length - prev);
}
break;
}
// Curve segment lengths.
if (curve != prevCurve) {
prevCurve = curve;
int 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.03f;
tmpy = (y1 - cy1 * 2 + cy2) * 0.03f;
dddfx = ((cx1 - cx2) * 3 - x1 + x2) * 0.006f;
dddfy = ((cy1 - cy2) * 3 - y1 + y2) * 0.006f;
ddfx = tmpx * 2 + dddfx;
ddfy = tmpy * 2 + dddfy;
dfx = (cx1 - x1) * 0.3f + tmpx + dddfx * 0.16666667f;
dfy = (cy1 - y1) * 0.3f + tmpy + dddfy * 0.16666667f;
curveLength = (float)Math.Sqrt(dfx * dfx + dfy * dfy);
segments[0] = curveLength;
for (ii = 1; ii < 8; ii++) {
dfx += ddfx;
dfy += ddfy;
ddfx += dddfx;
ddfy += dddfy;
curveLength += (float)Math.Sqrt(dfx * dfx + dfy * dfy);
segments[ii] = curveLength;
}
dfx += ddfx;
dfy += ddfy;
curveLength += (float)Math.Sqrt(dfx * dfx + dfy * dfy);
segments[8] = curveLength;
dfx += ddfx + dddfx;
dfy += ddfy + dddfy;
curveLength += (float)Math.Sqrt(dfx * dfx + dfy * dfy);
segments[9] = curveLength;
segment = 0;
}
// Weight by segment length.
p *= curveLength;
for (;; segment++) {
float length = segments[segment];
if (p > length) continue;
if (segment == 0)
p /= length;
else {
float prev = segments[segment - 1];
p = segment + (p - prev) / (length - prev);
}
break;
}
AddCurvePosition(p * 0.1f, x1, y1, cx1, cy1, cx2, cy2, x2, y2, output, o, tangents || (i > 0 && space < PathConstraint.Epsilon));
}
return output;
}
static void AddBeforePosition (float p, float[] temp, int i, float[] output, int o) {
float x1 = temp[i], y1 = temp[i + 1], dx = temp[i + 2] - x1, dy = temp[i + 3] - y1, r = MathUtils.Atan2(dy, dx);
output[o] = x1 + p * MathUtils.Cos(r);
output[o + 1] = y1 + p * MathUtils.Sin(r);
output[o + 2] = r;
}
static void AddAfterPosition (float p, float[] temp, int i, float[] output, int o) {
float x1 = temp[i + 2], y1 = temp[i + 3], dx = x1 - temp[i], dy = y1 - temp[i + 1], r = MathUtils.Atan2(dy, dx);
output[o] = x1 + p * MathUtils.Cos(r);
output[o + 1] = y1 + p * MathUtils.Sin(r);
output[o + 2] = r;
}
static void AddCurvePosition (float p, float x1, float y1, float cx1, float cy1, float cx2, float cy2, float x2, float y2,
float[] output, int o, bool tangents) {
if (p < PathConstraint.Epsilon || float.IsNaN(p)) {
output[o] = x1;
output[o + 1] = y1;
output[o + 2] = (float)Math.Atan2(cy1 - y1, cx1 - x1);
return;
}
float tt = p * p, ttt = tt * p, u = 1 - p, uu = u * u, uuu = uu * u;
float ut = u * p, ut3 = ut * 3, uut3 = u * ut3, utt3 = ut3 * p;
float x = x1 * uuu + cx1 * uut3 + cx2 * utt3 + x2 * ttt, y = y1 * uuu + cy1 * uut3 + cy2 * utt3 + y2 * ttt;
output[o] = x;
output[o + 1] = y;
if (tangents) {
if (p < 0.001f)
output[o + 2] = (float)Math.Atan2(cy1 - y1, cx1 - x1);
else
output[o + 2] = (float)Math.Atan2(y - (y1 * uu + cy1 * ut * 2 + cy2 * tt), x - (x1 * uu + cx1 * ut * 2 + cx2 * tt));
}
}
/// <summary>The position along the path.</summary>
public float Position { get { return position; } set { position = value; } }
/// <summary>The spacing between bones.</summary>
public float Spacing { get { return spacing; } set { spacing = value; } }
/// <summary>A percentage (0-1) that controls the mix between the constrained and unconstrained rotations.</summary>
public float RotateMix { get { return rotateMix; } set { rotateMix = value; } }
/// <summary>A percentage (0-1) that controls the mix between the constrained and unconstrained translations.</summary>
public float TranslateMix { get { return translateMix; } set { translateMix = value; } }
/// <summary>The bones that will be modified by this path constraint.</summary>
public ExposedList<Bone> Bones { get { return bones; } }
/// <summary>The slot whose path attachment will be used to constrained the bones.</summary>
public Slot Target { get { return target; } set { target = value; } }
public bool Active { get { return active; } }
/// <summary>The path constraint's setup pose data.</summary>
public PathConstraintData Data { get { return data; } }
}
}

View File

@@ -1,12 +0,0 @@
fileFormatVersion: 2
guid: 731d05fbc2874c74984813ce4c5bb8df
timeCreated: 1467213650
licenseType: Free
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -1,68 +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.
*****************************************************************************/
using System;
namespace Spine {
public class PathConstraintData : ConstraintData {
internal ExposedList<BoneData> bones = new ExposedList<BoneData>();
internal SlotData target;
internal PositionMode positionMode;
internal SpacingMode spacingMode;
internal RotateMode rotateMode;
internal float offsetRotation;
internal float position, spacing, rotateMix, translateMix;
public PathConstraintData (string name) : base(name) {
}
public ExposedList<BoneData> Bones { get { return bones; } }
public SlotData Target { get { return target; } set { target = value; } }
public PositionMode PositionMode { get { return positionMode; } set { positionMode = value; } }
public SpacingMode SpacingMode { get { return spacingMode; } set { spacingMode = value; } }
public RotateMode RotateMode { get { return rotateMode; } set { rotateMode = value; } }
public float OffsetRotation { get { return offsetRotation; } set { offsetRotation = value; } }
public float Position { get { return position; } set { position = value; } }
public float Spacing { get { return spacing; } set { spacing = value; } }
public float RotateMix { get { return rotateMix; } set { rotateMix = value; } }
public float TranslateMix { get { return translateMix; } set { translateMix = value; } }
}
public enum PositionMode {
Fixed, Percent
}
public enum SpacingMode {
Length, Fixed, Percent
}
public enum RotateMode {
Tangent, Chain, ChainScale
}
}

View File

@@ -1,12 +0,0 @@
fileFormatVersion: 2
guid: 9d836858269be96428428fb6764dfc3a
timeCreated: 1467213651
licenseType: Free
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant: