A spring

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

well done !! , very nice

Very nice!

Couple of small changes:

  1. 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).

  2. 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.

  3. 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.)

  4. 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

:smiley: 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! :wink:

@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 :smiley:
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 ^:)^