Howdy, Stranger!

It looks like you're new here. If you want to get involved, click one of these buttons!

Strandbeest Mechanism Simulation

edited January 2012 in Code Sharing Posts: 118

Simulation of Theo Jansen's Strandbeest (http://www.strandbeest.com). I first wanted to use the new physics engine for this, but decided against it after I discovered an instance can not change it's dimensions (which is the main purpose of this simulation).


-- Strandbeest Mechanism Simulation
-- invented by Theo Jansen
-- http://www.strandbeest.com/
-- Herwig Van Marck
    
-- Use this function to perform your initial setup
function setup()
    print("Pinch to Zoom the view")
    print("Set clear to 1 to clear path")
    print("Reduce step to slow down")
    touches = {}
    lastPinchDist = 0
    pinchDelta = 1.0
    pinchCenter = vec2(WIDTH/2,HEIGHT/2)
    offset = vec2(0,0)
    zoom = 1
    T=0
    parameter("step",0,0.3,0.12)
    iparameter("clear",0,1,0)
    parameter("a",10,200, 38*3)
    parameter("b", 10, 200, 41.5*3)
    parameter("c", 10, 200, 39.3*3)
    parameter("d", 10, 200, 40.1*3)
    parameter("e", 10, 200, 55.8*3)
    parameter("f", 10, 200, 39.4*3)
    parameter("g", 10, 200, 36.7*3)
    parameter("h", 10, 200, 65.7*3)
    parameter("i", 10, 200, 49*3)
    parameter("j", 10, 200, 50*3)
    parameter("k", 10, 200, 61.9*3)
    parameter("l", 10, 200, 7.8*3)
    parameter("m", 10, 200, 15*3)
    
    img=image(WIDTH,HEIGHT)
    spriteMode(CORNER)
end
 
function touched(touch)
    if touch.state == ENDED then
        touches[touch.id] = nil
    else
        touches[touch.id] = touch
    end
end

function processTouches()
    touchArr = {}
    for k,touch in pairs(touches) do
        -- push touches into array
        table.insert(touchArr,touch)
    end

    if #touchArr == 2 then
        t1 = vec2(touchArr[1].x,touchArr[1].y)
        t2 = vec2(touchArr[2].x,touchArr[2].y)

        dist = t1:dist(t2)
        if lastPinchDist > 0 then 
            pinchDelta = dist/lastPinchDist
            
        else
            offset= offset + ((t1 + t2)/2-pinchCenter)/zoom
        end
        pinchCenter = (t1 + t2)/2
        lastPinchDist = dist
    else
        pinchDelta = 1.0
        lastPinchDist = 0
    end
end

