Soft Bodies example - Modify 2d mesh in real time

It uses hidden control points (circle bodies) to modify the shape of the mesh, the function triangulate use all of these centers and then with a shader draw it.
Using some specific options of joints you can setup this behaviour, check out the code.

MyNode = class()

function MyNode:init(nSegments)
    touches = 0
    parameter.watch("touches")
    parameter.boolean("detach",false,detachInner)
    parameter.boolean("elastic",false,changeJoints)
    self.numSegments = nSegments or 12
    self.ptmRatio    = math.pi
    local center = vec2(WIDTH/2,HEIGHT/2)
    local springness = 4.0
    local deltaAngle = (2.0*math.pi)/self.numSegments
    local radius = 500
    self.bodies = {}
    local cos = math.cos
    local sin = math.sin
    for i=0, self.numSegments-1 do
        local theta = deltaAngle * i
        local x = radius * cos(theta)
        local y = radius * sin(theta)
        self.bodies[i] = self:createCircle(
            vec2( x/self.ptmRatio + math.random(-16,16), 
            y/self.ptmRatio + math.random(6) ) + center 
            ,37
        )
        self.bodies[i].info= {origin = vec2(x,y)}
    end
    -- inner circle
    self.inner = self:createCircle(center, 23)
    -- create joints
    self.inner.info = {
        joints = {}
    }
    self.inner.type = STATIC
    self.inner.fixedRotation = true
    for i=0, self.numSegments-1 do
        local neighborIndex = math.fmod (i+1, self.numSegments)
        local currentBody   = self.bodies[i]
        local neighborBody  = self.bodies[neighborIndex]
        
        currentBody.info.joint = physics.joint(
            DISTANCE, currentBody,neighborBody,
            currentBody.worldCenter,
            neighborBody.worldCenter
        )
        currentBody.info.color = color(
            math.random(10,80), math.random(20,60), math.random(166,255)
        )
        
        currentBody.info.joint.frequency = springness
        currentBody.info.joint.dampingRatio = 0.5
        
        -- connect center body with a joint
        self.inner.info.joints[i] = physics.joint(
            DISTANCE,
            currentBody, self.inner, 
            currentBody.worldCenter,
            center
        )
        
        self.inner.info.joints[i].frequency = springness
        self.inner.info.joints[i].dampingRatio = 0.1
    end
    -- create ground
    self.ground = physics.body(POLYGON,
        vec2(0,10),
        vec2(0,0),
        vec2(WIDTH,0),
        vec2(WIDTH,10)
    )
    self.ground.type = STATIC
    -- touches table
    self.touches = {}
    
    -- mesh
    self.m = mesh()
    
    self.m.shader = shader(
[[
uniform mat4 modelViewProjection;
attribute vec4 position;
attribute vec4 color;
varying lowp vec4 vColor;
varying mediump vec4 vPos;
void main()
{
    //Pass the mesh color to the fragment shader
    vColor = color;
    vPos = position;
    
    //Multiply the vertex position by our combined transform
    gl_Position = modelViewProjection * position;
}
]],
[[
//Default precision qualifier
precision highp float;

//interpolated vertex color for this fragment
varying lowp vec4 vColor;
varying mediump vec4 vPos;

//Brick variables
uniform vec4 brickColor;
uniform vec4 mortarColor;
uniform float time;
uniform vec3 brickSize;
uniform vec3 brickPct;

void main()
{
    vec3 color;
    vec3 position, useBrick;
    
    position = vPos.xyz / brickSize.xyz;
    
    if( fract(position.y * 0.5) > 0.5 )
    {
        position.x += 0.5;
        position.z += 1.0;
    }

    position = fract(position);
    useBrick = step(position, brickPct.xyz);
    
    color = mix(mortarColor.rgb, brickColor.rgb, 
                useBrick.x * useBrick.y * useBrick.z);
    color *= vColor.rgb;

    //Set the output color to the texture color
    gl_FragColor = vec4(color, 1.0);
    vec2 uv = gl_FragColor.xy/vec3(1.0,1.0,0.0).xy;
    gl_FragColor = vec4(uv, 0.5 + 0.5 * sin(time), 1.0);
}

]])
    --self.m.shader = shader("Patterns:Bricks")
    self.m.shader.brickColor = vec4(0.1,0.3,0.8,1.0)  
    self.m.shader.mortarColor= vec4(0.8,0.8,0.8,1.0)
    self.m.shader.brickSize  = vec3(33,13,23) 
    self.m.shader.brickPct   = vec3(0.9,0.85,0.85)
    self.m.shader.time = ElapsedTime
    self.bg = readImage("physicsnotebook:background3")
