index cbd7956657b03d543ae0b16617d04f7a3bad5089..8f8a811dc0d94d905ba4c51874241e80ac2be173 100644 (file)
#!/usr/bin/env python
'''
Copyright (C) 2006 Jean-Francois Barraud, barraud@math.univ-lille1.fr
+Copyright (C) 2010 Alvin Penner, penner@vaxxine.com
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
import inkex, cubicsuperpath, bezmisc, simplestyle
import copy, math, re
-def parseTransform(transf,mat=[[1.0,0.0,0.0],[0.0,1.0,0.0]]):
+def parseTransform(transf,mat=[[1.0, 0.0, 0.0], [0.0, 1.0, 0.0]]):
if transf=="" or transf==None:
return(mat)
- result=re.match("(translate|scale|rotate|skewX|skewY|matrix)\(([^)]*)\)",transf)
+ stransf = transf.strip()
+ result=re.match("(translate|scale|rotate|skewX|skewY|matrix)\s*\(([^)]*)\)\s*,?",stransf)
#-- translate --
if result.group(1)=="translate":
- args=result.group(2).split(",")
+ args=result.group(2).replace(',',' ').split()
dx=float(args[0])
if len(args)==1:
dy=0.0
dy=float(args[1])
matrix=[[1,0,dx],[0,1,dy]]
#-- scale --
- if result.groups(1)=="scale":
- args=result.group(2).split(",")
+ if result.group(1)=="scale":
+ args=result.group(2).replace(',',' ').split()
sx=float(args[0])
if len(args)==1:
sy=sx
sy=float(args[1])
matrix=[[sx,0,0],[0,sy,0]]
#-- rotate --
- if result.groups(1)=="rotate":
- args=result.group(2).split(",")
+ if result.group(1)=="rotate":
+ args=result.group(2).replace(',',' ').split()
a=float(args[0])*math.pi/180
if len(args)==1:
cx,cy=(0.0,0.0)
else:
- cx,cy=args[1:]
+ cx,cy=map(float,args[1:])
matrix=[[math.cos(a),-math.sin(a),cx],[math.sin(a),math.cos(a),cy]]
+ matrix=composeTransform(matrix,[[1,0,-cx],[0,1,-cy]])
#-- skewX --
- if result.groups(1)=="skewX":
+ if result.group(1)=="skewX":
a=float(result.group(2))*math.pi/180
matrix=[[1,math.tan(a),0],[0,1,0]]
-#-- skewX --
- if result.groups(1)=="skewX":
+#-- skewY --
+ if result.group(1)=="skewY":
a=float(result.group(2))*math.pi/180
matrix=[[1,0,0],[math.tan(a),1,0]]
#-- matrix --
if result.group(1)=="matrix":
- a11,a21,a12,a22,v1,v2=result.group(2).split(",")
- matrix=[[float(a11),float(a12),float(v1)],[float(a21),float(a22),float(v2)]]
-
+ a11,a21,a12,a22,v1,v2=result.group(2).replace(',',' ').split()
+ matrix=[[float(a11),float(a12),float(v1)], [float(a21),float(a22),float(v2)]]
+
matrix=composeTransform(mat,matrix)
- if result.end()<len(transf):
- return(parseTransform(transf[result.end():],matrix))
+ if result.end() < len(stransf):
+ return(parseTransform(stransf[result.end():], matrix))
else:
return matrix
def formatTransform(mat):
- return("matrix(%f,%f,%f,%f,%f,%f)"%(mat[0][0],mat[1][0],mat[0][1],mat[1][1],mat[0][2],mat[1][2]))
+ return ("matrix(%f,%f,%f,%f,%f,%f)" % (mat[0][0], mat[1][0], mat[0][1], mat[1][1], mat[0][2], mat[1][2]))
def composeTransform(M1,M2):
- a11=M1[0][0]*M2[0][0]+M1[0][1]*M2[1][0]
- a12=M1[0][0]*M2[0][1]+M1[0][1]*M2[1][1]
- a21=M1[1][0]*M2[0][0]+M1[1][1]*M2[1][0]
- a22=M1[1][0]*M2[0][1]+M1[1][1]*M2[1][1]
+ a11 = M1[0][0]*M2[0][0] + M1[0][1]*M2[1][0]
+ a12 = M1[0][0]*M2[0][1] + M1[0][1]*M2[1][1]
+ a21 = M1[1][0]*M2[0][0] + M1[1][1]*M2[1][0]
+ a22 = M1[1][0]*M2[0][1] + M1[1][1]*M2[1][1]
- v1=M1[0][0]*M2[0][2]+M1[0][1]*M2[1][2]+M1[0][2]
- v2=M1[1][0]*M2[0][2]+M1[1][1]*M2[1][2]+M1[1][2]
+ v1 = M1[0][0]*M2[0][2] + M1[0][1]*M2[1][2] + M1[0][2]
+ v2 = M1[1][0]*M2[0][2] + M1[1][1]*M2[1][2] + M1[1][2]
return [[a11,a12,v1],[a21,a22,v2]]
def applyTransformToNode(mat,node):
node.set("transform", newtransf)
def applyTransformToPoint(mat,pt):
- x=mat[0][0]*pt[0]+mat[0][1]*pt[1]+mat[0][2]
- y=mat[1][0]*pt[0]+mat[1][1]*pt[1]+mat[1][2]
+ x = mat[0][0]*pt[0] + mat[0][1]*pt[1] + mat[0][2]
+ y = mat[1][0]*pt[0] + mat[1][1]*pt[1] + mat[1][2]
pt[0]=x
pt[1]=y
def fuseTransform(node):
if node.get('d')==None:
- #FIX ME: how do you raise errors?
+ #FIXME: how do you raise errors?
raise AssertionError, 'can not fuse "transform" of elements that have no "d" attribute'
t = node.get("transform")
if t == None:
elif b2 is None:
return b1
else:
- return((min(b1[0],b2[0]),max(b1[1],b2[1]),min(b1[2],b2[2]),max(b1[3],b2[3])))
+ return((min(b1[0],b2[0]), max(b1[1],b2[1]), min(b1[2],b2[2]), max(b1[3],b2[3])))
def roughBBox(path):
xmin,xMax,ymin,yMax = path[0][0][0][0],path[0][0][0][0],path[0][0][0][1],path[0][0][0][1]
yMax = max(yMax,pt[1])
return xmin,xMax,ymin,yMax
+def refinedBBox(path):
+ xmin,xMax,ymin,yMax = path[0][0][1][0],path[0][0][1][0],path[0][0][1][1],path[0][0][1][1]
+ for pathcomp in path:
+ for i in range(1, len(pathcomp)):
+ cmin, cmax = cubicExtrema(pathcomp[i-1][1][0], pathcomp[i-1][2][0], pathcomp[i][0][0], pathcomp[i][1][0])
+ xmin = min(xmin, cmin)
+ xMax = max(xMax, cmax)
+ cmin, cmax = cubicExtrema(pathcomp[i-1][1][1], pathcomp[i-1][2][1], pathcomp[i][0][1], pathcomp[i][1][1])
+ ymin = min(ymin, cmin)
+ yMax = max(yMax, cmax)
+ return xmin,xMax,ymin,yMax
+
+def cubicExtrema(y0, y1, y2, y3):
+ cmin = min(y0, y3)
+ cmax = max(y0, y3)
+ d1 = y1 - y0
+ d2 = y2 - y1
+ d3 = y3 - y2
+ if (d1 - 2*d2 + d3):
+ if (d2*d2 > d1*d3):
+ t = (d1 - d2 + math.sqrt(d2*d2 - d1*d3))/(d1 - 2*d2 + d3)
+ if (t > 0) and (t < 1):
+ y = y0*(1-t)*(1-t)*(1-t) + 3*y1*t*(1-t)*(1-t) + 3*y2*t*t*(1-t) + y3*t*t*t
+ cmin = min(cmin, y)
+ cmax = max(cmax, y)
+ t = (d1 - d2 - math.sqrt(d2*d2 - d1*d3))/(d1 - 2*d2 + d3)
+ if (t > 0) and (t < 1):
+ y = y0*(1-t)*(1-t)*(1-t) + 3*y1*t*(1-t)*(1-t) + 3*y2*t*t*(1-t) + y3*t*t*t
+ cmin = min(cmin, y)
+ cmax = max(cmax, y)
+ elif (d3 - d1):
+ t = -d1/(d3 - d1)
+ if (t > 0) and (t < 1):
+ y = y0*(1-t)*(1-t)*(1-t) + 3*y1*t*(1-t)*(1-t) + 3*y2*t*t*(1-t) + y3*t*t*t
+ cmin = min(cmin, y)
+ cmax = max(cmax, y)
+ return cmin, cmax
+
def computeBBox(aList,mat=[[1,0,0],[0,1,0]]):
bbox=None
for node in aList:
m = parseTransform(node.get('transform'))
m = composeTransform(mat,m)
#TODO: text not supported!
+ d = None
if node.get("d"):
d = node.get('d')
+ elif node.get('points'):
+ d = 'M' + node.get('points')
+ elif node.tag in [ inkex.addNS('rect','svg'), 'rect' ]:
+ d = 'M' + node.get('x', '0') + ',' + node.get('y', '0') + \
+ 'h' + node.get('width') + 'v' + node.get('height') + \
+ 'h-' + node.get('width')
+ elif node.tag in [ inkex.addNS('line','svg'), 'line' ]:
+ d = 'M' + node.get('x1') + ',' + node.get('y1') + \
+ ' ' + node.get('x2') + ',' + node.get('y2')
+ elif node.tag in [ inkex.addNS('circle','svg'), 'circle', \
+ inkex.addNS('ellipse','svg'), 'ellipse' ]:
+ rx = node.get('r')
+ if rx is not None:
+ ry = rx
+ else:
+ rx = node.get('rx')
+ ry = node.get('ry')
+ cx = float(node.get('cx', '0'))
+ cy = float(node.get('cy', '0'))
+ x1 = cx - float(rx)
+ x2 = cx + float(rx)
+ d = 'M %f %f ' % (x1, cy) + \
+ 'A' + rx + ',' + ry + ' 0 1 0 %f,%f' % (x2, cy) + \
+ 'A' + rx + ',' + ry + ' 0 1 0 %f,%f' % (x1, cy)
+
+ if d is not None:
p = cubicsuperpath.parsePath(d)
applyTransformToPath(m,p)
- bbox=boxunion(roughBBox(p),bbox)
-
- elif node.tag == inkex.addNS('rect','svg') or node.tag=='rect':
- w = float(node.get('width'))/2.
- h = float(node.get('height'))/2.
- x = float(node.get('x'))
- y = float(node.get('y'))
- C = [x + w , y + h ]
- applyTransformToPoint(mat,C)
- xmin = C[0] - abs(m[0][0]) * w - abs(m[0][1]) * h
- xmax = C[0] + abs(m[0][0]) * w + abs(m[0][1]) * h
- ymin = C[1] - abs(m[1][0]) * w - abs(m[1][1]) * h
- ymax = C[1] + abs(m[1][0]) * w + abs(m[1][1]) * h
- bbox = xmin,xmax,ymin,ymax
-
+ bbox=boxunion(refinedBBox(p),bbox)
+
elif node.tag == inkex.addNS('use','svg') or node.tag=='use':
refid=node.get(inkex.addNS('href','xlink'))
path = '//*[@id="%s"]' % refid[1:]
return bbox
-# vim: expandtab shiftwidth=4 tabstop=8 softtabstop=4 encoding=utf-8 textwidth=99
+# vim: expandtab shiftwidth=4 tabstop=8 softtabstop=4 fileencoding=utf-8 textwidth=99