Newtons cradle (help)

Can anyone explain what I’m doing wrong or why this isn’t working. With a real Newtons cradle, only the left and right balls should be moving. The 3 middle balls should remain stationary. I can’t figure out why 4 balls are moving and what I need to adjust.


--# Main
-- Newtons cradle

function setup()
    supportedOrientations(LANDSCAPE_LEFT)
    displayMode(FULLSCREEN)
   
    -- create 5 hanger points, a1-a5
    a1=physics.body(CIRCLE,0)
    a1.type=STATIC
    a1.x=WIDTH/2-200
    a1.y=700
    
    a2=physics.body(CIRCLE,0)
    a2.type=STATIC
    a2.x=WIDTH/2-100
    a2.y=700
    
    a3=physics.body(CIRCLE,0)
    a3.type=STATIC
    a3.x=WIDTH/2
    a3.y=700
    
    a4=physics.body(CIRCLE,0)
    a4.type=STATIC
    a4.x=WIDTH/2+100
    a4.y=700
    
    a5=physics.body(CIRCLE,0)
    a5.type=STATIC
    a5.x=WIDTH/2+200
    a5.y=700 
       
    -- create 5 balls, c1-c5
    c1=physics.body(CIRCLE,50)
    c1.type=DYNAMIC
    c1.restitution=1
    c1.x=WIDTH/2-200
    c1.y=200
    
    -- give the left ball a velocity to the left
    c1.linearVelocity=vec2(-200,0)
         
    c2=physics.body(CIRCLE,50)
    c2.type=DYNAMIC
    c2.restitution=1
    c2.x=WIDTH/2-100
    c2.y=200    
    
    c3=physics.body(CIRCLE,50)
    c3.type=DYNAMIC
    c3.restitution=1
    c3.x=WIDTH/2
    c3.y=200 
    
    c4=physics.body(CIRCLE,50)
    c4.type=DYNAMIC
    c4.restitution=1
    c4.x=WIDTH/2+100
    c4.y=200 
    
    c5=physics.body(CIRCLE,50)
    c5.type=DYNAMIC
    c5.restitution=1
    c5.x=WIDTH/2+200
    c5.y=200 
  
    -- create a joint for each ball and hanger
    physics.joint(REVOLUTE,c1,a1,a1.position)   
    physics.joint(REVOLUTE,c2,a2,a2.position)  
    physics.joint(REVOLUTE,c3,a3,a3.position)
    physics.joint(REVOLUTE,c4,a4,a4.position)  
    physics.joint(REVOLUTE,c5,a5,a5.position)
end

function draw()
    background(40, 40, 50)

    -- draw each ball
    fill(255) 
    ellipse(c1.x,c1.y,c1.radius*2)
    ellipse(c2.x,c2.y,c2.radius*2) 
    ellipse(c3.x,c3.y,c3.radius*2)
    ellipse(c4.x,c4.y,c4.radius*2) 
    ellipse(c5.x,c5.y,c5.radius*2)
            
    -- draw a line from each hanger to each ball
    stroke(255, 255, 255)
    strokeWidth(3)    
    line(c1.x,c1.y,a1.x,a1.y) 
    line(c2.x,c2.y,a2.x,a2.y)  
    line(c3.x,c3.y,a3.x,a3.y) 
    line(c4.x,c4.y,a4.x,a4.y)  
    line(c5.x,c5.y,a5.x,a5.y)    
end

Hi @dave1707,

Have you checked this already?: http://twolivesleft.com/Codea/Talk/discussion/621/newton-s-cradle/p1

It will give you perhaps an idea.

Hi @dave1707,

That’s interesting, I coded a similar example yesterday with the same result as you. But following the link provided by @quezadav was very helpful (thanks!).
@Herwig mentioned there that he had to put some extra space between the balls. So I tried this with your example, and it almost worked. Adjusting the restitiution of the balls from 1 to .95 further improved the simulation.

--# Main
-- Newtons cradle

function setup()
    supportedOrientations(LANDSCAPE_LEFT)
    displayMode(FULLSCREEN)
   
    -- create 5 hanger points, a1-a5
    a1=physics.body(CIRCLE,0)
    a1.type=STATIC
    a1.x=WIDTH/2-202
    a1.y=700
    
    a2=physics.body(CIRCLE,0)
    a2.type=STATIC
    a2.x=WIDTH/2-101
    a2.y=700
    
    a3=physics.body(CIRCLE,0)
    a3.type=STATIC
    a3.x=WIDTH/2
    a3.y=700
    
    a4=physics.body(CIRCLE,0)
    a4.type=STATIC
    a4.x=WIDTH/2+101
    a4.y=700
    
    a5=physics.body(CIRCLE,0)
    a5.type=STATIC
    a5.x=WIDTH/2+202
    a5.y=700 
       
    -- create 5 balls, c1-c5
    c1=physics.body(CIRCLE,50)
    c1.type=DYNAMIC
    c1.restitution=.95
    c1.x=WIDTH/2-202
    c1.y=200
    
    -- give the left ball a velocity to the left
    c1.linearVelocity=vec2(-200,0)
         
    c2=physics.body(CIRCLE,50)
    c2.type=DYNAMIC
    c2.restitution=.95
    c2.x=WIDTH/2-101
    c2.y=200    
    
    c3=physics.body(CIRCLE,50)
    c3.type=DYNAMIC
    c3.restitution=.95
    c3.x=WIDTH/2
    c3.y=200 
    
    c4=physics.body(CIRCLE,50)
    c4.type=DYNAMIC
    c4.restitution=.95
    c4.x=WIDTH/2+101
    c4.y=200 
    
    c5=physics.body(CIRCLE,50)
    c5.type=DYNAMIC
    c5.restitution=.95
    c5.x=WIDTH/2+202
    c5.y=200 
  
    -- create a joint for each ball and hanger
    physics.joint(REVOLUTE,c1,a1,a1.position)   
    physics.joint(REVOLUTE,c2,a2,a2.position)  
    physics.joint(REVOLUTE,c3,a3,a3.position)
    physics.joint(REVOLUTE,c4,a4,a4.position)  
    physics.joint(REVOLUTE,c5,a5,a5.position)
