···11+/**
22+ * Smoothly transitions between two values.
33+ *
44+ * This function helps you move from a starting value (source) to an ending value (target)
55+ * based on an amount between 0 and 1. If the amount is 0, you get the source value.
66+ * If it's 1, you get the target value. Values in between give you a mix of both.
77+ *
88+ * @param {number} source - The value to start from.
99+ * @param {number} target - The value to move towards.
1010+ * @param {number} amount - A number between 0 and 1 that controls how much to move
1111+ * from the source to the target.
1212+ * @returns {number} The value in between source and target, depending on the amount.
1313+ */
1414+export function smoothStep(
1515+ source: number,
1616+ target: number,
1717+ amount: number,
1818+): number {
1919+ return source * (1 - amount) + target * amount;
2020+}
2121+2222+/**
2323+ * Smoothly transitions between two values, adjusting for different frame rates.
2424+ *
2525+ * This function helps you move from a starting value (source) to an ending value (target),
2626+ * but it also adjusts for how fast your program is running (its framerate). This ensures that
2727+ * the transition is smooth and consistent, even if the framerate changes.
2828+ *
2929+ * If the `frameDelta` (the time between frames) is not provided, the function will simply move
3030+ * from the source to the target based on the rate you provide, similar to the basic smooth transition.
3131+ *
3232+ * If the `frameDelta` is provided, the function takes this into account to make sure that the transition
3333+ * speed is consistent, no matter how fast or slow the program runs.
3434+ *
3535+ * @param {number} source - The value to start from.
3636+ * @param {number} target - The value to move towards.
3737+ * @param {number} rate - Controls how quickly to move from source to target.
3838+ * A value of 0 means no movement, and 1 means an instant jump to the target.
3939+ * Values in between result in smoother, more gradual transitions.
4040+ * @param {number} frameDelta - The time between frames (in seconds), used to adjust the rate
4141+ * and ensure smooth transitions at any framerate.
4242+ * @param {number} [targetFps=60] - The target frames per second, usually 60 by default.
4343+ * @returns {number} The value in between source and target, adjusted for framerate.
4444+ */
4545+export function framerateIndependentSmoothStep(
4646+ source: number,
4747+ target: number,
4848+ rate: number,
4949+ frameDelta: number,
5050+ targetFps = 60,
5151+): number {
5252+ if (typeof frameDelta === "undefined") {
5353+ return smoothStep(source, target, rate);
5454+ }
5555+5656+ const relativeDelta = frameDelta / (1 / targetFps);
5757+ const smoothing = 1 - rate;
5858+ return smoothStep(source, target, 1 - Math.pow(smoothing, relativeDelta));
5959+}
6060+6161+/**
6262+ * Converts an angle from degrees to radians.
6363+ *
6464+ * @param {number} degrees - The angle in degrees.
6565+ * @returns {number} The angle in radians.
6666+ */
6767+export function degreesToRadians(degrees: number): number {
6868+ return degrees * (Math.PI / 180);
6969+}
7070+7171+/**
7272+ * Converts an angle from radians to degrees.
7373+ *
7474+ * @param {number} radians - The angle in radians.
7575+ * @returns {number} The angle in degrees.
7676+ */
7777+export function radiansToDegrees(radians: number): number {
7878+ return radians * (180 / Math.PI);
7979+}
8080+8181+/**
8282+ * Restricts a value to stay within a certain range.
8383+ *
8484+ * This function makes sure a number doesn't go below a minimum value or above a maximum value.
8585+ * If the number is lower than the minimum, it returns the minimum. If it's higher than the maximum,
8686+ * it returns the maximum. If the number is within the range, it stays unchanged.
8787+ *
8888+ * @param {number} value - The number you want to restrict.
8989+ * @param {number} min - The lowest value the number can be.
9090+ * @param {number} max - The highest value the number can be.
9191+ * @returns {number} The number, but kept within the specified range.
9292+ */
9393+export function clamp(value: number, min: number, max: number): number {
9494+ return Math.max(min, Math.min(max, value));
9595+}
9696+9797+/**
9898+ * Loops a number around a range.
9999+ *
100100+ * This function makes a number loop back to the start if it goes past the end of a range,
101101+ * or to the end if it goes below the start. For example, if you're counting seconds on a clock
102102+ * and you reach 60, you wrap back around to 0.
103103+ *
104104+ * @param {number} value - The number you want to loop.
105105+ * @param {number} min - The start of the range.
106106+ * @param {number} max - The end of the range.
107107+ * @returns {number} The wrapped number within the range.
108108+ */
109109+export function wrap(value: number, min: number, max: number): number {
110110+ return ((value - min) % (max - min)) + min;
111111+}
112112+113113+/**
114114+ * Converts a number from one range to another.
115115+ *
116116+ * This function takes a number that's within one range of values and converts it to be within
117117+ * another range of values. For example, you might want to take a score out of 100 and convert it
118118+ * to a percentage out of 10, or take a health bar that's between 0 and 100 and map it to a progress
119119+ * bar that's between 0 and 1.
120120+ *
121121+ * @param {number} value - The number you want to convert.
122122+ * @param {number} inMin - The start of the current range.
123123+ * @param {number} inMax - The end of the current range.
124124+ * @param {number} outMin - The start of the new range.
125125+ * @param {number} outMax - The end of the new range.
126126+ * @returns {number} The number converted to the new range.
127127+ */
128128+export function map(
129129+ value: number,
130130+ inMin: number,
131131+ inMax: number,
132132+ outMin: number,
133133+ outMax: number,
134134+): number {
135135+ return ((value - inMin) * (outMax - outMin)) / (inMax - inMin) + outMin;
136136+}
137137+138138+const epsilon = 0.001;
139139+140140+export class AnimatedValue {
141141+ _value: number;
142142+ _target: number;
143143+144144+ rate: number;
145145+146146+ needsUpdate: boolean = false;
147147+148148+ constructor(opts?: { value: number; rate: number }) {
149149+ this._value = opts?.value ?? 0;
150150+ this._target = opts?.value ?? 0;
151151+ this.rate = opts?.rate ?? 0.1;
152152+ }
153153+154154+ get value() {
155155+ return this._value;
156156+ }
157157+158158+ set value(value: number) {
159159+ this._target = value;
160160+ this.needsUpdate = true;
161161+ }
162162+163163+ update(dt: number) {
164164+ if (!this.needsUpdate) return false;
165165+166166+ this._value = framerateIndependentSmoothStep(
167167+ this._value,
168168+ this._target,
169169+ this.rate,
170170+ dt,
171171+ );
172172+173173+ if (Math.abs(this._value - this._target) > epsilon) {
174174+ this.needsUpdate = true;
175175+ } else {
176176+ this.needsUpdate = false;
177177+ }
178178+ return true;
179179+ }
180180+}