The image is created by the following source-code. Requirements:
#!/usr/bin/python
# -*- coding: utf8 -*-
from lxml import etree
import os
import scipy as sc
import scipy.optimize as op
import scipy.integrate as ig
from math import *
class SvgDocument:
'''
creates a svg document structure using lxml.etree
'''
def __init__ (self, name, width=800, height=600, bgcolor='#ffffff',
center=None, unit=1.0):
self.name = name
self.width = int(width)
self.height = int(height)
self.unit = float(unit)
if center == None: self.center = [width / 2., height / 2.]
else: self.center = [float(i) for i in center]
# create document structure
self.svg = etree.Element('svg',
nsmap={None:'http://www.w3.org/2000/svg',
'xlink':'http://www.w3.org/1999/xlink'})
self.svg.set('version', '1.1')
self.svg.set('baseProfile', 'full')
self.svg.set('width', str(int(width)))
self.svg.set('height', str(int(height)))
# title
self.title = etree.SubElement(self.svg, 'title')
self.title.text = self.name
# background
if bgcolor != None:
self.background = etree.SubElement(self.svg, 'rect')
self.background.set('id', 'background')
self.background.set('x', '0')
self.background.set('y', '0')
self.background.set('width', str(width))
self.background.set('height', str(height))
self.background.set('fill', bgcolor)
# image elements
self.content = etree.SubElement(self.svg, 'g')
self.content.set('id', 'image')
self.content.set('transform',
'translate({0},{1}) scale({2},-{2})'.format(
self.center[0], self.center[1], self.unit))
def draw_object(self, name, params, group=None):
'''
Draw arbitraty svg object.
Params must be a dictionary of valid svg parameters.
'''
if group == None:
obj = etree.SubElement(self.content, name)
else:
obj = etree.SubElement(group, name)
for i, j in params.iteritems():
obj.set(str(i), str(j))
return obj
def write(self, filename=None):
# write content to file
if filename == None: filename = self.name
outfile = open(filename + '.svg', 'w')
outfile.write(etree.tostring(self.svg, xml_declaration=True,
pretty_print=True, encoding='utf-8'))
outfile.close()
print 'image written to', filename + '.svg'
# physics: calculate the catenary curve
def catenary(x, r):
# catenary curve f(x) with l=2 and horizontal distance 2r
if r == 1.0:
return 0.0
# search a-value for given length and distance
def fct(a):
if a == 0.0: return r - 1.0
return sinh(r * a) / a - 1.0
x0 = 1.0
while fct(x0) > 0.0: x0 /= 2.0
while fct(2.0 * x0) < 0.0: x0 *= 2.0
a = 1.0 / op.brentq(fct, x0, 2*x0)
return a * (cosh(x / a) - cosh(r / a))
render_mult = 8 # supersampling to improve quality
folder = 'frames'
commands = ['mkdir ' + folder]
for c in commands: print c; os.system(c)
# render frames
frames = 100
names = []
for i in range(frames):
name = 'catenary_{0:0>2}'.format(i)
names.append(name)
w, h = 150, 80
r0 = 65.0
doc = SvgDocument(name, width=w, height=h, center=[w/2, 10], unit=1)
a = 0.5 + 0.5 * cos(i * 2*pi / frames)
r = a**1.5 + 12*a**2 - 21*a**2.5 + 9*a**3
for ri in [-r, r]:
doc.draw_object('circle', {'cx':str(ri*r0), 'cy':'0', 'r':'7',
'style':'fill:#888; stroke:none'})
if r != 0.0:
x = sc.linspace(-r, r, 401)
path_data = zip(r0 * x, [r0 * catenary(xx, r) for xx in x])
else:
path_data = [[0.0, 0.0], [0.0, -r0], [0.0, 0.0]]
# append extra segment to have last point displayed
v = sc.array(path_data[-1]) - sc.array(path_data[-2])
v *= 0.1 / r0 / sqrt(v[0]**2 + v[1]**2)
path_data.append(sc.array(path_data[-1]) + v)
path = 'M ' + ' L '.join([str(xy[0]) + ',' + str(xy[1]) for xy in path_data])
doc.draw_object('path', {'d':path, 'stroke-linecap':'round',
'style':'fill:none; stroke:black; stroke-width:4',
'stroke-linejoin':'round', 'stroke-dasharray':'0,5'})
doc.write(filename=folder + '/' + name)
for name in names:
commands = ['rsvg -w{0} -h{1} {3}/{2}.svg {3}/{2}.png'.format(
w * render_mult, h * render_mult, name, folder),
'rm {1}/{0}.svg'.format(name, folder),
'convert -scale {0}x{1} {3}/{2}.png {3}/{2}.gif'.format(
w, h, name, folder),
'rm {1}/{0}.png'.format(name, folder)]
for c in commands: print c; os.system(c)
commands = ['gifsicle -d5 -l0 --colors 256 '
+ folder + '/*.gif > catenary_animation.gif',
'rm -rf ' + folder]
for c in commands: print c; os.system(c)