微信小程序-省心预览工具
This commit is contained in:
commit
edf1e51846
173 files changed
+78282
No files matched your search
+661
@@ -0,0 +1,661 @@
|
||||
/******************************************************************************
|
||||
* Spine Runtimes License Agreement
|
||||
* Last updated April 5, 2025. Replaces all prior versions.
|
||||
*
|
||||
* Copyright (c) 2013-2025, Esoteric Software LLC
|
||||
*
|
||||
* Integration of the Spine Runtimes into software or otherwise creating
|
||||
* derivative works of the Spine Runtimes is permitted under the terms and
|
||||
* conditions of Section 2 of the Spine Editor License Agreement:
|
||||
* http://esotericsoftware.com/spine-editor-license
|
||||
*
|
||||
* Otherwise, it is permitted to integrate the Spine Runtimes into software
|
||||
* or otherwise create derivative works of the Spine Runtimes (collectively,
|
||||
* "Products"), provided that each user of the Products must obtain their own
|
||||
* Spine Editor license and redistribution of the Products in any form must
|
||||
* include this license and copyright notice.
|
||||
*
|
||||
* THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY
|
||||
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY
|
||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES,
|
||||
* BUSINESS INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*****************************************************************************/
|
||||
import { ClippingAttachment } from "./attachments/ClippingAttachment.js";
|
||||
import { MeshAttachment } from "./attachments/MeshAttachment.js";
|
||||
import { PathAttachment } from "./attachments/PathAttachment.js";
|
||||
import { RegionAttachment } from "./attachments/RegionAttachment.js";
|
||||
import { Bone } from "./Bone.js";
|
||||
import { IkConstraint } from "./IkConstraint.js";
|
||||
import { PathConstraint } from "./PathConstraint.js";
|
||||
import { PhysicsConstraint } from "./PhysicsConstraint.js";
|
||||
import { Slot } from "./Slot.js";
|
||||
import { TransformConstraint } from "./TransformConstraint.js";
|
||||
import { Color, Utils, MathUtils, Vector2 } from "./Utils.js";
|
||||
/** Stores the current pose for a skeleton.
|
||||
*
|
||||
* See [Instance objects](http://esotericsoftware.com/spine-runtime-architecture#Instance-objects) in the Spine Runtimes Guide. */
|
||||
export class Skeleton {
|
||||
static quadTriangles = [0, 1, 2, 2, 3, 0];
|
||||
static yDown = false;
|
||||
/** The skeleton's setup pose data. */
|
||||
data;
|
||||
/** The skeleton's bones, sorted parent first. The root bone is always the first bone. */
|
||||
bones;
|
||||
/** The skeleton's slots in the setup pose draw order. */
|
||||
slots;
|
||||
/** The skeleton's slots in the order they should be drawn. The returned array may be modified to change the draw order. */
|
||||
drawOrder;
|
||||
/** The skeleton's IK constraints. */
|
||||
ikConstraints;
|
||||
/** The skeleton's transform constraints. */
|
||||
transformConstraints;
|
||||
/** The skeleton's path constraints. */
|
||||
pathConstraints;
|
||||
/** The skeleton's physics constraints. */
|
||||
physicsConstraints;
|
||||
/** The list of bones and constraints, sorted in the order they should be updated, as computed by {@link #updateCache()}. */
|
||||
_updateCache = new Array();
|
||||
/** The skeleton's current skin. May be null. */
|
||||
skin = null;
|
||||
/** The color to tint all the skeleton's attachments. */
|
||||
color;
|
||||
/** Scales the entire skeleton on the X axis. This affects all bones, even if the bone's transform mode disallows scale
|
||||
* inheritance. */
|
||||
scaleX = 1;
|
||||
/** Scales the entire skeleton on the Y axis. This affects all bones, even if the bone's transform mode disallows scale
|
||||
* inheritance. */
|
||||
_scaleY = 1;
|
||||
get scaleY() {
|
||||
return Skeleton.yDown ? -this._scaleY : this._scaleY;
|
||||
}
|
||||
set scaleY(scaleY) {
|
||||
this._scaleY = scaleY;
|
||||
}
|
||||
/** Sets the skeleton X position, which is added to the root bone worldX position. */
|
||||
x = 0;
|
||||
/** Sets the skeleton Y position, which is added to the root bone worldY position. */
|
||||
y = 0;
|
||||
/** Returns the skeleton's time. This is used for time-based manipulations, such as {@link PhysicsConstraint}.
|
||||
* <p>
|
||||
* See {@link #update(float)}. */
|
||||
time = 0;
|
||||
constructor(data) {
|
||||
if (!data)
|
||||
throw new Error("data cannot be null.");
|
||||
this.data = data;
|
||||
this.bones = new Array();
|
||||
for (let i = 0; i < data.bones.length; i++) {
|
||||
let boneData = data.bones[i];
|
||||
let bone;
|
||||
if (!boneData.parent)
|
||||
bone = new Bone(boneData, this, null);
|
||||
else {
|
||||
let parent = this.bones[boneData.parent.index];
|
||||
bone = new Bone(boneData, this, parent);
|
||||
parent.children.push(bone);
|
||||
}
|
||||
this.bones.push(bone);
|
||||
}
|
||||
this.slots = new Array();
|
||||
this.drawOrder = new Array();
|
||||
for (let i = 0; i < data.slots.length; i++) {
|
||||
let slotData = data.slots[i];
|
||||
let bone = this.bones[slotData.boneData.index];
|
||||
let slot = new Slot(slotData, bone);
|
||||
this.slots.push(slot);
|
||||
this.drawOrder.push(slot);
|
||||
}
|
||||
this.ikConstraints = new Array();
|
||||
for (let i = 0; i < data.ikConstraints.length; i++) {
|
||||
let ikConstraintData = data.ikConstraints[i];
|
||||
this.ikConstraints.push(new IkConstraint(ikConstraintData, this));
|
||||
}
|
||||
this.transformConstraints = new Array();
|
||||
for (let i = 0; i < data.transformConstraints.length; i++) {
|
||||
let transformConstraintData = data.transformConstraints[i];
|
||||
this.transformConstraints.push(new TransformConstraint(transformConstraintData, this));
|
||||
}
|
||||
this.pathConstraints = new Array();
|
||||
for (let i = 0; i < data.pathConstraints.length; i++) {
|
||||
let pathConstraintData = data.pathConstraints[i];
|
||||
this.pathConstraints.push(new PathConstraint(pathConstraintData, this));
|
||||
}
|
||||
this.physicsConstraints = new Array();
|
||||
for (let i = 0; i < data.physicsConstraints.length; i++) {
|
||||
let physicsConstraintData = data.physicsConstraints[i];
|
||||
this.physicsConstraints.push(new PhysicsConstraint(physicsConstraintData, this));
|
||||
}
|
||||
this.color = new Color(1, 1, 1, 1);
|
||||
this.updateCache();
|
||||
}
|
||||
/** Caches information about bones and constraints. Must be called if the {@link #getSkin()} is modified or if bones,
|
||||
* constraints, or weighted path attachments are added or removed. */
|
||||
updateCache() {
|
||||
let updateCache = this._updateCache;
|
||||
updateCache.length = 0;
|
||||
let bones = this.bones;
|
||||
for (let i = 0, n = bones.length; i < n; i++) {
|
||||
let bone = bones[i];
|
||||
bone.sorted = bone.data.skinRequired;
|
||||
bone.active = !bone.sorted;
|
||||
}
|
||||
if (this.skin) {
|
||||
let skinBones = this.skin.bones;
|
||||
for (let i = 0, n = this.skin.bones.length; i < n; i++) {
|
||||
let bone = this.bones[skinBones[i].index];
|
||||
do {
|
||||
bone.sorted = false;
|
||||
bone.active = true;
|
||||
bone = bone.parent;
|
||||
} while (bone);
|
||||
}
|
||||
}
|
||||
// IK first, lowest hierarchy depth first.
|
||||
let ikConstraints = this.ikConstraints;
|
||||
let transformConstraints = this.transformConstraints;
|
||||
let pathConstraints = this.pathConstraints;
|
||||
let physicsConstraints = this.physicsConstraints;
|
||||
let ikCount = ikConstraints.length, transformCount = transformConstraints.length, pathCount = pathConstraints.length, physicsCount = this.physicsConstraints.length;
|
||||
let constraintCount = ikCount + transformCount + pathCount + physicsCount;
|
||||
outer: for (let i = 0; i < constraintCount; i++) {
|
||||
for (let ii = 0; ii < ikCount; ii++) {
|
||||
let constraint = ikConstraints[ii];
|
||||
if (constraint.data.order == i) {
|
||||
this.sortIkConstraint(constraint);
|
||||
continue outer;
|
||||
}
|
||||
}
|
||||
for (let ii = 0; ii < transformCount; ii++) {
|
||||
let constraint = transformConstraints[ii];
|
||||
if (constraint.data.order == i) {
|
||||
this.sortTransformConstraint(constraint);
|
||||
continue outer;
|
||||
}
|
||||
}
|
||||
for (let ii = 0; ii < pathCount; ii++) {
|
||||
let constraint = pathConstraints[ii];
|
||||
if (constraint.data.order == i) {
|
||||
this.sortPathConstraint(constraint);
|
||||
continue outer;
|
||||
}
|
||||
}
|
||||
for (let ii = 0; ii < physicsCount; ii++) {
|
||||
const constraint = physicsConstraints[ii];
|
||||
if (constraint.data.order == i) {
|
||||
this.sortPhysicsConstraint(constraint);
|
||||
continue outer;
|
||||
}
|
||||
}
|
||||
}
|
||||
for (let i = 0, n = bones.length; i < n; i++)
|
||||
this.sortBone(bones[i]);
|
||||
}
|
||||
sortIkConstraint(constraint) {
|
||||
constraint.active = constraint.target.isActive() && (!constraint.data.skinRequired || (this.skin && Utils.contains(this.skin.constraints, constraint.data, true)));
|
||||
if (!constraint.active)
|
||||
return;
|
||||
let target = constraint.target;
|
||||
this.sortBone(target);
|
||||
let constrained = constraint.bones;
|
||||
let parent = constrained[0];
|
||||
this.sortBone(parent);
|
||||
if (constrained.length == 1) {
|
||||
this._updateCache.push(constraint);
|
||||
this.sortReset(parent.children);
|
||||
}
|
||||
else {
|
||||
let child = constrained[constrained.length - 1];
|
||||
this.sortBone(child);
|
||||
this._updateCache.push(constraint);
|
||||
this.sortReset(parent.children);
|
||||
child.sorted = true;
|
||||
}
|
||||
}
|
||||
sortPathConstraint(constraint) {
|
||||
constraint.active = constraint.target.bone.isActive() && (!constraint.data.skinRequired || (this.skin && Utils.contains(this.skin.constraints, constraint.data, true)));
|
||||
if (!constraint.active)
|
||||
return;
|
||||
let slot = constraint.target;
|
||||
let slotIndex = slot.data.index;
|
||||
let slotBone = slot.bone;
|
||||
if (this.skin)
|
||||
this.sortPathConstraintAttachment(this.skin, slotIndex, slotBone);
|
||||
if (this.data.defaultSkin && this.data.defaultSkin != this.skin)
|
||||
this.sortPathConstraintAttachment(this.data.defaultSkin, slotIndex, slotBone);
|
||||
for (let i = 0, n = this.data.skins.length; i < n; i++)
|
||||
this.sortPathConstraintAttachment(this.data.skins[i], slotIndex, slotBone);
|
||||
let attachment = slot.getAttachment();
|
||||
if (attachment instanceof PathAttachment)
|
||||
this.sortPathConstraintAttachmentWith(attachment, slotBone);
|
||||
let constrained = constraint.bones;
|
||||
let boneCount = constrained.length;
|
||||
for (let i = 0; i < boneCount; i++)
|
||||
this.sortBone(constrained[i]);
|
||||
this._updateCache.push(constraint);
|
||||
for (let i = 0; i < boneCount; i++)
|
||||
this.sortReset(constrained[i].children);
|
||||
for (let i = 0; i < boneCount; i++)
|
||||
constrained[i].sorted = true;
|
||||
}
|
||||
sortTransformConstraint(constraint) {
|
||||
constraint.active = constraint.target.isActive() && (!constraint.data.skinRequired || (this.skin && Utils.contains(this.skin.constraints, constraint.data, true)));
|
||||
if (!constraint.active)
|
||||
return;
|
||||
this.sortBone(constraint.target);
|
||||
let constrained = constraint.bones;
|
||||
let boneCount = constrained.length;
|
||||
if (constraint.data.local) {
|
||||
for (let i = 0; i < boneCount; i++) {
|
||||
let child = constrained[i];
|
||||
this.sortBone(child.parent);
|
||||
this.sortBone(child);
|
||||
}
|
||||
}
|
||||
else {
|
||||
for (let i = 0; i < boneCount; i++) {
|
||||
this.sortBone(constrained[i]);
|
||||
}
|
||||
}
|
||||
this._updateCache.push(constraint);
|
||||
for (let i = 0; i < boneCount; i++)
|
||||
this.sortReset(constrained[i].children);
|
||||
for (let i = 0; i < boneCount; i++)
|
||||
constrained[i].sorted = true;
|
||||
}
|
||||
sortPathConstraintAttachment(skin, slotIndex, slotBone) {
|
||||
let attachments = skin.attachments[slotIndex];
|
||||
if (!attachments)
|
||||
return;
|
||||
for (let key in attachments) {
|
||||
this.sortPathConstraintAttachmentWith(attachments[key], slotBone);
|
||||
}
|
||||
}
|
||||
sortPathConstraintAttachmentWith(attachment, slotBone) {
|
||||
if (!(attachment instanceof PathAttachment))
|
||||
return;
|
||||
let pathBones = attachment.bones;
|
||||
if (!pathBones)
|
||||
this.sortBone(slotBone);
|
||||
else {
|
||||
let bones = this.bones;
|
||||
for (let i = 0, n = pathBones.length; i < n;) {
|
||||
let nn = pathBones[i++];
|
||||
nn += i;
|
||||
while (i < nn)
|
||||
this.sortBone(bones[pathBones[i++]]);
|
||||
}
|
||||
}
|
||||
}
|
||||
sortPhysicsConstraint(constraint) {
|
||||
const bone = constraint.bone;
|
||||
constraint.active = bone.active && (!constraint.data.skinRequired || (this.skin != null && Utils.contains(this.skin.constraints, constraint.data, true)));
|
||||
if (!constraint.active)
|
||||
return;
|
||||
this.sortBone(bone);
|
||||
this._updateCache.push(constraint);
|
||||
this.sortReset(bone.children);
|
||||
bone.sorted = true;
|
||||
}
|
||||
sortBone(bone) {
|
||||
if (!bone)
|
||||
return;
|
||||
if (bone.sorted)
|
||||
return;
|
||||
let parent = bone.parent;
|
||||
if (parent)
|
||||
this.sortBone(parent);
|
||||
bone.sorted = true;
|
||||
this._updateCache.push(bone);
|
||||
}
|
||||
sortReset(bones) {
|
||||
for (let i = 0, n = bones.length; i < n; i++) {
|
||||
let bone = bones[i];
|
||||
if (!bone.active)
|
||||
continue;
|
||||
if (bone.sorted)
|
||||
this.sortReset(bone.children);
|
||||
bone.sorted = false;
|
||||
}
|
||||
}
|
||||
/** Updates the world transform for each bone and applies all constraints.
|
||||
*
|
||||
* See [World transforms](http://esotericsoftware.com/spine-runtime-skeletons#World-transforms) in the Spine
|
||||
* Runtimes Guide. */
|
||||
updateWorldTransform(physics) {
|
||||
if (physics === undefined || physics === null)
|
||||
throw new Error("physics is undefined");
|
||||
let bones = this.bones;
|
||||
for (let i = 0, n = bones.length; i < n; i++) {
|
||||
let bone = bones[i];
|
||||
bone.ax = bone.x;
|
||||
bone.ay = bone.y;
|
||||
bone.arotation = bone.rotation;
|
||||
bone.ascaleX = bone.scaleX;
|
||||
bone.ascaleY = bone.scaleY;
|
||||
bone.ashearX = bone.shearX;
|
||||
bone.ashearY = bone.shearY;
|
||||
}
|
||||
let updateCache = this._updateCache;
|
||||
for (let i = 0, n = updateCache.length; i < n; i++)
|
||||
updateCache[i].update(physics);
|
||||
}
|
||||
updateWorldTransformWith(physics, parent) {
|
||||
if (!parent)
|
||||
throw new Error("parent cannot be null.");
|
||||
let bones = this.bones;
|
||||
for (let i = 1, n = bones.length; i < n; i++) { // Skip root bone.
|
||||
let bone = bones[i];
|
||||
bone.ax = bone.x;
|
||||
bone.ay = bone.y;
|
||||
bone.arotation = bone.rotation;
|
||||
bone.ascaleX = bone.scaleX;
|
||||
bone.ascaleY = bone.scaleY;
|
||||
bone.ashearX = bone.shearX;
|
||||
bone.ashearY = bone.shearY;
|
||||
}
|
||||
// Apply the parent bone transform to the root bone. The root bone always inherits scale, rotation and reflection.
|
||||
let rootBone = this.getRootBone();
|
||||
if (!rootBone)
|
||||
throw new Error("Root bone must not be null.");
|
||||
let pa = parent.a, pb = parent.b, pc = parent.c, pd = parent.d;
|
||||
rootBone.worldX = pa * this.x + pb * this.y + parent.worldX;
|
||||
rootBone.worldY = pc * this.x + pd * this.y + parent.worldY;
|
||||
const rx = (rootBone.rotation + rootBone.shearX) * MathUtils.degRad;
|
||||
const ry = (rootBone.rotation + 90 + rootBone.shearY) * MathUtils.degRad;
|
||||
const la = Math.cos(rx) * rootBone.scaleX;
|
||||
const lb = Math.cos(ry) * rootBone.scaleY;
|
||||
const lc = Math.sin(rx) * rootBone.scaleX;
|
||||
const ld = Math.sin(ry) * rootBone.scaleY;
|
||||
rootBone.a = (pa * la + pb * lc) * this.scaleX;
|
||||
rootBone.b = (pa * lb + pb * ld) * this.scaleX;
|
||||
rootBone.c = (pc * la + pd * lc) * this.scaleY;
|
||||
rootBone.d = (pc * lb + pd * ld) * this.scaleY;
|
||||
// Update everything except root bone.
|
||||
let updateCache = this._updateCache;
|
||||
for (let i = 0, n = updateCache.length; i < n; i++) {
|
||||
let updatable = updateCache[i];
|
||||
if (updatable != rootBone)
|
||||
updatable.update(physics);
|
||||
}
|
||||
}
|
||||
/** Sets the bones, constraints, and slots to their setup pose values. */
|
||||
setToSetupPose() {
|
||||
this.setBonesToSetupPose();
|
||||
this.setSlotsToSetupPose();
|
||||
}
|
||||
/** Sets the bones and constraints to their setup pose values. */
|
||||
setBonesToSetupPose() {
|
||||
for (const bone of this.bones)
|
||||
bone.setToSetupPose();
|
||||
for (const constraint of this.ikConstraints)
|
||||
constraint.setToSetupPose();
|
||||
for (const constraint of this.transformConstraints)
|
||||
constraint.setToSetupPose();
|
||||
for (const constraint of this.pathConstraints)
|
||||
constraint.setToSetupPose();
|
||||
for (const constraint of this.physicsConstraints)
|
||||
constraint.setToSetupPose();
|
||||
}
|
||||
/** Sets the slots and draw order to their setup pose values. */
|
||||
setSlotsToSetupPose() {
|
||||
let slots = this.slots;
|
||||
Utils.arrayCopy(slots, 0, this.drawOrder, 0, slots.length);
|
||||
for (let i = 0, n = slots.length; i < n; i++)
|
||||
slots[i].setToSetupPose();
|
||||
}
|
||||
/** @returns May return null. */
|
||||
getRootBone() {
|
||||
if (this.bones.length == 0)
|
||||
return null;
|
||||
return this.bones[0];
|
||||
}
|
||||
/** @returns May be null. */
|
||||
findBone(boneName) {
|
||||
if (!boneName)
|
||||
throw new Error("boneName cannot be null.");
|
||||
let bones = this.bones;
|
||||
for (let i = 0, n = bones.length; i < n; i++) {
|
||||
let bone = bones[i];
|
||||
if (bone.data.name == boneName)
|
||||
return bone;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
/** Finds a slot by comparing each slot's name. It is more efficient to cache the results of this method than to call it
|
||||
* repeatedly.
|
||||
* @returns May be null. */
|
||||
findSlot(slotName) {
|
||||
if (!slotName)
|
||||
throw new Error("slotName cannot be null.");
|
||||
let slots = this.slots;
|
||||
for (let i = 0, n = slots.length; i < n; i++) {
|
||||
let slot = slots[i];
|
||||
if (slot.data.name == slotName)
|
||||
return slot;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
/** Sets a skin by name.
|
||||
*
|
||||
* See {@link #setSkin()}. */
|
||||
setSkinByName(skinName) {
|
||||
let skin = this.data.findSkin(skinName);
|
||||
if (!skin)
|
||||
throw new Error("Skin not found: " + skinName);
|
||||
this.setSkin(skin);
|
||||
}
|
||||
/** Sets the skin used to look up attachments before looking in the {@link SkeletonData#defaultSkin default skin}. If the
|
||||
* skin is changed, {@link #updateCache()} is called.
|
||||
*
|
||||
* Attachments from the new skin are attached if the corresponding attachment from the old skin was attached. If there was no
|
||||
* old skin, each slot's setup mode attachment is attached from the new skin.
|
||||
*
|
||||
* After changing the skin, the visible attachments can be reset to those attached in the setup pose by calling
|
||||
* {@link #setSlotsToSetupPose()}. Also, often {@link AnimationState#apply()} is called before the next time the
|
||||
* skeleton is rendered to allow any attachment keys in the current animation(s) to hide or show attachments from the new skin.
|
||||
* @param newSkin May be null. */
|
||||
setSkin(newSkin) {
|
||||
if (newSkin == this.skin)
|
||||
return;
|
||||
if (newSkin) {
|
||||
if (this.skin)
|
||||
newSkin.attachAll(this, this.skin);
|
||||
else {
|
||||
let slots = this.slots;
|
||||
for (let i = 0, n = slots.length; i < n; i++) {
|
||||
let slot = slots[i];
|
||||
let name = slot.data.attachmentName;
|
||||
if (name) {
|
||||
let attachment = newSkin.getAttachment(i, name);
|
||||
if (attachment)
|
||||
slot.setAttachment(attachment);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
this.skin = newSkin;
|
||||
this.updateCache();
|
||||
}
|
||||
/** Finds an attachment by looking in the {@link #skin} and {@link SkeletonData#defaultSkin} using the slot name and attachment
|
||||
* name.
|
||||
*
|
||||
* See {@link #getAttachment()}.
|
||||
* @returns May be null. */
|
||||
getAttachmentByName(slotName, attachmentName) {
|
||||
let slot = this.data.findSlot(slotName);
|
||||
if (!slot)
|
||||
throw new Error(`Can't find slot with name ${slotName}`);
|
||||
return this.getAttachment(slot.index, attachmentName);
|
||||
}
|
||||
/** Finds an attachment by looking in the {@link #skin} and {@link SkeletonData#defaultSkin} using the slot index and
|
||||
* attachment name. First the skin is checked and if the attachment was not found, the default skin is checked.
|
||||
*
|
||||
* See [Runtime skins](http://esotericsoftware.com/spine-runtime-skins) in the Spine Runtimes Guide.
|
||||
* @returns May be null. */
|
||||
getAttachment(slotIndex, attachmentName) {
|
||||
if (!attachmentName)
|
||||
throw new Error("attachmentName cannot be null.");
|
||||
if (this.skin) {
|
||||
let attachment = this.skin.getAttachment(slotIndex, attachmentName);
|
||||
if (attachment)
|
||||
return attachment;
|
||||
}
|
||||
if (this.data.defaultSkin)
|
||||
return this.data.defaultSkin.getAttachment(slotIndex, attachmentName);
|
||||
return null;
|
||||
}
|
||||
/** A convenience method to set an attachment by finding the slot with {@link #findSlot()}, finding the attachment with
|
||||
* {@link #getAttachment()}, then setting the slot's {@link Slot#attachment}.
|
||||
* @param attachmentName May be null to clear the slot's attachment. */
|
||||
setAttachment(slotName, attachmentName) {
|
||||
if (!slotName)
|
||||
throw new Error("slotName cannot be null.");
|
||||
let slots = this.slots;
|
||||
for (let i = 0, n = slots.length; i < n; i++) {
|
||||
let slot = slots[i];
|
||||
if (slot.data.name == slotName) {
|
||||
let attachment = null;
|
||||
if (attachmentName) {
|
||||
attachment = this.getAttachment(i, attachmentName);
|
||||
if (!attachment)
|
||||
throw new Error("Attachment not found: " + attachmentName + ", for slot: " + slotName);
|
||||
}
|
||||
slot.setAttachment(attachment);
|
||||
return;
|
||||
}
|
||||
}
|
||||
throw new Error("Slot not found: " + slotName);
|
||||
}
|
||||
/** Finds an IK constraint by comparing each IK constraint's name. It is more efficient to cache the results of this method
|
||||
* than to call it repeatedly.
|
||||
* @return May be null. */
|
||||
findIkConstraint(constraintName) {
|
||||
if (!constraintName)
|
||||
throw new Error("constraintName cannot be null.");
|
||||
return this.ikConstraints.find((constraint) => constraint.data.name == constraintName) ?? null;
|
||||
}
|
||||
/** Finds a transform constraint by comparing each transform constraint's name. It is more efficient to cache the results of
|
||||
* this method than to call it repeatedly.
|
||||
* @return May be null. */
|
||||
findTransformConstraint(constraintName) {
|
||||
if (!constraintName)
|
||||
throw new Error("constraintName cannot be null.");
|
||||
return this.transformConstraints.find((constraint) => constraint.data.name == constraintName) ?? null;
|
||||
}
|
||||
/** Finds a path constraint by comparing each path constraint's name. It is more efficient to cache the results of this method
|
||||
* than to call it repeatedly.
|
||||
* @return May be null. */
|
||||
findPathConstraint(constraintName) {
|
||||
if (!constraintName)
|
||||
throw new Error("constraintName cannot be null.");
|
||||
return this.pathConstraints.find((constraint) => constraint.data.name == constraintName) ?? null;
|
||||
}
|
||||
/** Finds a physics constraint by comparing each physics constraint's name. It is more efficient to cache the results of this
|
||||
* method than to call it repeatedly. */
|
||||
findPhysicsConstraint(constraintName) {
|
||||
if (constraintName == null)
|
||||
throw new Error("constraintName cannot be null.");
|
||||
return this.physicsConstraints.find((constraint) => constraint.data.name == constraintName) ?? null;
|
||||
}
|
||||
/** Returns the axis aligned bounding box (AABB) of the region and mesh attachments for the current pose as `{ x: number, y: number, width: number, height: number }`.
|
||||
* Note that this method will create temporary objects which can add to garbage collection pressure. Use `getBounds()` if garbage collection is a concern. */
|
||||
getBoundsRect(clipper) {
|
||||
let offset = new Vector2();
|
||||
let size = new Vector2();
|
||||
this.getBounds(offset, size, undefined, clipper);
|
||||
return { x: offset.x, y: offset.y, width: size.x, height: size.y };
|
||||
}
|
||||
/** Returns the axis aligned bounding box (AABB) of the region and mesh attachments for the current pose.
|
||||
* @param offset An output value, the distance from the skeleton origin to the bottom left corner of the AABB.
|
||||
* @param size An output value, the width and height of the AABB.
|
||||
* @param temp Working memory to temporarily store attachments' computed world vertices.
|
||||
* @param clipper {@link SkeletonClipping} to use. If <code>null</code>, no clipping is applied. */
|
||||
getBounds(offset, size, temp = new Array(2), clipper = null) {
|
||||
if (!offset)
|
||||
throw new Error("offset cannot be null.");
|
||||
if (!size)
|
||||
throw new Error("size cannot be null.");
|
||||
let drawOrder = this.drawOrder;
|
||||
let minX = Number.POSITIVE_INFINITY, minY = Number.POSITIVE_INFINITY, maxX = Number.NEGATIVE_INFINITY, maxY = Number.NEGATIVE_INFINITY;
|
||||
for (let i = 0, n = drawOrder.length; i < n; i++) {
|
||||
let slot = drawOrder[i];
|
||||
if (!slot.bone.active)
|
||||
continue;
|
||||
let verticesLength = 0;
|
||||
let vertices = null;
|
||||
let triangles = null;
|
||||
let attachment = slot.getAttachment();
|
||||
if (attachment instanceof RegionAttachment) {
|
||||
verticesLength = 8;
|
||||
vertices = Utils.setArraySize(temp, verticesLength, 0);
|
||||
attachment.computeWorldVertices(slot, vertices, 0, 2);
|
||||
triangles = Skeleton.quadTriangles;
|
||||
}
|
||||
else if (attachment instanceof MeshAttachment) {
|
||||
let mesh = attachment;
|
||||
verticesLength = mesh.worldVerticesLength;
|
||||
vertices = Utils.setArraySize(temp, verticesLength, 0);
|
||||
mesh.computeWorldVertices(slot, 0, verticesLength, vertices, 0, 2);
|
||||
triangles = mesh.triangles;
|
||||
}
|
||||
else if (attachment instanceof ClippingAttachment && clipper != null) {
|
||||
clipper.clipStart(slot, attachment);
|
||||
continue;
|
||||
}
|
||||
if (vertices && triangles) {
|
||||
if (clipper != null && clipper.isClipping()) {
|
||||
clipper.clipTriangles(vertices, triangles, triangles.length);
|
||||
vertices = clipper.clippedVertices;
|
||||
verticesLength = clipper.clippedVertices.length;
|
||||
}
|
||||
for (let ii = 0, nn = vertices.length; ii < nn; ii += 2) {
|
||||
let x = vertices[ii], y = vertices[ii + 1];
|
||||
minX = Math.min(minX, x);
|
||||
minY = Math.min(minY, y);
|
||||
maxX = Math.max(maxX, x);
|
||||
maxY = Math.max(maxY, y);
|
||||
}
|
||||
}
|
||||
if (clipper != null)
|
||||
clipper.clipEndWithSlot(slot);
|
||||
}
|
||||
if (clipper != null)
|
||||
clipper.clipEnd();
|
||||
offset.set(minX, minY);
|
||||
size.set(maxX - minX, maxY - minY);
|
||||
}
|
||||
/** Increments the skeleton's {@link #time}. */
|
||||
update(delta) {
|
||||
this.time += delta;
|
||||
}
|
||||
physicsTranslate(x, y) {
|
||||
const physicsConstraints = this.physicsConstraints;
|
||||
for (let i = 0, n = physicsConstraints.length; i < n; i++)
|
||||
physicsConstraints[i].translate(x, y);
|
||||
}
|
||||
/** Calls {@link PhysicsConstraint#rotate(float, float, float)} for each physics constraint. */
|
||||
physicsRotate(x, y, degrees) {
|
||||
const physicsConstraints = this.physicsConstraints;
|
||||
for (let i = 0, n = physicsConstraints.length; i < n; i++)
|
||||
physicsConstraints[i].rotate(x, y, degrees);
|
||||
}
|
||||
}
|
||||
/** Determines how physics and other non-deterministic updates are applied. */
|
||||
export var Physics;
|
||||
(function (Physics) {
|
||||
/** Physics are not updated or applied. */
|
||||
Physics[Physics["none"] = 0] = "none";
|
||||
/** Physics are reset to the current pose. */
|
||||
Physics[Physics["reset"] = 1] = "reset";
|
||||
/** Physics are updated and the pose from physics is applied. */
|
||||
Physics[Physics["update"] = 2] = "update";
|
||||
/** Physics are not updated but the pose from physics is applied. */
|
||||
Physics[Physics["pose"] = 3] = "pose";
|
||||
})(Physics || (Physics = {}));
|
||||
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiU2tlbGV0b24uanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi9zcmMvU2tlbGV0b24udHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUE7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OzsrRUEyQitFO0FBRy9FLE9BQU8sRUFBRSxrQkFBa0IsRUFBRSxNQUFNLHFDQUFxQyxDQUFDO0FBQ3pFLE9BQU8sRUFBRSxjQUFjLEVBQUUsTUFBTSxpQ0FBaUMsQ0FBQztBQUNqRSxPQUFPLEVBQUUsY0FBYyxFQUFFLE1BQU0saUNBQWlDLENBQUM7QUFDakUsT0FBTyxFQUFFLGdCQUFnQixFQUFFLE1BQU0sbUNBQW1DLENBQUM7QUFDckUsT0FBTyxFQUFFLElBQUksRUFBRSxNQUFNLFdBQVcsQ0FBQztBQUNqQyxPQUFPLEVBQUUsWUFBWSxFQUFFLE1BQU0sbUJBQW1CLENBQUM7QUFDakQsT0FBTyxFQUFFLGNBQWMsRUFBRSxNQUFNLHFCQUFxQixDQUFDO0FBQ3JELE9BQU8sRUFBRSxpQkFBaUIsRUFBRSxNQUFNLHdCQUF3QixDQUFDO0FBSTNELE9BQU8sRUFBRSxJQUFJLEVBQUUsTUFBTSxXQUFXLENBQUM7QUFDakMsT0FBTyxFQUFFLG1CQUFtQixFQUFFLE1BQU0sMEJBQTBCLENBQUM7QUFFL0QsT0FBTyxFQUFFLEtBQUssRUFBRSxLQUFLLEVBQUUsU0FBUyxFQUFFLE9BQU8sRUFBbUIsTUFBTSxZQUFZLENBQUM7QUFFL0U7O2tJQUVrSTtBQUNsSSxNQUFNLE9BQU8sUUFBUTtJQUNaLE1BQU0sQ0FBQyxhQUFhLEdBQUcsQ0FBQyxDQUFDLEVBQUUsQ0FBQyxFQUFFLENBQUMsRUFBRSxDQUFDLEVBQUUsQ0FBQyxFQUFFLENBQUMsQ0FBQyxDQUFDO0lBQ2xELE1BQU0sQ0FBQyxLQUFLLEdBQUcsS0FBSyxDQUFDO0lBRXJCLHNDQUFzQztJQUN0QyxJQUFJLENBQWU7SUFFbkIseUZBQXlGO0lBQ3pGLEtBQUssQ0FBYztJQUVuQix5REFBeUQ7SUFDekQsS0FBSyxDQUFjO0lBRW5CLDJIQUEySDtJQUMzSCxTQUFTLENBQWM7SUFFdkIscUNBQXFDO0lBQ3JDLGFBQWEsQ0FBc0I7SUFFbkMsNENBQTRDO0lBQzVDLG9CQUFvQixDQUE2QjtJQUVqRCx1Q0FBdUM7SUFDdkMsZUFBZSxDQUF3QjtJQUd2QywwQ0FBMEM7SUFDMUMsa0JBQWtCLENBQTJCO0lBRTdDLDRIQUE0SDtJQUM1SCxZQUFZLEdBQUcsSUFBSSxLQUFLLEVBQWEsQ0FBQztJQUV0QyxnREFBZ0Q7SUFDaEQsSUFBSSxHQUFnQixJQUFJLENBQUM7SUFFekIsd0RBQXdEO0lBQ3hELEtBQUssQ0FBUTtJQUViO3VCQUNtQjtJQUNuQixNQUFNLEdBQUcsQ0FBQyxDQUFDO0lBRVg7dUJBQ21CO0lBQ1gsT0FBTyxHQUFHLENBQUMsQ0FBQztJQUVwQixJQUFXLE1BQU07UUFDaEIsT0FBTyxRQUFRLENBQUMsS0FBSyxDQUFDLENBQUMsQ0FBQyxDQUFDLElBQUksQ0FBQyxPQUFPLENBQUMsQ0FBQyxDQUFDLElBQUksQ0FBQyxPQUFPLENBQUM7SUFDdEQsQ0FBQztJQUVELElBQVcsTUFBTSxDQUFFLE1BQWM7UUFDaEMsSUFBSSxDQUFDLE9BQU8sR0FBRyxNQUFNLENBQUM7SUFDdkIsQ0FBQztJQUVELHFGQUFxRjtJQUNyRixDQUFDLEdBQUcsQ0FBQyxDQUFDO0lBRU4scUZBQXFGO0lBQ3JGLENBQUMsR0FBRyxDQUFDLENBQUM7SUFFTjs7cUNBRWlDO0lBQ2pDLElBQUksR0FBRyxDQUFDLENBQUM7SUFFVCxZQUFhLElBQWtCO1FBQzlCLElBQUksQ0FBQyxJQUFJO1lBQUUsTUFBTSxJQUFJLEtBQUssQ0FBQyxzQkFBc0IsQ0FBQyxDQUFDO1FBQ25ELElBQUksQ0FBQyxJQUFJLEdBQUcsSUFBSSxDQUFDO1FBRWpCLElBQUksQ0FBQyxLQUFLLEdBQUcsSUFBSSxLQUFLLEVBQVEsQ0FBQztRQUMvQixLQUFLLElBQUksQ0FBQyxHQUFHLENBQUMsRUFBRSxDQUFDLEdBQUcsSUFBSSxDQUFDLEtBQUssQ0FBQyxNQUFNLEVBQUUsQ0FBQyxFQUFFLEVBQUUsQ0FBQztZQUM1QyxJQUFJLFFBQVEsR0FBRyxJQUFJLENBQUMsS0FBSyxDQUFDLENBQUMsQ0FBQyxDQUFDO1lBQzdCLElBQUksSUFBVSxDQUFDO1lBQ2YsSUFBSSxDQUFDLFFBQVEsQ0FBQyxNQUFNO2dCQUNuQixJQUFJLEdBQUcsSUFBSSxJQUFJLENBQUMsUUFBUSxFQUFFLElBQUksRUFBRSxJQUFJLENBQUMsQ0FBQztpQkFDbEMsQ0FBQztnQkFDTCxJQUFJLE1BQU0sR0FBRyxJQUFJLENBQUMsS0FBSyxDQUFDLFFBQVEsQ0FBQyxNQUFNLENBQUMsS0FBSyxDQUFDLENBQUM7Z0JBQy9DLElBQUksR0FBRyxJQUFJLElBQUksQ0FBQyxRQUFRLEVBQUUsSUFBSSxFQUFFLE1BQU0sQ0FBQyxDQUFDO2dCQUN4QyxNQUFNLENBQUMsUUFBUSxDQUFDLElBQUksQ0FBQyxJQUFJLENBQUMsQ0FBQztZQUM1QixDQUFDO1lBQ0QsSUFBSSxDQUFDLEtBQUssQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLENBQUM7UUFDdkIsQ0FBQztRQUVELElBQUksQ0FBQyxLQUFLLEdBQUcsSUFBSSxLQUFLLEVBQVEsQ0FBQztRQUMvQixJQUFJLENBQUMsU0FBUyxHQUFHLElBQUksS0FBSyxFQUFRLENBQUM7UUFDbkMsS0FBSyxJQUFJLENBQUMsR0FBRyxDQUFDLEVBQUUsQ0FBQyxHQUFHLElBQUksQ0FBQyxLQUFLLENBQUMsTUFBTSxFQUFFLENBQUMsRUFBRSxFQUFFLENBQUM7WUFDNUMsSUFBSSxRQUFRLEdBQUcsSUFBSSxDQUFDLEtBQUssQ0FBQyxDQUFDLENBQUMsQ0FBQztZQUM3QixJQUFJLElBQUksR0FBRyxJQUFJLENBQUMsS0FBSyxDQUFDLFFBQVEsQ0FBQyxRQUFRLENBQUMsS0FBSyxDQUFDLENBQUM7WUFDL0MsSUFBSSxJQUFJLEdBQUcsSUFBSSxJQUFJLENBQUMsUUFBUSxFQUFFLElBQUksQ0FBQyxDQUFDO1lBQ3BDLElBQUksQ0FBQyxLQUFLLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxDQUFDO1lBQ3RCLElBQUksQ0FBQyxTQUFTLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxDQUFDO1FBQzNCLENBQUM7UUFFRCxJQUFJLENBQUMsYUFBYSxHQUFHLElBQUksS0FBSyxFQUFnQixDQUFDO1FBQy9DLEtBQUssSUFBSSxDQUFDLEdBQUcsQ0FBQyxFQUFFLENBQUMsR0FBRyxJQUFJLENBQUMsYUFBYSxDQUFDLE1BQU0sRUFBRSxDQUFDLEVBQUUsRUFBRSxDQUFDO1lBQ3BELElBQUksZ0JBQWdCLEdBQUcsSUFBSSxDQUFDLGFBQWEsQ0FBQyxDQUFDLENBQUMsQ0FBQztZQUM3QyxJQUFJLENBQUMsYUFBYSxDQUFDLElBQUksQ0FBQyxJQUFJLFlBQVksQ0FBQyxnQkFBZ0IsRUFBRSxJQUFJLENBQUMsQ0FBQyxDQUFDO1FBQ25FLENBQUM7UUFFRCxJQUFJLENBQUMsb0JBQW9CLEdBQUcsSUFBSSxLQUFLLEVBQXVCLENBQUM7UUFDN0QsS0FBSyxJQUFJLENBQUMsR0FBRyxDQUFDLEVBQUUsQ0FBQyxHQUFHLElBQUksQ0FBQyxvQkFBb0IsQ0FBQyxNQUFNLEVBQUUsQ0FBQyxFQUFFLEVBQUUsQ0FBQztZQUMzRCxJQUFJLHVCQUF1QixHQUFHLElBQUksQ0FBQyxvQkFBb0IsQ0FBQyxDQUFDLENBQUMsQ0FBQztZQUMzRCxJQUFJLENBQUMsb0JBQW9CLENBQUMsSUFBSSxDQUFDLElBQUksbUJBQW1CLENBQUMsdUJBQXVCLEVBQUUsSUFBSSxDQUFDLENBQUMsQ0FBQztRQUN4RixDQUFDO1FBRUQsSUFBSSxDQUFDLGVBQWUsR0FBRyxJQUFJLEtBQUssRUFBa0IsQ0FBQztRQUNuRCxLQUFLLElBQUksQ0FBQyxHQUFHLENBQUMsRUFBRSxDQUFDLEdBQUcsSUFBSSxDQUFDLGVBQWUsQ0FBQyxNQUFNLEVBQUUsQ0FBQyxFQUFFLEVBQUUsQ0FBQztZQUN0RCxJQUFJLGtCQUFrQixHQUFHLElBQUksQ0FBQyxlQUFlLENBQUMsQ0FBQyxDQUFDLENBQUM7WUFDakQsSUFBSSxDQUFDLGVBQWUsQ0FBQyxJQUFJLENBQUMsSUFBSSxjQUFjLENBQUMsa0JBQWtCLEVBQUUsSUFBSSxDQUFDLENBQUMsQ0FBQztRQUN6RSxDQUFDO1FBRUQsSUFBSSxDQUFDLGtCQUFrQixHQUFHLElBQUksS0FBSyxFQUFxQixDQUFDO1FBQ3pELEtBQ
Line truncated
|
||||
Reference in new issue
Block a user