Alan gave his dry-run talk for his Turing Award talk later this month (which was excellent by the way). I was supposed to demo the mass/spring model I did with him at the end, but did not realize he was waiting for me to let him know I was ready - and I was waiting for him to let me know when to come up to do it. Oh well. I expect the demo will be in great shape for the next talk. Besides, I think his talk was actually quite well done without my demo at the end. I think it might have broken his stride.
The demo is actually quite simple. There are three objects. What I call a TTapestry, which is the UI and rendering of the system, a TMass, which holds a position, summed forces, and a mass (usually 1.0), and TSpring, which connects two masses.
When initialized, TTapestry creates a grid of TMasses, roughly in a square, and generates a collection of TSprings, each of which is iniitalized with two masses that can have influence on each other. All three of these have a #compute method. The TTapestry compute sends the other two compute messages to the collection of masses and then springs.
springs do:[ :s | s compute. ].
masses do:[ :m | m compute. ].
self future: 20 perform: #compute.
the #addWind message just adds another force to the masses from a virtual wind.
The TSpring>>#compute uses the standard -kx force computation as below:
| v n k |
v _ (mass1 location - mass2 location).
n _ v normalized * rest.
v _ (v-n) * k.
mass1 addForce: v.
mass2 addForce: v negated.
All that this is doing is determining the vector distance between the two masses, subtracting the rest distance (where the x in -kx is zero) and then multiplying the rest by k. We then add this force and its equal and opposite reaction force to the appropriate masses.
Finally, we compute the effect of the forces on each mass:
| g accel t |
mass ~= 0.0 ifTrue:[
t := 0.05.
g := 0.0@-email@example.com.
force := force + g." add the gravity"
accel := force. " mass = 1.0"
velocity := velocity + (accel * t). "acceleration increases velocity"
velocity := velocity - (velocity * 0.1)."damp velocity"
location := location + (velocity * t). "change location"
force := 0@0@0. "clear force"
if the mass is 0.0 we don't do anything, as this means the mass is a fixed location - like the corner of the flag.
We could use the actual elapsed time here, but I am just using an artificial one, which I am setting to 0.05 of a second. I am presuming that the mass is equal to 1.0 so I don't have to divide to compute acceleration (f = ma, hence a = f/m).
Acceleration causes a change in the velocity, and velocity causes a change in position, so now we have moved the location of the mass.
This last line is very important. Now that we have resolved the forces from this time segment into an action, we need to clear the force vector. If we don't, this force value will continue to grow, which causes the system to become unstable.
That is all there is for the masses and springs. Really. The TTapestry adds a wind, but this just results in another force being added to the masses. What I find so interesting is that these two extremely simple objects generate such interesting behavior.
The only other interesting thing is the TTapestry>>#render:, but for some reason I screwed this up, so need to thing about it a bit before I post it here...