end

function draw()
    background(40, 40, 50)

    -- draw each ball
    fill(255) 
    ellipse(c1.x,c1.y,c1.radius*2)
    ellipse(c2.x,c2.y,c2.radius*2) 
    ellipse(c3.x,c3.y,c3.radius*2)
    ellipse(c4.x,c4.y,c4.radius*2) 
    ellipse(c5.x,c5.y,c5.radius*2)
            
    -- draw a line from each hanger to each ball
    stroke(255, 255, 255)
    strokeWidth(3)    
    line(c1.x,c1.y,a1.x,a1.y) 
    line(c2.x,c2.y,a2.x,a2.y)  
    line(c3.x,c3.y,a3.x,a3.y) 
    line(c4.x,c4.y,a4.x,a4.y)  
    line(c5.x,c5.y,a5.x,a5.y)    
end

I have been working through (a copy of) the Physics Lab example project. Below is the same idea, expressed as a new ‘Test 10’, but using a DISTANCE joint:


Test10 = class()

function Test10:init()
    self.title = "Newton's cradle"
    self.size = 50  -- Change this to change the size of the balls
end

function Test10:setup()
    local s = self.size
    for i = 1, 5 do
        local a = createCircle(WIDTH/2 + (i - 3) * s * 2, 700, 5)
        a.type = STATIC
        
        local c = createCircle(WIDTH/2 + (i - 3) * s * 2, 200, s * 0.99)
        c.restitution = 0.95
        c.friction = 0
        if i == 1 then c.linearVelocity = vec2(-200, 0) end

        local top = c.position + vec2(0, c.radius)
        
        local j = physics.joint(DISTANCE, a, c, a.position, top)
        debugDraw:addJoint(j)
    end
end

function Test10:draw()
end

function Test10:touched(touch)
end

Here is my updated version of Newtons cradle. Thanks to all who explained why the other one wasn’t working correctly. This one also has problems, but that’s kind of beyond our control. Is the problem because Codea is using single precision instead of double precision, I don’t know. I allow the ball count to range from 2 to 50. I also allow the number of pulled balls to be from 1 to ball count -1. I originally wrote this to play around with the physics.joint function.


--# Main
-- Newtons cradle

function setup()
    supportedOrientations(LANDSCAPE_LEFT)
    iparameter("balls",2,50,5)
    iparameter("number",1,20,1)
    tab1={}    -- table for objects
    radius=WIDTH/(balls*3)    -- starting radius
    prevBalls=0
    prevNumber=0
    getOffset()
    create()
end     

function draw()
    background(40, 40, 50)
    
    -- balls or number were changed with iparameters
    if prevBalls ~= balls or prevNumber ~= number then
        create()
    end
    
    -- draw the balls and lines
    for z=1,balls do
        -- draw the balls
        fill(255)  
        ball=tab1[z].bodyA              
        ellipse(ball.x,ball.y,ball.radius*2)
        
        -- draw line from hanger to ball
        stroke(255, 255, 255)
        strokeWidth(3)  
        hanger=tab1[z].bodyB                
        line(hanger.x,hanger.y,ball.x,ball.y) 
    end         
end

function create()
    -- remove previous objects
    if prevBalls ~= balls or prevNumber ~= number then 
        for z=1,prevBalls do
            tab1[z].bodyA:destroy()
            tab1[z]:destroy()
            tab1[z]=nil
        end
        
        -- limit number to less than balls
        if number > balls-1 then
            number = balls-1
        end
        
        prevBalls=balls       
        prevNumber=number
        radius=WIDTH/(balls*3)
        getOffset()
    end
    
    -- create new objects
    for z=1,balls do
        hanger=physics.body(CIRCLE,0)
        hanger.type=STATIC
        hanger.x=z*r2+offset
        hanger.y=700
        ball=physics.body(CIRCLE,radius)
        ball.restitution=1
        ball.x=z*r2+offset
        ball.y=200
        
        -- give number of balls a velocity
        if z<=number then
           ball.linearVelocity=vec2(-300,0) 
        end 
        
        -- join the balls and the hangers
        tab1[z]=physics.joint(REVOLUTE,ball,hanger,hanger.position)
    end 
end 

function getOffset()
    -- calculate offset to keep display centered
    r2=radius*2+2
    c=r2*balls-r2
    offset=((WIDTH-c)/2)-r2
end