Transix SVG animation script
This script will compromise two SVG images (created with autotrace) and produce the SVG and javascript code to animate the paths.
Examples:
convert one.png one.pnm
convert two.png two.pnm
autotrace -despeckle-level 15 -line-threshold 0 -output-format svg -output-file one.svg one.pnm
autotrace -despeckle-level 15 -line-threshold 0 -output-format svg -output-file two.svg two.pnm
$ transix.py s1:svgFileOne.svg s2:svgFileTwo.svg os:outputSvgFile.svg js:outputJavascript.js
s1: svg input file 1
s2: svg input file 2
os: output svg file name
js: output javascript file name
#!/usr/bin/python
import sys
import re
from random import random
fileOne, fileTwo, outputJSfile, fileOut = '','','','' # two input svg files, output javascript file, output svg file
debugarg = False
for arg in sys.argv:
if debugarg: print arg
if (arg == 'debug' ): debugarg = True
if (arg == '--help'):
print "\nParameters: \n\t[s1:svg file 1]\n\t[s2:svg file 2]\n\t[os:output svg file]\n\t[js:output javascript file]\n"
print "\nPrepare SVG files from rastered images using:\nautotrace -despeckle-level 15 -line-threshold 0 -output-format svg -output-file one.svg one.pnm\n"
print "\n\n"
exit()
if (arg[:3] == 's1:'): fileOne = arg[3:]; print "Reading from file 1: "+fileOne
if (arg[:3] == 's2:'): fileTwo = arg[3:]; print "Reading from file 2: "+fileTwo
if (arg[:3] == 'js:'): outputJSfile = arg[3:]
if (arg[:3] == 'os:'): fileOut = arg[3:]
if ( fileOne == '' ): fileOne = raw_input("Input SVG File 1: ")
if ( fileOne[:1] == "'" ): fileOne = fileOne[1:]
if ( fileOne[-1:] == " " ): fileOne = fileOne[:-1]
if ( fileOne[-1:] == "'" ): fileOne = fileOne[:-1]
if ( fileTwo == '' ): fileTwo = raw_input("Input SVG File 2: ")
if ( fileTwo[:1] == "'" ): fileTwo = fileTwo[1:]
if ( fileTwo[-1:] == " " ): fileTwo = fileTwo[:-1]
if ( fileTwo[-1:] == "'" ): fileTwo = fileTwo[:-1]
if ( fileOut == '' ): fileOut = raw_input("Output SVG File: ")
if ( fileOut[:1] == "'" ): fileOut = fileOut[1:]
if ( fileOut[-1:] == " " ): fileOut = fileOut[:-1]
if ( fileOut[-1:] == "'" ): fileOut = fileOut[:-1]
if ( outputJSfile == '' ): outputJSfile = raw_input("Output Javascript File: ")
if ( outputJSfile[:1] == "'" ): outputJSfile = outputJSfile[1:]
if ( outputJSfile[-1:] == " " ): outputJSfile = outputJSfile[:-1]
if ( outputJSfile[-1:] == "'" ): outputJSfile = outputJSfile[:-1]
transitionSpeed = 1500
partDelay = 1
partRandomize = 500
moveRandomize = 900
fileOneSVG = [] # array of lines from svg file 1
fileOneSVGColors = [] # color of element[]
fileOneSVGPaths = [] # path of element[]
fileOneSVGKeys = [] # key of path[]
fileOneSVGSubKeys = [] # legacy something
with open(fileOne) as f1: fileOneSVG = f1.readlines()
print "SVG file 1 imported."
fileTwoSVG = [] # array of lines from svg file 2
fileTwoSVGColors = [] # color of element[]
fileTwoSVGPaths = [] # path of element[]
fileTwoSVGKeys = [] # key of path[]
fileTwoSVGSubKeys = [] # legacy something
with open(fileTwo) as f2: fileTwoSVG = f2.readlines()
print "SVG file 2 imported."
fileOutSVGPaths = [] # path of element[] for output image
fileOutSVGColors = [] # color of element[] for output image
fileOutSVGcount = 0 # number of elements to output
fileTransitionSVGPaths = [] # path of element[] for final transition
fileTransitionSVGColors = [] # color of element[] for final transition
SVG1height, SVG1width, SVG1ecount, SVG2height, SVG2width, SVG2ecount = 0, 0, 0, 0, 0, 0
removestr = "1234567890.- "
for i, line in enumerate(fileOneSVG):
if (line.startswith("<svg ")):
SVG1height = line[line.find( 'height="' )+8:line.find( '"', line.find( 'height="' )+9 )] # get svg 1 height
SVG1width = line[line.find( 'width="' )+7:line.find( '"', line.find( 'width="' )+8 )] # get svg 1 width
if (line.startswith("<path ")):
pathstring = line[line.find( 'd="' )+3:line.find( '"', line.find( 'd="' )+4 )] # get path string
stylestring = line[line.find( 'style="' )+7:line.find( '"', line.find( 'style="' )+8 )] # get style string
fileOneSVGPaths.append(pathstring) # append to path list
fileOneSVGColors.append(stylestring) # append to color list
svgkey = pathstring
SVG1ecount = i
for j in range(0,len(removestr)): svgkey = svgkey.replace(removestr[j],"")
svgkeycompressed, lastchar = '', ''
fileOneSVGKeys.append( svgkey)
for j in range(0,len(removestr)): svgkeycompressed = svgkeycompressed.replace(removestr[j],"")
fileOneSVGSubKeys.append( svgkeycompressed )
print "Read " + str(len(fileOneSVGKeys)) + " (" + str(SVG1ecount) + ") SVG paths, colors, keys from file 1"
for i, line in enumerate(fileTwoSVG):
if (line.startswith("<svg ")):
SVG2height = line[line.find( 'height="' )+8:line.find( '"', line.find( 'height="' )+9 )]
SVG2width = line[line.find( 'width="' )+7:line.find( '"', line.find( 'width="' )+8 )]
if (line.startswith("<path ")):
pathstring = line[line.find( 'd="' )+3:line.find( '"', line.find( 'd="' )+4 )]
stylestring = line[line.find( 'style="' )+7:line.find( '"', line.find( 'style="' )+8 )]
fileTwoSVGPaths.append(pathstring)
fileTwoSVGColors.append(stylestring)
svgkey = pathstring
SVG2ecount = i
for j in range(0,len(removestr)): svgkey = svgkey.replace(removestr[j],"")
fileTwoSVGKeys.append( svgkey )
svgkeycompressed, lastchar = '', ''
for j in range(0,len(removestr)): svgkeycompressed = svgkeycompressed.replace(removestr[j],"")
fileTwoSVGSubKeys.append( svgkeycompressed )
print "Read " + str(len(fileTwoSVGKeys)) + " (" + str(SVG2ecount) + ") SVG paths, colors, keys from file 2"
if (SVG1ecount > SVG2ecount): fileOutSVGcount = SVG1ecount - 1
else: fileOutSVGcount = SVG2ecount - 1
print "Count 1: " + str(SVG1ecount) + " Count 2: " + str(SVG2ecount) + " Count Out:" + str(fileOutSVGcount)
for el in range( 0, fileOutSVGcount):
fileOutPath = "" # buffers
fileTransitionPath = ""
if (el < SVG1ecount-1): # if within range of SVG1,
if (el < SVG2ecount-1): # and withing SVG2
print "Key1: " + fileOneSVGKeys[el] + " - Key2:" + fileTwoSVGKeys[el] # generate compromise
elements1 = re.split( '[MLCz]', fileOneSVGPaths[el]) # get element values svg 1
elements2 = re.split( '[MLCz]', fileTwoSVGPaths[el]) # get element values svg 2
elp, elq = 0, 0
outKeys1, outKeys2, keyOp1, keyOp2, lastXone, lastYone, lastXtwo, lastYtwo = "","","","","","","",""
while (1):
if (elp < len(fileOneSVGKeys[el])):
keyOp1 = fileOneSVGKeys[el][elp]
else:
print "need filler 1"
keyOp1=""
if (elq < len(fileTwoSVGKeys[el])):
keyOp2 = fileTwoSVGKeys[el][elq]
else:
print "need filler 2"
keyOp2=""
if (keyOp1 == "" and keyOp2 == ""): break
print "1Key:" + keyOp1 + " " + elements1[elp+1]
print "2Key:" + keyOp2 + " " + elements2[elq+1]
print "lastx1, lasty1: " + lastXone + " " + lastYone
print "lastx2, lasty2: " + lastXtwo + " " + lastYtwo
if (keyOp1 == keyOp2):
print "match"
fileOutPath += keyOp1 + elements1[elp+1]
fileTransitionPath += keyOp1 + elements2[elq+1]
outKeys1 += keyOp1
outKeys2 += keyOp2
elp += 1
elq += 1
lastOne = re.split('[ ]', elements1[elp])
lastTwo = re.split('[ ]', elements2[elq])
lastXone = lastOne[ len(lastOne)-2 ]
lastYone = lastOne[ len(lastOne)-1 ]
lastXtwo = lastTwo[ len(lastTwo)-2 ]
lastYtwo = lastTwo[ len(lastTwo)-1 ]
else:
print "nomatch"
if (keyOp1 == "L"): # line to...
elp += 1
lastOne = re.split('[ ]', elements1[elp])
lastXone = lastOne[ len(lastOne)-2 ]
lastYone = lastOne[ len(lastOne)-1 ]
if (keyOp2 == "C"): # circle - add line to 2, increment elp
print "circle - add line to 2, increment elp"
outKeys1 += keyOp1
outKeys2 += "L"
print "fileOut:"+keyOp1 + elements1[elp]
fileOutPath += keyOp1 + elements1[elp]
print "transition:" + "L" + lastXtwo + " " + lastYtwo
fileTransitionPath += "L" + lastXtwo + " " + lastYtwo
if (keyOp2 == "z"): # end - add line to 2, increment elp
print "end - add line to 2, increment elp"
outKeys1 += keyOp1
outKeys2 += "L"
print "fileOut:" + keyOp1 + elements1[elp]
fileOutPath += keyOp1 + elements1[elp]
print "transition:" + "L" + lastXtwo + " " +lastYtwo
fileTransitionPath += "L" + lastXtwo + " " +lastYtwo
if (keyOp1 == "C"): # circle to...
elp += 1
lastOne = re.split('[ ]', elements1[elp])
lastXone = lastOne[ len(lastOne)-2 ]
lastYone = lastOne[ len(lastOne)-1 ]
if (keyOp2 == "L"): # line - add circle to 2, increment elp
print "line - add circle to 2, increment elp"
outKeys1 += keyOp1
outKeys2 += "C"
print "fileOut:" + keyOp1 + elements1[elp]
fileOutPath += keyOp1 + elements1[elp]
print "transition:" + "C" + lastXtwo + " " + lastYtwo + " " + lastXtwo + " " + lastYtwo + " " + lastXtwo + " " + lastYtwo
fileTransitionPath += "C" + lastXtwo + " " + lastYtwo + " " + lastXtwo + " " + lastYtwo + " " + lastXtwo + " " + lastYtwo
if (keyOp2 == "z"): # end - add circle to 2, increment elp
print "end - add circle to 2, increment elp"
outKeys1 += keyOp1
outKeys2 += "C"
print "fileOut:" + keyOp1 + elements1[elp]
fileOutPath += keyOp1 + elements1[elp]
print "transition:" + "C" + lastXtwo + " " + lastYtwo + " " + lastXtwo + " " + lastYtwo + " " + lastXtwo + " " + lastYtwo
fileTransitionPath += "C" + lastXtwo + " " + lastYtwo + " " + lastXtwo + " " + lastYtwo + " " + lastXtwo + " " + lastYtwo
if (keyOp1 == "z"): # end to...
elq += 1
lastTwo = re.split('[ ]', elements2[elq])
lastXtwo = lastTwo[ len(lastTwo)-2 ]
lastYtwo = lastTwo[ len(lastTwo)-1 ]
if (keyOp2 == "L"): # line - add line to 1, increment elq
print "line - add line to 1, increment elq"
outKeys1 += "L"
outKeys2 += "L"
print "fileOut:" + lastXone + " " + lastYone
fileOutPath += "L" + lastXone + " " + lastYone
print "transition:" + "L" + elements2[elq]
fileTransitionPath += "L" + elements2[elq]
if (keyOp2 == "C"): # circle - add circle to 1, increment elq
print "circle - add circle to 1, increment elq"
outKeys1 += "C"
outKeys2 += "C"
print "fileOut:" +"C" + lastXone + " " + lastYone + " " + lastXone + " " + lastYone + " " + lastXone + " " + lastYone
fileOutPath += "C" + lastXone + " " + lastYone + " " + lastXone + " " + lastYone + " " + lastXone + " " + lastYone
print "transition:" + "C" + elements2[elq]
fileTransitionPath += "C" + elements2[elq]
print "generated keyout 1:" + outKeys1
print "generated keyout 2:" + outKeys2
print "Generated output : " + fileOutPath
print "Generated transition: " + fileTransitionPath
fileOutSVGPaths.append(fileOutPath)
fileTransitionSVGPaths.append(fileTransitionPath)
else: # generate padded svg 2 path
print "Key1: " + fileOneSVGKeys[el] + " - Key2: empty "
keebuffer = ""
elements1 = re.split( '[MLCz]', fileOneSVGPaths[el]) # get element values svg 1
for ek, kee in enumerate(fileOneSVGKeys[el]):
if (kee == "M" or kee == "L"):
keebuffer += kee + elements1[1]
if (kee == "C"):
keebuffer += kee + elements1[1] + " " + elements1[1] + " " + elements1[1]
if (kee == "z"):
keebuffer += kee
print "Generated Path from file 1 = " + keebuffer
fileOutSVGPaths.append(fileOneSVGPaths[el])
fileTransitionSVGPaths.append(keebuffer)
else: # if not within range of SVG1
print "Key1: empty - Key2: " + fileTwoSVGKeys[el]
keebuffer = ""
elements2 = re.split( '[MLCz]', fileTwoSVGPaths[el]) # get element values svg 2
for ek, kee in enumerate(fileTwoSVGKeys[el]):
if (kee == "M" or kee == "L"):
keebuffer += kee + elements2[1]
if (kee == "C"):
keebuffer += kee + elements2[1] + " " + elements2[1] + " " + elements2[1]
if (kee == "z"):
keebuffer += kee
print "Generated Path from file 2 = " + keebuffer
fileOutSVGPaths.append(keebuffer)
fileTransitionSVGPaths.append(fileTwoSVGPaths[el])
print "Writing new SVG (" + fileOut + ") with padded paths and ID's."
print "paths:" + str( len(fileOutSVGPaths) )
print "transitions:" + str( len(fileTransitionSVGPaths) )
with open(fileOut, 'w') as f2:
f2.write( '<?xml version="1.0" standalone="yes"?>'+"\n"+'<svg width="' + SVG1width + '" height="' + SVG1height + '">'+"\n")
for i, path in enumerate(fileOutSVGPaths):
thisStyle = "fill:rgba(0,0,0,1);stroke:none;"
if (i < len(fileOneSVGColors)): thisStyle = fileOneSVGColors[i]
f2.write( "<path id=\"path_" + str(i) + "\" style=\"" + thisStyle + "\" d=\"" + path + "\" />\n" )
f2.write( '</svg>')
with open(outputJSfile, 'w') as f3:
f3.write('function transitionImage(){ ')
tid = 2000
for i, path in enumerate(fileTransitionSVGPaths):
tid += partDelay
thisFill = "rgba(0,0,0,1)"
if (i < len(fileTwoSVGColors)): thisFill = fileTwoSVGColors[i][5:12]
thisDelay = tid - partRandomize + (partRandomize * random())
thisTransition = transitionSpeed - moveRandomize + (moveRandomize * random())
f3.write(" setTimeout(\" d3.select('#path_" + str(i) + "').transition().duration(" + str(thisTransition) + ").attr('d', '" + path + "').transition().duration(" + str(thisTransition) + ").style('fill','" + thisFill + "'); \", " + str(thisDelay) + ");\n")
f3.write('}; ')
f3.write('function transitionImageBack(){ ')
ll = len(fileOutSVGPaths)
print "elements:" + str(ll)
tid = 2000
for i, path in enumerate( reversed(fileOutSVGPaths) ):
tid = tid + partDelay
j = ll - (i)
thisFill = "rgba(0,0,0,1)"
if ( j < len(fileOneSVGColors)): thisFill = fileOneSVGColors[j-1][5:12]
thisDelay = tid - partRandomize + (partRandomize * random())
thisTransition = transitionSpeed - moveRandomize + (moveRandomize * random())
f3.write(" setTimeout(\" d3.select('#path_" + str(j-1) + "').transition().duration(" + str(thisTransition) + ").attr('d', '" + path + "').transition().duration(" + str(thisTransition) + ").style('fill','" + thisFill + "'); \", "+ str(thisDelay) +");\n")
f3.write('}; ')