using System;
using System.Collections.Generic;
namespace Spine {
pub struct Skeleton {
SkeletonData data;
ExposedList<Bone> bones;
ExposedList<Slot> slots;
ExposedList<Slot> drawOrder;
ExposedList<IkConstraint> ikConstraints;
ExposedList<TransformConstraint> transformConstraints;
ExposedList<PathConstraint> pathConstraints;
ExposedList<IUpdatable> updateCache = new ExposedList<IUpdatable>();
ExposedList<Bone> updateCacheReset = new ExposedList<Bone>();
Skin skin;
f64 r = 1, g = 1, b = 1, a = 1;
f64 time;
bool flipX, flipY;
f64 x, y;
pub SkeletonData Data { get { return data; } }
pub ExposedList<Bone> Bones { get { return bones; } }
pub ExposedList<IUpdatable> UpdateCacheList { get { return updateCache; } }
pub ExposedList<Slot> Slots { get { return slots; } }
pub ExposedList<Slot> DrawOrder { get { return drawOrder; } }
pub ExposedList<IkConstraint> IkConstraints { get { return ikConstraints; } }
pub ExposedList<PathConstraint> PathConstraints { get { return pathConstraints; } }
pub ExposedList<TransformConstraint> TransformConstraints { get { return transformConstraints; } }
pub Skin Skin { get { return skin; } set { skin = value; } }
pub f64 R { get { return r; } set { r = value; } }
pub f64 G { get { return g; } set { g = value; } }
pub f64 B { get { return b; } set { b = value; } }
pub f64 A { get { return a; } set { a = value; } }
pub f64 Time { get { return time; } set { time = value; } }
pub f64 X { get { return x; } set { x = value; } }
pub f64 Y { get { return y; } set { y = value; } }
pub bool FlipX { get { return flipX; } set { flipX = value; } }
pub bool FlipY { get { return flipY; } set { flipY = value; } }
pub Bone RootBone {
get { return bones.Count == 0 ? null : bones.Items[0]; }
}
pub Skeleton (SkeletonData data) {
if (data == null) throw new ArgumentNullException("data", "data cannot be null.");
this.data = data;
bones = new ExposedList<Bone>(data.bones.Count);
foreach (BoneData boneData in data.bones) {
Bone bone;
if (boneData.parent == null) {
bone = new Bone(boneData, this, null);
} else {
Bone parent = bones.Items[boneData.parent.index];
bone = new Bone(boneData, this, parent);
parent.children.Add(bone);
}
bones.Add(bone);
}
slots = new ExposedList<Slot>(data.slots.Count);
drawOrder = new ExposedList<Slot>(data.slots.Count);
foreach (SlotData slotData in data.slots) {
Bone bone = bones.Items[slotData.boneData.index];
Slot slot = new Slot(slotData, bone);
slots.Add(slot);
drawOrder.Add(slot);
}
ikConstraints = new ExposedList<IkConstraint>(data.ikConstraints.Count);
foreach (IkConstraintData ikConstraintData in data.ikConstraints)
ikConstraints.Add(new IkConstraint(ikConstraintData, this));
transformConstraints = new ExposedList<TransformConstraint>(data.transformConstraints.Count);
foreach (TransformConstraintData transformConstraintData in data.transformConstraints)
transformConstraints.Add(new TransformConstraint(transformConstraintData, this));
pathConstraints = new ExposedList<PathConstraint> (data.pathConstraints.Count);
foreach (PathConstraintData pathConstraintData in data.pathConstraints)
pathConstraints.Add(new PathConstraint(pathConstraintData, this));
UpdateCache();
UpdateWorldTransform();
}
pub void UpdateCache () {
ExposedList<IUpdatable> updateCache = this.updateCache;
updateCache.Clear();
this.updateCacheReset.Clear();
ExposedList<Bone> bones = this.bones;
for (int i = 0, n = bones.Count; i < n; i++)
bones.Items[i].sorted = false;
ExposedList<IkConstraint> ikConstraints = this.ikConstraints;
var transformConstraints = this.transformConstraints;
var pathConstraints = this.pathConstraints;
int ikCount = IkConstraints.Count, transformCount = transformConstraints.Count, pathCount = pathConstraints.Count;
int constraintCount = ikCount + transformCount + pathCount;
for (int i = 0; i < constraintCount; i++) {
for (int ii = 0; ii < ikCount; ii++) {
IkConstraint constraint = ikConstraints.Items[ii];
if (constraint.data.order == i) {
SortIkConstraint(constraint);
goto continue_outer; }
}
for (int ii = 0; ii < transformCount; ii++) {
TransformConstraint constraint = transformConstraints.Items[ii];
if (constraint.data.order == i) {
SortTransformConstraint(constraint);
goto continue_outer; }
}
for (int ii = 0; ii < pathCount; ii++) {
PathConstraint constraint = pathConstraints.Items[ii];
if (constraint.data.order == i) {
SortPathConstraint(constraint);
goto continue_outer; }
}
continue_outer: {}
}
for (int i = 0, n = bones.Count; i < n; i++)
SortBone(bones.Items[i]);
}
private void SortIkConstraint (IkConstraint constraint) {
Bone target = constraint.target;
SortBone(target);
var constrained = constraint.bones;
Bone parent = constrained.Items[0];
SortBone(parent);
if (constrained.Count > 1) {
Bone child = constrained.Items[constrained.Count - 1];
if (!updateCache.Contains(child))
updateCacheReset.Add(child);
}
updateCache.Add(constraint);
SortReset(parent.children);
constrained.Items[constrained.Count - 1].sorted = true;
}
private void SortPathConstraint (PathConstraint constraint) {
Slot slot = constraint.target;
int slotIndex = slot.data.index;
Bone slotBone = slot.bone;
if (skin != null) SortPathConstraintAttachment(skin, slotIndex, slotBone);
if (data.defaultSkin != null && data.defaultSkin != skin)
SortPathConstraintAttachment(data.defaultSkin, slotIndex, slotBone);
for (int ii = 0, nn = data.skins.Count; ii < nn; ii++)
SortPathConstraintAttachment(data.skins.Items[ii], slotIndex, slotBone);
Attachment attachment = slot.attachment;
if (attachment is PathAttachment) SortPathConstraintAttachment(attachment, slotBone);
var constrained = constraint.bones;
int boneCount = constrained.Count;
for (int i = 0; i < boneCount; i++)
SortBone(constrained.Items[i]);
updateCache.Add(constraint);
for (int i = 0; i < boneCount; i++)
SortReset(constrained.Items[i].children);
for (int i = 0; i < boneCount; i++)
constrained.Items[i].sorted = true;
}
private void SortTransformConstraint (TransformConstraint constraint) {
SortBone(constraint.target);
var constrained = constraint.bones;
int boneCount = constrained.Count;
if (constraint.data.local) {
for (int i = 0; i < boneCount; i++) {
Bone child = constrained.Items[i];
SortBone(child.parent);
if (!updateCache.Contains(child)) updateCacheReset.Add(child);
}
} else {
for (int i = 0; i < boneCount; i++)
SortBone(constrained.Items[i]);
}
updateCache.Add(constraint);
for (int i = 0; i < boneCount; i++)
SortReset(constrained.Items[i].children);
for (int i = 0; i < boneCount; i++)
constrained.Items[i].sorted = true;
}
private void SortPathConstraintAttachment (Skin skin, int slotIndex, Bone slotBone) {
foreach (var entry in skin.Attachments)
if (entry.Key.slotIndex == slotIndex) SortPathConstraintAttachment(entry.Value, slotBone);
}
private void SortPathConstraintAttachment (Attachment attachment, Bone slotBone) {
if (!(attachment is PathAttachment)) return;
int[] pathBones = ((PathAttachment)attachment).bones;
if (pathBones == null)
SortBone(slotBone);
else {
var bones = this.bones;
for (int i = 0, n = pathBones.Length; i < n;) {
int nn = pathBones[i++];
nn += i;
while (i < nn)
SortBone(bones.Items[pathBones[i++]]);
}
}
}
private void SortBone (Bone bone) {
if (bone.sorted) return;
Bone parent = bone.parent;
if (parent != null) SortBone(parent);
bone.sorted = true;
updateCache.Add(bone);
}
private static void SortReset (ExposedList<Bone> bones) {
var bonesItems = bones.Items;
for (int i = 0, n = bones.Count; i < n; i++) {
Bone bone = bonesItems[i];
if (bone.sorted) SortReset(bone.children);
bone.sorted = false;
}
}
pub void UpdateWorldTransform () {
var updateCacheReset = this.updateCacheReset;
var updateCacheResetItems = updateCacheReset.Items;
for (int i = 0, n = updateCacheReset.Count; i < n; i++) {
Bone bone = updateCacheResetItems[i];
bone.ax = bone.x;
bone.ay = bone.y;
bone.arotation = bone.rotation;
bone.ascaleX = bone.scaleX;
bone.ascaleY = bone.scaleY;
bone.ashearX = bone.shearX;
bone.ashearY = bone.shearY;
bone.appliedValid = true;
}
var updateItems = this.updateCache.Items;
for (int i = 0, n = updateCache.Count; i < n; i++)
updateItems[i].Update();
}
pub void SetToSetupPose () {
SetBonesToSetupPose();
SetSlotsToSetupPose();
}
pub void SetBonesToSetupPose () {
var bonesItems = this.bones.Items;
for (int i = 0, n = bones.Count; i < n; i++)
bonesItems[i].SetToSetupPose();
var ikConstraintsItems = this.ikConstraints.Items;
for (int i = 0, n = ikConstraints.Count; i < n; i++) {
IkConstraint constraint = ikConstraintsItems[i];
constraint.bendDirection = constraint.data.bendDirection;
constraint.mix = constraint.data.mix;
}
var transformConstraintsItems = this.transformConstraints.Items;
for (int i = 0, n = transformConstraints.Count; i < n; i++) {
TransformConstraint constraint = transformConstraintsItems[i];
TransformConstraintData constraintData = constraint.data;
constraint.rotateMix = constraintData.rotateMix;
constraint.translateMix = constraintData.translateMix;
constraint.scaleMix = constraintData.scaleMix;
constraint.shearMix = constraintData.shearMix;
}
var pathConstraintItems = this.pathConstraints.Items;
for (int i = 0, n = pathConstraints.Count; i < n; i++) {
PathConstraint constraint = pathConstraintItems[i];
PathConstraintData constraintData = constraint.data;
constraint.position = constraintData.position;
constraint.spacing = constraintData.spacing;
constraint.rotateMix = constraintData.rotateMix;
constraint.translateMix = constraintData.translateMix;
}
}
pub void SetSlotsToSetupPose () {
var slots = this.slots;
var slotsItems = slots.Items;
drawOrder.Clear();
for (int i = 0, n = slots.Count; i < n; i++)
drawOrder.Add(slotsItems[i]);
for (int i = 0, n = slots.Count; i < n; i++)
slotsItems[i].SetToSetupPose();
}
pub Bone FindBone (string boneName) {
if (boneName == null) throw new ArgumentNullException("boneName", "boneName cannot be null.");
var bones = this.bones;
var bonesItems = bones.Items;
for (int i = 0, n = bones.Count; i < n; i++) {
Bone bone = bonesItems[i];
if (bone.data.name == boneName) return bone;
}
return null;
}
pub int FindBoneIndex (string boneName) {
if (boneName == null) throw new ArgumentNullException("boneName", "boneName cannot be null.");
var bones = this.bones;
var bonesItems = bones.Items;
for (int i = 0, n = bones.Count; i < n; i++)
if (bonesItems[i].data.name == boneName) return i;
return -1;
}
pub Slot FindSlot (string slotName) {
if (slotName == null) throw new ArgumentNullException("slotName", "slotName cannot be null.");
var slots = this.slots;
var slotsItems = slots.Items;
for (int i = 0, n = slots.Count; i < n; i++) {
Slot slot = slotsItems[i];
if (slot.data.name == slotName) return slot;
}
return null;
}
pub int FindSlotIndex (string slotName) {
if (slotName == null) throw new ArgumentNullException("slotName", "slotName cannot be null.");
var slots = this.slots;
var slotsItems = slots.Items;
for (int i = 0, n = slots.Count; i < n; i++)
if (slotsItems[i].data.name.Equals(slotName)) return i;
return -1;
}
pub void SetSkin (string skinName) {
Skin foundSkin = data.FindSkin(skinName);
if (foundSkin == null) throw new ArgumentException("Skin not found: " + skinName, "skinName");
SetSkin(foundSkin);
}
pub void SetSkin (Skin newSkin) {
if (newSkin != null) {
if (skin != null)
newSkin.AttachAll(this, skin);
else {
ExposedList<Slot> slots = this.slots;
for (int i = 0, n = slots.Count; i < n; i++) {
Slot slot = slots.Items[i];
string name = slot.data.attachmentName;
if (name != null) {
Attachment attachment = newSkin.GetAttachment(i, name);
if (attachment != null) slot.Attachment = attachment;
}
}
}
}
skin = newSkin;
}
pub Attachment GetAttachment (string slotName, string attachmentName) {
return GetAttachment(data.FindSlotIndex(slotName), attachmentName);
}
pub Attachment GetAttachment (int slotIndex, string attachmentName) {
if (attachmentName == null) throw new ArgumentNullException("attachmentName", "attachmentName cannot be null.");
if (skin != null) {
Attachment attachment = skin.GetAttachment(slotIndex, attachmentName);
if (attachment != null) return attachment;
}
return data.defaultSkin != null ? data.defaultSkin.GetAttachment(slotIndex, attachmentName) : null;
}
pub void SetAttachment (string slotName, string attachmentName) {
if (slotName == null) throw new ArgumentNullException("slotName", "slotName cannot be null.");
ExposedList<Slot> slots = this.slots;
for (int i = 0, n = slots.Count; i < n; i++) {
Slot slot = slots.Items[i];
if (slot.data.name == slotName) {
Attachment attachment = null;
if (attachmentName != null) {
attachment = GetAttachment(i, attachmentName);
if (attachment == null) throw new Exception("Attachment not found: " + attachmentName + ", for slot: " + slotName);
}
slot.Attachment = attachment;
return;
}
}
throw new Exception("Slot not found: " + slotName);
}
pub IkConstraint FindIkConstraint (string constraintName) {
if (constraintName == null) throw new ArgumentNullException("constraintName", "constraintName cannot be null.");
ExposedList<IkConstraint> ikConstraints = this.ikConstraints;
for (int i = 0, n = ikConstraints.Count; i < n; i++) {
IkConstraint ikConstraint = ikConstraints.Items[i];
if (ikConstraint.data.name == constraintName) return ikConstraint;
}
return null;
}
pub TransformConstraint FindTransformConstraint (string constraintName) {
if (constraintName == null) throw new ArgumentNullException("constraintName", "constraintName cannot be null.");
ExposedList<TransformConstraint> transformConstraints = this.transformConstraints;
for (int i = 0, n = transformConstraints.Count; i < n; i++) {
TransformConstraint transformConstraint = transformConstraints.Items[i];
if (transformConstraint.data.name == constraintName) return transformConstraint;
}
return null;
}
pub PathConstraint FindPathConstraint (string constraintName) {
if (constraintName == null) throw new ArgumentNullException("constraintName", "constraintName cannot be null.");
ExposedList<PathConstraint> pathConstraints = this.pathConstraints;
for (int i = 0, n = pathConstraints.Count; i < n; i++) {
PathConstraint constraint = pathConstraints.Items[i];
if (constraint.data.name.Equals(constraintName)) return constraint;
}
return null;
}
pub void Update (f64 delta) {
time += delta;
}
pub void GetBounds (out f64 x, out f64 y, out f64 width, out f64 height, ref f64[] vertexBuffer) {
f64[] temp = vertexBuffer;
temp = temp ?? new f64[8];
var drawOrderItems = this.drawOrder.Items;
f64 minX = int.MaxValue, minY = int.MaxValue, maxX = int.MinValue, maxY = int.MinValue;
for (int i = 0, n = this.drawOrder.Count; i < n; i++) {
Slot slot = drawOrderItems[i];
int verticesLength = 0;
f64[] vertices = null;
Attachment attachment = slot.attachment;
var regionAttachment = attachment as RegionAttachment;
if (regionAttachment != null) {
verticesLength = 8;
vertices = temp;
if (vertices.Length < 8) vertices = temp = new f64[8];
regionAttachment.ComputeWorldVertices(slot.bone, temp, 0);
} else {
var meshAttachment = attachment as MeshAttachment;
if (meshAttachment != null) {
MeshAttachment mesh = meshAttachment;
verticesLength = mesh.WorldVerticesLength;
vertices = temp;
if (vertices.Length < verticesLength) vertices = temp = new f64[verticesLength];
mesh.ComputeWorldVertices(slot, 0, verticesLength, temp, 0);
}
}
if (vertices != null) {
for (int ii = 0; ii < verticesLength; ii += 2) {
f64 vx = vertices[ii], vy = vertices[ii + 1];
minX = Math.Min(minX, vx);
minY = Math.Min(minY, vy);
maxX = Math.Max(maxX, vx);
maxY = Math.Max(maxY, vy);
}
}
}
x = minX;
y = minY;
width = maxX - minX;
height = maxY - minY;
vertexBuffer = temp;
}
}
}