dragonbones-runtime
Version:
the tools to build dragonbones file for diffrent framework
610 lines (561 loc) • 22 kB
text/typescript
/**
* The MIT License (MIT)
*
* Copyright (c) 2012-2017 DragonBones team and other contributors
*
* 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 dragonBones {
/**
* - Bone is one of the most important logical units in the armature animation system,
* and is responsible for the realization of translate, rotation, scaling in the animations.
* A armature can contain multiple bones.
* @see dragonBones.BoneData
* @see dragonBones.Armature
* @see dragonBones.Slot
* @version DragonBones 3.0
* @language en_US
*/
/**
* - 骨骼在骨骼动画体系中是最重要的逻辑单元之一,负责动画中的平移、旋转、缩放的实现。
* 一个骨架中可以包含多个骨骼。
* @see dragonBones.BoneData
* @see dragonBones.Armature
* @see dragonBones.Slot
* @version DragonBones 3.0
* @language zh_CN
*/
export class Bone extends TransformObject {
public static toString(): string {
return "[class dragonBones.Bone]";
}
/**
* - The offset mode.
* @see #offset
* @version DragonBones 5.5
* @language en_US
*/
/**
* - 偏移模式。
* @see #offset
* @version DragonBones 5.5
* @language zh_CN
*/
public offsetMode: OffsetMode;
/**
* @internal
* @private
*/
public readonly animationPose: Transform = new Transform();
/**
* @internal
* @private
*/
public _transformDirty: boolean;
/**
* @internal
* @private
*/
public _childrenTransformDirty: boolean;
protected _localDirty: boolean;
/**
* @internal
* @private
*/
public _hasConstraint: boolean;
private _visible: boolean;
protected _cachedFrameIndex: number;
/**
* @internal
* @private
*/
public readonly _blendState: BlendState = new BlendState();
/**
* @internal
* @private
*/
public _boneData: BoneData;
/**
* @internal
* @private
*/
public _cachedFrameIndices: Array<number> | null;
/**
* @inheritDoc
*/
protected _onClear(): void {
super._onClear();
this.offsetMode = OffsetMode.Additive;
this.animationPose.identity();
this._transformDirty = false;
this._childrenTransformDirty = false;
this._localDirty = true;
this._hasConstraint = false;
this._visible = true;
this._cachedFrameIndex = -1;
this._blendState.clear();
this._boneData = null as any; //
this._cachedFrameIndices = null;
}
/**
* @private
*/
protected _updateGlobalTransformMatrix(isCache: boolean): void {
const boneData = this._boneData;
const parent = this._parent;
const flipX = this._armature.flipX;
const flipY = this._armature.flipY === DragonBones.yDown;
let inherit = parent !== null;
let rotation = 0.0;
const global = this.global;
const globalTransformMatrix = this.globalTransformMatrix;
if (this.offsetMode === OffsetMode.Additive) {
if (this.origin !== null) {
// global.copyFrom(this.origin).add(this.offset).add(this.animationPose);
global.x = this.origin.x + this.offset.x + this.animationPose.x;
global.y = this.origin.y + this.offset.y + this.animationPose.y;
global.skew = this.origin.skew + this.offset.skew + this.animationPose.skew;
global.rotation = this.origin.rotation + this.offset.rotation + this.animationPose.rotation;
global.scaleX = this.origin.scaleX * this.offset.scaleX * this.animationPose.scaleX;
global.scaleY = this.origin.scaleY * this.offset.scaleY * this.animationPose.scaleY;
}
else {
global.copyFrom(this.offset).add(this.animationPose);
}
}
else if (this.offsetMode === OffsetMode.None) {
if (this.origin !== null) {
global.copyFrom(this.origin).add(this.animationPose);
}
else {
global.copyFrom(this.animationPose);
}
}
else {
inherit = false;
global.copyFrom(this.offset);
}
if (inherit) {
const parentMatrix = parent._boneData.type === BoneType.Bone ? parent.globalTransformMatrix : (parent as Surface)._getGlobalTransformMatrix(global.x, global.y);
if (boneData.inheritScale) {
if (!boneData.inheritRotation) {
parent.updateGlobalTransform();
if (flipX && flipY) {
rotation = global.rotation - (parent.global.rotation + Math.PI);
}
else if (flipX) {
rotation = global.rotation + parent.global.rotation + Math.PI;
}
else if (flipY) {
rotation = global.rotation + parent.global.rotation;
}
else {
rotation = global.rotation - parent.global.rotation;
}
global.rotation = rotation;
}
global.toMatrix(globalTransformMatrix);
globalTransformMatrix.concat(parentMatrix);
if (boneData.inheritTranslation) {
global.x = globalTransformMatrix.tx;
global.y = globalTransformMatrix.ty;
}
else {
globalTransformMatrix.tx = global.x;
globalTransformMatrix.ty = global.y;
}
if (isCache) {
global.fromMatrix(globalTransformMatrix);
}
else {
this._globalDirty = true;
}
}
else {
if (boneData.inheritTranslation) {
const x = global.x;
const y = global.y;
global.x = parentMatrix.a * x + parentMatrix.c * y + parentMatrix.tx;
global.y = parentMatrix.b * x + parentMatrix.d * y + parentMatrix.ty;
}
else {
if (flipX) {
global.x = -global.x;
}
if (flipY) {
global.y = -global.y;
}
}
if (boneData.inheritRotation) {
parent.updateGlobalTransform();
if (parent.global.scaleX < 0.0) {
rotation = global.rotation + parent.global.rotation + Math.PI;
}
else {
rotation = global.rotation + parent.global.rotation;
}
if (parentMatrix.a * parentMatrix.d - parentMatrix.b * parentMatrix.c < 0.0) {
rotation -= global.rotation * 2.0;
if (flipX !== flipY || boneData.inheritReflection) {
global.skew += Math.PI;
}
}
global.rotation = rotation;
}
else if (flipX || flipY) {
if (flipX && flipY) {
rotation = global.rotation + Math.PI;
}
else {
if (flipX) {
rotation = Math.PI - global.rotation;
}
else {
rotation = -global.rotation;
}
global.skew += Math.PI;
}
global.rotation = rotation;
}
global.toMatrix(globalTransformMatrix);
}
}
else {
if (flipX || flipY) {
if (flipX) {
global.x = -global.x;
}
if (flipY) {
global.y = -global.y;
}
if (flipX && flipY) {
rotation = global.rotation + Math.PI;
}
else {
if (flipX) {
rotation = Math.PI - global.rotation;
}
else {
rotation = -global.rotation;
}
global.skew += Math.PI;
}
global.rotation = rotation;
}
global.toMatrix(globalTransformMatrix);
}
}
/**
* @inheritDoc
*/
public _setArmature(value: Armature | null): void {
if (this._armature === value) {
return;
}
let oldSlots: Array<Slot> | null = null;
let oldBones: Array<Bone> | null = null;
if (this._armature !== null) {
oldSlots = this.getSlots();
oldBones = this.getBones();
this._armature._removeBoneFromBoneList(this);
}
this._armature = value as any; //
if (this._armature !== null) {
this._armature._addBoneToBoneList(this);
}
if (oldSlots !== null) {
for (const slot of oldSlots) {
if (slot.parent === this) {
slot._setArmature(this._armature);
}
}
}
if (oldBones !== null) {
for (const bone of oldBones) {
if (bone.parent === this) {
bone._setArmature(this._armature);
}
}
}
}
/**
* @internal
* @private
*/
public init(boneData: BoneData): void {
if (this._boneData !== null) {
return;
}
this._boneData = boneData;
//
this.origin = this._boneData.transform;
}
/**
* @internal
* @private
*/
public update(cacheFrameIndex: number): void {
this._blendState.dirty = false;
if (cacheFrameIndex >= 0 && this._cachedFrameIndices !== null) {
const cachedFrameIndex = this._cachedFrameIndices[cacheFrameIndex];
if (cachedFrameIndex >= 0 && this._cachedFrameIndex === cachedFrameIndex) { // Same cache.
this._transformDirty = false;
}
else if (cachedFrameIndex >= 0) { // Has been Cached.
this._transformDirty = true;
this._cachedFrameIndex = cachedFrameIndex;
}
else {
if (this._hasConstraint) { // Update constraints.
for (const constraint of this._armature._constraints) {
if (constraint._root === this) {
constraint.update();
}
}
}
if (
this._transformDirty ||
(this._parent !== null && this._parent._childrenTransformDirty)
) { // Dirty.
this._transformDirty = true;
this._cachedFrameIndex = -1;
}
else if (this._cachedFrameIndex >= 0) { // Same cache, but not set index yet.
this._transformDirty = false;
this._cachedFrameIndices[cacheFrameIndex] = this._cachedFrameIndex;
}
else { // Dirty.
this._transformDirty = true;
this._cachedFrameIndex = -1;
}
}
}
else {
if (this._hasConstraint) { // Update constraints.
for (const constraint of this._armature._constraints) {
if (constraint._root === this) {
constraint.update();
}
}
}
if (this._transformDirty || (this._parent !== null && this._parent._childrenTransformDirty)) { // Dirty.
cacheFrameIndex = -1;
this._transformDirty = true;
this._cachedFrameIndex = -1;
}
}
if (this._transformDirty) {
this._transformDirty = false;
this._childrenTransformDirty = true;
//
if (this._cachedFrameIndex < 0) {
const isCache = cacheFrameIndex >= 0;
if (this._localDirty) {
this._updateGlobalTransformMatrix(isCache);
}
if (isCache && this._cachedFrameIndices !== null) {
this._cachedFrameIndex = this._cachedFrameIndices[cacheFrameIndex] = this._armature._armatureData.setCacheFrame(this.globalTransformMatrix, this.global);
}
}
else {
this._armature._armatureData.getCacheFrame(this.globalTransformMatrix, this.global, this._cachedFrameIndex);
}
//
}
else if (this._childrenTransformDirty) {
this._childrenTransformDirty = false;
}
this._localDirty = true;
}
/**
* @internal
* @private
*/
public updateByConstraint(): void {
if (this._localDirty) {
this._localDirty = false;
if (this._transformDirty || (this._parent !== null && this._parent._childrenTransformDirty)) {
this._updateGlobalTransformMatrix(true);
}
this._transformDirty = true;
}
}
/**
* - Forces the bone to update the transform in the next frame.
* When the bone is not animated or its animation state is finished, the bone will not continue to update,
* and when the skeleton must be updated for some reason, the method needs to be called explicitly.
* @example
* <pre>
* let bone = armature.getBone("arm");
* bone.offset.scaleX = 2.0;
* bone.invalidUpdate();
* </pre>
* @version DragonBones 3.0
* @language en_US
*/
/**
* - 强制骨骼在下一帧更新变换。
* 当该骨骼没有动画状态或其动画状态播放完成时,骨骼将不在继续更新,而此时由于某些原因必须更新骨骼时,则需要显式调用该方法。
* @example
* <pre>
* let bone = armature.getBone("arm");
* bone.offset.scaleX = 2.0;
* bone.invalidUpdate();
* </pre>
* @version DragonBones 3.0
* @language zh_CN
*/
public invalidUpdate(): void {
this._transformDirty = true;
}
/**
* - Check whether the bone contains a specific bone or slot.
* @see dragonBones.Bone
* @see dragonBones.Slot
* @version DragonBones 3.0
* @language en_US
*/
/**
* - 检查该骨骼是否包含特定的骨骼或插槽。
* @see dragonBones.Bone
* @see dragonBones.Slot
* @version DragonBones 3.0
* @language zh_CN
*/
public contains(value: TransformObject): boolean {
if (value === this) {
return false;
}
let ancestor: TransformObject | null = value;
while (ancestor !== this && ancestor !== null) {
ancestor = ancestor.parent;
}
return ancestor === this;
}
/**
* - The bone data.
* @version DragonBones 4.5
* @language en_US
*/
/**
* - 骨骼数据。
* @version DragonBones 4.5
* @language zh_CN
*/
public get boneData(): BoneData {
return this._boneData;
}
/**
* - The visible of all slots in the bone.
* @default true
* @see dragonBones.Slot#visible
* @version DragonBones 3.0
* @language en_US
*/
/**
* - 此骨骼所有插槽的可见。
* @default true
* @see dragonBones.Slot#visible
* @version DragonBones 3.0
* @language zh_CN
*/
public get visible(): boolean {
return this._visible;
}
public set visible(value: boolean) {
if (this._visible === value) {
return;
}
this._visible = value;
for (const slot of this._armature.getSlots()) {
if (slot._parent === this) {
slot._updateVisible();
}
}
}
/**
* - The bone name.
* @version DragonBones 3.0
* @language en_US
*/
/**
* - 骨骼名称。
* @version DragonBones 3.0
* @language zh_CN
*/
public get name(): string {
return this._boneData.name;
}
/**
* - Deprecated, please refer to {@link dragonBones.Armature#getBones()}.
* @deprecated
* @language en_US
*/
/**
* - 已废弃,请参考 {@link dragonBones.Armature#getBones()}。
* @deprecated
* @language zh_CN
*/
public getBones(): Array<Bone> {
const bones = new Array<Bone>();
for (const bone of this._armature.getBones()) {
if (bone.parent === this) {
bones.push(bone);
}
}
return bones;
}
/**
* - Deprecated, please refer to {@link dragonBones.Armature#getSlots()}.
* @deprecated
* @language en_US
*/
/**
* - 已废弃,请参考 {@link dragonBones.Armature#getSlots()}。
* @deprecated
* @language zh_CN
*/
public getSlots(): Array<Slot> {
const slots = new Array<Slot>();
for (const slot of this._armature.getSlots()) {
if (slot.parent === this) {
slots.push(slot);
}
}
return slots;
}
/**
* - Deprecated, please refer to {@link dragonBones.Armature#getSlot()}.
* @deprecated
* @language en_US
*/
/**
* - 已废弃,请参考 {@link dragonBones.Armature#getSlot()}。
* @deprecated
* @language zh_CN
*/
public get slot(): Slot | null {
for (const slot of this._armature.getSlots()) {
if (slot.parent === this) {
return slot;
}
}
return null;
}
}
}