function draw()
    if (clear==1) then
        img=image(WIDTH,HEIGHT)
        clear=0
    end
    -- This sets the background color to black
    background(0, 0, 0)
    
    font("Arial-BoldMT")
    fontSize(50)
    fill(0, 19, 255, 255)
    text("Theo Jansen's",WIDTH/2,HEIGHT -60)
    text("Strandbeest Mechanism",WIDTH/2,HEIGHT -120)
    -- compute pinch delta
    processTouches()
    -- scale by pinch delta
    zoom = math.max( zoom*pinchDelta, 0.2 )

    translate(pinchCenter.x-offset.x*zoom,pinchCenter.y-offset.y*zoom)
    
    scale(zoom,zoom)
    sprite(img,-WIDTH/2,-HEIGHT/2)
    --translate(pinchCenter.x,pinchCenter.y)

    pinchDelta = 1.0
    
    -- This sets the line thickness
    strokeWidth(4)
    
    font("AmericanTypewriter-Condensed")
    fontSize(24)
    fill(248, 248, 248, 255)
    -- Draw mechanism
    x1=0
    y1=0
    x2=a
    y2=l
    stroke(255, 0, 0, 255)
    line(x1,y1,x2,y1)
    text("a",(x2-x1)/2,y1-8)
    line(x2,y1,x2,y2)
    text("l",x2+8,(y2-y1)/2)
    x3=x2+m*math.cos(T)
    y3=y2+m*math.sin(T)
    stroke(251, 255, 0, 255)
    pushStyle()
    fill(0, 0, 0, 0)
    ellipse(x2,y2,m*2,m*2)
    fill(251,255,0,255)
    text("m",x2+m,y2+m+8)
    popStyle()
    line(x2,y2,x3,y3)
    tmp=math.acos((j^2-b^2-x3^2-y3^2)/(-2*b*math.sqrt(x3^2+y3^2)))+math.atan(y3/x3)
    x4=b*math.cos(tmp)
    y4=b*math.sin(tmp)
    stroke(255, 255, 255, 255)
    line(x1,y1,x4,y4)
    text("b",(x1+x4)/2+8,(y1+y4)/2)
    line(x3,y3,x4,y4)
    text("j",(x3+x4)/2,(y3+y4)/2+16)
    tmp2=math.atan(y3/x3)-math.acos((k^2-c^2-x3^2-y3^2)/(-2*c*math.sqrt(x3^2+y3^2)))
    x7=c*math.cos(tmp2)
    y7=c*math.sin(tmp2)
    line(x1,y1,x7,y7)
    text("c",(x1+x7)/2-8,(y1+y7)/2)
    line(x3,y3,x7,y7)
    text("k",(x3+x7)/2+8,(y3+y7)/2-4)
    tmp3=math.acos((e^2-b^2-d^2)/(-2*b*d))+tmp
    x5=d*math.cos(tmp3)
    y5=d*math.sin(tmp3)
    line(x1,y1,x5,y5)
    text("d",(x1+x5)/2,(y1+y5)/2+8)
    line(x4,y4,x5,y5)
    text("e",(x4+x5)/2+8,(y4+y5)/2)
    n=math.sqrt(c^2+d^2-2*math.cos(2*math.pi-tmp3+tmp2)*c*d)
    tmp4=math.acos((n^2-f^2-g^2)/(-2*f*g))
    tmp5=math.acos((f^2-n^2-g^2)/(-2*n*g))+math.acos((d^2-n^2-c^2)/(-2*n*c))
    o=math.sqrt(c^2+g^2-2*math.cos(tmp5)*c*g)
    tmp6=math.acos((g^2-o^2-c^2)/(-2*o*c))
    x6=o*math.cos(tmp2-tmp6)
    y6=o*math.sin(tmp2-tmp6)
    line(x5,y5,x6,y6)
    text("f",(x5+x6)/2-8,(y5+y6)/2)
    line(x7,y7,x6,y6)
    text("g",(x6+x7)/2,(y6+y7)/2-8)
    tmp7=math.acos((h^2-i^2-g^2)/(-2*i*g))+tmp5
    p=math.sqrt(c^2+i^2-2*math.cos(tmp7)*c*i)
    tmp8=tmp2-math.asin(i*math.sin(tmp7)/p)
    x8=p*math.cos(tmp8)
    y8=p*math.sin(tmp8)
    line(x6,y6,x8,y8)
    text("h",(x6+x8)/2-8,(y6+y8)/2-4)
    line(x7,y7,x8,y8)
    text("i",(x7+x8)/2+8,(y7+y8)/2)
    if (math.abs(x8)<WIDTH/2 and math.abs(y8)<HEIGHT/2) then
        img:set(x8+WIDTH/2,y8+HEIGHT/2,0,0,255,255)
    end
    T=T+step
end
    
    