end

function MyNode:createCircle(pos,r)
    local circle = physics.body(CIRCLE, r)
    circle.density = 0.1
    circle.restitution = 0.05
    circle.friction = 1.0
    circle.type = DYNAMIC
    circle.x = pos.x
    circle.y = pos.y
    circle.fixedRotation = true
    return circle
end

function MyNode:draw()
    sprite(self.bg, WIDTH/2, HEIGHT/2, WIDTH, HEIGHT)
    
    fill(255)
    -- draw ground
    rectMode(CENTER)
    rect(
        self.ground.points[1].x/2, self.ground.points[1].y/2,
        self.ground.points[1].x, self.ground.points[1].y
    )
    strokeWidth(1)
    lineCapMode(ROUND)
    ellipseMode(RADIUS)
    
    -- draw inner joints
    for i=0,#self.inner.info.joints do
        local joint = self.inner.info.joints[i]
       --[[ line(
                joint.anchorA.x, joint.anchorA.y,
                joint.anchorB.x, joint.anchorB.y
        )]]--
    end
    local vs = {}
    for i=0, self.numSegments-1 do
        local body = self.bodies[i]
        -- draw joint also
        if body.info.joint then
           --[[ line(
                body.info.joint.anchorA.x, body.info.joint.anchorA.y,
                body.info.joint.anchorB.x, body.info.joint.anchorB.y
            )]]--
        end
        fill(body.info.color)
        ellipse(body.x, body.y, body.radius) 
        table.insert(vs, vec2(body.x,body.y))
    end
    fill(0)
    ellipse(self.inner.x, self.inner.y,self.inner.radius)
    --table.sort(vs, compareV2)
    self.m.vertices = triangulate(vs)
    self.m:setColors(127,127,127,155) 
    self.m.shader.time = ElapsedTime
    blendMode(MULTIPLY)
    self.m:draw()
    blendMode(NORMAL)
end

function MyNode:isTouchingBody(touch)
    local tpoint = vec2(touch.x, touch.y)
    for i=0, self.numSegments-1 do
        local body = self.bodies[i]
        if body:testPoint(tpoint) then 
           return body
        end
    end
    return nil
end

function MyNode:isDraggingKey(key)
    for i,b in ipairs(self.touches) do
        if key==b.key then return i end
    end
    return -1
end

function MyNode:touched(touch)
    -- check if it is touching a body:
    local index = self:isDraggingKey(touch.id)
    if index>-1 then
        local tpoint = vec2(touch.x,touch.y)
        if self.touches[index].body then
            local t =  self.touches[index]
           -- if t.body.info.origin:dist(tpoint)<t.body.radius*12 then
            if t.origin:dist(tpoint)v2.x and v1.y>v2.y)
    return v1.x<v2.x and v1.y<v2.y
end

-- SoftBody
node = nil
-- Use this function to perform your initial setup
function setup()
    node = MyNode()
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
    node:draw()
end

function touched(touch)
    node:touched(touch)
end


```


Video:
http://www.youtube.com/watch?v=fKIyPqNOwwk

Haha. Thanks. This was on my todo list and I was about to start on it.

This is amazing! I love soft bodies)))