Inverse Kinematics (IK) Example

I don’t think I’ve posted this. I found it in my projects so I thought I’d share it (I ported the solver from somewhere and can’t remember where now).

(Edit: The code for the Bone class is pretty terrible and relies on the solver updating the hierarchy each frame.)

You should be able to copy this and long-press the New Project button to paste it as a new project.

--# Bone
Bone = class()

function Bone:init(len)
    -- you can accept and set parameters here
    self.pos = vec2(0,0)
    self.len = len or 100
    self.angle = 0
    self.child = nil
end

function Bone:draw()
    pushStyle()
    
    stroke(255)
    strokeWidth(3.0)
    smooth()
    
    local endPoint = vec2(self.len, 0)
    endPoint = endPoint:rotate(self.angle) + self.pos
    
    line(self.pos.x, self.pos.y,
         endPoint.x, endPoint.y) 
        
    noStroke()
    fill(255)
    ellipse(self.pos.x, self.pos.y, 10)
    ellipse(endPoint.x, endPoint.y, 10)
    
    popStyle()
    
    if self.child then
        self.child.pos = endPoint
        self.child.angle = self.child.angle + self.angle
        self.child:draw()
    end
end

--# IKSolver
IKSolver = class()

function IKSolver:init(b1, b2)
    self.b1 = b1
    self.b2 = b2
    self.target = vec2(0,0)
    self.foundSolution = false
end

function IKSolver:updateSolution()
    local targetDistSqr = self.b1.pos:distSqr(self.target)
    
    local found = true
    local eps = 0.0001
    local sinAngle2 = 0
    local cosAngle2 = 0
    local cosAngle2Denom = 2*self.b1.len*self.b2.len
    
    local b1LenSqr = self.b1.len * self.b1.len
    local b2LenSqr = self.b2.len * self.b2.len
    
    if cosAngle2Denom > eps  then
        cosAngle2 = (targetDistSqr - b1LenSqr - b2LenSqr) / cosAngle2Denom
        
        if cosAngle2 < -1.0 or cosAngle2 > 1.0 then
            found = false
        end
        
        cosAngle2 = math.max(-1, math.min(1, cosAngle2))
        
        self.b2.angle = math.acos(cosAngle2)
        
        sinAngle2 = math.sin(self.b2.angle)
    else
        local totalLenSqr = (self.b1.len + self.b2.len) * 
                            (self.b1.len + self.b2.len)
                            
        if targetDistSqr < (totalLenSqr - eps) or
           targetDistSqr > (totalLenSqr + eps) then
            found = false
        end
        
        self.b2.angle = 0
        cosAngle2 = 1.0
        sinAngle2 = 0.0
    end
    
    local triAdj = self.b1.len + self.b2.len * cosAngle2
    local triOpp = self.b2.len * sinAngle2
    
    local tanY = self.target.y * triAdj - self.target.x * triOpp
    local tanX = self.target.x * triAdj + self.target.y * triOpp
    
    self.b1.angle = math.atan2( tanY, tanX )
    
    self.foundSolution = found
end

--# Main
-- IK

-- Use this function to perform your initial setup
function setup()

    b1 = Bone(100)
    b2 = Bone(100)
    
    b1.child = b2
    
    solver = IKSolver(b1, b2)

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)
    
    translate( WIDTH/2, HEIGHT/2 )

    -- Do your drawing here
    solver.target = vec2(CurrentTouch.x - WIDTH/2, CurrentTouch.y - HEIGHT/2)
    solver:updateSolution()
    
    b1:draw()
    
    ellipse(solver.target.x, solver.target.y, 50)
end

```

This is brilliant! One step closer to rigging 3D models. Thanks!!!

amazing .

@Ric_Esrey not sure how this would work with the IK stuff, but here:
http://twolivesleft.com/Codea/Talk/discussion/2901/skeletal-animation-shader

Is my skeletal mesh rigging, it uses tweens on the joint rotation to animate a mesh.

Interesting.
In principle you should be able to do this with the physics API?
Ho, btw, i still have to run Dave “black screen bug correction” on all projects copied, yours included @simeon. Is it on purpose, or have you forgotten about this issue? (it’s been 6 month+ with this bug now…). Just to know.

Nice, I can see the spiders of Limbo going on over those baby legs :slight_smile:
this is genius… @spacemonkey well, you can use physics REVOLUTE joints with CIRCLE bodies to replace this behaviour :wink:

@Jmv38 I never quite understood what the cause or problem to fix was, maybe it’s an IOS version thing. I’d happily tweak the code, but I don’t know what to change, I think it’s to do with cut and paste off github

@juaxix the physics system is 2d, the 3d rigging example is thinking in 3d, so I’m not sure what benefit I’d get with the extra complexity of modelling the skeleton in psuedo 3d to make it work with physics.

@spacemonkey sorry, my comment was about @simeon code. I was no clear.

So, I’m looking at this code and thinking - it’s 2D; would adding z to it make it into a 3D solver? Would it be that simple?