Code

share/extension/*.py: noop: minor whitespace regularizations ahead of adding vim...
[inkscape.git] / share / extensions / simpletransform.py
1 #!/usr/bin/env python
2 '''
3 Copyright (C) 2006 Jean-Francois Barraud, barraud@math.univ-lille1.fr
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18 barraud@math.univ-lille1.fr
20 This code defines several functions to make handling of transform
21 attribute easier.
22 '''
23 import inkex, cubicsuperpath, bezmisc, simplestyle
24 import copy, math, re
26 def parseTransform(transf,mat=[[1.0,0.0,0.0],[0.0,1.0,0.0]]):
27     if transf=="" or transf==None:
28         return(mat)
29     result=re.match("(translate|scale|rotate|skewX|skewY|matrix)\(([^)]*)\)",transf)
30 #-- translate --
31     if result.group(1)=="translate":
32         args=result.group(2).split(",")
33         dx=float(args[0])
34         if len(args)==1:
35             dy=0.0
36         else:
37             dy=float(args[1])
38         matrix=[[1,0,dx],[0,1,dy]]
39 #-- scale --
40     if result.groups(1)=="scale":
41         args=result.group(2).split(",")
42         sx=float(args[0])
43         if len(args)==1:
44             sy=sx
45         else:
46             sy=float(args[1])
47         matrix=[[sx,0,0],[0,sy,0]]
48 #-- rotate --
49     if result.groups(1)=="rotate":
50         args=result.group(2).split(",")
51         a=float(args[0])*math.pi/180
52         if len(args)==1:
53             cx,cy=(0.0,0.0)
54         else:
55             cx,cy=args[1:]
56         matrix=[[math.cos(a),-math.sin(a),cx],[math.sin(a),math.cos(a),cy]]
57 #-- skewX --
58     if result.groups(1)=="skewX":
59         a=float(result.group(2))*math.pi/180
60         matrix=[[1,math.tan(a),0],[0,1,0]]
61 #-- skewX --
62     if result.groups(1)=="skewX":
63         a=float(result.group(2))*math.pi/180
64         matrix=[[1,0,0],[math.tan(a),1,0]]
65 #-- matrix --
66     if result.group(1)=="matrix":
67         a11,a21,a12,a22,v1,v2=result.group(2).split(",")
68         matrix=[[float(a11),float(a12),float(v1)],[float(a21),float(a22),float(v2)]]
69     
70     matrix=composeTransform(mat,matrix)
71     if result.end()<len(transf):
72         return(parseTransform(transf[result.end():],matrix))
73     else:
74         return matrix
76 def formatTransform(mat):
77     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]))
79 def composeTransform(M1,M2):
80     a11=M1[0][0]*M2[0][0]+M1[0][1]*M2[1][0]
81     a12=M1[0][0]*M2[0][1]+M1[0][1]*M2[1][1]
82     a21=M1[1][0]*M2[0][0]+M1[1][1]*M2[1][0]
83     a22=M1[1][0]*M2[0][1]+M1[1][1]*M2[1][1]
85     v1=M1[0][0]*M2[0][2]+M1[0][1]*M2[1][2]+M1[0][2]
86     v2=M1[1][0]*M2[0][2]+M1[1][1]*M2[1][2]+M1[1][2]
87     return [[a11,a12,v1],[a21,a22,v2]]
89 def applyTransformToNode(mat,node):
90     m=parseTransform(node.get("transform"))
91     newtransf=formatTransform(composeTransform(mat,m))
92     node.set("transform", newtransf)
94 def applyTransformToPoint(mat,pt):
95     x=mat[0][0]*pt[0]+mat[0][1]*pt[1]+mat[0][2]
96     y=mat[1][0]*pt[0]+mat[1][1]*pt[1]+mat[1][2]
97     pt[0]=x
98     pt[1]=y
100 def applyTransformToPath(mat,path):
101     for comp in path:
102         for ctl in comp:
103             for pt in ctl:
104                 applyTransformToPoint(mat,pt)
106 def fuseTransform(node):
107     if node.get('d')==None:
108         #FIX ME: how do you raise errors?
109         raise AssertionError, 'can not fuse "transform" of elements that have no "d" attribute'
110     t = node.get("transform")
111     if t == None:
112         return
113     m = parseTransform(t)
114     d = node.get('d')
115     p = cubicsuperpath.parsePath(d)
116     applyTransformToPath(m,p)
117     node.set('d', cubicsuperpath.formatPath(p))
118     del node.attrib["transform"]
120 ####################################################################
121 ##-- Some functions to compute a rough bbox of a given list of objects.
122 ##-- this should be shipped out in an separate file...
124 def boxunion(b1,b2):
125     if b1 is None:
126         return b2
127     elif b2 is None:
128         return b1    
129     else:
130         return((min(b1[0],b2[0]),max(b1[1],b2[1]),min(b1[2],b2[2]),max(b1[3],b2[3])))
132 def roughBBox(path):
133     xmin,xMax,ymin,yMax = path[0][0][0][0],path[0][0][0][0],path[0][0][0][1],path[0][0][0][1]
134     for pathcomp in path:
135         for ctl in pathcomp:
136             for pt in ctl:
137                 xmin = min(xmin,pt[0])
138                 xMax = max(xMax,pt[0])
139                 ymin = min(ymin,pt[1])
140                 yMax = max(yMax,pt[1])
141     return xmin,xMax,ymin,yMax
143 def computeBBox(aList,mat=[[1,0,0],[0,1,0]]):
144     bbox=None
145     for node in aList:
146         m = parseTransform(node.get('transform'))
147         m = composeTransform(mat,m)
148         #TODO: text not supported!
149         if node.get("d"):
150             d = node.get('d')
151             p = cubicsuperpath.parsePath(d)
152             applyTransformToPath(m,p)
153             bbox=boxunion(roughBBox(p),bbox)
155         elif node.tag == inkex.addNS('rect','svg') or node.tag=='rect':
156             w = float(node.get('width'))/2.
157             h = float(node.get('height'))/2.
158             x = float(node.get('x'))
159             y = float(node.get('y'))
160             C = [x + w , y + h ]
161             applyTransformToPoint(mat,C)
162             xmin = C[0] - abs(m[0][0]) * w - abs(m[0][1]) * h
163             xmax = C[0] + abs(m[0][0]) * w + abs(m[0][1]) * h
164             ymin = C[1] - abs(m[1][0]) * w - abs(m[1][1]) * h
165             ymax = C[1] + abs(m[1][0]) * w + abs(m[1][1]) * h
166             bbox = xmin,xmax,ymin,ymax
167             
168         elif node.tag == inkex.addNS('use','svg') or node.tag=='use':
169             refid=node.get(inkex.addNS('href','xlink'))
170             path = '//*[@id="%s"]' % refid[1:]
171             refnode = node.xpath(path)
172             bbox=boxunion(computeBBox(refnode,m),bbox)
173             
174         bbox=boxunion(computeBBox(node,m),bbox)
175     return bbox