mirror of
https://github.com/EsotericSoftware/spine-runtimes.git
synced 2026-02-04 14:24:53 +08:00
[ts] Processed spine-core with biome.
This commit is contained in:
parent
2dfcc8d045
commit
9ea694b16f
@ -6,9 +6,15 @@
|
||||
"enabled": true,
|
||||
"rules": {
|
||||
"recommended": true,
|
||||
"complexity": {
|
||||
"noUselessConstructor": "off"
|
||||
},
|
||||
"correctness": {
|
||||
"noUnusedFunctionParameters": "off"
|
||||
},
|
||||
"style": {
|
||||
"useExponentiationOperator": "off"
|
||||
},
|
||||
"suspicious": {
|
||||
"noAssignInExpressions": "off"
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -27,12 +27,14 @@
|
||||
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*****************************************************************************/
|
||||
|
||||
import { Animation, MixBlend, AttachmentTimeline, MixDirection, RotateTimeline, DrawOrderTimeline, Timeline, EventTimeline } from "./Animation.js";
|
||||
import { AnimationStateData } from "./AnimationStateData.js";
|
||||
import { Skeleton } from "./Skeleton.js";
|
||||
import { Slot } from "./Slot.js";
|
||||
import { StringSet, Pool, Utils, MathUtils } from "./Utils.js";
|
||||
import { Event } from "./Event.js";
|
||||
/** biome-ignore-all lint/style/noNonNullAssertion: reference runtime expects some nullable to not be null */
|
||||
|
||||
import { Animation, AttachmentTimeline, DrawOrderTimeline, EventTimeline, MixBlend, MixDirection, RotateTimeline, Timeline } from "./Animation.js";
|
||||
import type { AnimationStateData } from "./AnimationStateData.js";
|
||||
import type { Event } from "./Event.js";
|
||||
import type { Skeleton } from "./Skeleton.js";
|
||||
import type { Slot } from "./Slot.js";
|
||||
import { MathUtils, Pool, StringSet, Utils } from "./Utils.js";
|
||||
|
||||
|
||||
/** Applies animations over time, queues animations for later playback, mixes (crossfading) between animations, and applies
|
||||
@ -46,7 +48,7 @@ export class AnimationState {
|
||||
data: AnimationStateData;
|
||||
|
||||
/** The list of tracks that currently have animations, which may contain null entries. */
|
||||
readonly tracks = new Array<TrackEntry | null>();
|
||||
readonly tracks = [] as (TrackEntry | null)[];
|
||||
|
||||
/** Multiplier for the delta time when the animation state is updated, causing time for all animations and mixes to play slower
|
||||
* or faster. Defaults to 1.
|
||||
@ -55,8 +57,8 @@ export class AnimationState {
|
||||
timeScale = 1;
|
||||
unkeyedState = 0;
|
||||
|
||||
readonly events = new Array<Event>();
|
||||
readonly listeners = new Array<AnimationStateListener>();
|
||||
readonly events = [] as Event[];
|
||||
readonly listeners = [] as AnimationStateListener[];
|
||||
queue = new EventQueue(this);
|
||||
propertyIDs = new StringSet();
|
||||
animationsChanged = false;
|
||||
@ -70,9 +72,9 @@ export class AnimationState {
|
||||
/** Increments each track entry {@link TrackEntry#trackTime()}, setting queued animations as current if needed. */
|
||||
update (delta: number) {
|
||||
delta *= this.timeScale;
|
||||
let tracks = this.tracks;
|
||||
const tracks = this.tracks;
|
||||
for (let i = 0, n = tracks.length; i < n; i++) {
|
||||
let current = tracks[i];
|
||||
const current = tracks[i];
|
||||
if (!current) continue;
|
||||
|
||||
current.animationLast = current.nextAnimationLast;
|
||||
@ -90,10 +92,10 @@ export class AnimationState {
|
||||
let next = current.next;
|
||||
if (next) {
|
||||
// When the next entry's delay is passed, change to the next entry, preserving leftover time.
|
||||
let nextTime = current.trackLast - next.delay;
|
||||
const nextTime = current.trackLast - next.delay;
|
||||
if (nextTime >= 0) {
|
||||
next.delay = 0;
|
||||
next.trackTime += current.timeScale == 0 ? 0 : (nextTime / current.timeScale + delta) * next.timeScale;
|
||||
next.trackTime += current.timeScale === 0 ? 0 : (nextTime / current.timeScale + delta) * next.timeScale;
|
||||
current.trackTime += currentDelta;
|
||||
this.setCurrent(i, next, true);
|
||||
while (next.mixingFrom) {
|
||||
@ -127,18 +129,18 @@ export class AnimationState {
|
||||
|
||||
/** Returns true when all mixing from entries are complete. */
|
||||
updateMixingFrom (to: TrackEntry, delta: number): boolean {
|
||||
let from = to.mixingFrom;
|
||||
const from = to.mixingFrom;
|
||||
if (!from) return true;
|
||||
|
||||
let finished = this.updateMixingFrom(from, delta);
|
||||
const finished = this.updateMixingFrom(from, delta);
|
||||
|
||||
from.animationLast = from.nextAnimationLast;
|
||||
from.trackLast = from.nextTrackLast;
|
||||
|
||||
// The from entry was applied at least once and the mix is complete.
|
||||
if (to.nextTrackLast != -1 && to.mixTime >= to.mixDuration) {
|
||||
if (to.nextTrackLast !== -1 && to.mixTime >= to.mixDuration) {
|
||||
// Mixing is complete for all entries before the from entry or the mix is instantaneous.
|
||||
if (from.totalAlpha == 0 || to.mixDuration == 0) {
|
||||
if (from.totalAlpha === 0 || to.mixDuration === 0) {
|
||||
to.mixingFrom = from.mixingFrom;
|
||||
if (from.mixingFrom != null) from.mixingFrom.mixingTo = to;
|
||||
to.interruptAlpha = from.interruptAlpha;
|
||||
@ -159,15 +161,15 @@ export class AnimationState {
|
||||
if (!skeleton) throw new Error("skeleton cannot be null.");
|
||||
if (this.animationsChanged) this._animationsChanged();
|
||||
|
||||
let events = this.events;
|
||||
let tracks = this.tracks;
|
||||
const events = this.events;
|
||||
const tracks = this.tracks;
|
||||
let applied = false;
|
||||
|
||||
for (let i = 0, n = tracks.length; i < n; i++) {
|
||||
let current = tracks[i];
|
||||
const current = tracks[i];
|
||||
if (!current || current.delay > 0) continue;
|
||||
applied = true;
|
||||
let blend: MixBlend = i == 0 ? MixBlend.first : current.mixBlend;
|
||||
const blend: MixBlend = i === 0 ? MixBlend.first : current.mixBlend;
|
||||
|
||||
// Apply mixing from entries first.
|
||||
let alpha = current.alpha;
|
||||
@ -185,10 +187,10 @@ export class AnimationState {
|
||||
applyTime = current.animation!.duration - applyTime;
|
||||
applyEvents = null;
|
||||
}
|
||||
let timelines = current.animation!.timelines;
|
||||
let timelineCount = timelines.length;
|
||||
if ((i == 0 && alpha == 1) || blend == MixBlend.add) {
|
||||
if (i == 0) attachments = true;
|
||||
const timelines = current.animation!.timelines;
|
||||
const timelineCount = timelines.length;
|
||||
if ((i === 0 && alpha === 1) || blend === MixBlend.add) {
|
||||
if (i === 0) attachments = true;
|
||||
for (let ii = 0; ii < timelineCount; ii++) {
|
||||
// Fixes issue #302 on IOS9 where mix, blend sometimes became undefined and caused assets
|
||||
// to sometimes stop rendering when using color correction, as their RGBA values become NaN.
|
||||
@ -201,15 +203,15 @@ export class AnimationState {
|
||||
timeline.apply(skeleton, animationLast, applyTime, applyEvents, alpha, blend, MixDirection.in, false);
|
||||
}
|
||||
} else {
|
||||
let timelineMode = current.timelineMode;
|
||||
const timelineMode = current.timelineMode;
|
||||
|
||||
let shortestRotation = current.shortestRotation;
|
||||
let firstFrame = !shortestRotation && current.timelinesRotation.length != timelineCount << 1;
|
||||
const shortestRotation = current.shortestRotation;
|
||||
const firstFrame = !shortestRotation && current.timelinesRotation.length !== timelineCount << 1;
|
||||
if (firstFrame) current.timelinesRotation.length = timelineCount << 1;
|
||||
|
||||
for (let ii = 0; ii < timelineCount; ii++) {
|
||||
let timeline = timelines[ii];
|
||||
let timelineBlend = timelineMode[ii] == SUBSEQUENT ? blend : MixBlend.setup;
|
||||
const timeline = timelines[ii];
|
||||
const timelineBlend = timelineMode[ii] === SUBSEQUENT ? blend : MixBlend.setup;
|
||||
if (!shortestRotation && timeline instanceof RotateTimeline) {
|
||||
this.applyRotateTimeline(timeline, skeleton, applyTime, alpha, timelineBlend, current.timelinesRotation, ii << 1, firstFrame);
|
||||
} else if (timeline instanceof AttachmentTimeline) {
|
||||
@ -234,7 +236,7 @@ export class AnimationState {
|
||||
const slots = skeleton.slots;
|
||||
for (let i = 0, n = skeleton.slots.length; i < n; i++) {
|
||||
const slot = slots[i];
|
||||
if (slot.attachmentState == setupState) {
|
||||
if (slot.attachmentState === setupState) {
|
||||
const attachmentName = slot.data.attachmentName;
|
||||
slot.pose.setAttachment(!attachmentName ? null : skeleton.getAttachment(slot.data.index, attachmentName));
|
||||
}
|
||||
@ -246,23 +248,23 @@ export class AnimationState {
|
||||
}
|
||||
|
||||
applyMixingFrom (to: TrackEntry, skeleton: Skeleton, blend: MixBlend) {
|
||||
let from = to.mixingFrom!;
|
||||
const from = to.mixingFrom!;
|
||||
if (from.mixingFrom) this.applyMixingFrom(from, skeleton, blend);
|
||||
|
||||
let mix = 0;
|
||||
if (to.mixDuration == 0) { // Single frame mix to undo mixingFrom changes.
|
||||
if (to.mixDuration === 0) { // Single frame mix to undo mixingFrom changes.
|
||||
mix = 1;
|
||||
if (blend == MixBlend.first) blend = MixBlend.setup;
|
||||
if (blend === MixBlend.first) blend = MixBlend.setup;
|
||||
} else {
|
||||
mix = to.mixTime / to.mixDuration;
|
||||
if (mix > 1) mix = 1;
|
||||
if (blend != MixBlend.first) blend = from.mixBlend;
|
||||
if (blend !== MixBlend.first) blend = from.mixBlend;
|
||||
}
|
||||
|
||||
let attachments = mix < from.mixAttachmentThreshold, drawOrder = mix < from.mixDrawOrderThreshold;
|
||||
let timelines = from.animation!.timelines;
|
||||
let timelineCount = timelines.length;
|
||||
let alphaHold = from.alpha * to.interruptAlpha, alphaMix = alphaHold * (1 - mix);
|
||||
const attachments = mix < from.mixAttachmentThreshold, drawOrder = mix < from.mixDrawOrderThreshold;
|
||||
const timelines = from.animation!.timelines;
|
||||
const timelineCount = timelines.length;
|
||||
const alphaHold = from.alpha * to.interruptAlpha, alphaMix = alphaHold * (1 - mix);
|
||||
let animationLast = from.animationLast, animationTime = from.getAnimationTime(), applyTime = animationTime;
|
||||
let events = null;
|
||||
if (from.reverse)
|
||||
@ -270,20 +272,20 @@ export class AnimationState {
|
||||
else if (mix < from.eventThreshold)
|
||||
events = this.events;
|
||||
|
||||
if (blend == MixBlend.add) {
|
||||
if (blend === MixBlend.add) {
|
||||
for (let i = 0; i < timelineCount; i++)
|
||||
timelines[i].apply(skeleton, animationLast, applyTime, events, alphaMix, blend, MixDirection.out, false);
|
||||
} else {
|
||||
let timelineMode = from.timelineMode;
|
||||
let timelineHoldMix = from.timelineHoldMix;
|
||||
const timelineMode = from.timelineMode;
|
||||
const timelineHoldMix = from.timelineHoldMix;
|
||||
|
||||
let shortestRotation = from.shortestRotation;
|
||||
let firstFrame = !shortestRotation && from.timelinesRotation.length != timelineCount << 1;
|
||||
const shortestRotation = from.shortestRotation;
|
||||
const firstFrame = !shortestRotation && from.timelinesRotation.length !== timelineCount << 1;
|
||||
if (firstFrame) from.timelinesRotation.length = timelineCount << 1;
|
||||
|
||||
from.totalAlpha = 0;
|
||||
for (let i = 0; i < timelineCount; i++) {
|
||||
let timeline = timelines[i];
|
||||
const timeline = timelines[i];
|
||||
let direction = MixDirection.out;
|
||||
let timelineBlend: MixBlend;
|
||||
let alpha = 0;
|
||||
@ -305,11 +307,12 @@ export class AnimationState {
|
||||
timelineBlend = MixBlend.setup;
|
||||
alpha = alphaHold;
|
||||
break;
|
||||
default: // HOLD_MIX
|
||||
default: { // HOLD_MIX
|
||||
timelineBlend = MixBlend.setup;
|
||||
let holdMix = timelineHoldMix[i];
|
||||
const holdMix = timelineHoldMix[i];
|
||||
alpha = alphaHold * Math.max(0, 1 - holdMix.mixTime / holdMix.mixDuration);
|
||||
break;
|
||||
}
|
||||
}
|
||||
from.totalAlpha += alpha;
|
||||
|
||||
@ -320,7 +323,7 @@ export class AnimationState {
|
||||
else {
|
||||
// This fixes the WebKit 602 specific issue described at https://esotericsoftware.com/forum/d/10109-ios-10-disappearing-graphics
|
||||
Utils.webkit602BugfixHelper(alpha, blend);
|
||||
if (drawOrder && timeline instanceof DrawOrderTimeline && timelineBlend == MixBlend.setup)
|
||||
if (drawOrder && timeline instanceof DrawOrderTimeline && timelineBlend === MixBlend.setup)
|
||||
direction = MixDirection.in;
|
||||
timeline.apply(skeleton, animationLast, applyTime, events, alpha, timelineBlend, direction, false);
|
||||
}
|
||||
@ -340,7 +343,7 @@ export class AnimationState {
|
||||
if (!slot.bone.active) return;
|
||||
|
||||
if (time < timeline.frames[0]) { // Time is before first frame.
|
||||
if (blend == MixBlend.setup || blend == MixBlend.first)
|
||||
if (blend === MixBlend.setup || blend === MixBlend.first)
|
||||
this.setAttachment(skeleton, slot, slot.data.attachmentName, attachments);
|
||||
} else
|
||||
this.setAttachment(skeleton, slot, timeline.attachmentNames[Timeline.search(timeline.frames, time)], attachments);
|
||||
@ -359,20 +362,22 @@ export class AnimationState {
|
||||
|
||||
if (firstFrame) timelinesRotation[i] = 0;
|
||||
|
||||
if (alpha == 1) {
|
||||
if (alpha === 1) {
|
||||
timeline.apply(skeleton, 0, time, null, 1, blend, MixDirection.in, false);
|
||||
return;
|
||||
}
|
||||
|
||||
let bone = skeleton.bones[timeline.boneIndex];
|
||||
const bone = skeleton.bones[timeline.boneIndex];
|
||||
if (!bone.active) return;
|
||||
const pose = bone.pose, setup = bone.data.setup;
|
||||
let frames = timeline.frames;
|
||||
const frames = timeline.frames;
|
||||
let r1 = 0, r2 = 0;
|
||||
if (time < frames[0]) {
|
||||
switch (blend) {
|
||||
// biome-ignore lint/suspicious/noFallthroughSwitchClause: reference runtime does fall through
|
||||
case MixBlend.setup:
|
||||
pose.rotation = setup.rotation;
|
||||
// biome-ignore lint/suspicious/useDefaultSwitchClauseLast: needed for fall through
|
||||
default:
|
||||
return;
|
||||
case MixBlend.first:
|
||||
@ -380,14 +385,14 @@ export class AnimationState {
|
||||
r2 = setup.rotation;
|
||||
}
|
||||
} else {
|
||||
r1 = blend == MixBlend.setup ? setup.rotation : pose.rotation;
|
||||
r1 = blend === MixBlend.setup ? setup.rotation : pose.rotation;
|
||||
r2 = setup.rotation + timeline.getCurveValue(time);
|
||||
}
|
||||
|
||||
// Mix between rotations using the direction of the shortest route on the first frame while detecting crosses.
|
||||
let total = 0, diff = r2 - r1;
|
||||
diff -= Math.ceil(diff / 360 - 0.5) * 360;
|
||||
if (diff == 0) {
|
||||
if (diff === 0) {
|
||||
total = timelinesRotation[i];
|
||||
} else {
|
||||
let lastTotal = 0, lastDiff = 0;
|
||||
@ -398,19 +403,19 @@ export class AnimationState {
|
||||
lastTotal = timelinesRotation[i];
|
||||
lastDiff = timelinesRotation[i + 1];
|
||||
}
|
||||
let loops = lastTotal - lastTotal % 360;
|
||||
const loops = lastTotal - lastTotal % 360;
|
||||
total = diff + loops;
|
||||
let current = diff >= 0, dir = lastTotal >= 0;
|
||||
if (Math.abs(lastDiff) <= 90 && MathUtils.signum(lastDiff) != MathUtils.signum(diff)) {
|
||||
if (Math.abs(lastDiff) <= 90 && MathUtils.signum(lastDiff) !== MathUtils.signum(diff)) {
|
||||
if (Math.abs(lastTotal - loops) > 180) {
|
||||
total += 360 * MathUtils.signum(lastTotal);
|
||||
dir = current;
|
||||
} else if (loops != 0)
|
||||
} else if (loops !== 0)
|
||||
total -= 360 * MathUtils.signum(lastTotal);
|
||||
else
|
||||
dir = current;
|
||||
}
|
||||
if (dir != current) total += 360 * MathUtils.signum(lastTotal);
|
||||
if (dir !== current) total += 360 * MathUtils.signum(lastTotal);
|
||||
timelinesRotation[i] = total;
|
||||
}
|
||||
timelinesRotation[i + 1] = diff;
|
||||
@ -418,15 +423,15 @@ export class AnimationState {
|
||||
}
|
||||
|
||||
queueEvents (entry: TrackEntry, animationTime: number) {
|
||||
let animationStart = entry.animationStart, animationEnd = entry.animationEnd;
|
||||
let duration = animationEnd - animationStart;
|
||||
let trackLastWrapped = entry.trackLast % duration;
|
||||
const animationStart = entry.animationStart, animationEnd = entry.animationEnd;
|
||||
const duration = animationEnd - animationStart;
|
||||
const trackLastWrapped = entry.trackLast % duration;
|
||||
|
||||
// Queue events before complete.
|
||||
let events = this.events;
|
||||
const events = this.events;
|
||||
let i = 0, n = events.length;
|
||||
for (; i < n; i++) {
|
||||
let event = events[i];
|
||||
const event = events[i];
|
||||
if (event.time < trackLastWrapped) break;
|
||||
if (event.time > animationEnd) continue; // Discard events outside animation start/end.
|
||||
this.queue.event(entry, event);
|
||||
@ -435,7 +440,7 @@ export class AnimationState {
|
||||
// Queue complete if completed a loop iteration or the animation.
|
||||
let complete = false;
|
||||
if (entry.loop) {
|
||||
if (duration == 0)
|
||||
if (duration === 0)
|
||||
complete = true;
|
||||
else {
|
||||
const cycles = Math.floor(entry.trackTime / duration);
|
||||
@ -447,7 +452,7 @@ export class AnimationState {
|
||||
|
||||
// Queue events after complete.
|
||||
for (; i < n; i++) {
|
||||
let event = events[i];
|
||||
const event = events[i];
|
||||
if (event.time < animationStart) continue; // Discard events outside animation start/end.
|
||||
this.queue.event(entry, event);
|
||||
}
|
||||
@ -458,7 +463,7 @@ export class AnimationState {
|
||||
* It may be desired to use {@link AnimationState#setEmptyAnimation()} to mix the skeletons back to the setup pose,
|
||||
* rather than leaving them in their current pose. */
|
||||
clearTracks () {
|
||||
let oldDrainDisabled = this.queue.drainDisabled;
|
||||
const oldDrainDisabled = this.queue.drainDisabled;
|
||||
this.queue.drainDisabled = true;
|
||||
for (let i = 0, n = this.tracks.length; i < n; i++)
|
||||
this.clearTrack(i);
|
||||
@ -473,7 +478,7 @@ export class AnimationState {
|
||||
* rather than leaving them in their current pose. */
|
||||
clearTrack (trackIndex: number) {
|
||||
if (trackIndex >= this.tracks.length) return;
|
||||
let current = this.tracks[trackIndex];
|
||||
const current = this.tracks[trackIndex];
|
||||
if (!current) return;
|
||||
|
||||
this.queue.end(current);
|
||||
@ -482,7 +487,7 @@ export class AnimationState {
|
||||
|
||||
let entry = current;
|
||||
while (true) {
|
||||
let from = entry.mixingFrom;
|
||||
const from = entry.mixingFrom;
|
||||
if (!from) break;
|
||||
this.queue.end(from);
|
||||
entry.mixingFrom = null;
|
||||
@ -496,7 +501,7 @@ export class AnimationState {
|
||||
}
|
||||
|
||||
setCurrent (index: number, current: TrackEntry, interrupt: boolean) {
|
||||
let from = this.expandToIndex(index);
|
||||
const from = this.expandToIndex(index);
|
||||
this.tracks[index] = current;
|
||||
current.previous = null;
|
||||
|
||||
@ -538,8 +543,8 @@ export class AnimationState {
|
||||
}
|
||||
|
||||
private setAnimation1 (trackIndex: number, animationName: string, loop: boolean = false) {
|
||||
let animation = this.data.skeletonData.findAnimation(animationName);
|
||||
if (!animation) throw new Error("Animation not found: " + animationName);
|
||||
const animation = this.data.skeletonData.findAnimation(animationName);
|
||||
if (!animation) throw new Error(`Animation not found: ${animationName}`);
|
||||
return this.setAnimation2(trackIndex, animation, loop);
|
||||
}
|
||||
|
||||
@ -568,7 +573,7 @@ export class AnimationState {
|
||||
} else
|
||||
this.clearNext(current);
|
||||
}
|
||||
let entry = this.trackEntry(trackIndex, animation, loop, current);
|
||||
const entry = this.trackEntry(trackIndex, animation, loop, current);
|
||||
this.setCurrent(trackIndex, entry, interrupt);
|
||||
this.queue.drain();
|
||||
return entry;
|
||||
@ -596,8 +601,8 @@ export class AnimationState {
|
||||
}
|
||||
|
||||
private addAnimation1 (trackIndex: number, animationName: string, loop: boolean = false, delay: number = 0) {
|
||||
let animation = this.data.skeletonData.findAnimation(animationName);
|
||||
if (!animation) throw new Error("Animation not found: " + animationName);
|
||||
const animation = this.data.skeletonData.findAnimation(animationName);
|
||||
if (!animation) throw new Error(`Animation not found: ${animationName}`);
|
||||
return this.addAnimation2(trackIndex, animation, loop, delay);
|
||||
}
|
||||
|
||||
@ -610,7 +615,7 @@ export class AnimationState {
|
||||
last = last.next;
|
||||
}
|
||||
|
||||
let entry = this.trackEntry(trackIndex, animation, loop, last);
|
||||
const entry = this.trackEntry(trackIndex, animation, loop, last);
|
||||
|
||||
if (!last) {
|
||||
this.setCurrent(trackIndex, entry, true);
|
||||
@ -644,7 +649,7 @@ export class AnimationState {
|
||||
* See <a href='https://esotericsoftware.com/spine-applying-animations/#Empty-animations'>Empty animations</a> in the Spine
|
||||
* Runtimes Guide. */
|
||||
setEmptyAnimation (trackIndex: number, mixDuration: number = 0) {
|
||||
let entry = this.setAnimation(trackIndex, AnimationState.emptyAnimation, false);
|
||||
const entry = this.setAnimation(trackIndex, AnimationState.emptyAnimation, false);
|
||||
entry.mixDuration = mixDuration;
|
||||
entry.trackEnd = mixDuration;
|
||||
return entry;
|
||||
@ -664,7 +669,7 @@ export class AnimationState {
|
||||
* @return A track entry to allow further customization of animation playback. References to the track entry must not be kept
|
||||
* after the {@link AnimationStateListener#dispose(TrackEntry)} event occurs. */
|
||||
addEmptyAnimation (trackIndex: number, mixDuration: number = 0, delay: number = 0) {
|
||||
let entry = this.addAnimation(trackIndex, AnimationState.emptyAnimation, false, delay);
|
||||
const entry = this.addAnimation(trackIndex, AnimationState.emptyAnimation, false, delay);
|
||||
if (delay <= 0) entry.delay = Math.max(entry.delay + entry.mixDuration - mixDuration, 0);
|
||||
entry.mixDuration = mixDuration;
|
||||
entry.trackEnd = mixDuration;
|
||||
@ -676,10 +681,10 @@ export class AnimationState {
|
||||
* See <a href='https://esotericsoftware.com/spine-applying-animations/#Empty-animations'>Empty animations</a> in the Spine
|
||||
* Runtimes Guide. */
|
||||
setEmptyAnimations (mixDuration: number = 0) {
|
||||
let oldDrainDisabled = this.queue.drainDisabled;
|
||||
const oldDrainDisabled = this.queue.drainDisabled;
|
||||
this.queue.drainDisabled = true;
|
||||
for (let i = 0, n = this.tracks.length; i < n; i++) {
|
||||
let current = this.tracks[i];
|
||||
const current = this.tracks[i];
|
||||
if (current) this.setEmptyAnimation(current.trackIndex, mixDuration);
|
||||
}
|
||||
this.queue.drainDisabled = oldDrainDisabled;
|
||||
@ -695,7 +700,7 @@ export class AnimationState {
|
||||
|
||||
/** @param last May be null. */
|
||||
trackEntry (trackIndex: number, animation: Animation, loop: boolean, last: TrackEntry | null) {
|
||||
let entry = this.trackEntryPool.obtain();
|
||||
const entry = this.trackEntryPool.obtain();
|
||||
entry.reset();
|
||||
entry.trackIndex = trackIndex;
|
||||
entry.animation = animation;
|
||||
@ -745,30 +750,30 @@ export class AnimationState {
|
||||
this.animationsChanged = false;
|
||||
|
||||
this.propertyIDs.clear();
|
||||
let tracks = this.tracks;
|
||||
const tracks = this.tracks;
|
||||
for (let i = 0, n = tracks.length; i < n; i++) {
|
||||
let entry = tracks[i];
|
||||
if (!entry) continue;
|
||||
while (entry.mixingFrom)
|
||||
entry = entry.mixingFrom;
|
||||
do {
|
||||
if (!entry.mixingTo || entry.mixBlend != MixBlend.add) this.computeHold(entry);
|
||||
if (!entry.mixingTo || entry.mixBlend !== MixBlend.add) this.computeHold(entry);
|
||||
entry = entry.mixingTo;
|
||||
} while (entry);
|
||||
}
|
||||
}
|
||||
|
||||
computeHold (entry: TrackEntry) {
|
||||
let to = entry.mixingTo;
|
||||
let timelines = entry.animation!.timelines;
|
||||
let timelinesCount = entry.animation!.timelines.length;
|
||||
let timelineMode = entry.timelineMode;
|
||||
const to = entry.mixingTo;
|
||||
const timelines = entry.animation!.timelines;
|
||||
const timelinesCount = entry.animation!.timelines.length;
|
||||
const timelineMode = entry.timelineMode;
|
||||
timelineMode.length = timelinesCount;
|
||||
let timelineHoldMix = entry.timelineHoldMix;
|
||||
const timelineHoldMix = entry.timelineHoldMix;
|
||||
timelineHoldMix.length = 0;
|
||||
let propertyIDs = this.propertyIDs;
|
||||
const propertyIDs = this.propertyIDs;
|
||||
|
||||
if (to && to.holdPrevious) {
|
||||
if (to?.holdPrevious) {
|
||||
for (let i = 0; i < timelinesCount; i++)
|
||||
timelineMode[i] = propertyIDs.addAll(timelines[i].getPropertyIds()) ? HOLD_FIRST : HOLD_SUBSEQUENT;
|
||||
return;
|
||||
@ -776,8 +781,8 @@ export class AnimationState {
|
||||
|
||||
outer:
|
||||
for (let i = 0; i < timelinesCount; i++) {
|
||||
let timeline = timelines[i];
|
||||
let ids = timeline.getPropertyIds();
|
||||
const timeline = timelines[i];
|
||||
const ids = timeline.getPropertyIds();
|
||||
if (!propertyIDs.addAll(ids))
|
||||
timelineMode[i] = SUBSEQUENT;
|
||||
else if (!to || timeline instanceof AttachmentTimeline || timeline instanceof DrawOrderTimeline
|
||||
@ -812,7 +817,7 @@ export class AnimationState {
|
||||
|
||||
/** Removes the listener added with {@link #addListener()}. */
|
||||
removeListener (listener: AnimationStateListener) {
|
||||
let index = this.listeners.indexOf(listener);
|
||||
const index = this.listeners.indexOf(listener);
|
||||
if (index >= 0) this.listeners.splice(index, 1);
|
||||
}
|
||||
|
||||
@ -1009,9 +1014,9 @@ export class TrackEntry {
|
||||
* The `mixBlend` can be set for a new track entry only before {@link AnimationState#apply()} is next
|
||||
* called. */
|
||||
mixBlend = MixBlend.replace;
|
||||
timelineMode = new Array<number>();
|
||||
timelineHoldMix = new Array<TrackEntry>();
|
||||
timelinesRotation = new Array<number>();
|
||||
timelineMode = [] as number[];
|
||||
timelineHoldMix = [] as TrackEntry[];
|
||||
timelinesRotation = [] as number[];
|
||||
|
||||
reset () {
|
||||
this.next = null;
|
||||
@ -1030,8 +1035,8 @@ export class TrackEntry {
|
||||
* `animationStart` time. */
|
||||
getAnimationTime () {
|
||||
if (this.loop) {
|
||||
let duration = this.animationEnd - this.animationStart;
|
||||
if (duration == 0) return this.animationStart;
|
||||
const duration = this.animationEnd - this.animationStart;
|
||||
if (duration === 0) return this.animationStart;
|
||||
return (this.trackTime % duration) + this.animationStart;
|
||||
}
|
||||
return Math.min(this.trackTime + this.animationStart, this.animationEnd);
|
||||
@ -1061,8 +1066,8 @@ export class TrackEntry {
|
||||
}
|
||||
|
||||
getTrackComplete () {
|
||||
let duration = this.animationEnd - this.animationStart;
|
||||
if (duration != 0) {
|
||||
const duration = this.animationEnd - this.animationStart;
|
||||
if (duration !== 0) {
|
||||
if (this.loop) return duration * (1 + ((this.trackTime / duration) | 0)); // Completion of next loop.
|
||||
if (this.trackTime < duration) return duration; // Before duration.
|
||||
}
|
||||
@ -1073,7 +1078,7 @@ export class TrackEntry {
|
||||
* <p>
|
||||
* See {@link AnimationState#apply(Skeleton)}. */
|
||||
wasApplied () {
|
||||
return this.nextTrackLast != -1;
|
||||
return this.nextTrackLast !== -1;
|
||||
}
|
||||
|
||||
/** Returns true if there is a {@link #getNext()} track entry and it will become the current track entry during the next
|
||||
@ -1084,7 +1089,7 @@ export class TrackEntry {
|
||||
}
|
||||
|
||||
export class EventQueue {
|
||||
objects: Array<any> = [];
|
||||
objects: Array<EventType | TrackEntry | Event> = [];
|
||||
drainDisabled = false;
|
||||
animState: AnimationState;
|
||||
|
||||
@ -1137,28 +1142,29 @@ export class EventQueue {
|
||||
const entry = objects[i + 1] as TrackEntry;
|
||||
switch (type) {
|
||||
case EventType.start:
|
||||
if (entry.listener && entry.listener.start) entry.listener.start(entry);
|
||||
if (entry.listener?.start) entry.listener.start(entry);
|
||||
for (let ii = 0; ii < listeners.length; ii++) {
|
||||
const listener = listeners[ii];
|
||||
if (listener.start) listener.start(entry);
|
||||
}
|
||||
break;
|
||||
case EventType.interrupt:
|
||||
if (entry.listener && entry.listener.interrupt) entry.listener.interrupt(entry);
|
||||
if (entry.listener?.interrupt) entry.listener.interrupt(entry);
|
||||
for (let ii = 0; ii < listeners.length; ii++) {
|
||||
const listener = listeners[ii];
|
||||
if (listener.interrupt) listener.interrupt(entry);
|
||||
}
|
||||
break;
|
||||
// biome-ignore lint/suspicious/noFallthroughSwitchClause: reference runtime does fall through
|
||||
case EventType.end:
|
||||
if (entry.listener && entry.listener.end) entry.listener.end(entry);
|
||||
if (entry.listener?.end) entry.listener.end(entry);
|
||||
for (let ii = 0; ii < listeners.length; ii++) {
|
||||
const listener = listeners[ii];
|
||||
if (listener.end) listener.end(entry);
|
||||
}
|
||||
// Fall through.
|
||||
case EventType.dispose:
|
||||
if (entry.listener && entry.listener.dispose) entry.listener.dispose(entry);
|
||||
if (entry.listener?.dispose) entry.listener.dispose(entry);
|
||||
for (let ii = 0; ii < listeners.length; ii++) {
|
||||
const listener = listeners[ii];
|
||||
if (listener.dispose) listener.dispose(entry);
|
||||
@ -1166,20 +1172,21 @@ export class EventQueue {
|
||||
this.animState.trackEntryPool.free(entry);
|
||||
break;
|
||||
case EventType.complete:
|
||||
if (entry.listener && entry.listener.complete) entry.listener.complete(entry);
|
||||
if (entry.listener?.complete) entry.listener.complete(entry);
|
||||
for (let ii = 0; ii < listeners.length; ii++) {
|
||||
const listener = listeners[ii];
|
||||
if (listener.complete) listener.complete(entry);
|
||||
}
|
||||
break;
|
||||
case EventType.event:
|
||||
case EventType.event: {
|
||||
const event = objects[i++ + 2] as Event;
|
||||
if (entry.listener && entry.listener.event) entry.listener.event(entry, event);
|
||||
if (entry.listener?.event) entry.listener.event(entry, event);
|
||||
for (let ii = 0; ii < listeners.length; ii++) {
|
||||
const listener = listeners[ii];
|
||||
if (listener.event) listener.event(entry, event);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
this.clear();
|
||||
|
||||
@ -27,9 +27,9 @@
|
||||
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*****************************************************************************/
|
||||
|
||||
import { Animation } from "./Animation.js";
|
||||
import { SkeletonData } from "./SkeletonData.js";
|
||||
import { StringMap } from "./Utils.js";
|
||||
import type { Animation } from "./Animation.js";
|
||||
import type { SkeletonData } from "./SkeletonData.js";
|
||||
import type { StringMap } from "./Utils.js";
|
||||
|
||||
|
||||
/** Stores mix (crossfade) durations to be applied when {@link AnimationState} animations are changed. */
|
||||
@ -50,12 +50,12 @@ export class AnimationStateData {
|
||||
/** Sets a mix duration by animation name.
|
||||
*
|
||||
* See {@link #setMix()}. */
|
||||
setMix (fromName: string, to: string, duration: number): any;
|
||||
setMix (fromName: string, to: string, duration: number): void;
|
||||
|
||||
/** Sets the mix duration when changing from the specified animation to the other.
|
||||
*
|
||||
* See {@link TrackEntry#mixDuration}. */
|
||||
setMix (from: Animation, to: Animation, duration: number): any;
|
||||
setMix (from: Animation, to: Animation, duration: number): void;
|
||||
|
||||
setMix (from: string | Animation, to: string | Animation, duration: number) {
|
||||
if (typeof from === "string")
|
||||
@ -64,25 +64,25 @@ export class AnimationStateData {
|
||||
}
|
||||
|
||||
private setMix1 (fromName: string, toName: string, duration: number) {
|
||||
let from = this.skeletonData.findAnimation(fromName);
|
||||
if (!from) throw new Error("Animation not found: " + fromName);
|
||||
let to = this.skeletonData.findAnimation(toName);
|
||||
if (!to) throw new Error("Animation not found: " + toName);
|
||||
const from = this.skeletonData.findAnimation(fromName);
|
||||
if (!from) throw new Error(`Animation not found: ${fromName}`);
|
||||
const to = this.skeletonData.findAnimation(toName);
|
||||
if (!to) throw new Error(`Animation not found: ${toName}`);
|
||||
this.setMix2(from, to, duration);
|
||||
}
|
||||
|
||||
private setMix2 (from: Animation, to: Animation, duration: number) {
|
||||
if (!from) throw new Error("from cannot be null.");
|
||||
if (!to) throw new Error("to cannot be null.");
|
||||
let key = from.name + "." + to.name;
|
||||
const key = `${from.name}.${to.name}`;
|
||||
this.animationToMixTime[key] = duration;
|
||||
}
|
||||
|
||||
/** Returns the mix duration to use when changing from the specified animation to the other, or the {@link #defaultMix} if
|
||||
* no mix duration has been set. */
|
||||
getMix (from: Animation, to: Animation) {
|
||||
let key = from.name + "." + to.name;
|
||||
let value = this.animationToMixTime[key];
|
||||
const key = `${from.name}.${to.name}`;
|
||||
const value = this.animationToMixTime[key];
|
||||
return value === undefined ? this.defaultMix : value;
|
||||
}
|
||||
}
|
||||
|
||||
@ -27,9 +27,13 @@
|
||||
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*****************************************************************************/
|
||||
|
||||
import { Texture } from "./Texture.js";
|
||||
import type { Texture } from "./Texture.js";
|
||||
import { TextureAtlas } from "./TextureAtlas.js";
|
||||
import { Disposable, StringMap } from "./Utils.js";
|
||||
import type { Disposable, StringMap } from "./Utils.js";
|
||||
|
||||
type AssetData = (Uint8Array | string | Texture | TextureAtlas | object) & Partial<Disposable>;
|
||||
type AssetCallback<T extends AssetData> = (path: string, data: T) => void;
|
||||
type ErrorCallback = (path: string, message: string) => void;
|
||||
|
||||
export class AssetManagerBase implements Disposable {
|
||||
private pathPrefix: string = "";
|
||||
@ -52,7 +56,7 @@ export class AssetManagerBase implements Disposable {
|
||||
return this.pathPrefix + path;
|
||||
}
|
||||
|
||||
private success (callback: (path: string, data: any) => void, path: string, asset: any) {
|
||||
private success<T extends AssetData> (callback: AssetCallback<T>, path: string, asset: T) {
|
||||
this.toLoad--;
|
||||
this.loaded++;
|
||||
this.cache.assets[path] = asset;
|
||||
@ -68,8 +72,8 @@ export class AssetManagerBase implements Disposable {
|
||||
}
|
||||
|
||||
loadAll () {
|
||||
let promise = new Promise((resolve: (assetManager: AssetManagerBase) => void, reject: (errors: StringMap<string>) => void) => {
|
||||
let check = () => {
|
||||
const promise = new Promise((resolve: (assetManager: AssetManagerBase) => void, reject: (errors: StringMap<string>) => void) => {
|
||||
const check = () => {
|
||||
if (this.isLoadingComplete()) {
|
||||
if (this.hasErrors()) reject(this.errors);
|
||||
else resolve(this);
|
||||
@ -93,7 +97,7 @@ export class AssetManagerBase implements Disposable {
|
||||
|
||||
if (this.reuseAssets(path, success, error)) return;
|
||||
|
||||
this.cache.assetsLoaded[path] = new Promise<any>((resolve, reject) => {
|
||||
this.cache.assetsLoaded[path] = new Promise<Uint8Array>((resolve, reject) => {
|
||||
this.downloader.downloadBinary(path, (data: Uint8Array): void => {
|
||||
this.success(success, path, data);
|
||||
resolve(data);
|
||||
@ -124,7 +128,7 @@ export class AssetManagerBase implements Disposable {
|
||||
|
||||
if (this.reuseAssets(path, success, error)) return;
|
||||
|
||||
this.cache.assetsLoaded[path] = new Promise<any>((resolve, reject) => {
|
||||
this.cache.assetsLoaded[path] = new Promise<object>((resolve, reject) => {
|
||||
this.downloader.downloadJson(path, (data: object): void => {
|
||||
this.success(success, path, data);
|
||||
resolve(data);
|
||||
@ -136,36 +140,44 @@ export class AssetManagerBase implements Disposable {
|
||||
});
|
||||
}
|
||||
|
||||
reuseAssets (path: string,
|
||||
success: (path: string, data: any) => void = () => { },
|
||||
error: (path: string, message: string) => void = () => { }) {
|
||||
const loadedStatus = this.cache.assetsLoaded[path];
|
||||
|
||||
reuseAssets<T extends AssetData> (
|
||||
path: string,
|
||||
success: AssetCallback<T> = () => { },
|
||||
error: ErrorCallback = () => { }
|
||||
) {
|
||||
const loadedStatus = this.cache.getAsset(path);
|
||||
const alreadyExistsOrLoading = loadedStatus !== undefined;
|
||||
if (alreadyExistsOrLoading) {
|
||||
this.cache.assetsLoaded[path] = loadedStatus
|
||||
.then(data => {
|
||||
// necessary when user preloads an image into the cache.
|
||||
// texture loader is not avaiable in the cache, so we transform in GLTexture at first use
|
||||
data = (data instanceof Image || data instanceof ImageBitmap) ? this.textureLoader(data) : data;
|
||||
this.success(success, path, data);
|
||||
data = (data instanceof Image || data instanceof ImageBitmap) ? this.textureLoader(data) as T : data;
|
||||
this.success(success, path, data as T);
|
||||
return data;
|
||||
})
|
||||
.catch(errorMsg => this.error(error, path, errorMsg));
|
||||
.catch(errorMsg => {
|
||||
this.error(error, path, errorMsg);
|
||||
return undefined;
|
||||
});
|
||||
}
|
||||
return alreadyExistsOrLoading;
|
||||
}
|
||||
|
||||
loadTexture (path: string,
|
||||
success: (path: string, texture: Texture) => void = () => { },
|
||||
error: (path: string, message: string) => void = () => { }) {
|
||||
loadTexture (
|
||||
path: string,
|
||||
success: AssetCallback<Texture> = () => { },
|
||||
error: ErrorCallback = () => { }
|
||||
) {
|
||||
|
||||
path = this.start(path);
|
||||
|
||||
if (this.reuseAssets(path, success, error)) return;
|
||||
|
||||
this.cache.assetsLoaded[path] = new Promise<any>((resolve, reject) => {
|
||||
let isBrowser = !!(typeof window !== 'undefined' && typeof navigator !== 'undefined' && window.document);
|
||||
let isWebWorker = !isBrowser; // && typeof importScripts !== 'undefined';
|
||||
this.cache.assetsLoaded[path] = new Promise<Texture>((resolve, reject) => {
|
||||
const isBrowser = !!(typeof window !== 'undefined' && typeof navigator !== 'undefined' && window.document);
|
||||
const isWebWorker = !isBrowser; // && typeof importScripts !== 'undefined';
|
||||
if (isWebWorker) {
|
||||
fetch(path, { mode: <RequestMode>"cors" }).then((response) => {
|
||||
if (response.ok) return response.blob();
|
||||
@ -182,7 +194,7 @@ export class AssetManagerBase implements Disposable {
|
||||
};
|
||||
});
|
||||
} else {
|
||||
let image = new Image();
|
||||
const image = new Image();
|
||||
image.crossOrigin = "anonymous";
|
||||
image.onload = () => {
|
||||
const texture = this.createTexture(path, image);
|
||||
@ -200,28 +212,29 @@ export class AssetManagerBase implements Disposable {
|
||||
});
|
||||
}
|
||||
|
||||
loadTextureAtlas (path: string,
|
||||
success: (path: string, atlas: TextureAtlas) => void = () => { },
|
||||
error: (path: string, message: string) => void = () => { },
|
||||
loadTextureAtlas (
|
||||
path: string,
|
||||
success: AssetCallback<TextureAtlas> = () => { },
|
||||
error: ErrorCallback = () => { },
|
||||
fileAlias?: { [keyword: string]: string }
|
||||
) {
|
||||
let index = path.lastIndexOf("/");
|
||||
let parent = index >= 0 ? path.substring(0, index + 1) : "";
|
||||
const index = path.lastIndexOf("/");
|
||||
const parent = index >= 0 ? path.substring(0, index + 1) : "";
|
||||
path = this.start(path);
|
||||
|
||||
if (this.reuseAssets(path, success, error)) return;
|
||||
|
||||
this.cache.assetsLoaded[path] = new Promise<any>((resolve, reject) => {
|
||||
this.cache.assetsLoaded[path] = new Promise<TextureAtlas>((resolve, reject) => {
|
||||
this.downloader.downloadText(path, (atlasText: string): void => {
|
||||
try {
|
||||
const atlas = this.createTextureAtlas(path, atlasText);
|
||||
let toLoad = atlas.pages.length, abort = false;
|
||||
for (let page of atlas.pages) {
|
||||
this.loadTexture(!fileAlias ? parent + page.name : fileAlias[page.name!],
|
||||
for (const page of atlas.pages) {
|
||||
this.loadTexture(!fileAlias ? parent + page.name : fileAlias[page.name],
|
||||
(imagePath: string, texture: Texture) => {
|
||||
if (!abort) {
|
||||
page.setTexture(texture);
|
||||
if (--toLoad == 0) {
|
||||
if (--toLoad === 0) {
|
||||
this.success(success, path, atlas);
|
||||
resolve(atlas);
|
||||
}
|
||||
@ -237,8 +250,8 @@ export class AssetManagerBase implements Disposable {
|
||||
}
|
||||
);
|
||||
}
|
||||
} catch (e) {
|
||||
const errorMsg = `Couldn't parse texture atlas ${path}: ${(e as any).message}`;
|
||||
} catch (e: unknown) {
|
||||
const errorMsg = `Couldn't parse texture atlas ${path}: ${(e as Error).message}`;
|
||||
this.error(error, path, errorMsg);
|
||||
reject(errorMsg);
|
||||
}
|
||||
@ -250,23 +263,23 @@ export class AssetManagerBase implements Disposable {
|
||||
});
|
||||
}
|
||||
|
||||
loadTextureAtlasButNoTextures (path: string,
|
||||
success: (path: string, atlas: TextureAtlas) => void = () => { },
|
||||
error: (path: string, message: string) => void = () => { },
|
||||
fileAlias?: { [keyword: string]: string }
|
||||
loadTextureAtlasButNoTextures (
|
||||
path: string,
|
||||
success: AssetCallback<TextureAtlas> = () => { },
|
||||
error: ErrorCallback = () => { },
|
||||
) {
|
||||
path = this.start(path);
|
||||
|
||||
if (this.reuseAssets(path, success, error)) return;
|
||||
|
||||
this.cache.assetsLoaded[path] = new Promise<any>((resolve, reject) => {
|
||||
this.cache.assetsLoaded[path] = new Promise<TextureAtlas>((resolve, reject) => {
|
||||
this.downloader.downloadText(path, (atlasText: string): void => {
|
||||
try {
|
||||
const atlas = this.createTextureAtlas(path, atlasText);
|
||||
this.success(success, path, atlas);
|
||||
resolve(atlas);
|
||||
} catch (e) {
|
||||
const errorMsg = `Couldn't parse texture atlas ${path}: ${(e as any).message}`;
|
||||
const errorMsg = `Couldn't parse texture atlas ${path}: ${(e as Error).message}`;
|
||||
this.error(error, path, errorMsg);
|
||||
reject(errorMsg);
|
||||
}
|
||||
@ -334,15 +347,15 @@ export class AssetManagerBase implements Disposable {
|
||||
|
||||
require (path: string) {
|
||||
path = this.pathPrefix + path;
|
||||
let asset = this.cache.assets[path];
|
||||
const asset = this.cache.assets[path];
|
||||
if (asset) return asset;
|
||||
let error = this.errors[path];
|
||||
throw Error("Asset not found: " + path + (error ? "\n" + error : ""));
|
||||
const error = this.errors[path];
|
||||
throw Error(`Asset not found: ${path}${error ? `\n${error}` : ""}`);
|
||||
}
|
||||
|
||||
remove (path: string) {
|
||||
path = this.pathPrefix + path;
|
||||
let asset = this.cache.assets[path];
|
||||
const asset = this.cache.assets[path];
|
||||
if (asset.dispose) asset.dispose();
|
||||
delete this.cache.assets[path];
|
||||
delete this.cache.assetsRefCount[path];
|
||||
@ -351,8 +364,8 @@ export class AssetManagerBase implements Disposable {
|
||||
}
|
||||
|
||||
removeAll () {
|
||||
for (let path in this.cache.assets) {
|
||||
let asset = this.cache.assets[path];
|
||||
for (const path in this.cache.assets) {
|
||||
const asset = this.cache.assets[path];
|
||||
if (asset.dispose) asset.dispose();
|
||||
}
|
||||
this.cache.assets = {};
|
||||
@ -361,7 +374,7 @@ export class AssetManagerBase implements Disposable {
|
||||
}
|
||||
|
||||
isLoadingComplete (): boolean {
|
||||
return this.toLoad == 0;
|
||||
return this.toLoad === 0;
|
||||
}
|
||||
|
||||
getToLoad (): number {
|
||||
@ -423,9 +436,9 @@ export class AssetManagerBase implements Disposable {
|
||||
}
|
||||
|
||||
export class AssetCache {
|
||||
public assets: StringMap<any> = {};
|
||||
public assets: StringMap<AssetData> = {};
|
||||
public assetsRefCount: StringMap<number> = {};
|
||||
public assetsLoaded: StringMap<Promise<any>> = {};
|
||||
public assetsLoaded: StringMap<Promise<AssetData | undefined>> = {};
|
||||
|
||||
static AVAILABLE_CACHES = new Map<string, AssetCache>();
|
||||
static getCache (id: string) {
|
||||
@ -437,14 +450,22 @@ export class AssetCache {
|
||||
return newCache;
|
||||
}
|
||||
|
||||
async addAsset (path: string, asset: any) {
|
||||
async addAsset<T extends AssetData> (path: string, asset: T): Promise<T> {
|
||||
this.assetsLoaded[path] = Promise.resolve(asset);
|
||||
this.assets[path] = await asset;
|
||||
this.assets[path] = asset;
|
||||
return asset;
|
||||
}
|
||||
|
||||
getAsset<T extends AssetData> (path: string): Promise<T> | undefined {
|
||||
return this.assetsLoaded[path] as Promise<T> | undefined;
|
||||
}
|
||||
}
|
||||
|
||||
type DownloaderSuccessCallback<T extends AssetData = AssetData> = (data: T) => void;
|
||||
type DownloaderErrorCallback = (status: number, responseText: string) => void;
|
||||
|
||||
export class Downloader {
|
||||
private callbacks: StringMap<Array<Function>> = {};
|
||||
private callbacks: StringMap<Array<DownloaderSuccessCallback | DownloaderErrorCallback>> = {};
|
||||
rawDataUris: StringMap<string> = {};
|
||||
|
||||
dataUriToString (dataUri: string) {
|
||||
@ -453,7 +474,7 @@ export class Downloader {
|
||||
}
|
||||
|
||||
let base64Idx = dataUri.indexOf("base64,");
|
||||
if (base64Idx != -1) {
|
||||
if (base64Idx !== -1) {
|
||||
base64Idx += "base64,".length;
|
||||
return atob(dataUri.substr(base64Idx));
|
||||
} else {
|
||||
@ -465,7 +486,7 @@ export class Downloader {
|
||||
var binary_string = window.atob(base64);
|
||||
var len = binary_string.length;
|
||||
var bytes = new Uint8Array(len);
|
||||
for (var i = 0; i < len; i++) {
|
||||
for (let i = 0; i < len; i++) {
|
||||
bytes[i] = binary_string.charCodeAt(i);
|
||||
}
|
||||
return bytes;
|
||||
@ -477,12 +498,12 @@ export class Downloader {
|
||||
}
|
||||
|
||||
let base64Idx = dataUri.indexOf("base64,");
|
||||
if (base64Idx == -1) throw new Error("Not a binary data URI.");
|
||||
if (base64Idx === -1) throw new Error("Not a binary data URI.");
|
||||
base64Idx += "base64,".length;
|
||||
return this.base64ToUint8Array(dataUri.substr(base64Idx));
|
||||
}
|
||||
|
||||
downloadText (url: string, success: (data: string) => void, error: (status: number, responseText: string) => void) {
|
||||
downloadText (url: string, success: DownloaderSuccessCallback<string>, error: DownloaderErrorCallback) {
|
||||
if (this.start(url, success, error)) return;
|
||||
|
||||
const rawDataUri = this.rawDataUris[url];
|
||||
@ -496,10 +517,10 @@ export class Downloader {
|
||||
return;
|
||||
}
|
||||
|
||||
let request = new XMLHttpRequest();
|
||||
const request = new XMLHttpRequest();
|
||||
request.overrideMimeType("text/html");
|
||||
request.open("GET", rawDataUri ? rawDataUri : url, true);
|
||||
let done = () => {
|
||||
const done = () => {
|
||||
this.finish(url, request.status, request.responseText);
|
||||
};
|
||||
request.onload = done;
|
||||
@ -507,13 +528,13 @@ export class Downloader {
|
||||
request.send();
|
||||
}
|
||||
|
||||
downloadJson (url: string, success: (data: object) => void, error: (status: number, responseText: string) => void) {
|
||||
downloadJson (url: string, success: DownloaderSuccessCallback<object>, error: DownloaderErrorCallback) {
|
||||
this.downloadText(url, (data: string): void => {
|
||||
success(JSON.parse(data));
|
||||
}, error);
|
||||
}
|
||||
|
||||
downloadBinary (url: string, success: (data: Uint8Array) => void, error: (status: number, responseText: string) => void) {
|
||||
downloadBinary (url: string, success: (data: Uint8Array) => void, error: DownloaderErrorCallback) {
|
||||
if (this.start(url, success, error)) return;
|
||||
|
||||
const rawDataUri = this.rawDataUris[url];
|
||||
@ -527,14 +548,14 @@ export class Downloader {
|
||||
return;
|
||||
}
|
||||
|
||||
let request = new XMLHttpRequest();
|
||||
const request = new XMLHttpRequest();
|
||||
request.open("GET", rawDataUri ? rawDataUri : url, true);
|
||||
request.responseType = "arraybuffer";
|
||||
let onerror = () => {
|
||||
const onerror = () => {
|
||||
this.finish(url, request.status, request.response);
|
||||
};
|
||||
request.onload = () => {
|
||||
if (request.status == 200 || request.status == 0)
|
||||
if (request.status === 200 || request.status === 0)
|
||||
this.finish(url, 200, new Uint8Array(request.response as ArrayBuffer));
|
||||
else
|
||||
onerror();
|
||||
@ -543,21 +564,25 @@ export class Downloader {
|
||||
request.send();
|
||||
}
|
||||
|
||||
private start (url: string, success: any, error: any) {
|
||||
private start<T extends AssetData> (url: string, success: DownloaderSuccessCallback<T>, error: DownloaderErrorCallback) {
|
||||
let callbacks = this.callbacks[url];
|
||||
try {
|
||||
if (callbacks) return true;
|
||||
this.callbacks[url] = callbacks = [];
|
||||
} finally {
|
||||
callbacks.push(success, error);
|
||||
callbacks.push(success as DownloaderSuccessCallback<AssetData>, error);
|
||||
}
|
||||
}
|
||||
|
||||
private finish (url: string, status: number, data: any) {
|
||||
let callbacks = this.callbacks[url];
|
||||
private finish (url: string, status: number, data: AssetData) {
|
||||
const callbacks = this.callbacks[url];
|
||||
delete this.callbacks[url];
|
||||
let args = status == 200 || status == 0 ? [data] : [status, data];
|
||||
for (let i = args.length - 1, n = callbacks.length; i < n; i += 2)
|
||||
callbacks[i].apply(null, args);
|
||||
if (status === 200 || status === 0) {
|
||||
for (let i = 0, n = callbacks.length; i < n; i += 2)
|
||||
(callbacks[i] as DownloaderSuccessCallback)(data);
|
||||
} else {
|
||||
for (let i = 1, n = callbacks.length; i < n; i += 2)
|
||||
(callbacks[i] as DownloaderErrorCallback)(status, data as string);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -27,8 +27,8 @@
|
||||
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*****************************************************************************/
|
||||
|
||||
import { BoneData } from "./BoneData.js";
|
||||
import { BoneLocal } from "./BoneLocal.js";
|
||||
import type { BoneData } from "./BoneData.js";
|
||||
import type { BoneLocal } from "./BoneLocal.js";
|
||||
import { BonePose } from "./BonePose.js";
|
||||
import { PosedActive } from "./PosedActive.js";
|
||||
|
||||
@ -42,7 +42,7 @@ export class Bone extends PosedActive<BoneData, BoneLocal, BonePose> {
|
||||
parent: Bone | null = null;
|
||||
|
||||
/** The immediate children of this bone. */
|
||||
children = new Array<Bone>();
|
||||
children = [] as Bone[];
|
||||
|
||||
sorted = false;
|
||||
|
||||
|
||||
@ -29,9 +29,8 @@
|
||||
|
||||
import { BoneLocal } from "./BoneLocal.js";
|
||||
import { PosedData } from "./PosedData.js";
|
||||
import { Color } from "./Utils.js";
|
||||
|
||||
import type { Skeleton } from "./Skeleton.js";
|
||||
import { Color } from "./Utils.js";
|
||||
|
||||
/** The setup pose for a bone. */
|
||||
export class BoneData extends PosedData<BoneLocal> {
|
||||
|
||||
@ -28,7 +28,7 @@
|
||||
*****************************************************************************/
|
||||
|
||||
import { Inherit } from "./BoneData.js";
|
||||
import { Pose } from "./Pose.js"
|
||||
import type { Pose } from "./Pose.js"
|
||||
|
||||
/** Stores a bone's local pose. */
|
||||
export class BoneLocal implements Pose<BoneLocal> {
|
||||
|
||||
@ -27,13 +27,13 @@
|
||||
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*****************************************************************************/
|
||||
|
||||
import { Bone } from "./Bone.js";
|
||||
import type { Bone } from "./Bone.js";
|
||||
import { Inherit } from "./BoneData.js";
|
||||
import { BoneLocal } from "./BoneLocal.js";
|
||||
import { Physics } from "./Physics.js";
|
||||
import { Skeleton } from "./Skeleton.js";
|
||||
import { Update } from "./Update.js";
|
||||
import { MathUtils, Vector2 } from "./Utils.js";
|
||||
import type { Physics } from "./Physics.js";
|
||||
import type { Skeleton } from "./Skeleton.js";
|
||||
import type { Update } from "./Update.js";
|
||||
import { MathUtils, type Vector2 } from "./Utils.js";
|
||||
|
||||
/** The applied pose for a bone. This is the {@link Bone} pose with constraints applied and the world transform computed by
|
||||
* {@link Skeleton#updateWorldTransform()}. */
|
||||
@ -76,7 +76,7 @@ export class BonePose extends BoneLocal implements Update {
|
||||
else
|
||||
this.world = skeleton._update;
|
||||
|
||||
let rotation = this.rotation;
|
||||
const rotation = this.rotation;
|
||||
const scaleX = this.scaleX;
|
||||
const scaleY = this.scaleY;
|
||||
const shearX = this.shearX;
|
||||
@ -123,7 +123,7 @@ export class BonePose extends BoneLocal implements Update {
|
||||
break;
|
||||
}
|
||||
case Inherit.NoRotationOrReflection: {
|
||||
let sx = 1 / skeleton.scaleX, sy = 1 / skeleton.scaleY;
|
||||
const sx = 1 / skeleton.scaleX, sy = 1 / skeleton.scaleY;
|
||||
pa *= sx;
|
||||
pc *= sy;
|
||||
let s = pa * pa + pc * pc;
|
||||
@ -160,7 +160,7 @@ export class BonePose extends BoneLocal implements Update {
|
||||
za *= s;
|
||||
zc *= s;
|
||||
s = Math.sqrt(za * za + zc * zc);
|
||||
if (this.inherit == Inherit.NoScale && (pa * pd - pb * pc < 0) != (skeleton.scaleX < 0 != skeleton.scaleY < 0)) s = -s;
|
||||
if (this.inherit === Inherit.NoScale && (pa * pd - pb * pc < 0) !== (skeleton.scaleX < 0 !== skeleton.scaleY < 0)) s = -s;
|
||||
r = Math.PI / 2 + Math.atan2(zc, za);
|
||||
const zb = Math.cos(r) * s;
|
||||
const zd = Math.sin(r) * s;
|
||||
@ -198,7 +198,7 @@ export class BonePose extends BoneLocal implements Update {
|
||||
if (!this.bone.parent) {
|
||||
this.x = this.worldX - skeleton.x;
|
||||
this.y = this.worldY - skeleton.y;
|
||||
let a = this.a, b = this.b, c = this.c, d = this.d;
|
||||
const a = this.a, b = this.b, c = this.c, d = this.d;
|
||||
this.rotation = MathUtils.atan2Deg(c, a);
|
||||
this.scaleX = Math.sqrt(a * a + c * c);
|
||||
this.scaleY = Math.sqrt(b * b + d * d);
|
||||
@ -211,12 +211,12 @@ export class BonePose extends BoneLocal implements Update {
|
||||
let pa = parent.a, pb = parent.b, pc = parent.c, pd = parent.d;
|
||||
let pid = 1 / (pa * pd - pb * pc);
|
||||
let ia = pd * pid, ib = pb * pid, ic = pc * pid, id = pa * pid;
|
||||
let dx = this.worldX - parent.worldX, dy = this.worldY - parent.worldY;
|
||||
const dx = this.worldX - parent.worldX, dy = this.worldY - parent.worldY;
|
||||
this.x = (dx * ia - dy * ib);
|
||||
this.y = (dy * id - dx * ic);
|
||||
|
||||
let ra, rb, rc, rd;
|
||||
if (this.inherit == Inherit.OnlyTranslation) {
|
||||
let ra: number, rb: number, rc: number, rd: number;
|
||||
if (this.inherit === Inherit.OnlyTranslation) {
|
||||
ra = this.a;
|
||||
rb = this.b;
|
||||
rc = this.c;
|
||||
@ -224,7 +224,7 @@ export class BonePose extends BoneLocal implements Update {
|
||||
} else {
|
||||
switch (this.inherit) {
|
||||
case Inherit.NoRotationOrReflection: {
|
||||
let s = Math.abs(pa * pd - pb * pc) / (pa * pa + pc * pc);
|
||||
const s = Math.abs(pa * pd - pb * pc) / (pa * pa + pc * pc);
|
||||
pb = -pc * skeleton.scaleX * s / skeleton.scaleY;
|
||||
pd = pa * skeleton.scaleY * s / skeleton.scaleX;
|
||||
pid = 1 / (pa * pd - pb * pc);
|
||||
@ -233,7 +233,7 @@ export class BonePose extends BoneLocal implements Update {
|
||||
break;
|
||||
}
|
||||
case Inherit.NoScale:
|
||||
case Inherit.NoScaleOrReflection:
|
||||
case Inherit.NoScaleOrReflection: {
|
||||
let r = this.rotation * MathUtils.degRad, cos = Math.cos(r), sin = Math.sin(r);
|
||||
pa = (pa * cos + pb * sin) / skeleton.scaleX;
|
||||
pc = (pc * cos + pd * sin) / skeleton.scaleY;
|
||||
@ -242,7 +242,7 @@ export class BonePose extends BoneLocal implements Update {
|
||||
pa *= s;
|
||||
pc *= s;
|
||||
s = Math.sqrt(pa * pa + pc * pc);
|
||||
if (this.inherit == Inherit.NoScale && pid < 0 != (skeleton.scaleX < 0 != skeleton.scaleY < 0)) s = -s;
|
||||
if (this.inherit === Inherit.NoScale && pid < 0 !== (skeleton.scaleX < 0 !== skeleton.scaleY < 0)) s = -s;
|
||||
r = MathUtils.PI / 2 + Math.atan2(pc, pa);
|
||||
pb = Math.cos(r) * s;
|
||||
pd = Math.sin(r) * s;
|
||||
@ -251,6 +251,7 @@ export class BonePose extends BoneLocal implements Update {
|
||||
ib = pb * pid;
|
||||
ic = pc * pid;
|
||||
id = pa * pid;
|
||||
}
|
||||
}
|
||||
ra = ia * this.a - ib * this.c;
|
||||
rb = ia * this.b - ib * this.d;
|
||||
@ -261,7 +262,7 @@ export class BonePose extends BoneLocal implements Update {
|
||||
this.shearX = 0;
|
||||
this.scaleX = Math.sqrt(ra * ra + rc * rc);
|
||||
if (this.scaleX > 0.0001) {
|
||||
let det = ra * rd - rb * rc;
|
||||
const det = ra * rd - rb * rc;
|
||||
this.scaleY = det / this.scaleX;
|
||||
this.shearY = -MathUtils.atan2Deg(ra * rb + rc * rd, det);
|
||||
this.rotation = MathUtils.atan2Deg(rc, ra);
|
||||
@ -341,8 +342,8 @@ export class BonePose extends BoneLocal implements Update {
|
||||
/** Transforms a point from world coordinates to the bone's local coordinates. */
|
||||
public worldToLocal (world: Vector2): Vector2 {
|
||||
if (world == null) throw new Error("world cannot be null.");
|
||||
let det = this.a * this.d - this.b * this.c;
|
||||
let x = world.x - this.worldX, y = world.y - this.worldY;
|
||||
const det = this.a * this.d - this.b * this.c;
|
||||
const x = world.x - this.worldX, y = world.y - this.worldY;
|
||||
world.x = (x * this.d - y * this.b) / det;
|
||||
world.y = (y * this.a - x * this.c) / det;
|
||||
return world;
|
||||
@ -351,7 +352,7 @@ export class BonePose extends BoneLocal implements Update {
|
||||
/** Transforms a point from the bone's local coordinates to world coordinates. */
|
||||
public localToWorld (local: Vector2): Vector2 {
|
||||
if (local == null) throw new Error("local cannot be null.");
|
||||
let x = local.x, y = local.y;
|
||||
const x = local.x, y = local.y;
|
||||
local.x = x * this.a + y * this.b + this.worldX;
|
||||
local.y = x * this.c + y * this.d + this.worldY;
|
||||
return local;
|
||||
@ -372,14 +373,14 @@ export class BonePose extends BoneLocal implements Update {
|
||||
/** Transforms a world rotation to a local rotation. */
|
||||
public worldToLocalRotation (worldRotation: number): number {
|
||||
worldRotation *= MathUtils.degRad;
|
||||
let sin = Math.sin(worldRotation), cos = Math.cos(worldRotation);
|
||||
const sin = Math.sin(worldRotation), cos = Math.cos(worldRotation);
|
||||
return MathUtils.atan2Deg(this.a * sin - this.c * cos, this.d * cos - this.b * sin) + this.rotation - this.shearX;
|
||||
}
|
||||
|
||||
/** Transforms a local rotation to a world rotation. */
|
||||
localToWorldRotation (localRotation: number): number {
|
||||
localRotation = (localRotation - this.rotation - this.shearX) * MathUtils.degRad;
|
||||
let sin = Math.sin(localRotation), cos = Math.cos(localRotation);
|
||||
const sin = Math.sin(localRotation), cos = Math.cos(localRotation);
|
||||
return MathUtils.atan2Deg(cos * this.c + sin * this.d, cos * this.a + sin * this.b);
|
||||
}
|
||||
|
||||
|
||||
@ -27,17 +27,17 @@
|
||||
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*****************************************************************************/
|
||||
|
||||
import { ConstraintData } from "./ConstraintData.js";
|
||||
import { Physics } from "./Physics.js";
|
||||
import { Pose } from "./Pose.js";
|
||||
import type { ConstraintData } from "./ConstraintData.js";
|
||||
import type { Physics } from "./Physics.js";
|
||||
import type { Pose } from "./Pose.js";
|
||||
import { PosedActive } from "./PosedActive.js";
|
||||
import { Skeleton } from "./Skeleton.js";
|
||||
import { Update } from "./Update.js";
|
||||
import type { Skeleton } from "./Skeleton.js";
|
||||
import type { Update } from "./Update.js";
|
||||
|
||||
export abstract class Constraint<
|
||||
T extends Constraint<T, D, P>,
|
||||
D extends ConstraintData<T, P>,
|
||||
P extends Pose<any>>
|
||||
P extends Pose<P>>
|
||||
extends PosedActive<D, P, P> implements Update {
|
||||
|
||||
constructor (data: D, pose: P, constrained: P) {
|
||||
|
||||
@ -27,15 +27,15 @@
|
||||
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*****************************************************************************/
|
||||
|
||||
import { Constraint } from "./Constraint.js";
|
||||
import { Pose } from "./Pose.js";
|
||||
import type { Constraint } from "./Constraint.js";
|
||||
import type { Pose } from "./Pose.js";
|
||||
import { PosedData } from "./PosedData.js";
|
||||
import { Skeleton } from "./Skeleton.js";
|
||||
import type { Skeleton } from "./Skeleton.js";
|
||||
|
||||
/** The base class for all constraint datas. */
|
||||
export abstract class ConstraintData<
|
||||
T extends Constraint<any, any, any>,
|
||||
P extends Pose<any>>
|
||||
T extends Constraint<T, ConstraintData<T, P>, P>,
|
||||
P extends Pose<P>>
|
||||
extends PosedData<P> {
|
||||
|
||||
constructor (name: string, setup: P) {
|
||||
|
||||
@ -27,7 +27,7 @@
|
||||
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*****************************************************************************/
|
||||
|
||||
import { EventData } from "./EventData.js";
|
||||
import type { EventData } from "./EventData.js";
|
||||
|
||||
import type { Timeline } from "./Animation.js";
|
||||
import type { AnimationStateListener } from "./AnimationState.js";
|
||||
|
||||
@ -27,14 +27,14 @@
|
||||
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*****************************************************************************/
|
||||
|
||||
import { Bone } from "./Bone.js";
|
||||
import type { Bone } from "./Bone.js";
|
||||
import { Inherit } from "./BoneData.js";
|
||||
import { BonePose } from "./BonePose.js";
|
||||
import type { BonePose } from "./BonePose.js";
|
||||
import { Constraint } from "./Constraint.js";
|
||||
import { IkConstraintData } from "./IkConstraintData.js";
|
||||
import type { IkConstraintData } from "./IkConstraintData.js";
|
||||
import { IkConstraintPose } from "./IkConstraintPose.js";
|
||||
import { Physics } from "./Physics.js";
|
||||
import { Skeleton } from "./Skeleton.js";
|
||||
import type { Physics } from "./Physics.js";
|
||||
import type { Skeleton } from "./Skeleton.js";
|
||||
import { MathUtils } from "./Utils.js";
|
||||
|
||||
/** Stores the current pose for an IK constraint. An IK constraint adjusts the rotation of 1 or 2 constrained bones so the tip of
|
||||
@ -52,7 +52,7 @@ export class IkConstraint extends Constraint<IkConstraint, IkConstraintData, IkC
|
||||
super(data, new IkConstraintPose(), new IkConstraintPose());
|
||||
if (!skeleton) throw new Error("skeleton cannot be null.");
|
||||
|
||||
this.bones = new Array<BonePose>();
|
||||
this.bones = [] as BonePose[];
|
||||
for (const boneData of data.bones)
|
||||
this.bones.push(skeleton.bones[boneData.index].constrained);
|
||||
|
||||
@ -107,16 +107,17 @@ export class IkConstraint extends Constraint<IkConstraint, IkConstraintData, IkC
|
||||
stretchOrBendDir: boolean | number, uniformOrStretch: boolean, mixOrUniform: number | boolean, softness?: number, mix?: number) {
|
||||
|
||||
if (typeof targetXorChild === "number")
|
||||
this.apply1(skeleton, boneOrParent, targetXorChild, targetYOrTargetX, compressOrTargetY as boolean, stretchOrBendDir as boolean, uniformOrStretch, mixOrUniform as number);
|
||||
IkConstraint.apply1(skeleton, boneOrParent, targetXorChild, targetYOrTargetX, compressOrTargetY as boolean, stretchOrBendDir as boolean, uniformOrStretch, mixOrUniform as number);
|
||||
else
|
||||
this.apply2(skeleton, boneOrParent, targetXorChild as BonePose, targetYOrTargetX, compressOrTargetY as number, stretchOrBendDir as number,
|
||||
IkConstraint.apply2(skeleton, boneOrParent, targetXorChild as BonePose, targetYOrTargetX, compressOrTargetY as number, stretchOrBendDir as number,
|
||||
uniformOrStretch, mixOrUniform as boolean, softness as number, mix as number);
|
||||
}
|
||||
|
||||
private static apply1 (skeleton: Skeleton, bone: BonePose, targetX: number, targetY: number, compress: boolean, stretch: boolean, uniform: boolean, mix: number) {
|
||||
bone.modifyLocal(skeleton);
|
||||
|
||||
let p = bone.bone.parent!.applied;
|
||||
// biome-ignore lint/style/noNonNullAssertion: reference runtime
|
||||
const p = bone.bone.parent!.applied;
|
||||
|
||||
let pa = p.a, pb = p.b, pc = p.c, pd = p.d;
|
||||
let rotationIK = -bone.shearX - bone.rotation, tx = 0, ty = 0;
|
||||
@ -126,17 +127,19 @@ export class IkConstraint extends Constraint<IkConstraint, IkConstraintData, IkC
|
||||
tx = (targetX - bone.worldX) * MathUtils.signum(skeleton.scaleX);
|
||||
ty = (targetY - bone.worldY) * MathUtils.signum(skeleton.scaleY);
|
||||
break;
|
||||
case Inherit.NoRotationOrReflection:
|
||||
let s = Math.abs(pa * pd - pb * pc) / Math.max(0.0001, pa * pa + pc * pc);
|
||||
let sa = pa / skeleton.scaleX;
|
||||
let sc = pc / skeleton.scaleY;
|
||||
// biome-ignore lint/suspicious/noFallthroughSwitchClause: reference runtime
|
||||
case Inherit.NoRotationOrReflection: {
|
||||
const s = Math.abs(pa * pd - pb * pc) / Math.max(0.0001, pa * pa + pc * pc);
|
||||
const sa = pa / skeleton.scaleX;
|
||||
const sc = pc / skeleton.scaleY;
|
||||
pb = -sc * s * skeleton.scaleX;
|
||||
pd = sa * s * skeleton.scaleY;
|
||||
rotationIK += Math.atan2(sc, sa) * MathUtils.radDeg;
|
||||
}
|
||||
// Fall through
|
||||
default:
|
||||
let x = targetX - p.worldX, y = targetY - p.worldY;
|
||||
let d = pa * pd - pb * pc;
|
||||
default: {
|
||||
const x = targetX - p.worldX, y = targetY - p.worldY;
|
||||
const d = pa * pd - pb * pc;
|
||||
if (Math.abs(d) <= 0.0001) {
|
||||
tx = 0;
|
||||
ty = 0;
|
||||
@ -144,6 +147,7 @@ export class IkConstraint extends Constraint<IkConstraint, IkConstraintData, IkC
|
||||
tx = (x * pd - y * pb) / d - bone.x;
|
||||
ty = (y * pa - x * pc) / d - bone.y;
|
||||
}
|
||||
}
|
||||
}
|
||||
rotationIK += MathUtils.atan2Deg(ty, tx);
|
||||
if (bone.scaleX < 0) rotationIK += 180;
|
||||
@ -174,7 +178,7 @@ export class IkConstraint extends Constraint<IkConstraint, IkConstraintData, IkC
|
||||
/** Applies 2 bone IK. The target is specified in the world coordinate system.
|
||||
* @param child A direct descendant of the parent bone. */
|
||||
private static apply2 (skeleton: Skeleton, parent: BonePose, child: BonePose, targetX: number, targetY: number, bendDir: number, stretch: boolean, uniform: boolean, softness: number, mix: number) {
|
||||
if (parent.inherit != Inherit.Normal || child.inherit != Inherit.Normal) return;
|
||||
if (parent.inherit !== Inherit.Normal || child.inherit !== Inherit.Normal) return;
|
||||
parent.modifyLocal(skeleton);
|
||||
child.modifyLocal(skeleton);
|
||||
let px = parent.x, py = parent.y, psx = parent.scaleX, psy = parent.scaleY, csx = child.scaleX;
|
||||
@ -197,7 +201,7 @@ export class IkConstraint extends Constraint<IkConstraint, IkConstraintData, IkC
|
||||
} else
|
||||
os2 = 0;
|
||||
let cwx = 0, cwy = 0, a = parent.a, b = parent.b, c = parent.c, d = parent.d;
|
||||
let u = Math.abs(psx - psy) <= 0.0001;
|
||||
const u = Math.abs(psx - psy) <= 0.0001;
|
||||
if (!u || stretch) {
|
||||
child.y = 0;
|
||||
cwx = a * child.x + parent.worldX;
|
||||
@ -206,15 +210,16 @@ export class IkConstraint extends Constraint<IkConstraint, IkConstraintData, IkC
|
||||
cwx = a * child.x + b * child.y + parent.worldX;
|
||||
cwy = c * child.x + d * child.y + parent.worldY;
|
||||
}
|
||||
let pp = parent.bone.parent!.applied;
|
||||
// biome-ignore lint/style/noNonNullAssertion: reference-runtime
|
||||
const pp = parent.bone.parent!.applied;
|
||||
a = pp.a;
|
||||
b = pp.b;
|
||||
c = pp.c;
|
||||
d = pp.d;
|
||||
let id = a * d - b * c, x = cwx - pp.worldX, y = cwy - pp.worldY;
|
||||
id = Math.abs(id) <= 0.0001 ? 0 : 1 / id;
|
||||
let dx = (x * d - y * b) * id - px, dy = (y * a - x * c) * id - py;
|
||||
let l1 = Math.sqrt(dx * dx + dy * dy), l2 = child.bone.data.length * csx, a1, a2;
|
||||
const dx = (x * d - y * b) * id - px, dy = (y * a - x * c) * id - py;
|
||||
let l1 = Math.sqrt(dx * dx + dy * dy), l2 = child.bone.data.length * csx, a1: number, a2: number;
|
||||
if (l1 < 0.0001) {
|
||||
IkConstraint.apply(skeleton, parent, targetX, targetY, false, stretch, false, mix);
|
||||
child.rotation = 0;
|
||||
@ -224,9 +229,9 @@ export class IkConstraint extends Constraint<IkConstraint, IkConstraintData, IkC
|
||||
y = targetY - pp.worldY;
|
||||
let tx = (x * d - y * b) * id - px, ty = (y * a - x * c) * id - py;
|
||||
let dd = tx * tx + ty * ty;
|
||||
if (softness != 0) {
|
||||
if (softness !== 0) {
|
||||
softness *= psx * (csx + 1) * 0.5;
|
||||
let td = Math.sqrt(dd), sd = td - l1 - l2 * psx + softness;
|
||||
const td = Math.sqrt(dd), sd = td - l1 - l2 * psx + softness;
|
||||
if (sd > 0) {
|
||||
let p = Math.min(1, sd / (softness * 2)) - 1;
|
||||
p = (sd - softness * (1 - p * p)) / td;
|
||||
@ -235,6 +240,7 @@ export class IkConstraint extends Constraint<IkConstraint, IkConstraintData, IkC
|
||||
dd = tx * tx + ty * ty;
|
||||
}
|
||||
}
|
||||
// biome-ignore lint/suspicious/noConfusingLabels: reference runtime
|
||||
outer:
|
||||
if (u) {
|
||||
l2 *= psx;
|
||||
@ -258,16 +264,16 @@ export class IkConstraint extends Constraint<IkConstraint, IkConstraintData, IkC
|
||||
} else {
|
||||
a = psx * l2;
|
||||
b = psy * l2;
|
||||
let aa = a * a, bb = b * b, ta = Math.atan2(ty, tx);
|
||||
const aa = a * a, bb = b * b, ta = Math.atan2(ty, tx);
|
||||
c = bb * l1 * l1 + aa * dd - aa * bb;
|
||||
let c1 = -2 * bb * l1, c2 = bb - aa;
|
||||
const c1 = -2 * bb * l1, c2 = bb - aa;
|
||||
d = c1 * c1 - 4 * c2 * c;
|
||||
if (d >= 0) {
|
||||
let q = Math.sqrt(d);
|
||||
if (c1 < 0) q = -q;
|
||||
q = -(c1 + q) * 0.5;
|
||||
let r0 = q / c2, r1 = c / q;
|
||||
let r = Math.abs(r0) < Math.abs(r1) ? r0 : r1;
|
||||
const r = Math.abs(r0) < Math.abs(r1) ? r0 : r1;
|
||||
r0 = dd - r * r;
|
||||
if (r0 >= 0) {
|
||||
y = Math.sqrt(r0) * bendDir;
|
||||
@ -305,7 +311,7 @@ export class IkConstraint extends Constraint<IkConstraint, IkConstraintData, IkC
|
||||
a2 = maxAngle * bendDir;
|
||||
}
|
||||
}
|
||||
let os = Math.atan2(child.y, child.x) * s2;
|
||||
const os = Math.atan2(child.y, child.x) * s2;
|
||||
a1 = (a1 - os) * MathUtils.radDeg + os1 - parent.rotation;
|
||||
if (a1 > 180)
|
||||
a1 -= 360;
|
||||
|
||||
@ -27,18 +27,18 @@
|
||||
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*****************************************************************************/
|
||||
|
||||
import { BoneData } from "./BoneData.js";
|
||||
import type { BoneData } from "./BoneData.js";
|
||||
import { ConstraintData } from "./ConstraintData.js";
|
||||
import { IkConstraint } from "./IkConstraint.js";
|
||||
import { IkConstraintPose } from "./IkConstraintPose.js";
|
||||
import { Skeleton } from "./Skeleton.js";
|
||||
import type { Skeleton } from "./Skeleton.js";
|
||||
|
||||
/** Stores the setup pose for an {@link IkConstraint}.
|
||||
*
|
||||
* See [IK constraints](http://esotericsoftware.com/spine-ik-constraints) in the Spine User Guide. */
|
||||
export class IkConstraintData extends ConstraintData<IkConstraint, IkConstraintPose> {
|
||||
/** The bones that are constrained by this IK constraint. */
|
||||
bones = new Array<BoneData>();
|
||||
bones = [] as BoneData[];
|
||||
|
||||
private _target: BoneData | null = null;
|
||||
/** The bone that is the IK target. */
|
||||
|
||||
@ -27,7 +27,7 @@
|
||||
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*****************************************************************************/
|
||||
|
||||
import { Pose } from "./Pose";
|
||||
import type { Pose } from "./Pose";
|
||||
|
||||
/** Stores the current pose for an IK constraint. */
|
||||
export class IkConstraintPose implements Pose<IkConstraintPose> {
|
||||
|
||||
@ -27,18 +27,18 @@
|
||||
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*****************************************************************************/
|
||||
|
||||
import { Attachment } from "./attachments/Attachment.js";
|
||||
import type { Attachment } from "./attachments/Attachment.js";
|
||||
import { PathAttachment } from "./attachments/PathAttachment.js";
|
||||
import { Bone } from "./Bone.js";
|
||||
import { BonePose } from "./BonePose.js";
|
||||
import type { Bone } from "./Bone.js";
|
||||
import type { BonePose } from "./BonePose.js";
|
||||
import { Constraint } from "./Constraint.js";
|
||||
import { PathConstraintData, RotateMode, SpacingMode, PositionMode } from "./PathConstraintData.js";
|
||||
import { type PathConstraintData, PositionMode, RotateMode, SpacingMode } from "./PathConstraintData.js";
|
||||
import { PathConstraintPose } from "./PathConstraintPose.js";
|
||||
import { Physics } from "./Physics.js";
|
||||
import { Skeleton } from "./Skeleton.js";
|
||||
import { Skin, SkinEntry } from "./Skin.js";
|
||||
import { Slot } from "./Slot.js";
|
||||
import { Utils, MathUtils } from "./Utils.js";
|
||||
import type { Physics } from "./Physics.js";
|
||||
import type { Skeleton } from "./Skeleton.js";
|
||||
import type { Skin } from "./Skin.js";
|
||||
import type { Slot } from "./Slot.js";
|
||||
import { MathUtils, Utils } from "./Utils.js";
|
||||
|
||||
|
||||
/** Stores the current pose for a path constraint. A path constraint adjusts the rotation, translation, and scale of the
|
||||
@ -58,16 +58,16 @@ export class PathConstraint extends Constraint<PathConstraint, PathConstraintDat
|
||||
/** The slot whose path attachment will be used to constrained the bones. */
|
||||
slot: Slot;
|
||||
|
||||
spaces = new Array<number>(); positions = new Array<number>();
|
||||
world = new Array<number>(); curves = new Array<number>(); lengths = new Array<number>();
|
||||
segments = new Array<number>();
|
||||
spaces = [] as number[]; positions = [] as number[];
|
||||
world = [] as number[]; curves = [] as number[]; lengths = [] as number[];
|
||||
segments = [] as number[];
|
||||
|
||||
constructor (data: PathConstraintData, skeleton: Skeleton) {
|
||||
super(data, new PathConstraintPose(), new PathConstraintPose());
|
||||
if (!skeleton) throw new Error("skeleton cannot be null.");
|
||||
this.data = data;
|
||||
|
||||
this.bones = new Array<BonePose>();
|
||||
this.bones = [] as BonePose[];
|
||||
for (const boneData of this.data.bones)
|
||||
this.bones.push(skeleton.bones[boneData.index].constrained);
|
||||
|
||||
@ -81,44 +81,44 @@ export class PathConstraint extends Constraint<PathConstraint, PathConstraintDat
|
||||
}
|
||||
|
||||
update (skeleton: Skeleton, physics: Physics) {
|
||||
let attachment = this.slot.applied.attachment;
|
||||
const attachment = this.slot.applied.attachment;
|
||||
if (!(attachment instanceof PathAttachment)) return;
|
||||
|
||||
const p = this.applied;
|
||||
let mixRotate = p.mixRotate, mixX = p.mixX, mixY = p.mixY;
|
||||
const mixRotate = p.mixRotate, mixX = p.mixX, mixY = p.mixY;
|
||||
if (mixRotate === 0 && mixX === 0 && mixY === 0) return;
|
||||
|
||||
let data = this.data;
|
||||
let tangents = data.rotateMode == RotateMode.Tangent, scale = data.rotateMode == RotateMode.ChainScale;
|
||||
const data = this.data;
|
||||
const tangents = data.rotateMode === RotateMode.Tangent, scale = data.rotateMode === RotateMode.ChainScale;
|
||||
|
||||
let bones = this.bones;
|
||||
let boneCount = bones.length, spacesCount = tangents ? boneCount : boneCount + 1;
|
||||
let spaces = Utils.setArraySize(this.spaces, spacesCount), lengths: Array<number> = scale ? this.lengths = Utils.setArraySize(this.lengths, boneCount) : [];
|
||||
let spacing = p.spacing;
|
||||
const bones = this.bones;
|
||||
const boneCount = bones.length, spacesCount = tangents ? boneCount : boneCount + 1;
|
||||
const spaces = Utils.setArraySize(this.spaces, spacesCount), lengths: Array<number> = scale ? this.lengths = Utils.setArraySize(this.lengths, boneCount) : [];
|
||||
const spacing = p.spacing;
|
||||
|
||||
switch (data.spacingMode) {
|
||||
case SpacingMode.Percent:
|
||||
if (scale) {
|
||||
for (let i = 0, n = spacesCount - 1; i < n; i++) {
|
||||
let bone = bones[i];
|
||||
let setupLength = bone.bone.data.length;
|
||||
let x = setupLength * bone.a, y = setupLength * bone.c;
|
||||
const bone = bones[i];
|
||||
const setupLength = bone.bone.data.length;
|
||||
const x = setupLength * bone.a, y = setupLength * bone.c;
|
||||
lengths[i] = Math.sqrt(x * x + y * y);
|
||||
}
|
||||
}
|
||||
Utils.arrayFill(spaces, 1, spacesCount, spacing);
|
||||
break;
|
||||
case SpacingMode.Proportional:
|
||||
case SpacingMode.Proportional: {
|
||||
let sum = 0;
|
||||
for (let i = 0, n = spacesCount - 1; i < n;) {
|
||||
let bone = bones[i];
|
||||
let setupLength = bone.bone.data.length;
|
||||
const bone = bones[i];
|
||||
const setupLength = bone.bone.data.length;
|
||||
if (setupLength < PathConstraint.epsilon) {
|
||||
if (scale) lengths[i] = 0;
|
||||
spaces[++i] = spacing;
|
||||
} else {
|
||||
let x = setupLength * bone.a, y = setupLength * bone.c;
|
||||
let length = Math.sqrt(x * x + y * y);
|
||||
const x = setupLength * bone.a, y = setupLength * bone.c;
|
||||
const length = Math.sqrt(x * x + y * y);
|
||||
if (scale) lengths[i] = length;
|
||||
spaces[++i] = length;
|
||||
sum += length;
|
||||
@ -130,42 +130,44 @@ export class PathConstraint extends Constraint<PathConstraint, PathConstraintDat
|
||||
spaces[i] *= sum;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
let lengthSpacing = data.spacingMode == SpacingMode.Length;
|
||||
}
|
||||
default: {
|
||||
const lengthSpacing = data.spacingMode === SpacingMode.Length;
|
||||
for (let i = 0, n = spacesCount - 1; i < n;) {
|
||||
let bone = bones[i];
|
||||
let setupLength = bone.bone.data.length;
|
||||
const bone = bones[i];
|
||||
const setupLength = bone.bone.data.length;
|
||||
if (setupLength < PathConstraint.epsilon) {
|
||||
if (scale) lengths[i] = 0;
|
||||
spaces[++i] = spacing;
|
||||
} else {
|
||||
let x = setupLength * bone.a, y = setupLength * bone.c;
|
||||
let length = Math.sqrt(x * x + y * y);
|
||||
const x = setupLength * bone.a, y = setupLength * bone.c;
|
||||
const length = Math.sqrt(x * x + y * y);
|
||||
if (scale) lengths[i] = length;
|
||||
spaces[++i] = (lengthSpacing ? Math.max(0, setupLength + spacing) : spacing) * length / setupLength;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let positions = this.computeWorldPositions(skeleton, attachment, spacesCount, tangents);
|
||||
const positions = this.computeWorldPositions(skeleton, attachment, spacesCount, tangents);
|
||||
let boneX = positions[0], boneY = positions[1], offsetRotation = data.offsetRotation;
|
||||
let tip = false;
|
||||
if (offsetRotation == 0)
|
||||
tip = data.rotateMode == RotateMode.Chain;
|
||||
if (offsetRotation === 0)
|
||||
tip = data.rotateMode === RotateMode.Chain;
|
||||
else {
|
||||
tip = false;
|
||||
let bone = this.slot.bone.applied;
|
||||
const bone = this.slot.bone.applied;
|
||||
offsetRotation *= bone.a * bone.d - bone.b * bone.c > 0 ? MathUtils.degRad : -MathUtils.degRad;
|
||||
}
|
||||
for (let i = 0, ip = 3, u = skeleton._update; i < boneCount; i++, ip += 3) {
|
||||
let bone = bones[i];
|
||||
const bone = bones[i];
|
||||
bone.worldX += (boneX - bone.worldX) * mixX;
|
||||
bone.worldY += (boneY - bone.worldY) * mixY;
|
||||
let x = positions[ip], y = positions[ip + 1], dx = x - boneX, dy = y - boneY;
|
||||
const x = positions[ip], y = positions[ip + 1], dx = x - boneX, dy = y - boneY;
|
||||
if (scale) {
|
||||
let length = lengths[i];
|
||||
if (length != 0) {
|
||||
let s = (Math.sqrt(dx * dx + dy * dy) / length - 1) * mixRotate + 1;
|
||||
const length = lengths[i];
|
||||
if (length !== 0) {
|
||||
const s = (Math.sqrt(dx * dx + dy * dy) / length - 1) * mixRotate + 1;
|
||||
bone.a *= s;
|
||||
bone.c *= s;
|
||||
}
|
||||
@ -176,7 +178,7 @@ export class PathConstraint extends Constraint<PathConstraint, PathConstraintDat
|
||||
let a = bone.a, b = bone.b, c = bone.c, d = bone.d, r = 0, cos = 0, sin = 0;
|
||||
if (tangents)
|
||||
r = positions[ip - 1];
|
||||
else if (spaces[i + 1] == 0)
|
||||
else if (spaces[i + 1] === 0)
|
||||
r = positions[ip + 2];
|
||||
else
|
||||
r = Math.atan2(dy, dx);
|
||||
@ -184,7 +186,7 @@ export class PathConstraint extends Constraint<PathConstraint, PathConstraintDat
|
||||
if (tip) {
|
||||
cos = Math.cos(r);
|
||||
sin = Math.sin(r);
|
||||
let length = bone.bone.data.length;
|
||||
const length = bone.bone.data.length;
|
||||
boneX += (length * (cos * a - sin * c) - dx) * mixRotate;
|
||||
boneY += (length * (sin * a + cos * c) - dy) * mixRotate;
|
||||
} else {
|
||||
@ -207,19 +209,19 @@ export class PathConstraint extends Constraint<PathConstraint, PathConstraintDat
|
||||
}
|
||||
|
||||
computeWorldPositions (skeleton: Skeleton, path: PathAttachment, spacesCount: number, tangents: boolean) {
|
||||
let slot = this.slot;
|
||||
const slot = this.slot;
|
||||
let position = this.applied.position;
|
||||
let spaces = this.spaces, out = Utils.setArraySize(this.positions, spacesCount * 3 + 2), world: Array<number> = this.world;
|
||||
let closed = path.closed;
|
||||
const closed = path.closed;
|
||||
let verticesLength = path.worldVerticesLength, curveCount = verticesLength / 6, prevCurve = PathConstraint.NONE;
|
||||
|
||||
if (!path.constantSpeed) {
|
||||
let lengths = path.lengths;
|
||||
const lengths = path.lengths;
|
||||
curveCount -= closed ? 1 : 2;
|
||||
let pathLength = lengths[curveCount];
|
||||
if (this.data.positionMode == PositionMode.Percent) position *= pathLength;
|
||||
const pathLength = lengths[curveCount];
|
||||
if (this.data.positionMode === PositionMode.Percent) position *= pathLength;
|
||||
|
||||
let multiplier;
|
||||
let multiplier: number;
|
||||
switch (this.data.spacingMode) {
|
||||
case SpacingMode.Percent: multiplier = pathLength; break;
|
||||
case SpacingMode.Proportional: multiplier = pathLength / spacesCount; break;
|
||||
@ -228,7 +230,7 @@ export class PathConstraint extends Constraint<PathConstraint, PathConstraintDat
|
||||
|
||||
world = Utils.setArraySize(this.world, 8);
|
||||
for (let i = 0, o = 0, curve = 0; i < spacesCount; i++, o += 3) {
|
||||
let space = spaces[i] * multiplier;
|
||||
const space = spaces[i] * multiplier;
|
||||
position += space;
|
||||
let p = position;
|
||||
|
||||
@ -237,14 +239,14 @@ export class PathConstraint extends Constraint<PathConstraint, PathConstraintDat
|
||||
if (p < 0) p += pathLength;
|
||||
curve = 0;
|
||||
} else if (p < 0) {
|
||||
if (prevCurve != PathConstraint.BEFORE) {
|
||||
if (prevCurve !== PathConstraint.BEFORE) {
|
||||
prevCurve = PathConstraint.BEFORE;
|
||||
path.computeWorldVertices(skeleton, slot, 2, 4, world, 0, 2);
|
||||
}
|
||||
this.addBeforePosition(p, world, 0, out, o);
|
||||
continue;
|
||||
} else if (p > pathLength) {
|
||||
if (prevCurve != PathConstraint.AFTER) {
|
||||
if (prevCurve !== PathConstraint.AFTER) {
|
||||
prevCurve = PathConstraint.AFTER;
|
||||
path.computeWorldVertices(skeleton, slot, verticesLength - 6, 4, world, 0, 2);
|
||||
}
|
||||
@ -254,26 +256,26 @@ export class PathConstraint extends Constraint<PathConstraint, PathConstraintDat
|
||||
|
||||
// Determine curve containing position.
|
||||
for (; ; curve++) {
|
||||
let length = lengths[curve];
|
||||
const length = lengths[curve];
|
||||
if (p > length) continue;
|
||||
if (curve == 0)
|
||||
if (curve === 0)
|
||||
p /= length;
|
||||
else {
|
||||
let prev = lengths[curve - 1];
|
||||
const prev = lengths[curve - 1];
|
||||
p = (p - prev) / (length - prev);
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (curve != prevCurve) {
|
||||
if (curve !== prevCurve) {
|
||||
prevCurve = curve;
|
||||
if (closed && curve == curveCount) {
|
||||
if (closed && curve === curveCount) {
|
||||
path.computeWorldVertices(skeleton, slot, verticesLength - 4, 4, world, 0, 2);
|
||||
path.computeWorldVertices(skeleton, slot, 0, 4, world, 4, 2);
|
||||
} else
|
||||
path.computeWorldVertices(skeleton, slot, curve * 6 + 2, 8, world, 0, 2);
|
||||
}
|
||||
this.addCurvePosition(p, world[0], world[1], world[2], world[3], world[4], world[5], world[6], world[7], out, o,
|
||||
tangents || (i > 0 && space == 0));
|
||||
tangents || (i > 0 && space === 0));
|
||||
}
|
||||
return out;
|
||||
}
|
||||
@ -294,7 +296,7 @@ export class PathConstraint extends Constraint<PathConstraint, PathConstraintDat
|
||||
}
|
||||
|
||||
// Curve lengths.
|
||||
let curves = Utils.setArraySize(this.curves, curveCount);
|
||||
const curves = Utils.setArraySize(this.curves, curveCount);
|
||||
let pathLength = 0;
|
||||
let x1 = world[0], y1 = world[1], cx1 = 0, cy1 = 0, cx2 = 0, cy2 = 0, x2 = 0, y2 = 0;
|
||||
let tmpx = 0, tmpy = 0, dddfx = 0, dddfy = 0, ddfx = 0, ddfy = 0, dfx = 0, dfy = 0;
|
||||
@ -330,19 +332,19 @@ export class PathConstraint extends Constraint<PathConstraint, PathConstraintDat
|
||||
y1 = y2;
|
||||
}
|
||||
|
||||
if (this.data.positionMode == PositionMode.Percent) position *= pathLength;
|
||||
if (this.data.positionMode === PositionMode.Percent) position *= pathLength;
|
||||
|
||||
let multiplier;
|
||||
let multiplier: number;
|
||||
switch (this.data.spacingMode) {
|
||||
case SpacingMode.Percent: multiplier = pathLength; break;
|
||||
case SpacingMode.Proportional: multiplier = pathLength / spacesCount; break;
|
||||
default: multiplier = 1;
|
||||
}
|
||||
|
||||
let segments = this.segments;
|
||||
const segments = this.segments;
|
||||
let curveLength = 0;
|
||||
for (let i = 0, o = 0, curve = 0, segment = 0; i < spacesCount; i++, o += 3) {
|
||||
let space = spaces[i] * multiplier;
|
||||
const space = spaces[i] * multiplier;
|
||||
position += space;
|
||||
let p = position;
|
||||
|
||||
@ -361,19 +363,19 @@ export class PathConstraint extends Constraint<PathConstraint, PathConstraintDat
|
||||
|
||||
// Determine curve containing position.
|
||||
for (; ; curve++) {
|
||||
let length = curves[curve];
|
||||
const length = curves[curve];
|
||||
if (p > length) continue;
|
||||
if (curve == 0)
|
||||
if (curve === 0)
|
||||
p /= length;
|
||||
else {
|
||||
let prev = curves[curve - 1];
|
||||
const prev = curves[curve - 1];
|
||||
p = (p - prev) / (length - prev);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// Curve segment lengths.
|
||||
if (curve != prevCurve) {
|
||||
if (curve !== prevCurve) {
|
||||
prevCurve = curve;
|
||||
let ii = curve * 6;
|
||||
x1 = world[ii];
|
||||
@ -416,30 +418,30 @@ export class PathConstraint extends Constraint<PathConstraint, PathConstraintDat
|
||||
// Weight by segment length.
|
||||
p *= curveLength;
|
||||
for (; ; segment++) {
|
||||
let length = segments[segment];
|
||||
const length = segments[segment];
|
||||
if (p > length) continue;
|
||||
if (segment == 0)
|
||||
if (segment === 0)
|
||||
p /= length;
|
||||
else {
|
||||
let prev = segments[segment - 1];
|
||||
const prev = segments[segment - 1];
|
||||
p = segment + (p - prev) / (length - prev);
|
||||
}
|
||||
break;
|
||||
}
|
||||
this.addCurvePosition(p * 0.1, x1, y1, cx1, cy1, cx2, cy2, x2, y2, out, o, tangents || (i > 0 && space == 0));
|
||||
this.addCurvePosition(p * 0.1, x1, y1, cx1, cy1, cx2, cy2, x2, y2, out, o, tangents || (i > 0 && space === 0));
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
addBeforePosition (p: number, temp: Array<number>, i: number, out: Array<number>, o: number) {
|
||||
let x1 = temp[i], y1 = temp[i + 1], dx = temp[i + 2] - x1, dy = temp[i + 3] - y1, r = Math.atan2(dy, dx);
|
||||
const x1 = temp[i], y1 = temp[i + 1], dx = temp[i + 2] - x1, dy = temp[i + 3] - y1, r = Math.atan2(dy, dx);
|
||||
out[o] = x1 + p * Math.cos(r);
|
||||
out[o + 1] = y1 + p * Math.sin(r);
|
||||
out[o + 2] = r;
|
||||
}
|
||||
|
||||
addAfterPosition (p: number, temp: Array<number>, i: number, out: Array<number>, o: number) {
|
||||
let x1 = temp[i + 2], y1 = temp[i + 3], dx = x1 - temp[i], dy = y1 - temp[i + 1], r = Math.atan2(dy, dx);
|
||||
const x1 = temp[i + 2], y1 = temp[i + 3], dx = x1 - temp[i], dy = y1 - temp[i + 1], r = Math.atan2(dy, dx);
|
||||
out[o] = x1 + p * Math.cos(r);
|
||||
out[o + 1] = y1 + p * Math.sin(r);
|
||||
out[o + 2] = r;
|
||||
@ -447,15 +449,15 @@ export class PathConstraint extends Constraint<PathConstraint, PathConstraintDat
|
||||
|
||||
addCurvePosition (p: number, x1: number, y1: number, cx1: number, cy1: number, cx2: number, cy2: number, x2: number, y2: number,
|
||||
out: Array<number>, o: number, tangents: boolean) {
|
||||
if (p == 0 || isNaN(p)) {
|
||||
if (p === 0 || Number.isNaN(p)) {
|
||||
out[o] = x1;
|
||||
out[o + 1] = y1;
|
||||
out[o + 2] = Math.atan2(cy1 - y1, cx1 - x1);
|
||||
return;
|
||||
}
|
||||
let tt = p * p, ttt = tt * p, u = 1 - p, uu = u * u, uuu = uu * u;
|
||||
let ut = u * p, ut3 = ut * 3, uut3 = u * ut3, utt3 = ut3 * p;
|
||||
let x = x1 * uuu + cx1 * uut3 + cx2 * utt3 + x2 * ttt, y = y1 * uuu + cy1 * uut3 + cy2 * utt3 + y2 * ttt;
|
||||
const tt = p * p, ttt = tt * p, u = 1 - p, uu = u * u, uuu = uu * u;
|
||||
const ut = u * p, ut3 = ut * 3, uut3 = u * ut3, utt3 = ut3 * p;
|
||||
const x = x1 * uuu + cx1 * uut3 + cx2 * utt3 + x2 * ttt, y = y1 * uuu + cy1 * uut3 + cy2 * utt3 + y2 * ttt;
|
||||
out[o] = x;
|
||||
out[o + 1] = y;
|
||||
if (tangents) {
|
||||
@ -470,7 +472,7 @@ export class PathConstraint extends Constraint<PathConstraint, PathConstraintDat
|
||||
const slotIndex = this.slot.data.index;
|
||||
const slotBone = this.slot.bone;
|
||||
if (skeleton.skin != null) this.sortPathSlot(skeleton, skeleton.skin, slotIndex, slotBone);
|
||||
if (skeleton.data.defaultSkin != null && skeleton.data.defaultSkin != skeleton.skin)
|
||||
if (skeleton.data.defaultSkin != null && skeleton.data.defaultSkin !== skeleton.skin)
|
||||
this.sortPathSlot(skeleton, skeleton.data.defaultSkin, slotIndex, slotBone);
|
||||
this.sortPath(skeleton, this.slot.pose.attachment, slotBone);
|
||||
const bones = this.bones;
|
||||
@ -491,7 +493,7 @@ export class PathConstraint extends Constraint<PathConstraint, PathConstraintDat
|
||||
const entries = skin.getAttachments();
|
||||
for (let i = 0, n = entries.length; i < n; i++) {
|
||||
const entry = entries[i];
|
||||
if (entry.slotIndex == slotIndex) this.sortPath(skeleton, entry.attachment, slotBone);
|
||||
if (entry.slotIndex === slotIndex) this.sortPath(skeleton, entry.attachment, slotBone);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -27,12 +27,12 @@
|
||||
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*****************************************************************************/
|
||||
|
||||
import { BoneData } from "./BoneData.js";
|
||||
import type { BoneData } from "./BoneData.js";
|
||||
import { ConstraintData } from "./ConstraintData.js";
|
||||
import { PathConstraint } from "./PathConstraint.js";
|
||||
import { PathConstraintPose } from "./PathConstraintPose.js";
|
||||
import { Skeleton } from "./Skeleton.js";
|
||||
import { SlotData } from "./SlotData.js";
|
||||
import type { Skeleton } from "./Skeleton.js";
|
||||
import type { SlotData } from "./SlotData.js";
|
||||
|
||||
|
||||
/** Stores the setup pose for a {@link PathConstraint}.
|
||||
@ -40,7 +40,7 @@ import { SlotData } from "./SlotData.js";
|
||||
* See [path constraints](http://esotericsoftware.com/spine-path-constraints) in the Spine User Guide. */
|
||||
export class PathConstraintData extends ConstraintData<PathConstraint, PathConstraintPose> {
|
||||
/** The bones that will be modified by this path constraint. */
|
||||
bones = new Array<BoneData>();
|
||||
bones = [] as BoneData[];
|
||||
|
||||
/** The slot whose path attachment will be used to constrained the bones. */
|
||||
public set slot (slotData: SlotData) { this._slot = slotData; }
|
||||
|
||||
@ -27,7 +27,7 @@
|
||||
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*****************************************************************************/
|
||||
|
||||
import { Pose } from "./Pose"
|
||||
import type { Pose } from "./Pose"
|
||||
|
||||
/** Stores a pose for a path constraint. */
|
||||
export class PathConstraintPose implements Pose<PathConstraintPose> {
|
||||
|
||||
@ -27,11 +27,11 @@
|
||||
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*****************************************************************************/
|
||||
|
||||
import { BoneData } from "./BoneData.js";
|
||||
import type { BoneData } from "./BoneData.js";
|
||||
import { ConstraintData } from "./ConstraintData.js";
|
||||
import { PhysicsConstraint } from "./PhysicsConstraint.js";
|
||||
import { PhysicsConstraintPose } from "./PhysicsConstraintPose.js";
|
||||
import { Skeleton } from "./Skeleton.js";
|
||||
import type { Skeleton } from "./Skeleton.js";
|
||||
|
||||
|
||||
/** Stores the setup pose for a {@link PhysicsConstraint}.
|
||||
|
||||
@ -27,7 +27,7 @@
|
||||
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*****************************************************************************/
|
||||
|
||||
import { Pose } from "./Pose"
|
||||
import type { Pose } from "./Pose"
|
||||
|
||||
/** Stores a pose for a physics constraint. */
|
||||
export class PhysicsConstraintPose implements Pose<PhysicsConstraintPose> {
|
||||
|
||||
@ -27,12 +27,12 @@
|
||||
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*****************************************************************************/
|
||||
|
||||
import { Pose } from "./Pose.js";
|
||||
import { PosedData } from "./PosedData.js";
|
||||
import type { Pose } from "./Pose.js";
|
||||
import type { PosedData } from "./PosedData.js";
|
||||
|
||||
export abstract class Posed<
|
||||
D extends PosedData<P>,
|
||||
P extends Pose<any>,
|
||||
P extends Pose<P>,
|
||||
A extends P> {
|
||||
|
||||
/** The constraint's setup pose data. */
|
||||
|
||||
@ -27,15 +27,15 @@
|
||||
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*****************************************************************************/
|
||||
|
||||
import { Pose } from "./Pose.js";
|
||||
import type { Pose } from "./Pose.js";
|
||||
import { Posed } from "./Posed.js";
|
||||
import { PosedData } from "./PosedData.js";
|
||||
import type { PosedData } from "./PosedData.js";
|
||||
|
||||
import type { Skeleton } from "./Skeleton";
|
||||
|
||||
export abstract class PosedActive<
|
||||
D extends PosedData<P>,
|
||||
P extends Pose<any>,
|
||||
P extends Pose<P>,
|
||||
A extends P>
|
||||
extends Posed<D, P, A> {
|
||||
|
||||
|
||||
@ -27,10 +27,10 @@
|
||||
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*****************************************************************************/
|
||||
|
||||
import { Pose } from "./Pose.js";
|
||||
import type { Pose } from "./Pose.js";
|
||||
|
||||
/** The base class for all constrained datas. */
|
||||
export abstract class PosedData<P extends Pose<any>> {
|
||||
export abstract class PosedData<P extends Pose<P>> {
|
||||
/** The constraint's name, which is unique across all constraints in the skeleton of the same type. */
|
||||
readonly name: string;
|
||||
|
||||
|
||||
@ -27,20 +27,20 @@
|
||||
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*****************************************************************************/
|
||||
|
||||
import { Attachment } from "./attachments/Attachment.js";
|
||||
import type { Attachment } from "./attachments/Attachment.js";
|
||||
import { ClippingAttachment } from "./attachments/ClippingAttachment.js";
|
||||
import { MeshAttachment } from "./attachments/MeshAttachment.js";
|
||||
import { RegionAttachment } from "./attachments/RegionAttachment.js";
|
||||
import { Bone } from "./Bone.js";
|
||||
import { Constraint } from "./Constraint.js";
|
||||
import { Physics } from "./Physics.js";
|
||||
import type { Constraint } from "./Constraint.js";
|
||||
import type { Physics } from "./Physics.js";
|
||||
import { PhysicsConstraint } from "./PhysicsConstraint.js";
|
||||
import { Posed } from "./Posed.js";
|
||||
import { SkeletonClipping } from "./SkeletonClipping.js";
|
||||
import { SkeletonData } from "./SkeletonData.js";
|
||||
import { Skin } from "./Skin.js";
|
||||
import type { Posed } from "./Posed.js";
|
||||
import type { SkeletonClipping } from "./SkeletonClipping.js";
|
||||
import type { SkeletonData } from "./SkeletonData.js";
|
||||
import type { Skin } from "./Skin.js";
|
||||
import { Slot } from "./Slot.js";
|
||||
import { Color, NumberArrayLike, Utils, Vector2 } from "./Utils.js";
|
||||
import { Color, type NumberArrayLike, Utils, Vector2 } from "./Utils.js";
|
||||
|
||||
/** Stores the current pose for a skeleton.
|
||||
*
|
||||
@ -65,15 +65,18 @@ export class Skeleton {
|
||||
drawOrder: Array<Slot>;
|
||||
|
||||
/** The skeleton's constraints. */
|
||||
// biome-ignore lint/suspicious/noExplicitAny: reference runtime does not restrict to specific types
|
||||
readonly constraints: Array<Constraint<any, any, any>>;
|
||||
|
||||
/** The skeleton's physics constraints. */
|
||||
readonly physics: Array<PhysicsConstraint>;
|
||||
|
||||
/** The list of bones and constraints, sorted in the order they should be updated, as computed by {@link updateCache()}. */
|
||||
readonly _updateCache = new Array();
|
||||
// biome-ignore lint/suspicious/noExplicitAny: reference runtime does not restrict to specific types
|
||||
readonly _updateCache = [] as any[];
|
||||
|
||||
readonly resetCache: Array<Posed<any, any, any>> = new Array();
|
||||
// biome-ignore lint/suspicious/noExplicitAny: reference runtime does not restrict to specific types
|
||||
readonly resetCache: Array<Posed<any, any, any>> = [];
|
||||
|
||||
/** The skeleton's current skin. May be null. */
|
||||
skin: Skin | null = null;
|
||||
@ -125,30 +128,31 @@ export class Skeleton {
|
||||
if (!data) throw new Error("data cannot be null.");
|
||||
this.data = data;
|
||||
|
||||
this.bones = new Array<Bone>();
|
||||
this.bones = [] as Bone[];
|
||||
for (let i = 0; i < data.bones.length; i++) {
|
||||
let boneData = data.bones[i];
|
||||
const boneData = data.bones[i];
|
||||
let bone: Bone;
|
||||
if (!boneData.parent)
|
||||
bone = new Bone(boneData, null);
|
||||
else {
|
||||
let parent = this.bones[boneData.parent.index];
|
||||
const parent = this.bones[boneData.parent.index];
|
||||
bone = new Bone(boneData, parent);
|
||||
parent.children.push(bone);
|
||||
}
|
||||
this.bones.push(bone);
|
||||
}
|
||||
|
||||
this.slots = new Array<Slot>();
|
||||
this.drawOrder = new Array<Slot>();
|
||||
this.slots = [] as Slot[];
|
||||
this.drawOrder = [] as Slot[];
|
||||
for (const slotData of this.data.slots) {
|
||||
let slot = new Slot(slotData, this);
|
||||
const slot = new Slot(slotData, this);
|
||||
this.slots.push(slot);
|
||||
this.drawOrder.push(slot);
|
||||
}
|
||||
|
||||
this.physics = new Array<PhysicsConstraint>();
|
||||
this.constraints = new Array<Constraint<any, any, any>>();
|
||||
this.physics = [] as PhysicsConstraint[];
|
||||
// biome-ignore lint/suspicious/noExplicitAny: reference runtime does not restrict to specific types
|
||||
this.constraints = [] as Constraint<any, any, any>[];
|
||||
for (const constraintData of this.data.constraints) {
|
||||
const constraint = constraintData.create(this);
|
||||
if (constraint instanceof PhysicsConstraint) this.physics.push(constraint);
|
||||
@ -166,20 +170,20 @@ export class Skeleton {
|
||||
this._updateCache.length = 0;
|
||||
this.resetCache.length = 0;
|
||||
|
||||
let slots = this.slots;
|
||||
const slots = this.slots;
|
||||
for (let i = 0, n = slots.length; i < n; i++)
|
||||
slots[i].usePose();
|
||||
|
||||
let bones = this.bones;
|
||||
const bones = this.bones;
|
||||
const boneCount = bones.length;
|
||||
for (let i = 0, n = boneCount; i < n; i++) {
|
||||
let bone = bones[i];
|
||||
const bone = bones[i];
|
||||
bone.sorted = bone.data.skinRequired;
|
||||
bone.active = !bone.sorted;
|
||||
bone.usePose();
|
||||
}
|
||||
if (this.skin) {
|
||||
let skinBones = this.skin.bones;
|
||||
const skinBones = this.skin.bones;
|
||||
for (let i = 0, n = this.skin.bones.length; i < n; i++) {
|
||||
let bone: Bone | null = this.bones[skinBones[i].index];
|
||||
do {
|
||||
@ -190,13 +194,14 @@ export class Skeleton {
|
||||
}
|
||||
}
|
||||
|
||||
let constraints = this.constraints;
|
||||
const constraints = this.constraints;
|
||||
let n = this.constraints.length;
|
||||
for (let i = 0; i < n; i++)
|
||||
constraints[i].usePose();
|
||||
for (let i = 0; i < n; i++) {
|
||||
const constraint = constraints[i];
|
||||
constraint.active = constraint.isSourceActive()
|
||||
// biome-ignore lint/complexity/useOptionalChain: changing to this might return undefined
|
||||
&& (!constraint.data.skinRequired || (this.skin != null && this.skin.constraints.includes(constraint.data)));
|
||||
if (constraint.active) constraint.sort(this);
|
||||
}
|
||||
@ -212,6 +217,7 @@ export class Skeleton {
|
||||
|
||||
}
|
||||
|
||||
// biome-ignore lint/suspicious/noExplicitAny: reference runtime does not restrict to specific types
|
||||
constrained (object: Posed<any, any, any>) {
|
||||
if (object.pose === object.applied) {
|
||||
object.useConstrained();
|
||||
@ -221,7 +227,7 @@ export class Skeleton {
|
||||
|
||||
sortBone (bone: Bone) {
|
||||
if (bone.sorted || !bone.active) return;
|
||||
let parent = bone.parent;
|
||||
const parent = bone.parent;
|
||||
if (parent) this.sortBone(parent);
|
||||
bone.sorted = true;
|
||||
this._updateCache.push(bone);
|
||||
@ -229,7 +235,7 @@ export class Skeleton {
|
||||
|
||||
sortReset (bones: Array<Bone>) {
|
||||
for (let i = 0, n = bones.length; i < n; i++) {
|
||||
let bone = bones[i];
|
||||
const bone = bones[i];
|
||||
if (bone.active) {
|
||||
if (bone.sorted) this.sortReset(bone.children);
|
||||
bone.sorted = false;
|
||||
@ -272,7 +278,7 @@ export class Skeleton {
|
||||
|
||||
/** Sets the slots and draw order to their setup pose values. */
|
||||
setupPoseSlots () {
|
||||
let slots = this.slots;
|
||||
const slots = this.slots;
|
||||
Utils.arrayCopy(slots, 0, this.drawOrder, 0, slots.length);
|
||||
for (let i = 0, n = slots.length; i < n; i++)
|
||||
slots[i].setupPose();
|
||||
@ -280,7 +286,7 @@ export class Skeleton {
|
||||
|
||||
/** Returns the root bone, or null if the skeleton has no bones. */
|
||||
getRootBone () {
|
||||
if (this.bones.length == 0) return null;
|
||||
if (this.bones.length === 0) return null;
|
||||
return this.bones[0];
|
||||
}
|
||||
|
||||
@ -288,9 +294,9 @@ export class Skeleton {
|
||||
* repeatedly. */
|
||||
findBone (boneName: string) {
|
||||
if (!boneName) throw new Error("boneName cannot be null.");
|
||||
let bones = this.bones;
|
||||
const bones = this.bones;
|
||||
for (let i = 0, n = bones.length; i < n; i++)
|
||||
if (bones[i].data.name == boneName) return bones[i];
|
||||
if (bones[i].data.name === boneName) return bones[i];
|
||||
return null;
|
||||
}
|
||||
|
||||
@ -298,9 +304,9 @@ export class Skeleton {
|
||||
* repeatedly. */
|
||||
findSlot (slotName: string) {
|
||||
if (!slotName) throw new Error("slotName cannot be null.");
|
||||
let slots = this.slots;
|
||||
const slots = this.slots;
|
||||
for (let i = 0, n = slots.length; i < n; i++)
|
||||
if (slots[i].data.name == slotName) return slots[i];
|
||||
if (slots[i].data.name === slotName) return slots[i];
|
||||
return null;
|
||||
}
|
||||
|
||||
@ -328,23 +334,23 @@ export class Skeleton {
|
||||
};
|
||||
|
||||
private setSkinByName (skinName: string) {
|
||||
let skin = this.data.findSkin(skinName);
|
||||
if (!skin) throw new Error("Skin not found: " + skinName);
|
||||
const skin = this.data.findSkin(skinName);
|
||||
if (!skin) throw new Error(`Skin not found: ${skinName}`);
|
||||
this.setSkin(skin);
|
||||
}
|
||||
|
||||
private setSkinBySkin (newSkin: Skin | null) {
|
||||
if (newSkin == this.skin) return;
|
||||
if (newSkin === this.skin) return;
|
||||
if (newSkin) {
|
||||
if (this.skin)
|
||||
newSkin.attachAll(this, this.skin);
|
||||
else {
|
||||
let slots = this.slots;
|
||||
const slots = this.slots;
|
||||
for (let i = 0, n = slots.length; i < n; i++) {
|
||||
let slot = slots[i];
|
||||
let name = slot.data.attachmentName;
|
||||
const slot = slots[i];
|
||||
const name = slot.data.attachmentName;
|
||||
if (name) {
|
||||
let attachment = newSkin.getAttachment(i, name);
|
||||
const attachment = newSkin.getAttachment(i, name);
|
||||
if (attachment) slot.pose.setAttachment(attachment);
|
||||
}
|
||||
}
|
||||
@ -378,7 +384,7 @@ export class Skeleton {
|
||||
* See {@link #getAttachment()}.
|
||||
* @returns May be null. */
|
||||
private getAttachmentByName (slotName: string, attachmentName: string): Attachment | null {
|
||||
let slot = this.data.findSlot(slotName);
|
||||
const slot = this.data.findSlot(slotName);
|
||||
if (!slot) throw new Error(`Can't find slot with name ${slotName}`);
|
||||
return this.getAttachment(slot.index, attachmentName);
|
||||
}
|
||||
@ -391,7 +397,7 @@ export class Skeleton {
|
||||
private getAttachmentByIndex (slotIndex: number, attachmentName: string): Attachment | null {
|
||||
if (!attachmentName) throw new Error("attachmentName cannot be null.");
|
||||
if (this.skin) {
|
||||
let attachment = this.skin.getAttachment(slotIndex, attachmentName);
|
||||
const attachment = this.skin.getAttachment(slotIndex, attachmentName);
|
||||
if (attachment) return attachment;
|
||||
}
|
||||
if (this.data.defaultSkin) return this.data.defaultSkin.getAttachment(slotIndex, attachmentName);
|
||||
@ -404,16 +410,17 @@ export class Skeleton {
|
||||
setAttachment (slotName: string, attachmentName: string) {
|
||||
if (!slotName) throw new Error("slotName cannot be null.");
|
||||
const slot = this.findSlot(slotName);
|
||||
if (!slot) throw new Error("Slot not found: " + slotName);
|
||||
if (!slot) throw new Error(`Slot not found: ${slotName}`);
|
||||
let attachment: Attachment | null = null;
|
||||
if (attachmentName) {
|
||||
attachment = this.getAttachment(slot.data.index, attachmentName);
|
||||
if (!attachment)
|
||||
throw new Error("Attachment not found: " + attachmentName + ", for slot: " + slotName);
|
||||
throw new Error(`Attachment not found: ${attachmentName}, for slot: ${slotName}`);
|
||||
}
|
||||
slot.pose.setAttachment(attachment);
|
||||
}
|
||||
|
||||
// biome-ignore lint/suspicious/noExplicitAny: reference runtime does not restrict to specific types
|
||||
findConstraint<T extends Constraint<any, any, any>> (constraintName: string, type: new () => T): T | null {
|
||||
if (constraintName == null) throw new Error("constraintName cannot be null.");
|
||||
if (type == null) throw new Error("type cannot be null.");
|
||||
@ -428,8 +435,8 @@ export class Skeleton {
|
||||
/** 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?: SkeletonClipping) {
|
||||
let offset = new Vector2();
|
||||
let size = new Vector2();
|
||||
const offset = new Vector2();
|
||||
const size = new Vector2();
|
||||
this.getBounds(offset, size, undefined, clipper);
|
||||
return { x: offset.x, y: offset.y, width: size.x, height: size.y };
|
||||
}
|
||||
@ -442,15 +449,15 @@ export class Skeleton {
|
||||
getBounds (offset: Vector2, size: Vector2, temp: Array<number> = new Array<number>(2), clipper: SkeletonClipping | null = null) {
|
||||
if (!offset) throw new Error("offset cannot be null.");
|
||||
if (!size) throw new Error("size cannot be null.");
|
||||
let drawOrder = this.drawOrder;
|
||||
const 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];
|
||||
const slot = drawOrder[i];
|
||||
if (!slot.bone.active) continue;
|
||||
let verticesLength = 0;
|
||||
let vertices: NumberArrayLike | null = null;
|
||||
let triangles: NumberArrayLike | null = null;
|
||||
let attachment = slot.pose.attachment;
|
||||
const attachment = slot.pose.attachment;
|
||||
if (attachment) {
|
||||
if (attachment instanceof RegionAttachment) {
|
||||
verticesLength = 8;
|
||||
@ -468,12 +475,12 @@ export class Skeleton {
|
||||
continue;
|
||||
}
|
||||
if (vertices && triangles) {
|
||||
if (clipper && clipper.isClipping() && clipper.clipTriangles(vertices, triangles, triangles.length)) {
|
||||
if (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];
|
||||
const x = vertices[ii], y = vertices[ii + 1];
|
||||
minX = Math.min(minX, x);
|
||||
minY = Math.min(minY, y);
|
||||
maxX = Math.max(maxX, x);
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -28,8 +28,8 @@
|
||||
*****************************************************************************/
|
||||
|
||||
import { BoundingBoxAttachment } from "./attachments/BoundingBoxAttachment.js";
|
||||
import { Skeleton } from "./Skeleton.js";
|
||||
import { NumberArrayLike, Pool, Utils } from "./Utils.js";
|
||||
import type { Skeleton } from "./Skeleton.js";
|
||||
import { type NumberArrayLike, Pool, Utils } from "./Utils.js";
|
||||
|
||||
/** Collects each visible {@link BoundingBoxAttachment} and computes the world vertices for its polygon. The polygon vertices are
|
||||
* provided along with convenience methods for doing hit detection. */
|
||||
@ -48,10 +48,10 @@ export class SkeletonBounds {
|
||||
maxY = 0;
|
||||
|
||||
/** The visible bounding boxes. */
|
||||
boundingBoxes = new Array<BoundingBoxAttachment>();
|
||||
boundingBoxes = [] as BoundingBoxAttachment[];
|
||||
|
||||
/** The world vertices for the bounding box polygons. */
|
||||
polygons = new Array<NumberArrayLike>();
|
||||
polygons = [] as NumberArrayLike[];
|
||||
|
||||
private polygonPool = new Pool<NumberArrayLike>(() => {
|
||||
return Utils.newFloatArray(16);
|
||||
@ -63,25 +63,25 @@ export class SkeletonBounds {
|
||||
* SkeletonBounds AABB methods will always return true. */
|
||||
update (skeleton: Skeleton, updateAabb: boolean) {
|
||||
if (!skeleton) throw new Error("skeleton cannot be null.");
|
||||
let boundingBoxes = this.boundingBoxes;
|
||||
let polygons = this.polygons;
|
||||
let polygonPool = this.polygonPool;
|
||||
let slots = skeleton.slots;
|
||||
let slotCount = slots.length;
|
||||
const boundingBoxes = this.boundingBoxes;
|
||||
const polygons = this.polygons;
|
||||
const polygonPool = this.polygonPool;
|
||||
const slots = skeleton.slots;
|
||||
const slotCount = slots.length;
|
||||
|
||||
boundingBoxes.length = 0;
|
||||
polygonPool.freeAll(polygons);
|
||||
polygons.length = 0;
|
||||
|
||||
for (let i = 0; i < slotCount; i++) {
|
||||
let slot = slots[i];
|
||||
const slot = slots[i];
|
||||
if (!slot.bone.active) continue;
|
||||
let attachment = slot.applied.attachment;
|
||||
const attachment = slot.applied.attachment;
|
||||
if (attachment instanceof BoundingBoxAttachment) {
|
||||
boundingBoxes.push(attachment);
|
||||
|
||||
let polygon = polygonPool.obtain();
|
||||
if (polygon.length != attachment.worldVerticesLength) {
|
||||
if (polygon.length !== attachment.worldVerticesLength) {
|
||||
polygon = Utils.newFloatArray(attachment.worldVerticesLength);
|
||||
}
|
||||
polygons.push(polygon);
|
||||
@ -101,13 +101,13 @@ export class SkeletonBounds {
|
||||
|
||||
aabbCompute () {
|
||||
let minX = Number.POSITIVE_INFINITY, minY = Number.POSITIVE_INFINITY, maxX = Number.NEGATIVE_INFINITY, maxY = Number.NEGATIVE_INFINITY;
|
||||
let polygons = this.polygons;
|
||||
const polygons = this.polygons;
|
||||
for (let i = 0, n = polygons.length; i < n; i++) {
|
||||
let polygon = polygons[i];
|
||||
let vertices = polygon;
|
||||
const polygon = polygons[i];
|
||||
const vertices = polygon;
|
||||
for (let ii = 0, nn = polygon.length; ii < nn; ii += 2) {
|
||||
let x = vertices[ii];
|
||||
let y = vertices[ii + 1];
|
||||
const x = vertices[ii];
|
||||
const y = vertices[ii + 1];
|
||||
minX = Math.min(minX, x);
|
||||
minY = Math.min(minY, y);
|
||||
maxX = Math.max(maxX, x);
|
||||
@ -127,13 +127,13 @@ export class SkeletonBounds {
|
||||
|
||||
/** Returns true if the axis aligned bounding box intersects the line segment. */
|
||||
aabbIntersectsSegment (x1: number, y1: number, x2: number, y2: number) {
|
||||
let minX = this.minX;
|
||||
let minY = this.minY;
|
||||
let maxX = this.maxX;
|
||||
let maxY = this.maxY;
|
||||
const minX = this.minX;
|
||||
const minY = this.minY;
|
||||
const maxX = this.maxX;
|
||||
const maxY = this.maxY;
|
||||
if ((x1 <= minX && x2 <= minX) || (y1 <= minY && y2 <= minY) || (x1 >= maxX && x2 >= maxX) || (y1 >= maxY && y2 >= maxY))
|
||||
return false;
|
||||
let m = (y2 - y1) / (x2 - x1);
|
||||
const m = (y2 - y1) / (x2 - x1);
|
||||
let y = m * (minX - x1) + y1;
|
||||
if (y > minY && y < maxY) return true;
|
||||
y = m * (maxX - x1) + y1;
|
||||
@ -153,7 +153,7 @@ export class SkeletonBounds {
|
||||
/** Returns the first bounding box attachment that contains the point, or null. When doing many checks, it is usually more
|
||||
* efficient to only call this method if {@link #aabbContainsPoint(float, float)} returns true. */
|
||||
containsPoint (x: number, y: number): BoundingBoxAttachment | null {
|
||||
let polygons = this.polygons;
|
||||
const polygons = this.polygons;
|
||||
for (let i = 0, n = polygons.length; i < n; i++)
|
||||
if (this.containsPointPolygon(polygons[i], x, y)) return this.boundingBoxes[i];
|
||||
return null;
|
||||
@ -161,16 +161,16 @@ export class SkeletonBounds {
|
||||
|
||||
/** Returns true if the polygon contains the point. */
|
||||
containsPointPolygon (polygon: NumberArrayLike, x: number, y: number) {
|
||||
let vertices = polygon;
|
||||
let nn = polygon.length;
|
||||
const vertices = polygon;
|
||||
const nn = polygon.length;
|
||||
|
||||
let prevIndex = nn - 2;
|
||||
let inside = false;
|
||||
for (let ii = 0; ii < nn; ii += 2) {
|
||||
let vertexY = vertices[ii + 1];
|
||||
let prevY = vertices[prevIndex + 1];
|
||||
const vertexY = vertices[ii + 1];
|
||||
const prevY = vertices[prevIndex + 1];
|
||||
if ((vertexY < y && prevY >= y) || (prevY < y && vertexY >= y)) {
|
||||
let vertexX = vertices[ii];
|
||||
const vertexX = vertices[ii];
|
||||
if (vertexX + (y - vertexY) / (prevY - vertexY) * (vertices[prevIndex] - vertexX) < x) inside = !inside;
|
||||
}
|
||||
prevIndex = ii;
|
||||
@ -182,7 +182,7 @@ export class SkeletonBounds {
|
||||
* is usually more efficient to only call this method if {@link #aabbIntersectsSegment()} returns
|
||||
* true. */
|
||||
intersectsSegment (x1: number, y1: number, x2: number, y2: number) {
|
||||
let polygons = this.polygons;
|
||||
const polygons = this.polygons;
|
||||
for (let i = 0, n = polygons.length; i < n; i++)
|
||||
if (this.intersectsSegmentPolygon(polygons[i], x1, y1, x2, y2)) return this.boundingBoxes[i];
|
||||
return null;
|
||||
@ -190,20 +190,20 @@ export class SkeletonBounds {
|
||||
|
||||
/** Returns true if the polygon contains any part of the line segment. */
|
||||
intersectsSegmentPolygon (polygon: NumberArrayLike, x1: number, y1: number, x2: number, y2: number) {
|
||||
let vertices = polygon;
|
||||
let nn = polygon.length;
|
||||
const vertices = polygon;
|
||||
const nn = polygon.length;
|
||||
|
||||
let width12 = x1 - x2, height12 = y1 - y2;
|
||||
let det1 = x1 * y2 - y1 * x2;
|
||||
const width12 = x1 - x2, height12 = y1 - y2;
|
||||
const det1 = x1 * y2 - y1 * x2;
|
||||
let x3 = vertices[nn - 2], y3 = vertices[nn - 1];
|
||||
for (let ii = 0; ii < nn; ii += 2) {
|
||||
let x4 = vertices[ii], y4 = vertices[ii + 1];
|
||||
let det2 = x3 * y4 - y3 * x4;
|
||||
let width34 = x3 - x4, height34 = y3 - y4;
|
||||
let det3 = width12 * height34 - height12 * width34;
|
||||
let x = (det1 * width34 - width12 * det2) / det3;
|
||||
const x4 = vertices[ii], y4 = vertices[ii + 1];
|
||||
const det2 = x3 * y4 - y3 * x4;
|
||||
const width34 = x3 - x4, height34 = y3 - y4;
|
||||
const det3 = width12 * height34 - height12 * width34;
|
||||
const x = (det1 * width34 - width12 * det2) / det3;
|
||||
if (((x >= x3 && x <= x4) || (x >= x4 && x <= x3)) && ((x >= x1 && x <= x2) || (x >= x2 && x <= x1))) {
|
||||
let y = (det1 * height34 - height12 * det2) / det3;
|
||||
const y = (det1 * height34 - height12 * det2) / det3;
|
||||
if (((y >= y3 && y <= y4) || (y >= y4 && y <= y3)) && ((y >= y1 && y <= y2) || (y >= y2 && y <= y1))) return true;
|
||||
}
|
||||
x3 = x4;
|
||||
@ -215,8 +215,8 @@ export class SkeletonBounds {
|
||||
/** Returns the polygon for the specified bounding box, or null. */
|
||||
getPolygon (boundingBox: BoundingBoxAttachment) {
|
||||
if (!boundingBox) throw new Error("boundingBox cannot be null.");
|
||||
let index = this.boundingBoxes.indexOf(boundingBox);
|
||||
return index == -1 ? null : this.polygons[index];
|
||||
const index = this.boundingBoxes.indexOf(boundingBox);
|
||||
return index === -1 ? null : this.polygons[index];
|
||||
}
|
||||
|
||||
/** The width of the axis aligned bounding box. */
|
||||
|
||||
@ -27,22 +27,22 @@
|
||||
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*****************************************************************************/
|
||||
|
||||
import { ClippingAttachment } from "./attachments/ClippingAttachment.js";
|
||||
import { Skeleton } from "./Skeleton.js";
|
||||
import { Slot } from "./Slot.js";
|
||||
import type { ClippingAttachment } from "./attachments/ClippingAttachment.js";
|
||||
import type { Skeleton } from "./Skeleton.js";
|
||||
import type { Slot } from "./Slot.js";
|
||||
import { Triangulator } from "./Triangulator.js";
|
||||
import { Utils, Color, NumberArrayLike } from "./Utils.js";
|
||||
import { type Color, type NumberArrayLike, Utils } from "./Utils.js";
|
||||
|
||||
export class SkeletonClipping {
|
||||
private triangulator = new Triangulator();
|
||||
private clippingPolygon = new Array<number>();
|
||||
private clipOutput = new Array<number>();
|
||||
clippedVertices = new Array<number>();
|
||||
private clippingPolygon = [] as number[];
|
||||
private clipOutput = [] as number[];
|
||||
clippedVertices = [] as number[];
|
||||
|
||||
/** An empty array unless {@link clipTrianglesUnpacked} was used. **/
|
||||
clippedUVs = new Array<number>();
|
||||
clippedUVs = [] as number[];
|
||||
|
||||
clippedTriangles = new Array<number>();
|
||||
clippedTriangles = [] as number[];
|
||||
|
||||
_clippedVerticesTyped = new Float32Array(1024);
|
||||
_clippedUVsTyped = new Float32Array(1024);
|
||||
@ -54,7 +54,7 @@ export class SkeletonClipping {
|
||||
clippedUVsLength = 0;
|
||||
clippedTrianglesLength = 0;
|
||||
|
||||
private scratch = new Array<number>();
|
||||
private scratch = [] as number[];
|
||||
|
||||
private clipAttachment: ClippingAttachment | null = null;
|
||||
private clippingPolygons: Array<Array<number>> | null = null;
|
||||
@ -63,14 +63,14 @@ export class SkeletonClipping {
|
||||
if (this.clipAttachment) return 0;
|
||||
this.clipAttachment = clip;
|
||||
|
||||
let n = clip.worldVerticesLength;
|
||||
let vertices = Utils.setArraySize(this.clippingPolygon, n);
|
||||
const n = clip.worldVerticesLength;
|
||||
const vertices = Utils.setArraySize(this.clippingPolygon, n);
|
||||
clip.computeWorldVertices(skeleton, slot, 0, n, vertices, 0, 2);
|
||||
let clippingPolygon = this.clippingPolygon;
|
||||
const clippingPolygon = this.clippingPolygon;
|
||||
SkeletonClipping.makeClockwise(clippingPolygon);
|
||||
let clippingPolygons = this.clippingPolygons = this.triangulator.decompose(clippingPolygon, this.triangulator.triangulate(clippingPolygon));
|
||||
const clippingPolygons = this.clippingPolygons = this.triangulator.decompose(clippingPolygon, this.triangulator.triangulate(clippingPolygon));
|
||||
for (let i = 0, n = clippingPolygons.length; i < n; i++) {
|
||||
let polygon = clippingPolygons[i];
|
||||
const polygon = clippingPolygons[i];
|
||||
SkeletonClipping.makeClockwise(polygon);
|
||||
polygon.push(polygon[0]);
|
||||
polygon.push(polygon[1]);
|
||||
@ -109,10 +109,11 @@ export class SkeletonClipping {
|
||||
|
||||
private clipTrianglesNoRender (vertices: NumberArrayLike, triangles: NumberArrayLike, trianglesLength: number): boolean {
|
||||
|
||||
let clipOutput = this.clipOutput, clippedVertices = this.clippedVertices;
|
||||
let clippedTriangles = this.clippedTriangles;
|
||||
let polygons = this.clippingPolygons!;
|
||||
let polygonsCount = polygons.length;
|
||||
const clipOutput = this.clipOutput, clippedVertices = this.clippedVertices;
|
||||
const clippedTriangles = this.clippedTriangles;
|
||||
// biome-ignore lint/style/noNonNullAssertion: clipStart define it
|
||||
const polygons = this.clippingPolygons!;
|
||||
const polygonsCount = polygons.length;
|
||||
|
||||
let index = 0;
|
||||
clippedVertices.length = 0;
|
||||
@ -120,31 +121,31 @@ export class SkeletonClipping {
|
||||
let clipOutputItems = null;
|
||||
for (let i = 0; i < trianglesLength; i += 3) {
|
||||
let v = triangles[i] << 1;
|
||||
let x1 = vertices[v], y1 = vertices[v + 1];
|
||||
const x1 = vertices[v], y1 = vertices[v + 1];
|
||||
|
||||
v = triangles[i + 1] << 1;
|
||||
let x2 = vertices[v], y2 = vertices[v + 1];
|
||||
const x2 = vertices[v], y2 = vertices[v + 1];
|
||||
|
||||
v = triangles[i + 2] << 1;
|
||||
let x3 = vertices[v], y3 = vertices[v + 1];
|
||||
const x3 = vertices[v], y3 = vertices[v + 1];
|
||||
|
||||
for (let p = 0; p < polygonsCount; p++) {
|
||||
let s = clippedVertices.length;
|
||||
if (this.clip(x1, y1, x2, y2, x3, y3, polygons[p], clipOutput)) {
|
||||
clipOutputItems = this.clipOutput;
|
||||
let clipOutputLength = clipOutput.length;
|
||||
if (clipOutputLength == 0) continue;
|
||||
const clipOutputLength = clipOutput.length;
|
||||
if (clipOutputLength === 0) continue;
|
||||
|
||||
let clipOutputCount = clipOutputLength >> 1;
|
||||
let clippedVerticesItems = Utils.setArraySize(clippedVertices, s + clipOutputCount * 2);
|
||||
const clippedVerticesItems = Utils.setArraySize(clippedVertices, s + clipOutputCount * 2);
|
||||
for (let ii = 0; ii < clipOutputLength; ii += 2, s += 2) {
|
||||
let x = clipOutputItems[ii], y = clipOutputItems[ii + 1];
|
||||
const x = clipOutputItems[ii], y = clipOutputItems[ii + 1];
|
||||
clippedVerticesItems[s] = x;
|
||||
clippedVerticesItems[s + 1] = y;
|
||||
}
|
||||
|
||||
s = clippedTriangles.length;
|
||||
let clippedTrianglesItems = Utils.setArraySize(clippedTriangles, s + 3 * (clipOutputCount - 2));
|
||||
const clippedTrianglesItems = Utils.setArraySize(clippedTriangles, s + 3 * (clipOutputCount - 2));
|
||||
clipOutputCount--;
|
||||
for (let ii = 1; ii < clipOutputCount; ii++, s += 3) {
|
||||
clippedTrianglesItems[s] = index;
|
||||
@ -154,7 +155,7 @@ export class SkeletonClipping {
|
||||
index += clipOutputCount + 1;
|
||||
|
||||
} else {
|
||||
let clippedVerticesItems = Utils.setArraySize(clippedVertices, s + 3 * 2);
|
||||
const clippedVerticesItems = Utils.setArraySize(clippedVertices, s + 3 * 2);
|
||||
clippedVerticesItems[s] = x1;
|
||||
clippedVerticesItems[s + 1] = y1;
|
||||
|
||||
@ -165,7 +166,7 @@ export class SkeletonClipping {
|
||||
clippedVerticesItems[s + 5] = y3;
|
||||
|
||||
s = clippedTriangles.length;
|
||||
let clippedTrianglesItems = Utils.setArraySize(clippedTriangles, s + 3);
|
||||
const clippedTrianglesItems = Utils.setArraySize(clippedTriangles, s + 3);
|
||||
clippedTrianglesItems[s] = index;
|
||||
clippedTrianglesItems[s + 1] = (index + 1);
|
||||
clippedTrianglesItems[s + 2] = (index + 2);
|
||||
@ -180,10 +181,11 @@ export class SkeletonClipping {
|
||||
private clipTrianglesRender (vertices: NumberArrayLike, triangles: NumberArrayLike, trianglesLength: number, uvs: NumberArrayLike,
|
||||
light: Color, dark: Color, twoColor: boolean, stride: number): boolean {
|
||||
|
||||
let clipOutput = this.clipOutput, clippedVertices = this.clippedVertices;
|
||||
let clippedTriangles = this.clippedTriangles;
|
||||
let polygons = this.clippingPolygons!;
|
||||
let polygonsCount = polygons.length;
|
||||
const clipOutput = this.clipOutput, clippedVertices = this.clippedVertices;
|
||||
const clippedTriangles = this.clippedTriangles;
|
||||
// biome-ignore lint/style/noNonNullAssertion: clipStart define it
|
||||
const polygons = this.clippingPolygons!;
|
||||
const polygonsCount = polygons.length;
|
||||
|
||||
let index = 0;
|
||||
clippedVertices.length = 0;
|
||||
@ -191,40 +193,40 @@ export class SkeletonClipping {
|
||||
let clipOutputItems = null;
|
||||
for (let i = 0; i < trianglesLength; i += 3) {
|
||||
let t = triangles[i];
|
||||
let u1 = uvs[t << 1], v1 = uvs[(t << 1) + 1];
|
||||
let x1 = vertices[t * stride], y1 = vertices[t * stride + 1];
|
||||
const u1 = uvs[t << 1], v1 = uvs[(t << 1) + 1];
|
||||
const x1 = vertices[t * stride], y1 = vertices[t * stride + 1];
|
||||
|
||||
t = triangles[i + 1];
|
||||
let u2 = uvs[t << 1], v2 = uvs[(t << 1) + 1];
|
||||
let x2 = vertices[t * stride], y2 = vertices[t * stride + 1];
|
||||
const u2 = uvs[t << 1], v2 = uvs[(t << 1) + 1];
|
||||
const x2 = vertices[t * stride], y2 = vertices[t * stride + 1];
|
||||
|
||||
t = triangles[i + 2];
|
||||
let u3 = uvs[t << 1], v3 = uvs[(t << 1) + 1];
|
||||
let x3 = vertices[t * stride], y3 = vertices[t * stride + 1];
|
||||
const u3 = uvs[t << 1], v3 = uvs[(t << 1) + 1];
|
||||
const x3 = vertices[t * stride], y3 = vertices[t * stride + 1];
|
||||
|
||||
for (let p = 0; p < polygonsCount; p++) {
|
||||
let s = clippedVertices.length;
|
||||
if (this.clip(x1, y1, x2, y2, x3, y3, polygons[p], clipOutput)) {
|
||||
clipOutputItems = this.clipOutput;
|
||||
let clipOutputLength = clipOutput.length;
|
||||
if (clipOutputLength == 0) continue;
|
||||
let d0 = y2 - y3, d1 = x3 - x2, d2 = x1 - x3, d4 = y3 - y1;
|
||||
let d = 1 / (d0 * d2 + d1 * (y1 - y3));
|
||||
const clipOutputLength = clipOutput.length;
|
||||
if (clipOutputLength === 0) continue;
|
||||
const d0 = y2 - y3, d1 = x3 - x2, d2 = x1 - x3, d4 = y3 - y1;
|
||||
const d = 1 / (d0 * d2 + d1 * (y1 - y3));
|
||||
|
||||
let clipOutputCount = clipOutputLength >> 1;
|
||||
let clippedVerticesItems = Utils.setArraySize(clippedVertices, s + clipOutputCount * stride);
|
||||
const clippedVerticesItems = Utils.setArraySize(clippedVertices, s + clipOutputCount * stride);
|
||||
for (let ii = 0; ii < clipOutputLength; ii += 2, s += stride) {
|
||||
let x = clipOutputItems[ii], y = clipOutputItems[ii + 1];
|
||||
const x = clipOutputItems[ii], y = clipOutputItems[ii + 1];
|
||||
clippedVerticesItems[s] = x;
|
||||
clippedVerticesItems[s + 1] = y;
|
||||
clippedVerticesItems[s + 2] = light.r;
|
||||
clippedVerticesItems[s + 3] = light.g;
|
||||
clippedVerticesItems[s + 4] = light.b;
|
||||
clippedVerticesItems[s + 5] = light.a;
|
||||
let c0 = x - x3, c1 = y - y3;
|
||||
let a = (d0 * c0 + d1 * c1) * d;
|
||||
let b = (d4 * c0 + d2 * c1) * d;
|
||||
let c = 1 - a - b;
|
||||
const c0 = x - x3, c1 = y - y3;
|
||||
const a = (d0 * c0 + d1 * c1) * d;
|
||||
const b = (d4 * c0 + d2 * c1) * d;
|
||||
const c = 1 - a - b;
|
||||
clippedVerticesItems[s + 6] = u1 * a + u2 * b + u3 * c;
|
||||
clippedVerticesItems[s + 7] = v1 * a + v2 * b + v3 * c;
|
||||
if (twoColor) {
|
||||
@ -236,7 +238,7 @@ export class SkeletonClipping {
|
||||
}
|
||||
|
||||
s = clippedTriangles.length;
|
||||
let clippedTrianglesItems = Utils.setArraySize(clippedTriangles, s + 3 * (clipOutputCount - 2));
|
||||
const clippedTrianglesItems = Utils.setArraySize(clippedTriangles, s + 3 * (clipOutputCount - 2));
|
||||
clipOutputCount--;
|
||||
for (let ii = 1; ii < clipOutputCount; ii++, s += 3) {
|
||||
clippedTrianglesItems[s] = index;
|
||||
@ -246,7 +248,7 @@ export class SkeletonClipping {
|
||||
index += clipOutputCount + 1;
|
||||
|
||||
} else {
|
||||
let clippedVerticesItems = Utils.setArraySize(clippedVertices, s + 3 * stride);
|
||||
const clippedVerticesItems = Utils.setArraySize(clippedVertices, s + 3 * stride);
|
||||
clippedVerticesItems[s] = x1;
|
||||
clippedVerticesItems[s + 1] = y1;
|
||||
clippedVerticesItems[s + 2] = light.r;
|
||||
@ -310,7 +312,7 @@ export class SkeletonClipping {
|
||||
}
|
||||
|
||||
s = clippedTriangles.length;
|
||||
let clippedTrianglesItems = Utils.setArraySize(clippedTriangles, s + 3);
|
||||
const clippedTrianglesItems = Utils.setArraySize(clippedTriangles, s + 3);
|
||||
clippedTrianglesItems[s] = index;
|
||||
clippedTrianglesItems[s + 1] = (index + 1);
|
||||
clippedTrianglesItems[s + 2] = (index + 2);
|
||||
@ -325,6 +327,7 @@ export class SkeletonClipping {
|
||||
public clipTrianglesUnpacked (vertices: NumberArrayLike, triangles: NumberArrayLike | Uint32Array, trianglesLength: number, uvs: NumberArrayLike) {
|
||||
const clipOutput = this.clipOutput;
|
||||
let clippedVertices = this._clippedVerticesTyped, clippedUVs = this._clippedUVsTyped, clippedTriangles = this._clippedTrianglesTyped;
|
||||
// biome-ignore lint/style/noNonNullAssertion: clipStart define it
|
||||
const polygons = this.clippingPolygons!;
|
||||
const polygonsCount = polygons.length;
|
||||
|
||||
@ -464,7 +467,7 @@ export class SkeletonClipping {
|
||||
/** Clips the input triangle against the convex, clockwise clipping area. If the triangle lies entirely within the clipping
|
||||
* area, false is returned. The clipping area must duplicate the first vertex at the end of the vertices list. */
|
||||
private clip (x1: number, y1: number, x2: number, y2: number, x3: number, y3: number, clippingArea: Array<number>, output: Array<number>) {
|
||||
let originalOutput = output;
|
||||
const originalOutput = output;
|
||||
let clipped = false;
|
||||
|
||||
// Avoid copy at the end.
|
||||
@ -486,20 +489,20 @@ export class SkeletonClipping {
|
||||
input.push(y1);
|
||||
output.length = 0;
|
||||
|
||||
let clippingVerticesLast = clippingArea.length - 4;
|
||||
let clippingVertices = clippingArea;
|
||||
const clippingVerticesLast = clippingArea.length - 4;
|
||||
const clippingVertices = clippingArea;
|
||||
for (let i = 0; ; i += 2) {
|
||||
let edgeX = clippingVertices[i], edgeY = clippingVertices[i + 1];
|
||||
let ex = edgeX - clippingVertices[i + 2], ey = edgeY - clippingVertices[i + 3];
|
||||
const edgeX = clippingVertices[i], edgeY = clippingVertices[i + 1];
|
||||
const ex = edgeX - clippingVertices[i + 2], ey = edgeY - clippingVertices[i + 3];
|
||||
|
||||
let outputStart = output.length;
|
||||
let inputVertices = input;
|
||||
const outputStart = output.length;
|
||||
const inputVertices = input;
|
||||
for (let ii = 0, nn = input.length - 2; ii < nn;) {
|
||||
let inputX = inputVertices[ii], inputY = inputVertices[ii + 1];
|
||||
const inputX = inputVertices[ii], inputY = inputVertices[ii + 1];
|
||||
ii += 2;
|
||||
let inputX2 = inputVertices[ii], inputY2 = inputVertices[ii + 1];
|
||||
let s2 = ey * (edgeX - inputX2) > ex * (edgeY - inputY2);
|
||||
let s1 = ey * (edgeX - inputX) - ex * (edgeY - inputY);
|
||||
const inputX2 = inputVertices[ii], inputY2 = inputVertices[ii + 1];
|
||||
const s2 = ey * (edgeX - inputX2) > ex * (edgeY - inputY2);
|
||||
const s1 = ey * (edgeX - inputX) - ex * (edgeY - inputY);
|
||||
if (s1 > 0) {
|
||||
if (s2) { // v1 inside, v2 inside
|
||||
output.push(inputX2);
|
||||
@ -507,7 +510,7 @@ export class SkeletonClipping {
|
||||
continue;
|
||||
}
|
||||
// v1 inside, v2 outside
|
||||
let ix = inputX2 - inputX, iy = inputY2 - inputY, t = s1 / (ix * ey - iy * ex);
|
||||
const ix = inputX2 - inputX, iy = inputY2 - inputY, t = s1 / (ix * ey - iy * ex);
|
||||
if (t >= 0 && t <= 1) {
|
||||
output.push(inputX + ix * t);
|
||||
output.push(inputY + iy * t);
|
||||
@ -517,7 +520,7 @@ export class SkeletonClipping {
|
||||
continue;
|
||||
}
|
||||
} else if (s2) { // v1 outside, v2 inside
|
||||
let ix = inputX2 - inputX, iy = inputY2 - inputY, t = s1 / (ix * ey - iy * ex);
|
||||
const ix = inputX2 - inputX, iy = inputY2 - inputY, t = s1 / (ix * ey - iy * ex);
|
||||
if (t >= 0 && t <= 1) {
|
||||
output.push(inputX + ix * t);
|
||||
output.push(inputY + iy * t);
|
||||
@ -532,7 +535,7 @@ export class SkeletonClipping {
|
||||
clipped = true;
|
||||
}
|
||||
|
||||
if (outputStart == output.length) { // All edges outside.
|
||||
if (outputStart === output.length) { // All edges outside.
|
||||
originalOutput.length = 0;
|
||||
return true;
|
||||
}
|
||||
@ -540,14 +543,14 @@ export class SkeletonClipping {
|
||||
output.push(output[0]);
|
||||
output.push(output[1]);
|
||||
|
||||
if (i == clippingVerticesLast) break;
|
||||
let temp = output;
|
||||
if (i === clippingVerticesLast) break;
|
||||
const temp = output;
|
||||
output = input;
|
||||
output.length = 0;
|
||||
input = temp;
|
||||
}
|
||||
|
||||
if (originalOutput != output) {
|
||||
if (originalOutput !== output) {
|
||||
originalOutput.length = 0;
|
||||
for (let i = 0, n = output.length - 2; i < n; i++)
|
||||
originalOutput[i] = output[i];
|
||||
@ -558,8 +561,8 @@ export class SkeletonClipping {
|
||||
}
|
||||
|
||||
public static makeClockwise (polygon: NumberArrayLike) {
|
||||
let vertices = polygon;
|
||||
let verticeslength = polygon.length;
|
||||
const vertices = polygon;
|
||||
const verticeslength = polygon.length;
|
||||
|
||||
let area = vertices[verticeslength - 2] * vertices[1] - vertices[0] * vertices[verticeslength - 1], p1x = 0, p1y = 0, p2x = 0, p2y = 0;
|
||||
for (let i = 0, n = verticeslength - 3; i < n; i += 2) {
|
||||
@ -572,8 +575,8 @@ export class SkeletonClipping {
|
||||
if (area < 0) return;
|
||||
|
||||
for (let i = 0, lastX = verticeslength - 2, n = verticeslength >> 1; i < n; i += 2) {
|
||||
let x = vertices[i], y = vertices[i + 1];
|
||||
let other = lastX - i;
|
||||
const x = vertices[i], y = vertices[i + 1];
|
||||
const other = lastX - i;
|
||||
vertices[i] = vertices[other];
|
||||
vertices[i + 1] = vertices[other + 1];
|
||||
vertices[other] = x;
|
||||
|
||||
@ -27,12 +27,12 @@
|
||||
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*****************************************************************************/
|
||||
|
||||
import { Animation } from "./Animation"
|
||||
import { BoneData } from "./BoneData.js";
|
||||
import { ConstraintData } from "./ConstraintData";
|
||||
import { EventData } from "./EventData.js";
|
||||
import { Skin } from "./Skin.js";
|
||||
import { SlotData } from "./SlotData.js";
|
||||
import type { Animation } from "./Animation"
|
||||
import type { BoneData } from "./BoneData.js";
|
||||
import type { ConstraintData } from "./ConstraintData";
|
||||
import type { EventData } from "./EventData.js";
|
||||
import type { Skin } from "./Skin.js";
|
||||
import type { SlotData } from "./SlotData.js";
|
||||
|
||||
/** Stores the setup pose and all of the stateless data for a skeleton.
|
||||
*
|
||||
@ -44,12 +44,12 @@ export class SkeletonData {
|
||||
name: string | null = null;
|
||||
|
||||
/** The skeleton's bones, sorted parent first. The root bone is always the first bone. */
|
||||
bones = new Array<BoneData>(); // Ordered parents first.
|
||||
bones = [] as BoneData[]; // Ordered parents first.
|
||||
|
||||
/** The skeleton's slots in the setup pose draw order. */
|
||||
slots = new Array<SlotData>(); // Setup pose draw order.
|
||||
slots = [] as SlotData[]; // Setup pose draw order.
|
||||
|
||||
skins = new Array<Skin>();
|
||||
skins = [] as Skin[];
|
||||
|
||||
/** The skeleton's default skin. By default this skin contains all attachments that were not in a skin in Spine.
|
||||
*
|
||||
@ -58,13 +58,14 @@ export class SkeletonData {
|
||||
defaultSkin: Skin | null = null;
|
||||
|
||||
/** The skeleton's events. */
|
||||
events = new Array<EventData>();
|
||||
events = [] as EventData[];
|
||||
|
||||
/** The skeleton's animations. */
|
||||
animations = new Array<Animation>();
|
||||
animations = [] as Animation[];
|
||||
|
||||
/** The skeleton's IK constraints. */
|
||||
constraints = new Array<ConstraintData<any, any>>();
|
||||
// biome-ignore lint/suspicious/noExplicitAny: reference runtime does not restrict to specific types
|
||||
constraints = [] as ConstraintData<any, any>[];
|
||||
|
||||
/** The X coordinate of the skeleton's axis aligned bounding box in the setup pose. */
|
||||
x: number = 0;
|
||||
@ -103,9 +104,9 @@ export class SkeletonData {
|
||||
* @returns May be null. */
|
||||
findBone (boneName: string) {
|
||||
if (!boneName) throw new Error("boneName cannot be null.");
|
||||
let bones = this.bones;
|
||||
const bones = this.bones;
|
||||
for (let i = 0, n = bones.length; i < n; i++)
|
||||
if (bones[i].name == boneName) return bones[i];
|
||||
if (bones[i].name === boneName) return bones[i];
|
||||
return null;
|
||||
}
|
||||
|
||||
@ -114,9 +115,9 @@ export class SkeletonData {
|
||||
* @returns May be null. */
|
||||
findSlot (slotName: string) {
|
||||
if (!slotName) throw new Error("slotName cannot be null.");
|
||||
let slots = this.slots;
|
||||
const slots = this.slots;
|
||||
for (let i = 0, n = slots.length; i < n; i++)
|
||||
if (slots[i].name == slotName) return slots[i];
|
||||
if (slots[i].name === slotName) return slots[i];
|
||||
return null;
|
||||
}
|
||||
|
||||
@ -125,9 +126,9 @@ export class SkeletonData {
|
||||
* @returns May be null. */
|
||||
findSkin (skinName: string) {
|
||||
if (!skinName) throw new Error("skinName cannot be null.");
|
||||
let skins = this.skins;
|
||||
const skins = this.skins;
|
||||
for (let i = 0, n = skins.length; i < n; i++)
|
||||
if (skins[i].name == skinName) return skins[i];
|
||||
if (skins[i].name === skinName) return skins[i];
|
||||
return null;
|
||||
}
|
||||
|
||||
@ -136,9 +137,9 @@ export class SkeletonData {
|
||||
* @returns May be null. */
|
||||
findEvent (eventDataName: string) {
|
||||
if (!eventDataName) throw new Error("eventDataName cannot be null.");
|
||||
let events = this.events;
|
||||
const events = this.events;
|
||||
for (let i = 0, n = events.length; i < n; i++)
|
||||
if (events[i].name == eventDataName) return events[i];
|
||||
if (events[i].name === eventDataName) return events[i];
|
||||
return null;
|
||||
}
|
||||
|
||||
@ -147,20 +148,21 @@ export class SkeletonData {
|
||||
* @returns May be null. */
|
||||
findAnimation (animationName: string) {
|
||||
if (!animationName) throw new Error("animationName cannot be null.");
|
||||
let animations = this.animations;
|
||||
const animations = this.animations;
|
||||
for (let i = 0, n = animations.length; i < n; i++)
|
||||
if (animations[i].name == animationName) return animations[i];
|
||||
if (animations[i].name === animationName) return animations[i];
|
||||
return null;
|
||||
}
|
||||
|
||||
// --- Constraints.
|
||||
|
||||
// biome-ignore lint/suspicious/noExplicitAny: reference runtime does not restrict to specific types
|
||||
findConstraint<T extends ConstraintData<any, any>> (constraintName: string, type: new (name: string) => T): T | null {
|
||||
if (!constraintName) throw new Error("constraintName cannot be null.");
|
||||
if (type == null) throw new Error("type cannot be null.");
|
||||
const constraints = this.constraints;
|
||||
for (let i = 0, n = this.constraints.length; i < n; i++) {
|
||||
let constraint = constraints[i];
|
||||
const constraint = constraints[i];
|
||||
if (constraint instanceof type && constraint.name === constraintName) return constraint as T;
|
||||
}
|
||||
return null;
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -69,6 +69,7 @@ export class SkeletonRendererCore {
|
||||
let indices: number[] | Uint32Array;
|
||||
let indicesCount: number;
|
||||
let attachmentColor: Color;
|
||||
// biome-ignore lint/suspicious/noExplicitAny: texture depends on the runtime
|
||||
let texture: any;
|
||||
|
||||
if (attachment instanceof RegionAttachment) {
|
||||
@ -270,6 +271,7 @@ interface RenderCommand {
|
||||
numVertices: number;
|
||||
numIndices: number;
|
||||
blendMode: BlendMode;
|
||||
// biome-ignore lint/suspicious/noExplicitAny: texture depends on the runtime
|
||||
texture: any;
|
||||
next?: RenderCommand;
|
||||
}
|
||||
|
||||
@ -27,12 +27,12 @@
|
||||
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*****************************************************************************/
|
||||
|
||||
import { Attachment } from "./attachments/Attachment.js";
|
||||
import type { Attachment } from "./attachments/Attachment.js";
|
||||
import { MeshAttachment } from "./attachments/MeshAttachment.js";
|
||||
import { BoneData } from "./BoneData.js";
|
||||
import { ConstraintData } from "./ConstraintData.js";
|
||||
import { Skeleton } from "./Skeleton.js";
|
||||
import { Color, StringMap } from "./Utils.js";
|
||||
import type { BoneData } from "./BoneData.js";
|
||||
import type { ConstraintData } from "./ConstraintData.js";
|
||||
import type { Skeleton } from "./Skeleton.js";
|
||||
import { Color, type StringMap } from "./Utils.js";
|
||||
|
||||
/** Stores an entry in the skin consisting of the slot index, name, and attachment **/
|
||||
export class SkinEntry {
|
||||
@ -47,9 +47,10 @@ export class Skin {
|
||||
/** The skin's name, which is unique across all skins in the skeleton. */
|
||||
name: string;
|
||||
|
||||
attachments = new Array<StringMap<Attachment>>();
|
||||
bones = Array<BoneData>();
|
||||
constraints = new Array<ConstraintData<any, any>>();
|
||||
attachments = [] as StringMap<Attachment>[];
|
||||
bones = [] as BoneData[];
|
||||
// biome-ignore lint/suspicious/noExplicitAny: reference runtime does not restrict to specific types
|
||||
constraints = [] as ConstraintData<any, any>[];
|
||||
|
||||
/** The color of the skin as it was in Spine, or a default color if nonessential data was not exported. */
|
||||
color = new Color(0.99607843, 0.61960787, 0.30980393, 1); // fe9e4fff
|
||||
@ -62,7 +63,7 @@ export class Skin {
|
||||
/** Adds an attachment to the skin for the specified slot index and name. */
|
||||
setAttachment (slotIndex: number, name: string, attachment: Attachment) {
|
||||
if (!attachment) throw new Error("attachment cannot be null.");
|
||||
let attachments = this.attachments;
|
||||
const attachments = this.attachments;
|
||||
if (slotIndex >= attachments.length) attachments.length = slotIndex + 1;
|
||||
if (!attachments[slotIndex]) attachments[slotIndex] = {};
|
||||
attachments[slotIndex][name] = attachment;
|
||||
@ -71,10 +72,10 @@ export class Skin {
|
||||
/** Adds all attachments, bones, and constraints from the specified skin to this skin. */
|
||||
addSkin (skin: Skin) {
|
||||
for (let i = 0; i < skin.bones.length; i++) {
|
||||
let bone = skin.bones[i];
|
||||
const bone = skin.bones[i];
|
||||
let contained = false;
|
||||
for (let ii = 0; ii < this.bones.length; ii++) {
|
||||
if (this.bones[ii] == bone) {
|
||||
if (this.bones[ii] === bone) {
|
||||
contained = true;
|
||||
break;
|
||||
}
|
||||
@ -83,10 +84,10 @@ export class Skin {
|
||||
}
|
||||
|
||||
for (let i = 0; i < skin.constraints.length; i++) {
|
||||
let constraint = skin.constraints[i];
|
||||
const constraint = skin.constraints[i];
|
||||
let contained = false;
|
||||
for (let ii = 0; ii < this.constraints.length; ii++) {
|
||||
if (this.constraints[ii] == constraint) {
|
||||
if (this.constraints[ii] === constraint) {
|
||||
contained = true;
|
||||
break;
|
||||
}
|
||||
@ -94,9 +95,9 @@ export class Skin {
|
||||
if (!contained) this.constraints.push(constraint);
|
||||
}
|
||||
|
||||
let attachments = skin.getAttachments();
|
||||
const attachments = skin.getAttachments();
|
||||
for (let i = 0; i < attachments.length; i++) {
|
||||
var attachment = attachments[i];
|
||||
const attachment = attachments[i];
|
||||
this.setAttachment(attachment.slotIndex, attachment.name, attachment.attachment);
|
||||
}
|
||||
}
|
||||
@ -105,10 +106,10 @@ export class Skin {
|
||||
* copied, instead a new linked mesh is created. The attachment copies can be modified without affecting the originals. */
|
||||
copySkin (skin: Skin) {
|
||||
for (let i = 0; i < skin.bones.length; i++) {
|
||||
let bone = skin.bones[i];
|
||||
const bone = skin.bones[i];
|
||||
let contained = false;
|
||||
for (let ii = 0; ii < this.bones.length; ii++) {
|
||||
if (this.bones[ii] == bone) {
|
||||
if (this.bones[ii] === bone) {
|
||||
contained = true;
|
||||
break;
|
||||
}
|
||||
@ -117,10 +118,10 @@ export class Skin {
|
||||
}
|
||||
|
||||
for (let i = 0; i < skin.constraints.length; i++) {
|
||||
let constraint = skin.constraints[i];
|
||||
const constraint = skin.constraints[i];
|
||||
let contained = false;
|
||||
for (let ii = 0; ii < this.constraints.length; ii++) {
|
||||
if (this.constraints[ii] == constraint) {
|
||||
if (this.constraints[ii] === constraint) {
|
||||
contained = true;
|
||||
break;
|
||||
}
|
||||
@ -128,9 +129,9 @@ export class Skin {
|
||||
if (!contained) this.constraints.push(constraint);
|
||||
}
|
||||
|
||||
let attachments = skin.getAttachments();
|
||||
const attachments = skin.getAttachments();
|
||||
for (let i = 0; i < attachments.length; i++) {
|
||||
var attachment = attachments[i];
|
||||
const attachment = attachments[i];
|
||||
if (!attachment.attachment) continue;
|
||||
if (attachment.attachment instanceof MeshAttachment) {
|
||||
attachment.attachment = attachment.attachment.newLinkedMesh();
|
||||
@ -144,24 +145,24 @@ export class Skin {
|
||||
|
||||
/** Returns the attachment for the specified slot index and name, or null. */
|
||||
getAttachment (slotIndex: number, name: string): Attachment | null {
|
||||
let dictionary = this.attachments[slotIndex];
|
||||
const dictionary = this.attachments[slotIndex];
|
||||
return dictionary ? dictionary[name] : null;
|
||||
}
|
||||
|
||||
/** Removes the attachment in the skin for the specified slot index and name, if any. */
|
||||
removeAttachment (slotIndex: number, name: string) {
|
||||
let dictionary = this.attachments[slotIndex];
|
||||
const dictionary = this.attachments[slotIndex];
|
||||
if (dictionary) delete dictionary[name];
|
||||
}
|
||||
|
||||
/** Returns all attachments in this skin. */
|
||||
getAttachments (): Array<SkinEntry> {
|
||||
let entries = new Array<SkinEntry>();
|
||||
for (var i = 0; i < this.attachments.length; i++) {
|
||||
let slotAttachments = this.attachments[i];
|
||||
const entries: SkinEntry[] = [];
|
||||
for (let i = 0; i < this.attachments.length; i++) {
|
||||
const slotAttachments = this.attachments[i];
|
||||
if (slotAttachments) {
|
||||
for (let name in slotAttachments) {
|
||||
let attachment = slotAttachments[name];
|
||||
for (const name in slotAttachments) {
|
||||
const attachment = slotAttachments[name];
|
||||
if (attachment) entries.push(new SkinEntry(i, name, attachment));
|
||||
}
|
||||
}
|
||||
@ -171,10 +172,10 @@ export class Skin {
|
||||
|
||||
/** Returns all attachments in this skin for the specified slot index. */
|
||||
getAttachmentsForSlot (slotIndex: number, attachments: Array<SkinEntry>) {
|
||||
let slotAttachments = this.attachments[slotIndex];
|
||||
const slotAttachments = this.attachments[slotIndex];
|
||||
if (slotAttachments) {
|
||||
for (let name in slotAttachments) {
|
||||
let attachment = slotAttachments[name];
|
||||
for (const name in slotAttachments) {
|
||||
const attachment = slotAttachments[name];
|
||||
if (attachment) attachments.push(new SkinEntry(slotIndex, name, attachment));
|
||||
}
|
||||
}
|
||||
@ -191,14 +192,14 @@ export class Skin {
|
||||
attachAll (skeleton: Skeleton, oldSkin: Skin) {
|
||||
let slotIndex = 0;
|
||||
for (let i = 0; i < skeleton.slots.length; i++) {
|
||||
let slot = skeleton.slots[i];
|
||||
let slotAttachment = slot.pose.getAttachment();
|
||||
const slot = skeleton.slots[i];
|
||||
const slotAttachment = slot.pose.getAttachment();
|
||||
if (slotAttachment && slotIndex < oldSkin.attachments.length) {
|
||||
let dictionary = oldSkin.attachments[slotIndex];
|
||||
for (let key in dictionary) {
|
||||
let skinAttachment: Attachment = dictionary[key];
|
||||
if (slotAttachment == skinAttachment) {
|
||||
let attachment = this.getAttachment(slotIndex, key);
|
||||
const dictionary = oldSkin.attachments[slotIndex];
|
||||
for (const key in dictionary) {
|
||||
const skinAttachment: Attachment = dictionary[key];
|
||||
if (slotAttachment === skinAttachment) {
|
||||
const attachment = this.getAttachment(slotIndex, key);
|
||||
if (attachment) slot.pose.setAttachment(attachment);
|
||||
break;
|
||||
}
|
||||
|
||||
@ -27,13 +27,13 @@
|
||||
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*****************************************************************************/
|
||||
|
||||
import { Animation } from "./Animation.js";
|
||||
import { BoneData } from "./BoneData.js";
|
||||
import type { Animation } from "./Animation.js";
|
||||
import type { BoneData } from "./BoneData.js";
|
||||
import { ConstraintData } from "./ConstraintData.js";
|
||||
import { Skeleton } from "./Skeleton.js";
|
||||
import type { Skeleton } from "./Skeleton.js";
|
||||
import { Slider } from "./Slider.js";
|
||||
import { SliderPose } from "./SliderPose.js";
|
||||
import { FromProperty } from "./TransformConstraintData.js";
|
||||
import type { FromProperty } from "./TransformConstraintData.js";
|
||||
|
||||
/** Stores the setup pose for a {@link SliderConstraint}.
|
||||
*
|
||||
|
||||
@ -27,7 +27,7 @@
|
||||
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*****************************************************************************/
|
||||
|
||||
import { Pose } from "./Pose.js";
|
||||
import type { Pose } from "./Pose.js";
|
||||
|
||||
/** Stores a pose for a slider. */
|
||||
export class SliderPose implements Pose<SliderPose> {
|
||||
|
||||
@ -27,10 +27,10 @@
|
||||
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*****************************************************************************/
|
||||
|
||||
import { Bone } from "./Bone.js";
|
||||
import type { Bone } from "./Bone.js";
|
||||
import { Posed } from "./Posed.js";
|
||||
import { Skeleton } from "./Skeleton.js";
|
||||
import { SlotData } from "./SlotData.js";
|
||||
import type { Skeleton } from "./Skeleton.js";
|
||||
import type { SlotData } from "./SlotData.js";
|
||||
import { SlotPose } from "./SlotPose.js";
|
||||
import { Color } from "./Utils.js";
|
||||
|
||||
@ -59,6 +59,7 @@ export class Slot extends Posed<SlotData, SlotPose, SlotPose> {
|
||||
|
||||
setupPose () {
|
||||
this.pose.color.setFromColor(this.data.setup.color);
|
||||
// biome-ignore lint/style/noNonNullAssertion: reference runtime
|
||||
if (this.pose.darkColor) this.pose.darkColor.setFromColor(this.data.setup.darkColor!);
|
||||
this.pose.sequenceIndex = this.data.setup.sequenceIndex;
|
||||
if (!this.data.attachmentName)
|
||||
|
||||
@ -27,11 +27,10 @@
|
||||
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*****************************************************************************/
|
||||
|
||||
import { BoneData } from "./BoneData.js";
|
||||
import type { BoneData } from "./BoneData.js";
|
||||
import { PosedData } from "./PosedData.js";
|
||||
import { SlotPose } from "./SlotPose.js";
|
||||
|
||||
import type { Skeleton } from "./Skeleton.js";
|
||||
import { SlotPose } from "./SlotPose.js";
|
||||
|
||||
/** Stores the setup pose for a {@link Slot}. */
|
||||
export class SlotData extends PosedData<SlotPose> {
|
||||
|
||||
@ -27,11 +27,11 @@
|
||||
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*****************************************************************************/
|
||||
|
||||
import { Pose } from "./Pose.js";
|
||||
import { Color } from "./Utils.js";
|
||||
import type { Attachment } from "./attachments/Attachment.js";
|
||||
import { VertexAttachment } from "./attachments/Attachment.js";
|
||||
import { Attachment } from "./attachments/Attachment.js";
|
||||
import type { Sequence } from "./attachments/Sequence.js";
|
||||
import type { Pose } from "./Pose.js";
|
||||
import { Color } from "./Utils.js";
|
||||
|
||||
/** Stores a slot's pose. Slots organize attachments for {@link Skeleton#drawOrder} purposes and provide a place to store state
|
||||
* for an attachment. State cannot be stored in an attachment itself because attachments are stateless and may be shared across
|
||||
@ -57,7 +57,7 @@ export class SlotPose implements Pose<SlotPose> {
|
||||
*
|
||||
* See {@link VertexAttachment.computeWorldVertices()} and
|
||||
* {@link DeformTimeline}. */
|
||||
readonly deform = new Array<number>();
|
||||
readonly deform = [] as number[];
|
||||
|
||||
SlotPose () {
|
||||
}
|
||||
@ -81,9 +81,9 @@ export class SlotPose implements Pose<SlotPose> {
|
||||
* The deform is not cleared if the old attachment has the same {@link VertexAttachment.getTimelineAttachment()} as the
|
||||
* specified attachment. */
|
||||
setAttachment (attachment: Attachment | null): void {
|
||||
if (this.attachment == attachment) return;
|
||||
if (this.attachment === attachment) return;
|
||||
if (!(attachment instanceof VertexAttachment) || !(this.attachment instanceof VertexAttachment)
|
||||
|| attachment.timelineAttachment != this.attachment.timelineAttachment) {
|
||||
|| attachment.timelineAttachment !== this.attachment.timelineAttachment) {
|
||||
this.deform.length = 0;
|
||||
}
|
||||
this.attachment = attachment;
|
||||
|
||||
@ -27,6 +27,8 @@
|
||||
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*****************************************************************************/
|
||||
|
||||
/** biome-ignore-all lint/suspicious/noExplicitAny: textures can be various type */
|
||||
|
||||
export abstract class Texture {
|
||||
protected _image: HTMLImageElement | ImageBitmap | any;
|
||||
|
||||
|
||||
@ -27,86 +27,86 @@
|
||||
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*****************************************************************************/
|
||||
|
||||
import { AssetManagerBase } from "./AssetManagerBase.js";
|
||||
import { TextureFilter, TextureWrap, Texture, TextureRegion } from "./Texture.js";
|
||||
import { Disposable, Utils, StringMap } from "./Utils.js";
|
||||
import type { AssetManagerBase } from "./AssetManagerBase.js";
|
||||
import { type Texture, TextureFilter, TextureRegion, TextureWrap } from "./Texture.js";
|
||||
import { type Disposable, type StringMap, Utils } from "./Utils.js";
|
||||
|
||||
export class TextureAtlas implements Disposable {
|
||||
pages = new Array<TextureAtlasPage>();
|
||||
regions = new Array<TextureAtlasRegion>();
|
||||
pages = [] as TextureAtlasPage[];
|
||||
regions = [] as TextureAtlasRegion[];
|
||||
|
||||
constructor (atlasText: string) {
|
||||
let reader = new TextureAtlasReader(atlasText);
|
||||
let entry = new Array<string>(4);
|
||||
const reader = new TextureAtlasReader(atlasText);
|
||||
const entry = new Array<string>(4);
|
||||
|
||||
let pageFields: StringMap<(page: TextureAtlasPage) => void> = {};
|
||||
pageFields["size"] = (page: TextureAtlasPage) => {
|
||||
page!.width = parseInt(entry[1]);
|
||||
page!.height = parseInt(entry[2]);
|
||||
const pageFields: StringMap<(page: TextureAtlasPage) => void> = {};
|
||||
pageFields.size = (page: TextureAtlasPage) => {
|
||||
page.width = parseInt(entry[1]);
|
||||
page.height = parseInt(entry[2]);
|
||||
};
|
||||
pageFields["format"] = () => {
|
||||
pageFields.format = () => {
|
||||
// page.format = Format[tuple[0]]; we don't need format in WebGL
|
||||
};
|
||||
pageFields["filter"] = (page: TextureAtlasPage) => {
|
||||
page!.minFilter = Utils.enumValue(TextureFilter, entry[1]);
|
||||
page!.magFilter = Utils.enumValue(TextureFilter, entry[2]);
|
||||
pageFields.filter = (page: TextureAtlasPage) => {
|
||||
page.minFilter = Utils.enumValue(TextureFilter, entry[1]);
|
||||
page.magFilter = Utils.enumValue(TextureFilter, entry[2]);
|
||||
};
|
||||
pageFields["repeat"] = (page: TextureAtlasPage) => {
|
||||
if (entry[1].indexOf('x') != -1) page!.uWrap = TextureWrap.Repeat;
|
||||
if (entry[1].indexOf('y') != -1) page!.vWrap = TextureWrap.Repeat;
|
||||
pageFields.repeat = (page: TextureAtlasPage) => {
|
||||
if (entry[1].indexOf('x') !== -1) page.uWrap = TextureWrap.Repeat;
|
||||
if (entry[1].indexOf('y') !== -1) page.vWrap = TextureWrap.Repeat;
|
||||
};
|
||||
pageFields["pma"] = (page: TextureAtlasPage) => {
|
||||
page!.pma = entry[1] == "true";
|
||||
pageFields.pma = (page: TextureAtlasPage) => {
|
||||
page.pma = entry[1] === "true";
|
||||
};
|
||||
|
||||
var regionFields: StringMap<(region: TextureAtlasRegion) => void> = {};
|
||||
regionFields["xy"] = (region: TextureAtlasRegion) => { // Deprecated, use bounds.
|
||||
regionFields.xy = (region: TextureAtlasRegion) => { // Deprecated, use bounds.
|
||||
region.x = parseInt(entry[1]);
|
||||
region.y = parseInt(entry[2]);
|
||||
};
|
||||
regionFields["size"] = (region: TextureAtlasRegion) => { // Deprecated, use bounds.
|
||||
regionFields.size = (region: TextureAtlasRegion) => { // Deprecated, use bounds.
|
||||
region.width = parseInt(entry[1]);
|
||||
region.height = parseInt(entry[2]);
|
||||
};
|
||||
regionFields["bounds"] = (region: TextureAtlasRegion) => {
|
||||
regionFields.bounds = (region: TextureAtlasRegion) => {
|
||||
region.x = parseInt(entry[1]);
|
||||
region.y = parseInt(entry[2]);
|
||||
region.width = parseInt(entry[3]);
|
||||
region.height = parseInt(entry[4]);
|
||||
};
|
||||
regionFields["offset"] = (region: TextureAtlasRegion) => { // Deprecated, use offsets.
|
||||
regionFields.offset = (region: TextureAtlasRegion) => { // Deprecated, use offsets.
|
||||
region.offsetX = parseInt(entry[1]);
|
||||
region.offsetY = parseInt(entry[2]);
|
||||
};
|
||||
regionFields["orig"] = (region: TextureAtlasRegion) => { // Deprecated, use offsets.
|
||||
regionFields.orig = (region: TextureAtlasRegion) => { // Deprecated, use offsets.
|
||||
region.originalWidth = parseInt(entry[1]);
|
||||
region.originalHeight = parseInt(entry[2]);
|
||||
};
|
||||
regionFields["offsets"] = (region: TextureAtlasRegion) => {
|
||||
regionFields.offsets = (region: TextureAtlasRegion) => {
|
||||
region.offsetX = parseInt(entry[1]);
|
||||
region.offsetY = parseInt(entry[2]);
|
||||
region.originalWidth = parseInt(entry[3]);
|
||||
region.originalHeight = parseInt(entry[4]);
|
||||
};
|
||||
regionFields["rotate"] = (region: TextureAtlasRegion) => {
|
||||
let value = entry[1];
|
||||
if (value == "true")
|
||||
regionFields.rotate = (region: TextureAtlasRegion) => {
|
||||
const value = entry[1];
|
||||
if (value === "true")
|
||||
region.degrees = 90;
|
||||
else if (value != "false")
|
||||
else if (value !== "false")
|
||||
region.degrees = parseInt(value);
|
||||
};
|
||||
regionFields["index"] = (region: TextureAtlasRegion) => {
|
||||
regionFields.index = (region: TextureAtlasRegion) => {
|
||||
region.index = parseInt(entry[1]);
|
||||
};
|
||||
|
||||
let line = reader.readLine();
|
||||
// Ignore empty lines before first entry.
|
||||
while (line && line.trim().length == 0)
|
||||
while (line && line.trim().length === 0)
|
||||
line = reader.readLine();
|
||||
// Header entries.
|
||||
while (true) {
|
||||
if (!line || line.trim().length == 0) break;
|
||||
if (reader.readEntry(entry, line) == 0) break; // Silently ignore all header fields.
|
||||
if (!line || line.trim().length === 0) break;
|
||||
if (reader.readEntry(entry, line) === 0) break; // Silently ignore all header fields.
|
||||
line = reader.readLine();
|
||||
}
|
||||
|
||||
@ -116,37 +116,37 @@ export class TextureAtlas implements Disposable {
|
||||
let values: number[][] | null = null;
|
||||
while (true) {
|
||||
if (line === null) break;
|
||||
if (line.trim().length == 0) {
|
||||
if (line.trim().length === 0) {
|
||||
page = null;
|
||||
line = reader.readLine();
|
||||
} else if (!page) {
|
||||
page = new TextureAtlasPage(line.trim());
|
||||
while (true) {
|
||||
if (reader.readEntry(entry, line = reader.readLine()) == 0) break;
|
||||
let field = pageFields[entry[0]];
|
||||
if (reader.readEntry(entry, line = reader.readLine()) === 0) break;
|
||||
const field = pageFields[entry[0]];
|
||||
if (field) field(page);
|
||||
}
|
||||
this.pages.push(page);
|
||||
} else {
|
||||
let region = new TextureAtlasRegion(page, line);
|
||||
const region = new TextureAtlasRegion(page, line);
|
||||
|
||||
while (true) {
|
||||
let count = reader.readEntry(entry, line = reader.readLine());
|
||||
if (count == 0) break;
|
||||
let field = regionFields[entry[0]];
|
||||
const count = reader.readEntry(entry, line = reader.readLine());
|
||||
if (count === 0) break;
|
||||
const field = regionFields[entry[0]];
|
||||
if (field)
|
||||
field(region);
|
||||
else {
|
||||
if (!names) names = [];
|
||||
if (!values) values = [];
|
||||
names.push(entry[0]);
|
||||
let entryValues: number[] = [];
|
||||
const entryValues: number[] = [];
|
||||
for (let i = 0; i < count; i++)
|
||||
entryValues.push(parseInt(entry[i + 1]));
|
||||
values.push(entryValues);
|
||||
}
|
||||
}
|
||||
if (region.originalWidth == 0 && region.originalHeight == 0) {
|
||||
if (region.originalWidth === 0 && region.originalHeight === 0) {
|
||||
region.originalWidth = region.width;
|
||||
region.originalHeight = region.height;
|
||||
}
|
||||
@ -158,7 +158,7 @@ export class TextureAtlas implements Disposable {
|
||||
}
|
||||
region.u = region.x / page.width;
|
||||
region.v = region.y / page.height;
|
||||
if (region.degrees == 90) {
|
||||
if (region.degrees === 90) {
|
||||
region.u2 = (region.x + region.height) / page.width;
|
||||
region.v2 = (region.y + region.width) / page.height;
|
||||
} else {
|
||||
@ -172,7 +172,7 @@ export class TextureAtlas implements Disposable {
|
||||
|
||||
findRegion (name: string): TextureAtlasRegion | null {
|
||||
for (let i = 0; i < this.regions.length; i++) {
|
||||
if (this.regions[i].name == name) {
|
||||
if (this.regions[i].name === name) {
|
||||
return this.regions[i];
|
||||
}
|
||||
}
|
||||
@ -180,8 +180,8 @@ export class TextureAtlas implements Disposable {
|
||||
}
|
||||
|
||||
setTextures (assetManager: AssetManagerBase, pathPrefix: string = "") {
|
||||
for (let page of this.pages)
|
||||
page.setTexture(assetManager.get(pathPrefix + page.name));
|
||||
for (const page of this.pages)
|
||||
page.setTexture(assetManager.get(pathPrefix + page.name) as Texture);
|
||||
}
|
||||
|
||||
dispose () {
|
||||
@ -208,20 +208,20 @@ class TextureAtlasReader {
|
||||
readEntry (entry: string[], line: string | null): number {
|
||||
if (!line) return 0;
|
||||
line = line.trim();
|
||||
if (line.length == 0) return 0;
|
||||
if (line.length === 0) return 0;
|
||||
|
||||
let colon = line.indexOf(':');
|
||||
if (colon == -1) return 0;
|
||||
const colon = line.indexOf(':');
|
||||
if (colon === -1) return 0;
|
||||
entry[0] = line.substr(0, colon).trim();
|
||||
for (let i = 1, lastMatch = colon + 1; ; i++) {
|
||||
let comma = line.indexOf(',', lastMatch);
|
||||
if (comma == -1) {
|
||||
const comma = line.indexOf(',', lastMatch);
|
||||
if (comma === -1) {
|
||||
entry[i] = line.substr(lastMatch).trim();
|
||||
return i;
|
||||
}
|
||||
entry[i] = line.substr(lastMatch, comma - lastMatch).trim();
|
||||
lastMatch = comma + 1;
|
||||
if (i == 4) return 4;
|
||||
if (i === 4) return 4;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -236,7 +236,7 @@ export class TextureAtlasPage {
|
||||
width: number = 0;
|
||||
height: number = 0;
|
||||
pma: boolean = false;
|
||||
regions = new Array<TextureAtlasRegion>();
|
||||
regions = [] as TextureAtlasRegion[];
|
||||
|
||||
constructor (name: string) {
|
||||
this.name = name;
|
||||
@ -246,7 +246,7 @@ export class TextureAtlasPage {
|
||||
this.texture = texture;
|
||||
texture.setFilters(this.minFilter, this.magFilter);
|
||||
texture.setWraps(this.uWrap, this.vWrap);
|
||||
for (let region of this.regions)
|
||||
for (const region of this.regions)
|
||||
region.texture = texture;
|
||||
}
|
||||
}
|
||||
|
||||
@ -27,12 +27,12 @@
|
||||
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*****************************************************************************/
|
||||
|
||||
import { Bone } from "./Bone.js";
|
||||
import { BonePose } from "./BonePose.js";
|
||||
import type { Bone } from "./Bone.js";
|
||||
import type { BonePose } from "./BonePose.js";
|
||||
import { Constraint } from "./Constraint.js";
|
||||
import { Physics } from "./Physics.js";
|
||||
import { Skeleton } from "./Skeleton.js";
|
||||
import { TransformConstraintData } from "./TransformConstraintData.js";
|
||||
import type { Physics } from "./Physics.js";
|
||||
import type { Skeleton } from "./Skeleton.js";
|
||||
import type { TransformConstraintData } from "./TransformConstraintData.js";
|
||||
import { TransformConstraintPose } from "./TransformConstraintPose.js";
|
||||
import { MathUtils } from "./Utils.js";
|
||||
|
||||
@ -53,7 +53,7 @@ export class TransformConstraint extends Constraint<TransformConstraint, Transfo
|
||||
super(data, new TransformConstraintPose(), new TransformConstraintPose());
|
||||
if (!skeleton) throw new Error("skeleton cannot be null.");
|
||||
|
||||
this.bones = new Array<BonePose>();
|
||||
this.bones = [] as BonePose[];
|
||||
for (const boneData of data.bones)
|
||||
this.bones.push(skeleton.bones[boneData.index].constrained);
|
||||
|
||||
@ -70,7 +70,7 @@ export class TransformConstraint extends Constraint<TransformConstraint, Transfo
|
||||
|
||||
update (skeleton: Skeleton, physics: Physics) {
|
||||
const p = this.applied;
|
||||
if (p.mixRotate == 0 && p.mixX == 0 && p.mixY == 0 && p.mixScaleX == 0 && p.mixScaleY == 0 && p.mixShearY == 0) return;
|
||||
if (p.mixRotate === 0 && p.mixX === 0 && p.mixY === 0 && p.mixScaleX === 0 && p.mixScaleY === 0 && p.mixShearY === 0) return;
|
||||
|
||||
const data = this.data;
|
||||
const localSource = data.localSource, localTarget = data.localTarget, additive = data.additive, clamp = data.clamp;
|
||||
|
||||
@ -27,13 +27,13 @@
|
||||
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*****************************************************************************/
|
||||
|
||||
import type { BoneData } from "./BoneData.js";
|
||||
import type { BonePose } from "./BonePose.js";
|
||||
import { ConstraintData } from "./ConstraintData.js";
|
||||
import { BoneData } from "./BoneData.js";
|
||||
import type { Skeleton } from "./Skeleton.js";
|
||||
import { TransformConstraint } from "./TransformConstraint.js";
|
||||
import { MathUtils } from "./Utils.js";
|
||||
import { Skeleton } from "./Skeleton.js";
|
||||
import { TransformConstraintPose } from "./TransformConstraintPose.js";
|
||||
import { BonePose } from "./BonePose.js";
|
||||
import { MathUtils } from "./Utils.js";
|
||||
|
||||
/** Stores the setup pose for a {@link TransformConstraint}.
|
||||
*
|
||||
@ -47,7 +47,7 @@ export class TransformConstraintData extends ConstraintData<TransformConstraint,
|
||||
public static readonly SHEARY = 5;
|
||||
|
||||
/** The bones that will be modified by this transform constraint. */
|
||||
bones = new Array<BoneData>();
|
||||
bones = [] as BoneData[];
|
||||
|
||||
/** The bone whose world transform will be copied to the constrained bones. */
|
||||
public set source (source: BoneData) { this._source = source; }
|
||||
@ -275,7 +275,7 @@ export class ToScaleX extends ToProperty {
|
||||
if (local) {
|
||||
if (additive)
|
||||
bone.scaleX *= 1 + (value - 1) * pose.mixScaleX;
|
||||
else if (bone.scaleX != 0) //
|
||||
else if (bone.scaleX !== 0) //
|
||||
bone.scaleX += (value - bone.scaleX) * pose.mixScaleX;
|
||||
} else if (additive) {
|
||||
const s = 1 + (value - 1) * pose.mixScaleX;
|
||||
@ -283,7 +283,7 @@ export class ToScaleX extends ToProperty {
|
||||
bone.c *= s;
|
||||
} else {
|
||||
let a = bone.a / skeleton.scaleX, c = bone.c / skeleton.scaleY, s = Math.sqrt(a * a + c * c);
|
||||
if (s != 0) {
|
||||
if (s !== 0) {
|
||||
s = 1 + (value - s) * pose.mixScaleX / s;
|
||||
bone.a *= s;
|
||||
bone.c *= s;
|
||||
@ -309,7 +309,7 @@ export class ToScaleY extends ToProperty {
|
||||
if (local) {
|
||||
if (additive)
|
||||
bone.scaleY *= 1 + (value - 1) * pose.mixScaleY;
|
||||
else if (bone.scaleY != 0) //
|
||||
else if (bone.scaleY !== 0) //
|
||||
bone.scaleY += (value - bone.scaleY) * pose.mixScaleY;
|
||||
} else if (additive) {
|
||||
const s = 1 + (value - 1) * pose.mixScaleY;
|
||||
@ -317,7 +317,7 @@ export class ToScaleY extends ToProperty {
|
||||
bone.d *= s;
|
||||
} else {
|
||||
let b = bone.b / skeleton.scaleX, d = bone.d / skeleton.scaleY, s = Math.sqrt(b * b + d * d);
|
||||
if (s != 0) {
|
||||
if (s !== 0) {
|
||||
s = 1 + (value - s) * pose.mixScaleY / s;
|
||||
bone.b *= s;
|
||||
bone.d *= s;
|
||||
|
||||
@ -27,7 +27,7 @@
|
||||
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*****************************************************************************/
|
||||
|
||||
import { Pose } from "./Pose"
|
||||
import type { Pose } from "./Pose"
|
||||
|
||||
/** Stores a pose for a transform constraint. */
|
||||
export class TransformConstraintPose implements Pose<TransformConstraintPose> {
|
||||
|
||||
@ -27,55 +27,56 @@
|
||||
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*****************************************************************************/
|
||||
|
||||
import { NumberArrayLike, Pool } from "./Utils.js";
|
||||
import { type NumberArrayLike, Pool } from "./Utils.js";
|
||||
|
||||
export class Triangulator {
|
||||
private convexPolygons = new Array<Array<number>>();
|
||||
private convexPolygonsIndices = new Array<Array<number>>();
|
||||
private convexPolygons = [] as Array<number>[];
|
||||
private convexPolygonsIndices = [] as Array<number>[];
|
||||
|
||||
private indicesArray = new Array<number>();
|
||||
private isConcaveArray = new Array<boolean>();
|
||||
private triangles = new Array<number>();
|
||||
private indicesArray = [] as number[];
|
||||
private isConcaveArray = [] as boolean[];
|
||||
private triangles = [] as number[];
|
||||
|
||||
private polygonPool = new Pool<Array<number>>(() => {
|
||||
return new Array<number>();
|
||||
return [] as number[];
|
||||
});
|
||||
|
||||
private polygonIndicesPool = new Pool<Array<number>>(() => {
|
||||
return new Array<number>();
|
||||
return [] as number[];
|
||||
});
|
||||
|
||||
public triangulate (verticesArray: NumberArrayLike): Array<number> {
|
||||
let vertices = verticesArray;
|
||||
const vertices = verticesArray;
|
||||
let vertexCount = verticesArray.length >> 1;
|
||||
|
||||
let indices = this.indicesArray;
|
||||
const indices = this.indicesArray;
|
||||
indices.length = 0;
|
||||
for (let i = 0; i < vertexCount; i++)
|
||||
indices[i] = i;
|
||||
|
||||
let isConcave = this.isConcaveArray;
|
||||
const isConcave = this.isConcaveArray;
|
||||
isConcave.length = 0;
|
||||
for (let i = 0, n = vertexCount; i < n; ++i)
|
||||
isConcave[i] = Triangulator.isConcave(i, vertexCount, vertices, indices);
|
||||
|
||||
let triangles = this.triangles;
|
||||
const triangles = this.triangles;
|
||||
triangles.length = 0;
|
||||
|
||||
while (vertexCount > 3) {
|
||||
// Find ear tip.
|
||||
let previous = vertexCount - 1, i = 0, next = 1;
|
||||
while (true) {
|
||||
// biome-ignore lint/suspicious/noConfusingLabels: reference runtime
|
||||
outer:
|
||||
if (!isConcave[i]) {
|
||||
let p1 = indices[previous] << 1, p2 = indices[i] << 1, p3 = indices[next] << 1;
|
||||
let p1x = vertices[p1], p1y = vertices[p1 + 1];
|
||||
let p2x = vertices[p2], p2y = vertices[p2 + 1];
|
||||
let p3x = vertices[p3], p3y = vertices[p3 + 1];
|
||||
for (let ii = (next + 1) % vertexCount; ii != previous; ii = (ii + 1) % vertexCount) {
|
||||
const p1 = indices[previous] << 1, p2 = indices[i] << 1, p3 = indices[next] << 1;
|
||||
const p1x = vertices[p1], p1y = vertices[p1 + 1];
|
||||
const p2x = vertices[p2], p2y = vertices[p2 + 1];
|
||||
const p3x = vertices[p3], p3y = vertices[p3 + 1];
|
||||
for (let ii = (next + 1) % vertexCount; ii !== previous; ii = (ii + 1) % vertexCount) {
|
||||
if (!isConcave[ii]) continue;
|
||||
let v = indices[ii] << 1;
|
||||
let vx = vertices[v], vy = vertices[v + 1];
|
||||
const v = indices[ii] << 1;
|
||||
const vx = vertices[v], vy = vertices[v + 1];
|
||||
if (Triangulator.positiveArea(p3x, p3y, p1x, p1y, vx, vy)) {
|
||||
if (Triangulator.positiveArea(p1x, p1y, p2x, p2y, vx, vy)) {
|
||||
if (Triangulator.positiveArea(p2x, p2y, p3x, p3y, vx, vy)) break outer;
|
||||
@ -85,7 +86,7 @@ export class Triangulator {
|
||||
break;
|
||||
}
|
||||
|
||||
if (next == 0) {
|
||||
if (next === 0) {
|
||||
do {
|
||||
if (!isConcave[i]) break;
|
||||
i--;
|
||||
@ -106,13 +107,13 @@ export class Triangulator {
|
||||
isConcave.splice(i, 1);
|
||||
vertexCount--;
|
||||
|
||||
let previousIndex = (vertexCount + i - 1) % vertexCount;
|
||||
let nextIndex = i == vertexCount ? 0 : i;
|
||||
const previousIndex = (vertexCount + i - 1) % vertexCount;
|
||||
const nextIndex = i === vertexCount ? 0 : i;
|
||||
isConcave[previousIndex] = Triangulator.isConcave(previousIndex, vertexCount, vertices, indices);
|
||||
isConcave[nextIndex] = Triangulator.isConcave(nextIndex, vertexCount, vertices, indices);
|
||||
}
|
||||
|
||||
if (vertexCount == 3) {
|
||||
if (vertexCount === 3) {
|
||||
triangles.push(indices[2]);
|
||||
triangles.push(indices[0]);
|
||||
triangles.push(indices[1]);
|
||||
@ -122,12 +123,12 @@ export class Triangulator {
|
||||
}
|
||||
|
||||
decompose (verticesArray: Array<number>, triangles: Array<number>): Array<Array<number>> {
|
||||
let vertices = verticesArray;
|
||||
let convexPolygons = this.convexPolygons;
|
||||
const vertices = verticesArray;
|
||||
const convexPolygons = this.convexPolygons;
|
||||
this.polygonPool.freeAll(convexPolygons);
|
||||
convexPolygons.length = 0;
|
||||
|
||||
let convexPolygonsIndices = this.convexPolygonsIndices;
|
||||
const convexPolygonsIndices = this.convexPolygonsIndices;
|
||||
this.polygonIndicesPool.freeAll(convexPolygonsIndices);
|
||||
convexPolygonsIndices.length = 0;
|
||||
|
||||
@ -140,18 +141,18 @@ export class Triangulator {
|
||||
// Merge subsequent triangles if they form a triangle fan.
|
||||
let fanBaseIndex = -1, lastWinding = 0;
|
||||
for (let i = 0, n = triangles.length; i < n; i += 3) {
|
||||
let t1 = triangles[i] << 1, t2 = triangles[i + 1] << 1, t3 = triangles[i + 2] << 1;
|
||||
let x1 = vertices[t1], y1 = vertices[t1 + 1];
|
||||
let x2 = vertices[t2], y2 = vertices[t2 + 1];
|
||||
let x3 = vertices[t3], y3 = vertices[t3 + 1];
|
||||
const t1 = triangles[i] << 1, t2 = triangles[i + 1] << 1, t3 = triangles[i + 2] << 1;
|
||||
const x1 = vertices[t1], y1 = vertices[t1 + 1];
|
||||
const x2 = vertices[t2], y2 = vertices[t2 + 1];
|
||||
const x3 = vertices[t3], y3 = vertices[t3 + 1];
|
||||
|
||||
// If the base of the last triangle is the same as this triangle, check if they form a convex polygon (triangle fan).
|
||||
let merged = false;
|
||||
if (fanBaseIndex == t1) {
|
||||
let o = polygon.length - 4;
|
||||
let winding1 = Triangulator.winding(polygon[o], polygon[o + 1], polygon[o + 2], polygon[o + 3], x3, y3);
|
||||
let winding2 = Triangulator.winding(x3, y3, polygon[0], polygon[1], polygon[2], polygon[3]);
|
||||
if (winding1 == lastWinding && winding2 == lastWinding) {
|
||||
if (fanBaseIndex === t1) {
|
||||
const o = polygon.length - 4;
|
||||
const winding1 = Triangulator.winding(polygon[o], polygon[o + 1], polygon[o + 2], polygon[o + 3], x3, y3);
|
||||
const winding2 = Triangulator.winding(x3, y3, polygon[0], polygon[1], polygon[2], polygon[3]);
|
||||
if (winding1 === lastWinding && winding2 === lastWinding) {
|
||||
polygon.push(x3);
|
||||
polygon.push(y3);
|
||||
polygonIndices.push(t3);
|
||||
@ -194,33 +195,33 @@ export class Triangulator {
|
||||
// Go through the list of polygons and try to merge the remaining triangles with the found triangle fans.
|
||||
for (let i = 0, n = convexPolygons.length; i < n; i++) {
|
||||
polygonIndices = convexPolygonsIndices[i];
|
||||
if (polygonIndices.length == 0) continue;
|
||||
let firstIndex = polygonIndices[0];
|
||||
let lastIndex = polygonIndices[polygonIndices.length - 1];
|
||||
if (polygonIndices.length === 0) continue;
|
||||
const firstIndex = polygonIndices[0];
|
||||
const lastIndex = polygonIndices[polygonIndices.length - 1];
|
||||
|
||||
polygon = convexPolygons[i];
|
||||
let o = polygon.length - 4;
|
||||
const o = polygon.length - 4;
|
||||
let prevPrevX = polygon[o], prevPrevY = polygon[o + 1];
|
||||
let prevX = polygon[o + 2], prevY = polygon[o + 3];
|
||||
let firstX = polygon[0], firstY = polygon[1];
|
||||
let secondX = polygon[2], secondY = polygon[3];
|
||||
let winding = Triangulator.winding(prevPrevX, prevPrevY, prevX, prevY, firstX, firstY);
|
||||
const firstX = polygon[0], firstY = polygon[1];
|
||||
const secondX = polygon[2], secondY = polygon[3];
|
||||
const winding = Triangulator.winding(prevPrevX, prevPrevY, prevX, prevY, firstX, firstY);
|
||||
|
||||
for (let ii = 0; ii < n; ii++) {
|
||||
if (ii == i) continue;
|
||||
let otherIndices = convexPolygonsIndices[ii];
|
||||
if (otherIndices.length != 3) continue;
|
||||
let otherFirstIndex = otherIndices[0];
|
||||
let otherSecondIndex = otherIndices[1];
|
||||
let otherLastIndex = otherIndices[2];
|
||||
if (ii === i) continue;
|
||||
const otherIndices = convexPolygonsIndices[ii];
|
||||
if (otherIndices.length !== 3) continue;
|
||||
const otherFirstIndex = otherIndices[0];
|
||||
const otherSecondIndex = otherIndices[1];
|
||||
const otherLastIndex = otherIndices[2];
|
||||
|
||||
let otherPoly = convexPolygons[ii];
|
||||
let x3 = otherPoly[otherPoly.length - 2], y3 = otherPoly[otherPoly.length - 1];
|
||||
const otherPoly = convexPolygons[ii];
|
||||
const x3 = otherPoly[otherPoly.length - 2], y3 = otherPoly[otherPoly.length - 1];
|
||||
|
||||
if (otherFirstIndex != firstIndex || otherSecondIndex != lastIndex) continue;
|
||||
let winding1 = Triangulator.winding(prevPrevX, prevPrevY, prevX, prevY, x3, y3);
|
||||
let winding2 = Triangulator.winding(x3, y3, firstX, firstY, secondX, secondY);
|
||||
if (winding1 == winding && winding2 == winding) {
|
||||
if (otherFirstIndex !== firstIndex || otherSecondIndex !== lastIndex) continue;
|
||||
const winding1 = Triangulator.winding(prevPrevX, prevPrevY, prevX, prevY, x3, y3);
|
||||
const winding2 = Triangulator.winding(x3, y3, firstX, firstY, secondX, secondY);
|
||||
if (winding1 === winding && winding2 === winding) {
|
||||
otherPoly.length = 0;
|
||||
otherIndices.length = 0;
|
||||
polygon.push(x3);
|
||||
@ -238,7 +239,7 @@ export class Triangulator {
|
||||
// Remove empty polygons that resulted from the merge step above.
|
||||
for (let i = convexPolygons.length - 1; i >= 0; i--) {
|
||||
polygon = convexPolygons[i];
|
||||
if (polygon.length == 0) {
|
||||
if (polygon.length === 0) {
|
||||
convexPolygons.splice(i, 1);
|
||||
this.polygonPool.free(polygon);
|
||||
polygonIndices = convexPolygonsIndices[i]
|
||||
@ -251,10 +252,10 @@ export class Triangulator {
|
||||
}
|
||||
|
||||
private static isConcave (index: number, vertexCount: number, vertices: NumberArrayLike, indices: NumberArrayLike): boolean {
|
||||
let previous = indices[(vertexCount + index - 1) % vertexCount] << 1;
|
||||
let current = indices[index] << 1;
|
||||
let next = indices[(index + 1) % vertexCount] << 1;
|
||||
return !this.positiveArea(vertices[previous], vertices[previous + 1], vertices[current], vertices[current + 1], vertices[next],
|
||||
const previous = indices[(vertexCount + index - 1) % vertexCount] << 1;
|
||||
const current = indices[index] << 1;
|
||||
const next = indices[(index + 1) % vertexCount] << 1;
|
||||
return !Triangulator.positiveArea(vertices[previous], vertices[previous + 1], vertices[current], vertices[current + 1], vertices[next],
|
||||
vertices[next + 1]);
|
||||
}
|
||||
|
||||
@ -263,7 +264,7 @@ export class Triangulator {
|
||||
}
|
||||
|
||||
private static winding (p1x: number, p1y: number, p2x: number, p2y: number, p3x: number, p3y: number): number {
|
||||
let px = p2x - p1x, py = p2y - p1y;
|
||||
const px = p2x - p1x, py = p2y - p1y;
|
||||
return p3x * py - p3y * px + px * p1y - p1x * py >= 0 ? 1 : -1;
|
||||
}
|
||||
}
|
||||
|
||||
@ -27,24 +27,26 @@
|
||||
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*****************************************************************************/
|
||||
|
||||
import { Skeleton } from "./Skeleton.js";
|
||||
import { MixBlend } from "./Animation.js";
|
||||
/** biome-ignore-all lint/complexity/noStaticOnlyClass: too much things to update */
|
||||
|
||||
import type { MixBlend } from "./Animation.js";
|
||||
import type { Skeleton } from "./Skeleton.js";
|
||||
|
||||
export interface StringMap<T> {
|
||||
[key: string]: T;
|
||||
}
|
||||
|
||||
export class IntSet {
|
||||
array = new Array<number | undefined>();
|
||||
array = [] as (number | undefined)[];
|
||||
|
||||
add (value: number): boolean {
|
||||
let contains = this.contains(value);
|
||||
const contains = this.contains(value);
|
||||
this.array[value | 0] = value | 0;
|
||||
return !contains;
|
||||
}
|
||||
|
||||
contains (value: number) {
|
||||
return this.array[value | 0] != undefined;
|
||||
return this.array[value | 0] !== undefined;
|
||||
}
|
||||
|
||||
remove (value: number) {
|
||||
@ -61,7 +63,7 @@ export class StringSet {
|
||||
size = 0;
|
||||
|
||||
add (value: string): boolean {
|
||||
let contains = this.entries[value];
|
||||
const contains = this.entries[value];
|
||||
this.entries[value] = true;
|
||||
if (!contains) {
|
||||
this.size++;
|
||||
@ -71,10 +73,10 @@ export class StringSet {
|
||||
}
|
||||
|
||||
addAll (values: string[]): boolean {
|
||||
let oldSize = this.size;
|
||||
for (var i = 0, n = values.length; i < n; i++)
|
||||
const oldSize = this.size;
|
||||
for (let i = 0, n = values.length; i < n; i++)
|
||||
this.add(values[i]);
|
||||
return oldSize != this.size;
|
||||
return oldSize !== this.size;
|
||||
}
|
||||
|
||||
contains (value: string) {
|
||||
@ -125,11 +127,11 @@ export class Color {
|
||||
}
|
||||
|
||||
setFromString (hex: string) {
|
||||
hex = hex.charAt(0) == '#' ? hex.substr(1) : hex;
|
||||
hex = hex.charAt(0) === '#' ? hex.substr(1) : hex;
|
||||
this.r = parseInt(hex.substr(0, 2), 16) / 255;
|
||||
this.g = parseInt(hex.substr(2, 2), 16) / 255;
|
||||
this.b = parseInt(hex.substr(4, 2), 16) / 255;
|
||||
this.a = hex.length != 8 ? 1 : parseInt(hex.substr(6, 2), 16) / 255;
|
||||
this.a = hex.length !== 8 ? 1 : parseInt(hex.substr(6, 2), 16) / 255;
|
||||
return this;
|
||||
}
|
||||
|
||||
@ -170,8 +172,8 @@ export class Color {
|
||||
}
|
||||
|
||||
toRgb888 () {
|
||||
const hex = (x: number) => ("0" + (x * 255).toString(16)).slice(-2);
|
||||
return Number("0x" + hex(this.r) + hex(this.g) + hex(this.b));
|
||||
const hex = (x: number) => (`0${(x * 255).toString(16)}`).slice(-2);
|
||||
return Number(`0x${hex(this.r)}${hex(this.g)}${hex(this.b)}`);
|
||||
}
|
||||
|
||||
static fromString (hex: string, color = new Color()): Color {
|
||||
@ -180,6 +182,7 @@ export class Color {
|
||||
}
|
||||
|
||||
export class MathUtils {
|
||||
// biome-ignore lint/suspicious/noApproximativeNumericConstant: reference runtime
|
||||
static PI = 3.1415927;
|
||||
static PI2 = MathUtils.PI * 2;
|
||||
static invPI2 = 1 / MathUtils.PI2;
|
||||
@ -215,7 +218,7 @@ export class MathUtils {
|
||||
}
|
||||
|
||||
static cbrt (x: number) {
|
||||
let y = Math.pow(Math.abs(x), 1 / 3);
|
||||
const y = Math.pow(Math.abs(x), 1 / 3);
|
||||
return x < 0 ? -y : y;
|
||||
}
|
||||
|
||||
@ -224,8 +227,8 @@ export class MathUtils {
|
||||
}
|
||||
|
||||
static randomTriangularWith (min: number, max: number, mode: number): number {
|
||||
let u = Math.random();
|
||||
let d = max - min;
|
||||
const u = Math.random();
|
||||
const d = max - min;
|
||||
if (u <= (mode - min) / d) return min + Math.sqrt(u * d * (mode - min));
|
||||
return max - Math.sqrt((1 - u) * d * (max - mode));
|
||||
}
|
||||
@ -252,7 +255,7 @@ export class Pow extends Interpolation {
|
||||
|
||||
applyInternal (a: number): number {
|
||||
if (a <= 0.5) return Math.pow(a * 2, this.power) / 2;
|
||||
return Math.pow((a - 1) * 2, this.power) / (this.power % 2 == 0 ? -2 : 2) + 1;
|
||||
return Math.pow((a - 1) * 2, this.power) / (this.power % 2 === 0 ? -2 : 2) + 1;
|
||||
}
|
||||
}
|
||||
|
||||
@ -262,7 +265,7 @@ export class PowOut extends Pow {
|
||||
}
|
||||
|
||||
applyInternal (a: number): number {
|
||||
return Math.pow(a - 1, this.power) * (this.power % 2 == 0 ? -1 : 1) + 1;
|
||||
return Math.pow(a - 1, this.power) * (this.power % 2 === 0 ? -1 : 1) + 1;
|
||||
}
|
||||
}
|
||||
|
||||
@ -280,9 +283,10 @@ export class Utils {
|
||||
array[i] = value;
|
||||
}
|
||||
|
||||
// biome-ignore lint/suspicious/noExplicitAny: ok any in this case
|
||||
static setArraySize<T> (array: Array<T>, size: number, value: any = 0): Array<T> {
|
||||
let oldSize = array.length;
|
||||
if (oldSize == size) return array;
|
||||
const oldSize = array.length;
|
||||
if (oldSize === size) return array;
|
||||
array.length = size;
|
||||
if (oldSize < size) {
|
||||
for (let i = oldSize; i < size; i++) array[i] = value;
|
||||
@ -290,13 +294,14 @@ export class Utils {
|
||||
return array;
|
||||
}
|
||||
|
||||
// biome-ignore lint/suspicious/noExplicitAny: ok any in this case
|
||||
static ensureArrayCapacity<T> (array: Array<T>, size: number, value: any = 0): Array<T> {
|
||||
if (array.length >= size) return array;
|
||||
return Utils.setArraySize(array, size, value);
|
||||
}
|
||||
|
||||
static newArray<T> (size: number, defaultValue: T): Array<T> {
|
||||
let array = new Array<T>(size);
|
||||
const array = new Array<T>(size);
|
||||
for (let i = 0; i < size; i++) array[i] = defaultValue;
|
||||
return array;
|
||||
}
|
||||
@ -305,7 +310,7 @@ export class Utils {
|
||||
if (Utils.SUPPORTS_TYPED_ARRAYS)
|
||||
return new Float32Array(size)
|
||||
else {
|
||||
let array = new Array<number>(size);
|
||||
const array = new Array<number>(size);
|
||||
for (let i = 0; i < array.length; i++) array[i] = 0;
|
||||
return array;
|
||||
}
|
||||
@ -315,7 +320,7 @@ export class Utils {
|
||||
if (Utils.SUPPORTS_TYPED_ARRAYS)
|
||||
return new Int16Array(size)
|
||||
else {
|
||||
let array = new Array<number>(size);
|
||||
const array = new Array<number>(size);
|
||||
for (let i = 0; i < array.length; i++) array[i] = 0;
|
||||
return array;
|
||||
}
|
||||
@ -334,11 +339,12 @@ export class Utils {
|
||||
}
|
||||
|
||||
static contains<T> (array: Array<T>, element: T, identity = true) {
|
||||
for (var i = 0; i < array.length; i++)
|
||||
if (array[i] == element) return true;
|
||||
for (let i = 0; i < array.length; i++)
|
||||
if (array[i] === element) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
// biome-ignore lint/suspicious/noExplicitAny: ok any in this case
|
||||
static enumValue (type: any, name: string) {
|
||||
return type[name[0].toUpperCase() + name.slice(1)];
|
||||
}
|
||||
@ -347,14 +353,14 @@ export class Utils {
|
||||
export class DebugUtils {
|
||||
static logBones (skeleton: Skeleton) {
|
||||
for (let i = 0; i < skeleton.bones.length; i++) {
|
||||
let bone = skeleton.bones[i].applied;
|
||||
console.log(bone.bone.data.name + ", " + bone.a + ", " + bone.b + ", " + bone.c + ", " + bone.d + ", " + bone.worldX + ", " + bone.worldY);
|
||||
const bone = skeleton.bones[i].applied;
|
||||
console.log(`${bone.bone.data.name}, ${bone.a}, ${bone.b}, ${bone.c}, ${bone.d}, ${bone.worldX}, ${bone.worldY}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export class Pool<T> {
|
||||
private items = new Array<T>();
|
||||
private items = [] as T[];
|
||||
private instantiator: () => T;
|
||||
|
||||
constructor (instantiator: () => T) {
|
||||
@ -362,11 +368,13 @@ export class Pool<T> {
|
||||
}
|
||||
|
||||
obtain () {
|
||||
// biome-ignore lint/style/noNonNullAssertion: length check
|
||||
return this.items.length > 0 ? this.items.pop()! : this.instantiator();
|
||||
}
|
||||
|
||||
free (item: T) {
|
||||
if ((item as any).reset) (item as any).reset();
|
||||
// biome-ignore lint/suspicious/noExplicitAny: T can be anything
|
||||
(item as any).reset?.();
|
||||
this.items.push(item);
|
||||
}
|
||||
|
||||
@ -391,14 +399,14 @@ export class Vector2 {
|
||||
}
|
||||
|
||||
length () {
|
||||
let x = this.x;
|
||||
let y = this.y;
|
||||
const x = this.x;
|
||||
const y = this.y;
|
||||
return Math.sqrt(x * x + y * y);
|
||||
}
|
||||
|
||||
normalize () {
|
||||
let len = this.length();
|
||||
if (len != 0) {
|
||||
const len = this.length();
|
||||
if (len !== 0) {
|
||||
this.x /= len;
|
||||
this.y /= len;
|
||||
}
|
||||
@ -417,7 +425,7 @@ export class TimeKeeper {
|
||||
private frameTime = 0;
|
||||
|
||||
update () {
|
||||
let now = Date.now() / 1000;
|
||||
const now = Date.now() / 1000;
|
||||
this.delta = now - this.lastTime;
|
||||
this.frameTime += this.delta;
|
||||
this.totalTime += this.delta;
|
||||
|
||||
@ -27,9 +27,9 @@
|
||||
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*****************************************************************************/
|
||||
|
||||
import { Skeleton } from "src/Skeleton.js";
|
||||
import { Slot } from "../Slot.js";
|
||||
import { NumberArrayLike, Utils } from "../Utils.js";
|
||||
import type { Skeleton } from "src/Skeleton.js";
|
||||
import type { Slot } from "../Slot.js";
|
||||
import { type NumberArrayLike, Utils } from "../Utils.js";
|
||||
|
||||
/** The base class for all attachments. */
|
||||
export abstract class Attachment {
|
||||
@ -88,17 +88,17 @@ export abstract class VertexAttachment extends Attachment {
|
||||
stride: number) {
|
||||
|
||||
count = offset + (count >> 1) * stride;
|
||||
let deformArray = slot.applied.deform;
|
||||
const deformArray = slot.applied.deform;
|
||||
let vertices = this.vertices;
|
||||
let bones = this.bones;
|
||||
const bones = this.bones;
|
||||
if (!bones) {
|
||||
if (deformArray.length > 0) vertices = deformArray;
|
||||
let bone = slot.bone.applied;
|
||||
let x = bone.worldX;
|
||||
let y = bone.worldY;
|
||||
let a = bone.a, b = bone.b, c = bone.c, d = bone.d;
|
||||
const bone = slot.bone.applied;
|
||||
const x = bone.worldX;
|
||||
const y = bone.worldY;
|
||||
const a = bone.a, b = bone.b, c = bone.c, d = bone.d;
|
||||
for (let v = start, w = offset; w < count; v += 2, w += stride) {
|
||||
let vx = vertices[v], vy = vertices[v + 1];
|
||||
const vx = vertices[v], vy = vertices[v + 1];
|
||||
worldVertices[w] = vx * a + vy * b + x;
|
||||
worldVertices[w + 1] = vx * c + vy * d + y;
|
||||
}
|
||||
@ -106,19 +106,19 @@ export abstract class VertexAttachment extends Attachment {
|
||||
}
|
||||
let v = 0, skip = 0;
|
||||
for (let i = 0; i < start; i += 2) {
|
||||
let n = bones[v];
|
||||
const n = bones[v];
|
||||
v += n + 1;
|
||||
skip += n;
|
||||
}
|
||||
let skeletonBones = skeleton.bones;
|
||||
if (deformArray.length == 0) {
|
||||
const skeletonBones = skeleton.bones;
|
||||
if (deformArray.length === 0) {
|
||||
for (let w = offset, b = skip * 3; w < count; w += stride) {
|
||||
let wx = 0, wy = 0;
|
||||
let n = bones[v++];
|
||||
n += v;
|
||||
for (; v < n; v++, b += 3) {
|
||||
let bone = skeletonBones[bones[v]].applied;
|
||||
let vx = vertices[b], vy = vertices[b + 1], weight = vertices[b + 2];
|
||||
const bone = skeletonBones[bones[v]].applied;
|
||||
const vx = vertices[b], vy = vertices[b + 1], weight = vertices[b + 2];
|
||||
wx += (vx * bone.a + vy * bone.b + bone.worldX) * weight;
|
||||
wy += (vx * bone.c + vy * bone.d + bone.worldY) * weight;
|
||||
}
|
||||
@ -126,14 +126,14 @@ export abstract class VertexAttachment extends Attachment {
|
||||
worldVertices[w + 1] = wy;
|
||||
}
|
||||
} else {
|
||||
let deform = deformArray;
|
||||
const deform = deformArray;
|
||||
for (let w = offset, b = skip * 3, f = skip << 1; w < count; w += stride) {
|
||||
let wx = 0, wy = 0;
|
||||
let n = bones[v++];
|
||||
n += v;
|
||||
for (; v < n; v++, b += 3, f += 2) {
|
||||
let bone = skeletonBones[bones[v]].applied;
|
||||
let vx = vertices[b] + deform[f], vy = vertices[b + 1] + deform[f + 1], weight = vertices[b + 2];
|
||||
const bone = skeletonBones[bones[v]].applied;
|
||||
const vx = vertices[b] + deform[f], vy = vertices[b + 1] + deform[f + 1], weight = vertices[b + 2];
|
||||
wx += (vx * bone.a + vy * bone.b + bone.worldX) * weight;
|
||||
wy += (vx * bone.c + vy * bone.d + bone.worldY) * weight;
|
||||
}
|
||||
|
||||
@ -27,14 +27,14 @@
|
||||
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*****************************************************************************/
|
||||
|
||||
import { Skin } from "../Skin.js";
|
||||
import { BoundingBoxAttachment } from "./BoundingBoxAttachment.js";
|
||||
import { ClippingAttachment } from "./ClippingAttachment.js";
|
||||
import { MeshAttachment } from "./MeshAttachment.js";
|
||||
import { PathAttachment } from "./PathAttachment.js";
|
||||
import { PointAttachment } from "./PointAttachment.js";
|
||||
import { RegionAttachment } from "./RegionAttachment.js";
|
||||
import { Sequence } from "./Sequence.js";
|
||||
import type { Skin } from "../Skin.js";
|
||||
import type { BoundingBoxAttachment } from "./BoundingBoxAttachment.js";
|
||||
import type { ClippingAttachment } from "./ClippingAttachment.js";
|
||||
import type { MeshAttachment } from "./MeshAttachment.js";
|
||||
import type { PathAttachment } from "./PathAttachment.js";
|
||||
import type { PointAttachment } from "./PointAttachment.js";
|
||||
import type { RegionAttachment } from "./RegionAttachment.js";
|
||||
import type { Sequence } from "./Sequence.js";
|
||||
|
||||
/** The interface which can be implemented to customize creating and populating attachments.
|
||||
*
|
||||
|
||||
@ -28,7 +28,7 @@
|
||||
*****************************************************************************/
|
||||
|
||||
import { Color } from "../Utils.js";
|
||||
import { VertexAttachment, Attachment } from "./Attachment.js";
|
||||
import { type Attachment, VertexAttachment } from "./Attachment.js";
|
||||
|
||||
/** An attachment with vertices that make up a polygon. Can be used for hit detection, creating physics bodies, spawning particle
|
||||
* effects, and more.
|
||||
@ -43,7 +43,7 @@ export class BoundingBoxAttachment extends VertexAttachment {
|
||||
}
|
||||
|
||||
copy (): Attachment {
|
||||
let copy = new BoundingBoxAttachment(this.name);
|
||||
const copy = new BoundingBoxAttachment(this.name);
|
||||
this.copyTo(copy);
|
||||
copy.color.setFromColor(this.color);
|
||||
return copy;
|
||||
|
||||
@ -27,9 +27,9 @@
|
||||
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*****************************************************************************/
|
||||
|
||||
import { SlotData } from "../SlotData.js";
|
||||
import type { SlotData } from "../SlotData.js";
|
||||
import { Color } from "../Utils.js";
|
||||
import { VertexAttachment, Attachment } from "./Attachment.js";
|
||||
import { type Attachment, VertexAttachment } from "./Attachment.js";
|
||||
|
||||
/** An attachment with vertices that make up a polygon used for clipping the rendering of other attachments. */
|
||||
export class ClippingAttachment extends VertexAttachment {
|
||||
@ -47,7 +47,7 @@ export class ClippingAttachment extends VertexAttachment {
|
||||
}
|
||||
|
||||
copy (): Attachment {
|
||||
let copy = new ClippingAttachment(this.name);
|
||||
const copy = new ClippingAttachment(this.name);
|
||||
this.copyTo(copy);
|
||||
copy.endSlot = this.endSlot;
|
||||
copy.color.setFromColor(this.color);
|
||||
|
||||
@ -27,9 +27,9 @@
|
||||
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*****************************************************************************/
|
||||
|
||||
import { TextureRegion } from "../Texture.js"
|
||||
import { Color } from "../Utils.js"
|
||||
import { Sequence } from "./Sequence.js"
|
||||
import type { TextureRegion } from "../Texture.js"
|
||||
import type { Color } from "../Utils.js"
|
||||
import type { Sequence } from "./Sequence.js"
|
||||
|
||||
export interface HasTextureRegion {
|
||||
/** The name used to find the {@link #region()}. */
|
||||
|
||||
@ -27,14 +27,14 @@
|
||||
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*****************************************************************************/
|
||||
|
||||
import { TextureRegion } from "../Texture.js";
|
||||
import type { Skeleton } from "src/Skeleton.js";
|
||||
import type { Slot } from "../Slot.js";
|
||||
import type { TextureRegion } from "../Texture.js";
|
||||
import { TextureAtlasRegion } from "../TextureAtlas.js";
|
||||
import { Color, NumberArrayLike, Utils } from "../Utils.js";
|
||||
import { VertexAttachment, Attachment } from "./Attachment.js";
|
||||
import { HasTextureRegion } from "./HasTextureRegion.js";
|
||||
import { Sequence } from "./Sequence.js";
|
||||
import { Slot } from "../Slot.js";
|
||||
import { Skeleton } from "src/Skeleton.js";
|
||||
import { Color, type NumberArrayLike, Utils } from "../Utils.js";
|
||||
import { type Attachment, VertexAttachment } from "./Attachment.js";
|
||||
import type { HasTextureRegion } from "./HasTextureRegion.js";
|
||||
import type { Sequence } from "./Sequence.js";
|
||||
|
||||
/** An attachment that displays a textured mesh. A mesh has hull vertices and internal vertices within the hull. Holes are not
|
||||
* supported. Each vertex has UVs (texture coordinates) and triangles are used to map an image on to the mesh.
|
||||
@ -88,14 +88,14 @@ export class MeshAttachment extends VertexAttachment implements HasTextureRegion
|
||||
* the {@link #regionUVs} are changed. */
|
||||
updateRegion () {
|
||||
if (!this.region) throw new Error("Region not set.");
|
||||
let regionUVs = this.regionUVs;
|
||||
if (!this.uvs || this.uvs.length != regionUVs.length) this.uvs = Utils.newFloatArray(regionUVs.length);
|
||||
let uvs = this.uvs;
|
||||
let n = this.uvs.length;
|
||||
const regionUVs = this.regionUVs;
|
||||
if (!this.uvs || this.uvs.length !== regionUVs.length) this.uvs = Utils.newFloatArray(regionUVs.length);
|
||||
const uvs = this.uvs;
|
||||
const n = this.uvs.length;
|
||||
let u = this.region.u, v = this.region.v, width = 0, height = 0;
|
||||
if (this.region instanceof TextureAtlasRegion) {
|
||||
let region = this.region, page = region.page;
|
||||
let textureWidth = page.width, textureHeight = page.height;
|
||||
const region = this.region, page = region.page;
|
||||
const textureWidth = page.width, textureHeight = page.height;
|
||||
switch (region.degrees) {
|
||||
case 90:
|
||||
u -= (region.originalHeight - region.offsetY - region.height) / textureWidth;
|
||||
@ -171,7 +171,7 @@ export class MeshAttachment extends VertexAttachment implements HasTextureRegion
|
||||
copy (): Attachment {
|
||||
if (this.parentMesh) return this.newLinkedMesh();
|
||||
|
||||
let copy = new MeshAttachment(this.name, this.path);
|
||||
const copy = new MeshAttachment(this.name, this.path);
|
||||
copy.region = this.region;
|
||||
copy.color.setFromColor(this.color);
|
||||
|
||||
@ -204,7 +204,7 @@ export class MeshAttachment extends VertexAttachment implements HasTextureRegion
|
||||
|
||||
/** Returns a new mesh with the {@link #parentMesh} set to this mesh's parent mesh, if any, else to this mesh. **/
|
||||
newLinkedMesh (): MeshAttachment {
|
||||
let copy = new MeshAttachment(this.name, this.path);
|
||||
const copy = new MeshAttachment(this.name, this.path);
|
||||
copy.region = this.region;
|
||||
copy.color.setFromColor(this.color);
|
||||
copy.timelineAttachment = this.timelineAttachment;
|
||||
|
||||
@ -28,7 +28,7 @@
|
||||
*****************************************************************************/
|
||||
|
||||
import { Color, Utils } from "../Utils.js";
|
||||
import { VertexAttachment, Attachment } from "./Attachment.js";
|
||||
import { type Attachment, VertexAttachment } from "./Attachment.js";
|
||||
|
||||
/** An attachment whose vertices make up a composite Bezier curve.
|
||||
*
|
||||
@ -54,7 +54,7 @@ export class PathAttachment extends VertexAttachment {
|
||||
}
|
||||
|
||||
copy (): Attachment {
|
||||
let copy = new PathAttachment(this.name);
|
||||
const copy = new PathAttachment(this.name);
|
||||
this.copyTo(copy);
|
||||
copy.lengths = new Array<number>(this.lengths.length);
|
||||
Utils.arrayCopy(this.lengths, 0, copy.lengths, 0, this.lengths.length);
|
||||
|
||||
@ -27,9 +27,9 @@
|
||||
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*****************************************************************************/
|
||||
|
||||
import { BonePose } from "src/BonePose.js";
|
||||
import { Color, Vector2, MathUtils } from "../Utils.js";
|
||||
import { VertexAttachment, Attachment } from "./Attachment.js";
|
||||
import type { BonePose } from "src/BonePose.js";
|
||||
import { Color, MathUtils, type Vector2 } from "../Utils.js";
|
||||
import { type Attachment, VertexAttachment } from "./Attachment.js";
|
||||
|
||||
/** 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
|
||||
@ -63,7 +63,7 @@ export class PointAttachment extends VertexAttachment {
|
||||
}
|
||||
|
||||
copy (): Attachment {
|
||||
let copy = new PointAttachment(this.name);
|
||||
const copy = new PointAttachment(this.name);
|
||||
copy.x = this.x;
|
||||
copy.y = this.y;
|
||||
copy.rotation = this.rotation;
|
||||
|
||||
@ -27,12 +27,12 @@
|
||||
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*****************************************************************************/
|
||||
|
||||
import { TextureRegion } from "../Texture.js";
|
||||
import { Color, MathUtils, NumberArrayLike, Utils } from "../Utils.js";
|
||||
import type { Slot } from "../Slot.js";
|
||||
import type { TextureRegion } from "../Texture.js";
|
||||
import { Color, MathUtils, type NumberArrayLike, Utils } from "../Utils.js";
|
||||
import { Attachment } from "./Attachment.js";
|
||||
import { HasTextureRegion } from "./HasTextureRegion.js";
|
||||
import { Sequence } from "./Sequence.js";
|
||||
import { Slot } from "../Slot.js";
|
||||
import type { HasTextureRegion } from "./HasTextureRegion.js";
|
||||
import type { Sequence } from "./Sequence.js";
|
||||
|
||||
/** An attachment that displays a textured quadrilateral.
|
||||
*
|
||||
@ -85,27 +85,27 @@ export class RegionAttachment extends Attachment implements HasTextureRegion {
|
||||
/** Calculates the {@link #offset} using the region settings. Must be called after changing region settings. */
|
||||
updateRegion (): void {
|
||||
if (!this.region) throw new Error("Region not set.");
|
||||
let region = this.region;
|
||||
let uvs = this.uvs;
|
||||
let regionScaleX = this.width / this.region.originalWidth * this.scaleX;
|
||||
let regionScaleY = this.height / this.region.originalHeight * this.scaleY;
|
||||
let localX = -this.width / 2 * this.scaleX + this.region.offsetX * regionScaleX;
|
||||
let localY = -this.height / 2 * this.scaleY + this.region.offsetY * regionScaleY;
|
||||
let localX2 = localX + this.region.width * regionScaleX;
|
||||
let localY2 = localY + this.region.height * regionScaleY;
|
||||
let radians = this.rotation * MathUtils.degRad;
|
||||
let cos = Math.cos(radians);
|
||||
let sin = Math.sin(radians);
|
||||
let x = this.x, y = this.y;
|
||||
let localXCos = localX * cos + x;
|
||||
let localXSin = localX * sin;
|
||||
let localYCos = localY * cos + y;
|
||||
let localYSin = localY * sin;
|
||||
let localX2Cos = localX2 * cos + x;
|
||||
let localX2Sin = localX2 * sin;
|
||||
let localY2Cos = localY2 * cos + y;
|
||||
let localY2Sin = localY2 * sin;
|
||||
let offset = this.offset;
|
||||
const region = this.region;
|
||||
const uvs = this.uvs;
|
||||
const regionScaleX = this.width / this.region.originalWidth * this.scaleX;
|
||||
const regionScaleY = this.height / this.region.originalHeight * this.scaleY;
|
||||
const localX = -this.width / 2 * this.scaleX + this.region.offsetX * regionScaleX;
|
||||
const localY = -this.height / 2 * this.scaleY + this.region.offsetY * regionScaleY;
|
||||
const localX2 = localX + this.region.width * regionScaleX;
|
||||
const localY2 = localY + this.region.height * regionScaleY;
|
||||
const radians = this.rotation * MathUtils.degRad;
|
||||
const cos = Math.cos(radians);
|
||||
const sin = Math.sin(radians);
|
||||
const x = this.x, y = this.y;
|
||||
const localXCos = localX * cos + x;
|
||||
const localXSin = localX * sin;
|
||||
const localYCos = localY * cos + y;
|
||||
const localYSin = localY * sin;
|
||||
const localX2Cos = localX2 * cos + x;
|
||||
const localX2Sin = localX2 * sin;
|
||||
const localY2Cos = localY2 * cos + y;
|
||||
const localY2Sin = localY2 * sin;
|
||||
const offset = this.offset;
|
||||
offset[0] = localXCos - localYSin;
|
||||
offset[1] = localYCos + localXSin;
|
||||
offset[2] = localXCos - localY2Sin;
|
||||
@ -124,7 +124,7 @@ export class RegionAttachment extends Attachment implements HasTextureRegion {
|
||||
uvs[5] = 1;
|
||||
uvs[6] = 1;
|
||||
uvs[7] = 0;
|
||||
} else if (region.degrees == 90) {
|
||||
} else if (region.degrees === 90) {
|
||||
uvs[0] = region.u2;
|
||||
uvs[1] = region.v2;
|
||||
uvs[2] = region.u;
|
||||
@ -156,10 +156,10 @@ export class RegionAttachment extends Attachment implements HasTextureRegion {
|
||||
computeWorldVertices (slot: Slot, worldVertices: NumberArrayLike, offset: number, stride: number) {
|
||||
if (this.sequence) this.sequence.apply(slot.applied, this);
|
||||
|
||||
let bone = slot.bone.applied;
|
||||
let vertexOffset = this.offset;
|
||||
let x = bone.worldX, y = bone.worldY;
|
||||
let a = bone.a, b = bone.b, c = bone.c, d = bone.d;
|
||||
const bone = slot.bone.applied;
|
||||
const vertexOffset = this.offset;
|
||||
const x = bone.worldX, y = bone.worldY;
|
||||
const a = bone.a, b = bone.b, c = bone.c, d = bone.d;
|
||||
let offsetX = 0, offsetY = 0;
|
||||
|
||||
offsetX = vertexOffset[0];
|
||||
@ -187,7 +187,7 @@ export class RegionAttachment extends Attachment implements HasTextureRegion {
|
||||
}
|
||||
|
||||
copy (): Attachment {
|
||||
let copy = new RegionAttachment(this.name, this.path);
|
||||
const copy = new RegionAttachment(this.name, this.path);
|
||||
copy.region = this.region;
|
||||
copy.x = this.x;
|
||||
copy.y = this.y;
|
||||
|
||||
@ -29,10 +29,9 @@
|
||||
|
||||
(() => {
|
||||
if (typeof Math.fround === "undefined") {
|
||||
Math.fround = (function (array) {
|
||||
return function (x: number) {
|
||||
return array[0] = x, array[0];
|
||||
};
|
||||
Math.fround = ((array) => (x: number) => {
|
||||
array[0] = x;
|
||||
return array[0];
|
||||
})(new Float32Array(1));
|
||||
}
|
||||
})();
|
||||
|
||||
@ -919,7 +919,7 @@ export class SpinePlayer implements Disposable {
|
||||
// Draw the background image.
|
||||
let bgImage = config.backgroundImage;
|
||||
if (bgImage) {
|
||||
let texture = this.assetManager!.require(bgImage.url);
|
||||
let texture = this.assetManager!.require(bgImage.url) as GLTexture;
|
||||
if (bgImage.x !== void 0 && bgImage.y !== void 0 && bgImage.width && bgImage.height)
|
||||
renderer.drawTexture(texture, bgImage.x, bgImage.y, bgImage.width, bgImage.height);
|
||||
else
|
||||
|
||||
@ -978,7 +978,8 @@ export class SpineWebComponentSkeleton extends HTMLElement implements Disposable
|
||||
const skeletonLoader = isBinary ? new SkeletonBinary(atlasLoader) : new SkeletonJson(atlasLoader);
|
||||
skeletonLoader.scale = scale;
|
||||
|
||||
const skeletonFileAsset = this.overlay.assetManager.require(skeletonPath);
|
||||
// biome-ignore lint/suspicious/noExplicitAny: it is any untile we have a json schema
|
||||
const skeletonFileAsset = this.overlay.assetManager.require(skeletonPath) as Record<string, any>;
|
||||
const skeletonFile = this.jsonSkeletonKey ? skeletonFileAsset[this.jsonSkeletonKey] : skeletonFileAsset;
|
||||
const skeletonData = (skeletonDataInput || this.skeleton?.data) ?? skeletonLoader.readSkeletonData(skeletonFile);
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user