-
Notifications
You must be signed in to change notification settings - Fork 18
/
bouncingballs.js
102 lines (83 loc) · 3.56 KB
/
bouncingballs.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
/**
* bouncingballs.js
*
* Bouncing Balls by Danny Wilson, originally for the FastLED library
*
* Originally by Danny Wilson - see https://github.com/fibonacci162/LEDs
*
* LEDstrip plugin
*
* Copyright (c) 2013 Dougal Campbell
*
* Distributed under the MIT License
*/
function BouncingBalls (ledstrip, opts) {
opts = opts || {};
this.ledstrip = ledstrip;
this.ledstrip.clear();
this.NUM_LEDS = this.ledstrip.size();
this.GRAVITY = -9.81; // Gravity constant m/s2
this.h0 = 1; // Initial height of balls in meters
this.NUM_BALLS = 5; // How many balls
this.h = new Array(this.NUM_BALLS); // height in meters
this.vImpact0 = Math.sqrt(-2 * this.GRAVITY * this.h0); // Initial impact velocity (v at t0)
this.vImpact = new Array(this.NUM_BALLS); // Impact velocity
this.tCycle = new Array(this.NUM_BALLS); // Time since last ground strike
this.pos = new Array(this.NUM_BALLS); // Position on LED strip
this.tLast = new Array(this.NUM_BALLS); // Clock time of last ground strike
this.COR = new Array(this.NUM_BALLS); // Coefficient of Restitution (bounce damping)
this.t = 0; // tick counter
return this;
}
BouncingBalls.prototype.init = function() {
for (var i = 0; i < this.NUM_BALLS; i++) {
this.tLast[i] = (new Date()).valueOf();
this.h[i] = this.h0;
this.pos[i] = 0;
this.vImpact[i] = this.vImpact0;
this.tCycle[i] = 0;
this.COR[i] = 0.90 - i/Math.pow(this.NUM_BALLS, 2);
}
return this;
}
BouncingBalls.prototype.bounce = function (tick) {
var i;
this.ledstrip.clear();
for (i = 0; i < this.NUM_BALLS; i++) {
this.tCycle[i] = (new Date()).valueOf() - this.tLast[i];
// A little kinematics equation calculates positon as a function of time, acceleration (gravity) and intial velocity
this.h[i] = 0.5 * this.GRAVITY * Math.pow( this.tCycle[i]/1000 , 2.0 ) + this.vImpact[i] * this.tCycle[i]/1000;
if ( this.h[i] < 0 ) {
this.h[i] = 0; // If the ball crossed the threshold of the "ground," put it back on the ground
this.vImpact[i] = this.COR[i] * this.vImpact[i] ; // and recalculate its new upward velocity as it's old velocity * COR
this.tLast[i] = (new Date()).valueOf();
if ( this.vImpact[i] < 0.01 ) this.vImpact[i] = this.vImpact0; // If the ball is barely moving, "pop" it back up at vImpact0
if ( this.vImpact[i] > this.vImpact0 ) this.vImpact[i] = this.vImpact0; // If gravity changes, cap the impact velocity
}
this.pos[i] = Math.round( this.h[i] * (this.NUM_LEDS - 1) / this.h0); // Map "h" to a "pos" integer index position on the LED strip
// Stay within our boundaries:
if (this.pos[i] < 0) this.pos[i] = 0;
if (this.pos[i] > (this.NUM_LEDS - 1)) this.pos[i] = this.NUM_LEDs - 1;
}
//Choose color of LEDs, then the "pos" LED on
for (i = 0 ; i < this.NUM_BALLS ; i++){
this.ledstrip.buffer[this.pos[i]] = this.ledstrip.hsl2rgb(Math.floor(((i+1) / this.NUM_BALLS) * 360), 1.0, 0.5);
}
this.ledstrip.send();
//Then off for the next loop around
//for (i = 0 ; i < this.NUM_BALLS ; i++) {
// this.ledstrip.buffer[this.pos[i]] = [0,0,0];
//}
return this;
}
BouncingBalls.prototype.setGravity = function (G) {
// Gravity should be a negative acceleration
if (G > 0) G *= -1;
this.GRAVITY = G;
// Reset vImpact0
this.vImpact0 = Math.sqrt(-2 * this.GRAVITY * this.h0);
}
BouncingBalls.prototype.animate = function() {
animation = requestAnimationFrame(this.animate.bind(this)); // preserve our context
this.bounce(this.t++); // calculate waves and increment tick
}