Code

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