Comments

  • Posts: 118

    Corrected the code just now (forgot to escape the '<') :-(

  • Posts: 2,820

    Cool. I'll try it whe. I get a chance. Just one thing that could easily reduce your code by atleast two lines is to use /n to create a new line, so you would do something like this:
    Pinch to Zoom the Viewer/nSet clear to one to clear path/nReduce steps to slow down..
    I just wanted to give you a little help. It's small, but I find it helps in the long run. :-D

  • omg this is wonterful!!
    you should know i alreadey met theo jansen personally and helped him digging out a forgotten old strandbeest... im a big fan!

  • SimeonSimeon Admin Mod
    Posts: 4,624

    This is great, @Herwig. I uploaded it to YouTube here:

  • Posts: 118

    Thanks! I haven't got round to trying the new video generation!

  • Posts: 118

    Updated version using my Zoom class (so labels are better when zooming in).

    -- Strandbeest Mechanism Simulation
    -- invented by Theo Jansen
    -- http://www.strandbeest.com/
    -- Herwig Van Marck
        
    -- Use this function to perform your initial setup
    function setup()
        zoom=Zoom(WIDTH/2,HEIGHT/2)
        print("Set clear to 1 to clear path")
        print("Reduce step to slow down")
        T=0
        parameter("step",0,0.3,0.12)
        iparameter("clear",0,1,0)
        parameter("a",10,200, 38*3)
        parameter("b", 10, 200, 41.5*3)
        parameter("c", 10, 200, 39.3*3)
        parameter("d", 10, 200, 40.1*3)
        parameter("e", 10, 200, 55.8*3)
        parameter("f", 10, 200, 39.4*3)
        parameter("g", 10, 200, 36.7*3)
        parameter("h", 10, 200, 65.7*3)
        parameter("i", 10, 200, 49*3)
        parameter("j", 10, 200, 50*3)
        parameter("k", 10, 200, 61.9*3)
        parameter("l", 10, 200, 7.8*3)
        parameter("m", 10, 200, 15*3)
        
        img=image(WIDTH,HEIGHT)
        spriteMode(CORNER)
    end
     
    function touched(touch)
        zoom:touched(touch)
    end
    
    function draw()
        zoom:draw()
        if (clear==1) then
            img=image(WIDTH,HEIGHT)
            clear=0
        end
        -- This sets the background color to black
        background(0, 0, 0)
        
        font("Arial-BoldMT")
        fontSize(50)
        fill(0, 19, 255, 255)
        
        zoom:text("Theo Jansen's",0,HEIGHT/2 -60)
        zoom:text("Strandbeest Mechanism",0,HEIGHT/2 -120)
    
        sprite(img,-WIDTH/2,-HEIGHT/2)
    
        strokeWidth(4)
        
        font("AmericanTypewriter-Condensed")
        fontSize(24)
        fill(248, 248, 248, 255)
        -- Draw mechanism
        x1=0
        y1=0
        x2=a
        y2=l
        stroke(255, 0, 0, 255)
        line(x1,y1,x2,y1)
        zoom:text("a",(x2-x1)/2,y1-8)
        line(x2,y1,x2,y2)
        zoom:text("l",x2+8,(y2-y1)/2)
        x3=x2+m*math.cos(T)
        y3=y2+m*math.sin(T)
        stroke(251, 255, 0, 255)
        pushStyle()
        fill(0, 0, 0, 0)
        ellipse(x2,y2,m*2,m*2)
        fill(251,255,0,255)
        zoom:text("m",x2+m,y2+m+8)
        popStyle()
        line(x2,y2,x3,y3)
        tmp=math.acos((j^2-b^2-x3^2-y3^2)/(-2*b*math.sqrt(x3^2+y3^2)))+math.atan(y3/x3)
        x4=b*math.cos(tmp)
        y4=b*math.sin(tmp)
        stroke(255, 255, 255, 255)
        line(x1,y1,x4,y4)
        zoom:text("b",(x1+x4)/2+8,(y1+y4)/2)
        line(x3,y3,x4,y4)
        zoom:text("j",(x3+x4)/2,(y3+y4)/2+16)
        tmp2=math.atan(y3/x3)-math.acos((k^2-c^2-x3^2-y3^2)/(-2*c*math.sqrt(x3^2+y3^2)))
        x7=c*math.cos(tmp2)
        y7=c*math.sin(tmp2)
        line(x1,y1,x7,y7)
        zoom:text("c",(x1+x7)/2-8,(y1+y7)/2)
        line(x3,y3,x7,y7)
        zoom:text("k",(x3+x7)/2+8,(y3+y7)/2-4)
        tmp3=math.acos((e^2-b^2-d^2)/(-2*b*d))+tmp
        x5=d*math.cos(tmp3)
        y5=d*math.sin(tmp3)
        line(x1,y1,x5,y5)
        zoom:text("d",(x1+x5)/2,(y1+y5)/2+8)
        line(x4,y4,x5,y5)
        zoom:text("e",(x4+x5)/2+8,(y4+y5)/2)
        n=math.sqrt(c^2+d^2-2*math.cos(2*math.pi-tmp3+tmp2)*c*d)
        tmp4=math.acos((n^2-f^2-g^2)/(-2*f*g))
        tmp5=math.acos((f^2-n^2-g^2)/(-2*n*g))+math.acos((d^2-n^2-c^2)/(-2*n*c))
        o=math.sqrt(c^2+g^2-2*math.cos(tmp5)*c*g)
        tmp6=math.acos((g^2-o^2-c^2)/(-2*o*c))
        x6=o*math.cos(tmp2-tmp6)
        y6=o*math.sin(tmp2-tmp6)
        line(x5,y5,x6,y6)
        zoom:text("f",(x5+x6)/2-8,(y5+y6)/2)
        line(x7,y7,x6,y6)
        zoom:text("g",(x6+x7)/2,(y6+y7)/2-8)
        tmp7=math.acos((h^2-i^2-g^2)/(-2*i*g))+tmp5
        p=math.sqrt(c^2+i^2-2*math.cos(tmp7)*c*i)
        tmp8=tmp2-math.asin(i*math.sin(tmp7)/p)
        x8=p*math.cos(tmp8)
        y8=p*math.sin(tmp8)
        line(x6,y6,x8,y8)
        zoom:text("h",(x6+x8)/2-8,(y6+y8)/2-4)
        line(x7,y7,x8,y8)
        zoom:text("i",(x7+x8)/2+8,(y7+y8)/2)
        if (math.abs(x8)<WIDTH/2 and math.abs(y8)<HEIGHT/2) then
            img:set(x8+WIDTH/2,y8+HEIGHT/2,0,0,255,255)
        end
        T=T+step
    end
    
    -- Zoom library
    -- Herwig Van Marck
    -- usage:
    --[[
    function setup()
        zoom=Zoom(WIDTH/2,HEIGHT/2)
    end
    function touched(touch)
        zoom:touched(touch)
    end
    function draw()
        zoom:draw()
    end
    ]]--
    
    Zoom = class()
    
    function Zoom:init(x,y)
        -- you can accept and set parameters here
        self.touches = {}
        self.initx=x
        self.inity=y
        self:clear()
        print("Tap and drag to move\nPinch to zoom\nDouble tap to reset")
    end
    
    function Zoom:clear()
        self.lastPinchDist = 0
        self.pinchDelta = 1.0
        self.center = vec2(self.initx,self.inity)
        self.offset = vec2(0,0)
        self.zoom = 1
        self.started = false
        self.started2 = false
    end
    
    function Zoom:touched(touch)
        -- Codea does not automatically call this method
        if touch.state == ENDED then
            self.touches[touch.id] = nil
        else
            self.touches[touch.id] = touch
            if (touch.tapCount==2) then
                self:clear()
            end
        end
    end
    
    function Zoom:processTouches()
        local touchArr = {}
        for k,touch in pairs(self.touches) do
            -- push touches into array
            table.insert(touchArr,touch)
        end
    
        if #touchArr == 2 then
            self.started = false
            local t1 = vec2(touchArr[1].x,touchArr[1].y)
            local t2 = vec2(touchArr[2].x,touchArr[2].y)
    
            local dist = t1:dist(t2)
            if self.started2 then
            --if self.lastPinchDist > 0 then 
                self.pinchDelta = dist/self.lastPinchDist          
            else
                self.offset= self.offset + ((t1 + t2)/2-self.center)/self.zoom
                self.started2 = true
            end
            self.center = (t1 + t2)/2
            self.lastPinchDist = dist
        elseif (#touchArr == 1) then
            self.started2 = false
            local t1 = vec2(touchArr[1].x,touchArr[1].y)
            self.pinchDelta = 1.0
            self.lastPinchDist = 0
            if not(self.started) then
                self.offset = self.offset + (t1-self.center)/self.zoom
                self.started = true
            end
            self.center=t1
        else
            self.pinchDelta = 1.0
            self.lastPinchDist = 0
            self.started = false
            self.started2 = false
        end
    end
    
    function Zoom:clip(x,y,w,h)
        clip(x*self.zoom+self.center.x- self.offset.x*self.zoom,
            y*self.zoom+self.center.y- self.offset.y*self.zoom,
            w*self.zoom+1,h*self.zoom+1)
    end
    
    function Zoom:text(str,x,y)
        local fSz = fontSize()
        local xt=x*self.zoom+self.center.x- self.offset.x*self.zoom
        local yt=y*self.zoom+self.center.y- self.offset.y*self.zoom
        fontSize(fSz*self.zoom)
        local xtsz,ytsz=textSize(str)
        tsz=xtsz
        if tsz<ytsz then tsz=ytsz end
        if (tsz>2048) then
            local eZoom= tsz/2048.0
            fontSize(fSz*self.zoom/eZoom)
            pushMatrix()
            resetMatrix()
            translate(xt,yt)
            scale(eZoom)
            text(str,0,0)
            popMatrix()
            fontSize(fSz)
        else
            pushMatrix()
            resetMatrix()
            fontSize(fSz*self.zoom)
            text(str,xt,yt)
            popMatrix()
            fontSize(fSz)
        end
    end
    
    function Zoom:draw()
        -- compute pinch delta
        self:processTouches()
        -- scale by pinch delta
        self.zoom = math.max( self.zoom*self.pinchDelta, 0.2 )
    
        translate(self.center.x- self.offset.x*self.zoom,
            self.center.y- self.offset.y*self.zoom)
        
        scale(self.zoom,self.zoom)
    
        self.pinchDelta = 1.0
    end
    
    
  • Posts: 622

    Just finished collecting your recent works. This one I'd never heard of. Really fun to watch.

  • Posts: 118

    When I first came across it I wanted to see how other dimensions worked. I managed to simulate it in Spacetime (on iPad), but I have to say it is much better in Codea!

Sign In or Register to comment.