Goo Balls: a Physics Lab experiment with soft bodies

The code below is intended to be added as a new tab to a copy of the Physics Lab example project and TestGooBalls() to the list of tests in the Main tab’s setup() function. An example of the test running is below:


--
-- Goo Balls, for Physics Lab
--

TestGooBalls = class()

function TestGooBalls:init()
    self.title = "Goo Balls - Touch viewer to create"
    self.radius = 50
    self.n = 10
    local length = self.radius * math.sin(math.pi/self.n) * 2
    self.width = length/5
    self.length = length * (1 + self.width/self.radius/2)
    self.freq = 6
    self.damp = 0.5
    self.gooBalls = {}
    
    self.createGooBall = function (centre)
        local gooBall = {}
        gooBall.position = centre
        gooBall.core = createCircle(centre.x, centre.y, self.radius/3)
        gooBall.boxes = {}
        gooBall.springs = {}
        gooBall.joints = {}
        for i = 1, self.n do
            local a = (i - 1)/self.n * 2 * math.pi
            local x = centre.x + math.cos(a) * self.radius
            local y = centre.y + math.sin(a) * self.radius
            local box = createBox(x, y, self.width, self.length)
            box.angle = (i - 1)/self.n * 360
            box.friction = 0.02
            gooBall.boxes[i] = box
            local spring = physics.joint(DISTANCE, gooBall.core, box,
                gooBall.core.position, box.position)
            spring.frequency = self.freq
            spring.dampingRation = self.damp
            gooBall.springs[i] = spring
        end
        for i = 1, self.n do
            local a = (i - 1/2)/self.n * 2 * math.pi
            local x = centre.x + math.cos(a) * self.radius
            local y = centre.y + math.sin(a) * self.radius        
            gooBall.joints[i] = physics.joint(REVOLUTE, gooBall.boxes[i],
                gooBall.boxes[i % self.n + 1], vec2(x, y))
        end
        return gooBall
    end        
end

function TestGooBalls:setup()
    createGround()
    local box1 = createBox(WIDTH/3, HEIGHT/2, 320, 10)
    box1.type = STATIC
    box1.angle = 300  
    local box2 = createBox(WIDTH * 2/3, HEIGHT/2, 320, 10)
    box2.type = STATIC
    box2.angle = 60
end

function TestGooBalls:draw()
end

function TestGooBalls:touched(touch)
    if touch.state == BEGAN then
        local pos = vec2(touch.x, touch.y)
        table.insert(self.gooBalls, self.createGooBall(pos))
    end
end