Last active
October 26, 2021 12:11
-
-
Save william-silversmith/cdf9f0a7fd1a3e06040b to your computer and use it in GitHub Desktop.
The Bounce Factory
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| /* bounceFactory | |
| * | |
| * Consult this article: https://medium.com/@willsilversmith/the-bounce-factory-3498de1e5262#.pn5rcjp15 | |
| * The variables below are annotate with comments that reference the article. | |
| * | |
| * Simulate a physical bouncing motion based on physics equations of motion. | |
| * | |
| * We assume mass and gravity = 1 as they are immaterial when we normalize both | |
| * the y and t axis to 1. The length of the animation in msec will determine "gravity" | |
| * and the elasticity will determine the number of bounces. | |
| * | |
| * Required: | |
| * [0] bounces: (positive int) how many bounces do you want | |
| * | |
| * Optional: | |
| * [1] threshold (epsilon): [0..1], (default 0.1%) percent of energy remaining | |
| * at which to terminate the animation | |
| * | |
| * Return: f(t), t in 0..1 | |
| */ | |
| function bounceFactory (bounces, threshold) { | |
| threshold = threshold || 0.001; | |
| function energy_to_height (energy) { | |
| return energy; // h = E/mg, Eqn. 4 | |
| } | |
| function height_to_energy (height) { | |
| return height; // E = mgh, Eqn. 4 | |
| } | |
| function bounce_time (height) { | |
| // 2 x the half bounce time measured from the peak | |
| return 2 * Math.sqrt(2 * height); // Modified Eqn. 7 | |
| } | |
| function speed (energy) { | |
| // E = 1/2 m v^2, s = |sqrt(2E/m)| | |
| return Math.sqrt(2 * energy); // Eqn. 8 | |
| } | |
| var height = 1; | |
| var potential = height_to_energy(height); | |
| var elasticity = Math.pow(threshold, 1 / bounces); // Eqn. 10 | |
| // The critical points are the points where the object contacts the "ground" | |
| // Since the object is initially suspended at 1 height, this either creates an | |
| // exception for the following code, or you can use the following trick of placing | |
| // a critical point behind 0 and representing the inital position as halfway though | |
| // that arc. | |
| var critical_points = [{ | |
| time: - bounce_time(height) / 2, | |
| energy: potential, | |
| }, | |
| { | |
| time: bounce_time(height) / 2, | |
| energy: potential * elasticity, | |
| }]; | |
| potential *= elasticity; | |
| height = energy_to_height(potential); | |
| var time = critical_points[1].time; | |
| for (var i = 1; i < bounces; i++) { | |
| time += bounce_time(height); | |
| potential *= elasticity; // Eqn. 2, remove energy after each bounce | |
| critical_points.push({ | |
| time: time, | |
| energy: potential, | |
| }); | |
| height = energy_to_height(potential); | |
| } | |
| var duration = time; // renaming to emphasize it's the total time now | |
| return function (t) { | |
| t = clamp(t, 0, 1); | |
| var tadj = t * duration; | |
| if (tadj === 0) { | |
| return 0; | |
| } | |
| else if (tadj >= duration) { | |
| return 1; | |
| } | |
| // Find the bounce point we are bouncing from, for very long animations (hours, days), | |
| // an binary search algorithm might be appropriate. | |
| var index; | |
| for (index = 0; index < critical_points.length; index++) { | |
| if (critical_points[index].time > tadj) { | |
| break; | |
| } | |
| } | |
| var bouncept = critical_points[index - 1]; | |
| // Bouncing from a bounce point effectively resets time as it is a discontinuity | |
| tadj -= bouncept.time; | |
| var v0 = speed(bouncept.energy); | |
| // Project position of object from bounce point to the current time | |
| var pos = v0 * tadj + -0.5 * tadj * tadj; // Eqn. 1 | |
| return 1 - pos; | |
| }; | |
| } | |
| function clamp (val, lower, upper) { | |
| return Math.max(Math.min(val, upper), lower); | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment