# Strandbeest Mechanism Simulation

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

`
``````

Corrected the code just now (forgot to escape the ‘<’)

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.

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!

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

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\
Pinch to zoom\
Double 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

``````

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

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!