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:


How to use:

Prepare 2 svg images generated from raster (pnm) images using imageMagick and autotrace:

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

Now, run the two svg files through transix to generate a combined SVG and javascript file.
$ 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


Download
Code: (python)
#!/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('}; ')