Here’s a simple simulation of a spring, adapted from a simulation in processing.
--Adapted from http://processing.org/examples/spring.html
function setup()
-- Spring drawing constants for top bar
springHeight = 32 --Height
left = WIDTH/2 - 100 --Left position
right = WIDTH/2 + 100 --Right position
max = 200 --Maximum Y value
min = 100 --Minimum Y value
move = false --If touch moving
--Spring simulation constants
M = 0.8 --Mass
K = 0.2 --Spring constant
D = 0.92 --Damping
R = HEIGHT/2 --Rest position
--Spring simulation variables
ps = R -- Position
vs = 0.0 -- Velocity
as = 0 --Acceleration
f = 0 --Force
end
-- This function gets called once every frame
function draw()
-- This sets a dark background color
background(40, 40, 50)
-- This sets the line thickness
strokeWidth(5)
-- Do your drawing here
rectMode(CORNERS)
drawSpring()
updateSpring()
end
function drawSpring()
--Draw base
fill(0, 0, 0, 255)
baseWidth = 0.5 * ps
rect(WIDTH/2-baseWidth, ps+springHeight, WIDTH/2+baseWidth, HEIGHT)
--Set color and draw top bar
if move then
fill(255)
else
fill(170, 47, 47, 255)
end
rect(left, ps, right, ps + springHeight);
end
function updateSpring()
--Update the spring position
if not move then
f = -K * (ps - R) --f=-ky
as = f / M --Set the acceleration, f=ma == a=f/m
vs = D * (vs + as) --Set the velocity
ps = ps + vs --Updated position
end
if math.abs(vs) < 0.1 then
vs = 0.0
end
function touched(touch)
if touch.state==MOVING and left < touch.x and touch.x < right
and ps < touch.y and touch.y < ps + springHeight then
move=true
else
move=false
end
--Position of top bar
if move then
ps = touch.y - springHeight/2
end
end
end
Very nice!
Couple of small changes:
-
The updating of the position and velocity has a small error in it. You need to use the current velocity to update the position, not the updated velocity. So the line ps = ps + vs
needs to go before the line vs = D*(vs + as)
, not after (but it needs to be after the line setting the force as the current position needs to be used to set the force).
-
I recommend using DeltaTime
as a step variable since in a more complicated simulation then this might not be constant. So ps = ps + DeltaTime*vs
and vs = vs + DeltaTime*as
. You’ll need to increase the spring constant to get a decent motion for that.
-
I was a bit bemused by the velocity updating line: vs = D*(vs + as)
. I don’t know of a model where that is right (though I’m no physicist). It should be vs = vs + DeltaTime * as
with the damping contributing to the force, so f = -ky - Dv
. I see no explanation for this on the processing page. (Incidentally, I notice that the processing example is the other way up to the Codea one.)
-
I found that when I was dragging the mass then if I dragged too fast I was in danger of letting go of it. I recommend setting the move
only if touch.state == BEGAN
or ENDED
(so true
for BEGAN
and false
for ENDED
). Then when the touch is moving, if you move it off the mass you still are dragging it.
Anyway, here’s my version (still with the upside-down though as I only just noticed that).
--Adapted from http://processing.org/examples/spring.html
function setup()
-- Spring drawing constants for top bar
springHeight = 32 --Height
left = WIDTH/2 - 100 --Left position
right = WIDTH/2 + 100 --Right position
max = 200 --Maximum Y value
min = 100 --Minimum Y value
move = false --If touch moving
--Spring simulation constants
M = 0.8 --Mass
K = 20 --Spring constant
D = 0.92 --Damping
R = HEIGHT/2 --Rest position
--Spring simulation variables
ps = R -- Position
vs = 0.0 -- Velocity
as = 0 --Acceleration
f = 0 --Force
end
-- This function gets called once every frame
function draw()
-- This sets a dark background color
background(40, 40, 50)
-- This sets the line thickness
strokeWidth(5)
-- Do your drawing here
rectMode(CORNERS)
drawSpring()
updateSpring()
end
function drawSpring()
--Draw base
fill(0, 0, 0, 255)
baseWidth = 0.5 * ps
rect(WIDTH/2-baseWidth, ps+springHeight, WIDTH/2+baseWidth, HEIGHT)
--Set color and draw top bar
if move then
fill(255)
else
fill(170, 47, 47, 255)
end
rect(left, ps, right, ps + springHeight);
end
function updateSpring()
--Update the spring position
if not move then
f = -K * (ps - R) - D*vs --f=-ky - Dv
as = f / M --Set the acceleration, f=ma == a=f/m
ps = ps + DeltaTime * vs --Updated position
vs = vs + DeltaTime * as --Set the velocity
end
if math.abs(vs) < 0.1 then
vs = 0.0
end
function touched(touch)
if touch.state==BEGAN and left < touch.x and touch.x < right
and ps < touch.y and touch.y < ps + springHeight then
move=true
elseif touch.state == ENDED then
move=false
end
--Position of top bar
if move then
ps = touch.y - springHeight/2
end
end
end
Thanks guys. I knew that the simulation could be improved. I just left it simple enough for newbies like me as an starting point.
@Andrew_ Stacey, thank you very much for the improvements. Those are the further steps needed for us newbies to make such simulations better.
Greetings.
Addendum: @Andrew_Stacey, you are absolutely right about the equations. I’ve not noticed that at a first glance, just “translated” the example as such.
Thanks again!
Thought I’d add an actual spring.
--Adapted from http://processing.org/examples/spring.html
function setup()
-- Spring drawing constants for top bar
springHeight = 32 --Height
left = WIDTH/2 - 200 --Left position
right = WIDTH/2 + 200 --Right position
max = 200 --Maximum Y value
min = 100 --Minimum Y value
move = false --If touch moving
--Spring simulation constants
M = 0.8 --Mass
K = 20 --Spring constant
D = 0.92 --Damping
R = HEIGHT/2 --Rest position
--Spring simulation variables
ps = R -- Position
vs = 0.0 -- Velocity
as = 0 --Acceleration
f = 0 --Force
sp = mesh()
sp.shader = shader([[
//
// A basic vertex shader
//
//This is the current model * view * projection matrix
// Codea sets it automatically
uniform mat4 modelViewProjection;
//This is the current mesh vertex position, color and tex coord
// Set automatically
attribute vec4 position;
attribute vec4 color;
attribute vec2 texCoord;
//This is an output variable that will be passed to the fragment shader
varying highp vec2 vTexCoord;
uniform float len;
uniform float width;
uniform float blur;
float twidth = width+blur;
uniform vec2 pts[4];
uniform int period;
float l = 2.*3.1415*float(period);
void main()
{
highp float t = position.y/len;
highp vec2 bpos = pts[0] + t*pts[3] + cos(l*t)*pts[1] + sin(l*t)*pts[2];
highp vec2 bdir = pts[3] - l*sin(l*t)*pts[1] + l*cos(l*t)*pts[2];
bdir = vec2(bdir.y,-bdir.x);
bdir = twidth*position.x*normalize(bdir);
bpos = bpos + bdir;
highp vec4 bzpos = vec4(bpos.x,bpos.y,0,1);
//Pass the mesh color to the fragment shader
vTexCoord = vec2(texCoord.x, 1.0 - texCoord.y);
//Multiply the vertex position by our combined transform
gl_Position = modelViewProjection * bzpos;
}
]],[[
//
// A basic fragment shader
//
//This represents the current texture on the mesh
uniform lowp sampler2D texture;
uniform highp float width;
uniform highp float blur;
highp float edge = blur/(width+blur);
uniform lowp vec4 colour;
//The interpolated texture coordinate for this fragment
varying highp vec2 vTexCoord;
void main()
{
//Sample the texture at the interpolated coordinate
lowp vec4 col = colour;
//if (vTexCoord.x < edge)
// col.a = col.a*vTexCoord.x/edge;
//if (vTexCoord.x > 1. - edge)
// col.a = col.a*(1.-vTexCoord.x)/edge;
//Set the output color to the texture color
col.a = mix( 0., col.a,
smoothstep( 0., edge, min(vTexCoord.x,1. - vTexCoord.x) ) );
gl_FragColor = col;
}
]])
for n=1,300 do
sp:addRect(0,(n-.5),1,1)
end
sp.shader.len = 300
sp.shader.width = 5
sp.shader.colour = color(255, 255, 255, 255)
sp.shader.period = 5
sp.shader.blur = 2
end
-- This function gets called once every frame
function draw()
-- This sets a dark background color
background(40, 40, 50)
-- This sets the line thickness
strokeWidth(5)
-- Do your drawing here
rectMode(CORNERS)
drawSpring()
updateSpring()
end
function drawSpring()
--Draw base
fill(0, 0, 0, 255)
baseWidth = 0.25 * (HEIGHT -ps )
rect(WIDTH/2-baseWidth, ps, WIDTH/2+baseWidth, 0)
--Set color and draw top bar
if move then
fill(255)
else
fill(170, 47, 47, 255)
end
rect(left, ps, right, ps + springHeight);
sp.shader.pts = {
vec2(WIDTH/2,ps+springHeight+47.5),
vec2(0,-50),
vec2(100,0),
vec2(0,HEIGHT-ps-springHeight+5)
}
sp:draw()
end
function updateSpring()
--Update the spring position
if not move then
f = -K * (ps - R) - D*vs --f=-ky - Dv
as = f / M --Set the acceleration, f=ma == a=f/m
ps = ps + DeltaTime * vs --Updated position
vs = vs + DeltaTime * as --Set the velocity
end
if math.abs(vs) < 0.1 then
vs = 0.0
end
function touched(touch)
if touch.state==BEGAN and left < touch.x and touch.x < right
and ps < touch.y and touch.y < ps + springHeight then
move=true
elseif touch.state == ENDED then
move=false
end
--Position of top bar
if move then
ps = touch.y - springHeight/2
end
end
end
lol.
But…, wouldn’t be funnier (and may be more realistic) if, instead of the spring base, you have a spring that widens and shortens its horizontal lenght? … And no mention to having the graph of the nice damped harmonic oscillation! 
@quezadev Yes, that would be a bit more realistic wouldn’t it! Still, I’m a mathematician - reality comes a poor second.
Regarding the graph, here’s a video from a related project. Maybe I’ll add springs in to the mix:
http://youtu.be/m_9Q2X-PJwE
(I can never remember how you embed videos here.)
@quezadev Just tried it and the amount the spring varies in width is negligible. It’s about 1% when stretching it to the whole screen.
@Andrew_Stacey, lol about the math joke. I’m an engineer but love math… madly 
Regarding the width change, after I posted my last comment, I thought the same (negligible change) and wanted to prove it myself… But you were faster and preciser as usual ^:)^