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('}; ')