summary | shortlog | log | commit | commitdiff | tree
raw | patch | inline | side by side (parent: 23f6218)
raw | patch | inline | side by side (parent: 23f6218)
author | popolon2 <popolon2@users.sourceforge.net> | |
Tue, 29 Jan 2008 00:23:07 +0000 (00:23 +0000) | ||
committer | popolon2 <popolon2@users.sourceforge.net> | |
Tue, 29 Jan 2008 00:23:07 +0000 (00:23 +0000) |
share/extensions/pathalongpath.inx | patch | blob | history | |
share/extensions/pathalongpath.py | patch | blob | history | |
share/extensions/pathmodifier.py | patch | blob | history | |
share/extensions/rubberstretch.py | patch | blob | history | |
share/extensions/simpletransform.py | [new file with mode: 0644] | patch | blob |
index ce489a9e2c2e260b458bf3b6c3e11b6a6a984f88..3011f015af36c8411c8bee3febde1a527adc4121 100644 (file)
<inkscape-extension>\r
<_name>Pattern along Path</_name>\r
<id>math.univ-lille1.barraud.pathdeform</id>\r
- <dependency type="executable" location="extensions">pathmodifier.py</dependency>\r
- <dependency type="executable" location="extensions">pathalongpath.py</dependency>\r
- <dependency type="executable" location="extensions">inkex.py</dependency>\r
- <param name="title" type="description">This effect bends a pattern object along an arbitrary "skeleton" path. The pattern can be a path or a group of paths. First, select the pattern object; then add to selection the skeleton path; then call this effect.</param>\r
+<dependency type="executable" location="extensions">pathmodifier.py</dependency>\r
+<dependency type="executable" location="extensions">pathalongpath.py</dependency>\r
+<dependency type="executable" location="extensions">inkex.py</dependency>\r
+<param name="title" type="description">This effect bends a pattern object along arbitrary "skeleton" paths. The pattern is the top most object in the selection. (groups of paths/shapes/clones... allowed)</param>\r
\r
- <param name="copymode" type="enum" _gui-text="Copies of the pattern:">\r
- <item value="Single">Single</item>\r
- <item value="Single, stretched">Single, stretched</item>\r
- <item value="Repeated">Repeated</item>\r
- <item value="Repeated, stretched">Repeated, stretched</item>\r
- </param>\r
+<param name="copymode" type="enum" _gui-text="Copies of the pattern:">\r
+ <item value="Single">Single</item>\r
+ <item value="Single, stretched">Single, stretched</item>\r
+ <item value="Repeated">Repeated</item>\r
+ <item value="Repeated, stretched">Repeated, stretched</item>\r
+</param>\r
\r
- <param name="kind" type="enum" _gui-text="Deformation type:">\r
- <item value="Snake">Snake</item>\r
- <item value="Ribbon">Ribbon</item>\r
- </param>\r
+<param name="kind" type="enum" _gui-text="Deformation type:">\r
+ <item value="Snake">Snake</item>\r
+ <item value="Ribbon">Ribbon</item>\r
+</param>\r
\r
- <param name="space" type="float" _gui-text="Space between copies:" min="-10000.0" max="10000.0" >0.0</param>\r
+<param name="space" type="float" _gui-text="Space between copies:" min="-10000.0" max="10000.0" >0.0</param>\r
\r
- <param name="noffset" type="float" _gui-text="Normal offset" min="-10000.0" max="10000.0">0.0</param>\r
- <param name="toffset" type="float" _gui-text="Tangential offset" min="-10000.0" max="10000.0" >0.0</param>\r
+<param name="noffset" type="float" _gui-text="Normal offset" min="-10000.0" max="10000.0">0.0</param>\r
+<param name="toffset" type="float" _gui-text="Tangential offset" min="-10000.0" max="10000.0" >0.0</param>\r
\r
- <param name="vertical" type="boolean" _gui-text="Pattern is vertical">false</param>\r
- <param name="duplicate" type="boolean" _gui-text="Duplicate the pattern before deformation">true</param>\r
+<param name="vertical" type="boolean" _gui-text="Pattern is vertical">false</param>\r
+<param name="duplicate" type="boolean" _gui-text="Duplicate the pattern before deformation">true</param>\r
<effect>\r
- <effects-menu>\r
- <submenu _name="Generate from Path"/>\r
- </effects-menu>\r
+<effects-menu>\r
+<submenu _name="Generate from Path"/>\r
+</effects-menu>\r
</effect>\r
<script>\r
<command reldir="extensions" interpreter="python">pathalongpath.py</command>\r
</script>\r
</inkscape-extension>\r
\r
- \r
+ \r
index e49aadeb46ed1d43b62e259928533f8ecea4fdc2..a8285b9f875a370f009ff6637f48f53bc3880a82 100644 (file)
'''\r
\r
import inkex, cubicsuperpath, bezmisc\r
-import pathmodifier \r
+import pathmodifier,simpletransform\r
\r
import copy, math, re, random\r
\r
help="duplicate pattern before deformation")\r
\r
def prepareSelectionList(self):\r
- ##first selected->pattern, all but first selected-> skeletons\r
- id = self.options.ids[-1]\r
+\r
+ idList=self.options.ids\r
+ idList=pathmodifier.zSort(self.document.getroot(),idList)\r
+ id = idList[-1]\r
self.patterns={id:self.selected[id]}\r
+\r
+## ##first selected->pattern, all but first selected-> skeletons\r
+## id = self.options.ids[-1]\r
+## self.patterns={id:self.selected[id]}\r
+\r
if self.options.duplicate:\r
self.patterns=self.duplicateNodes(self.patterns)\r
self.expandGroupsUnlinkClones(self.patterns, True, True)\r
self.options.repeat =True\r
self.options.stretch=True\r
\r
- bbox=self.computeBBox(self.patterns)\r
+ bbox=simpletransform.computeBBox(self.patterns.values())\r
+ \r
if self.options.vertical:\r
#flipxy(bbox)...\r
bbox=(-bbox[3],-bbox[2],-bbox[1],-bbox[0])\r
xoffset=self.skelcomp[0][0]-bbox[0]+self.options.toffset\r
yoffset=self.skelcomp[0][1]-(bbox[2]+bbox[3])/2-self.options.noffset\r
\r
+\r
if self.options.repeat:\r
NbCopies=max(1,int(round((length+self.options.space)/dx)))\r
width=dx*NbCopies\r
if not self.skelcompIsClosed:\r
width-=self.options.space\r
- bbox=bbox[0],bbox[0]+width,bbox[2],bbox[3]\r
+ bbox=bbox[0],bbox[0]+width, bbox[2],bbox[3]\r
new=[]\r
for sub in p:\r
for i in range(0,NbCopies,1):\r
index baa31fca221d986901d23fa0aceb36b57a889391..a7e312ae06a6d44ec43584c91d37f227485499e5 100644 (file)
along with this program; if not, write to the Free Software\r
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA\r
barraud@math.univ-lille1.fr\r
+\r
+This code defines a basic class (PathModifier) of effects whose purpose is\r
+to somehow deform given objects: one common tasks for all such effect is to\r
+convert shapes, groups, clones to paths. The class has several functions to\r
+make this (more or less!) easy.\r
+As an exemple, a second class (Diffeo) is derived from it,\r
+to implement deformations of the form X=f(x,y), Y=g(x,y)...\r
+\r
+TODO: Several handy functions are defined, that might in fact be of general\r
+interest and that should be shipped out in separate files...\r
'''\r
import inkex, cubicsuperpath, bezmisc, simplestyle\r
+from simpletransform import *\r
import copy, math, re, random\r
\r
-def parseTransform(transf,mat=[[1.0,0.0,0.0],[0.0,1.0,0.0]]):\r
- if transf=="":\r
- return(mat)\r
- result=re.match("(translate|scale|rotate|skewX|skewY|matrix)\(([^)]*)\)",transf)\r
-#-- translate --\r
- if result.group(1)=="translate":\r
- args=result.group(2).split(",")\r
- dx=float(args[0])\r
- if len(args)==1:\r
- dy=0.0\r
- else:\r
- dy=float(args[1])\r
- matrix=[[1,0,dx],[0,1,dy]]\r
-#-- scale --\r
- if result.groups(1)=="scale":\r
- args=result.group(2).split(",")\r
- sx=float(args[0])\r
- if len(args)==1:\r
- sy=sx\r
- else:\r
- sy=float(args[1])\r
- matrix=[[sx,0,0],[0,sy,0]]\r
-#-- rotate --\r
- if result.groups(1)=="rotate":\r
- args=result.group(2).split(",")\r
- a=float(args[0])*math.pi/180\r
- if len(args)==1:\r
- cx,cy=(0.0,0.0)\r
- else:\r
- cx,cy=args[1:]\r
- matrix=[[math.cos(a),-math.sin(a),cx],[math.sin(a),math.cos(a),cy]]\r
-#-- skewX --\r
- if result.groups(1)=="skewX":\r
- a=float(result.group(2))*math.pi/180\r
- matrix=[[1,math.tan(a),0],[0,1,0]]\r
-#-- skewX --\r
- if result.groups(1)=="skewX":\r
- a=float(result.group(2))*math.pi/180\r
- matrix=[[1,0,0],[math.tan(a),1,0]]\r
-#-- matrix --\r
- if result.group(1)=="matrix":\r
- a11,a21,a12,a22,v1,v2=result.group(2).split(",")\r
- matrix=[[float(a11),float(a12),float(v1)],[float(a21),float(a22),float(v2)]]\r
- \r
- matrix=composeTransform(mat,matrix)\r
- if result.end()<len(transf):\r
- return(parseTransform(transf[result.end():],matrix))\r
- else:\r
- return matrix\r
-\r
-def formatTransform(mat):\r
- 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
-\r
-def composeTransform(M1,M2):\r
- a11=M1[0][0]*M2[0][0]+M1[0][1]*M2[1][0]\r
- a12=M1[0][0]*M2[0][1]+M1[0][1]*M2[1][1]\r
- a21=M1[1][0]*M2[0][0]+M1[1][1]*M2[1][0]\r
- a22=M1[1][0]*M2[0][1]+M1[1][1]*M2[1][1]\r
-\r
- v1=M1[0][0]*M2[0][2]+M1[0][1]*M2[1][2]+M1[0][2]\r
- v2=M1[1][0]*M2[0][2]+M1[1][1]*M2[1][2]+M1[1][2]\r
- return [[a11,a12,v1],[a21,a22,v2]]\r
-\r
-def applyTransformToNode(mat,node):\r
- m=parseTransform(node.get("transform"))\r
- newtransf=formatTransform(composeTransform(mat,m))\r
- node.set("transform", newtransf)\r
-\r
-def applyTransformToPoint(mat,pt):\r
- x=mat[0][0]*pt[0]+mat[0][1]*pt[1]+mat[0][2]\r
- y=mat[1][0]*pt[0]+mat[1][1]*pt[1]+mat[1][2]\r
- pt[0]=x\r
- pt[1]=y\r
-\r
-def fuseTransform(node):\r
- t = node.get("transform")\r
- if t == None:\r
- return\r
- m=parseTransform(t)\r
- d = node.get('d')\r
- p=cubicsuperpath.parsePath(d)\r
- for comp in p:\r
- for ctl in comp:\r
- for pt in ctl:\r
- applyTransformToPoint(m,pt)\r
- node.set('d', cubicsuperpath.formatPath(p))\r
- del node.attrib["transform"]\r
-\r
-\r
-def boxunion(b1,b2):\r
- if b1 is None:\r
- return b2\r
- elif b2 is None:\r
- return b1 \r
- else:\r
- return((min(b1[0],b2[0]),max(b1[1],b2[1]),min(b1[2],b2[2]),max(b1[3],b2[3])))\r
-\r
-def roughBBox(path):\r
- 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
- for pathcomp in path:\r
- for ctl in pathcomp:\r
- for pt in ctl:\r
- xmin=min(xmin,pt[0])\r
- xMax=max(xMax,pt[0])\r
- ymin=min(ymin,pt[1])\r
- yMax=max(yMax,pt[1])\r
- return xmin,xMax,ymin,yMax\r
+####################################################################\r
+##-- zOrder computation...\r
+##-- this should be shipped out in a separate file. inkex.py?\r
+\r
+def zSort(inNode,idList):\r
+ sortedList=[]\r
+ theid = inNode.get("id")\r
+ if theid in idList:\r
+ sortedList.append(theid)\r
+ for child in inNode:\r
+ if len(sortedList)==len(idList):\r
+ break\r
+ sortedList+=zSort(child,idList)\r
+ return sortedList\r
\r
\r
class PathModifier(inkex.Effect):\r
##################################\r
#-- Selectionlists manipulation --\r
##################################\r
- def computeBBox(self, aList):\r
- bbox=None\r
- for id, node in aList.iteritems():\r
- if node.tag == inkex.addNS('path','svg') or node.tag == 'path':\r
- d = node.get('d')\r
- p = cubicsuperpath.parsePath(d)\r
- bbox=boxunion(roughBBox(p),bbox)\r
- return bbox\r
\r
def duplicateNodes(self, aList):\r
clones={}\r
if transferTransform:\r
applyTransformToNode(mat,child)\r
aList.update(self.expandGroups({child.get('id'):child}))\r
- if transferTransform:\r
+ if transferTransform and node.get("transform"):\r
del node.attrib["transform"]\r
del aList[id]\r
return(aList)\r
self.expandGroups(aList,transferTransform)\r
self.expandGroupsUnlinkClones(aList,transferTransform,doReplace)\r
#Hum... not very efficient if there are many clones of groups...\r
+\r
elif node.tag == inkex.addNS('use','svg') or node.tag=='use':\r
- refid=node.get(inkex.addNS('href','xlink'))\r
- path = '//*[@id="%s"]' % refid[1:]\r
- refnode = self.document.getroot().xpath(path,inkex.NSS)\r
- newnode=copy.deepcopy(refnode)\r
- self.recursNewIds(newnode)\r
-\r
- s = node.get('style')\r
- if s:\r
- style=simplestyle.parseStyle(s)\r
- refstyle=simplestyle.parseStyle(refnode.get('style'))\r
- style.update(refstyle)\r
- newnode.set('style',simplestyle.formatStyle(style))\r
- applyTransformToNode(parseTransform(node.get('transform')),newnode)\r
- if doReplace:\r
- parent=node.getparent()\r
- parent.insert(node.index,newnode)\r
- parent.remove(node)\r
- del aList[id]\r
+ refnode=self.refNode(node)\r
+ newnode=self.unlinkClone(node,doReplace)\r
+ del aList[id]\r
+\r
+ style = simplestyle.parseStyle(node.get('style') or "")\r
+ refstyle=simplestyle.parseStyle(refnode.get('style') or "")\r
+ style.update(refstyle)\r
+ newnode.set('style',simplestyle.formatStyle(style))\r
+\r
newid=newnode.get('id')\r
aList.update(self.expandGroupsUnlinkClones({newid:newnode},transferTransform,doReplace))\r
return aList\r
for child in node:\r
self.recursNewIds(child)\r
\r
+ def refNode(self,node):\r
+ if node.get(inkex.addNS('href','xlink')):\r
+ refid=node.get(inkex.addNS('href','xlink'))\r
+ path = '//*[@id="%s"]' % refid[1:]\r
+ newNode = self.document.getroot().xpath(path,inkex.NSS)[0]\r
+ return newNode\r
+ else:\r
+ raise AssertionError, "Trying to follow empty xlink.href attribute."\r
+\r
+ def unlinkClone(self,node,doReplace):\r
+ if node.tag == inkex.addNS('use','svg') or node.tag=='use':\r
+ newNode = copy.deepcopy(self.refNode(node))\r
+ self.recursNewIds(newNode)\r
+ applyTransformToNode(parseTransform(node.get('transform')),newNode)\r
+\r
+ if doReplace:\r
+ parent=node.getparent()\r
+ parent.insert(parent.index(node),newNode)\r
+ parent.remove(node)\r
+\r
+ return newNode\r
+ else:\r
+ raise AssertionError, "Only clones can be unlinked..."\r
+\r
\r
- \r
-# Had not been rewritten for ElementTree\r
-# def makeClonesReal(self,aList,doReplace=True,recursivelytransferTransform=True):\r
-# for id in aList.keys(): \r
-# node=aList[id]\r
-# if node.tagName == 'g':\r
-# childs={}\r
-# for child in node.childNodes:\r
-# if child.nodeType==child.ELEMENT_NODE:\r
-# childid=child.getAttributeNS(None,'id')\r
-# del aList[childid]\r
-# aList.update(self.makeClonesReal({childid:child},doReplace))\r
-# elif node.tagName == 'use':\r
-# refid=node.getAttributeNS(inkex.NSS[u'xlink'],'href')\r
-# path = '//*[@id="%s"]' % refid[1:]\r
-# refnode = xml.xpath.Evaluate(path,document)[0]\r
-# clone=refnode.cloneNode(True)\r
-# cloneid=self.uniqueId(clone.tagName)\r
-# clone.setAttributeNS(None,'id', cloneid)\r
-# style=simplestyle.parseStyle(node.getAttributeNS(None,u'style'))\r
-# refstyle=simplestyle.parseStyle(refnode.getAttributeNS(None,u'style'))\r
-# style.update(refstyle)\r
-# clone.setAttributeNS(None,'style',simplestyle.formatStyle(style))\r
-# applyTransformToNode(parseTransform(node.getAttributeNS(None,'transform')),clone)\r
-# if doReplace:\r
-# parent=node.parentNode\r
-# parent.insertBefore(clone,node)\r
-# parent.removeChild(node)\r
-# del aList[id]\r
-# aList.update(self.expandGroupsUnlinkClones({cloneid:clone}))\r
-# return aList\r
\r
################################\r
#-- Object conversion ----------\r
fuseTransform(newnode)\r
if doReplace:\r
parent=node.getparent()\r
- parent.insert(node.index,newnode)\r
+ parent.insert(parent.index(node),newnode)\r
parent.remove(node)\r
return newnode\r
\r
+ def groupToPath(self,node,doReplace=True):\r
+ if node.tag == inkex.addNS('g','svg'):\r
+ newNode = inkex.etree.SubElement(self.current_layer,inkex.addNS('path','svg')) \r
+\r
+ newstyle = simplestyle.parseStyle(node.get('style') or "")\r
+ newp = []\r
+ for child in node:\r
+ childstyle = simplestyle.parseStyle(child.get('style') or "")\r
+ childstyle.update(newstyle)\r
+ newstyle.update(childstyle)\r
+ childAsPath = self.objectToPath(child,False)\r
+ newp += cubicsuperpath.parsePath(childAsPath.get('d'))\r
+ newNode.set('d',cubicsuperpath.formatPath(newp))\r
+ newNode.set('style',simplestyle.formatStyle(newstyle))\r
+\r
+ self.current_layer.remove(newNode)\r
+ if doReplace:\r
+ parent=node.getparent()\r
+ parent.insert(parent.index(node),newNode)\r
+ parent.remove(node)\r
+\r
+ return newNode\r
+ else:\r
+ raise AssertionError\r
+ \r
def objectToPath(self,node,doReplace=True):\r
#--TODO: support other object types!!!!\r
#--TODO: make sure cubicsuperpath supports A and Q commands... \r
if node.tag == inkex.addNS('rect','svg'):\r
return(self.rectToPath(node,doReplace))\r
+ if node.tag == inkex.addNS('g','svg'):\r
+ return(self.groupToPath(node,doReplace))\r
elif node.tag == inkex.addNS('path','svg') or node.tag == 'path':\r
- attributes = node.keys()\r
- for attName in attributes:\r
- uri = None\r
- if attName[0] == '{':\r
- uri,attName = attName.split('}')\r
- uri = uri[1:]\r
- if uri in [inkex.NSS[u'sodipodi'],inkex.NSS[u'inkscape']]:\r
- #if attName not in ["d","id","style","transform"]:\r
- del node.attrib[inkex.addNS(attName,uri)]\r
+ #remove inkscape attributes, otherwise any modif of 'd' will be discarded!\r
+ for attName in node.attrib.keys():\r
+ if ("sodipodi" in attName) or ("inkscape" in attName):\r
+ del node.attrib[attName]\r
fuseTransform(node)\r
return node\r
+ elif node.tag == inkex.addNS('use','svg') or node.tag == 'use':\r
+ newNode = self.unlinkClone(node,doReplace)\r
+ return self.objectToPath(newNode,doReplace)\r
else:\r
inkex.debug("Please first convert objects to paths!...(got '%s')"%node.tag)\r
return None\r
def objectsToPaths(self,aList,doReplace=True):\r
newSelection={}\r
for id,node in aList.items():\r
- newnode=self.objectToPath(node,self.document)\r
+ newnode=self.objectToPath(node,doReplace)\r
del aList[id]\r
aList[newnode.get('id')]=newnode\r
\r
#-- overwrite this method in subclasses...\r
def effect(self):\r
#self.duplicateNodes(self.selected)\r
- self.expandGroupsUnlinkClones(self.selected, True)\r
+ #self.expandGroupsUnlinkClones(self.selected, True)\r
self.objectsToPaths(self.selected, True)\r
- self.bbox=self.computeBBox(self.selected)\r
+ self.bbox=computeBBox(self.selected.values())\r
for id, node in self.selected.iteritems():\r
if node.tag == inkex.addNS('path','svg'):\r
d = node.get('d')\r
p = cubicsuperpath.parsePath(d)\r
\r
- #do what ever you want with p!\r
+ #do what ever you want with p!\r
\r
- node.set('d',cubicsuperpath.formatPath(p))\r
+ node.set('d',cubicsuperpath.formatPath(p))\r
\r
\r
class Diffeo(PathModifier):\r
self.expandGroupsUnlinkClones(self.selected, True)\r
self.expandGroups(self.selected, True)\r
self.objectsToPaths(self.selected, True)\r
- self.bbox=self.computeBBox(self.selected)\r
+ self.bbox=computeBBox(self.selected.values())\r
for id, node in self.selected.iteritems():\r
if node.tag == inkex.addNS('path','svg') or node.tag=='path':\r
d = node.get('d')\r
\r
#e = Diffeo()\r
#e.affect()\r
-
-
+\r
+ \r
index 1ea533cf0c8dd7fb63c0796a18d890f49b807101..60b1738c63bea7ac2a8a8d144628ccc18c187c32 100644 (file)
'''\r
\r
import inkex, cubicsuperpath, bezmisc, pathmodifier\r
-import copy, math, re, random, xml.xpath\r
+import copy, math, re\r
\r
class RubberStretch(pathmodifier.Diffeo):\r
def __init__(self):\r
w,h=(self.bbox[1]-self.bbox[0])/2,(self.bbox[3]-self.bbox[2])/2\r
\r
x,y=(bpt[0]-x0),(bpt[1]-y0)\r
- sx=(1+b*(x/w+1)*(x/w-1))*2**(-a)\r
+ sx=(1+b*(x/w+1)*(x/w-1))*2**(-a)\r
sy=(1+b*(y/h+1)*(y/h-1))*2**(-a)\r
bpt[0]=x0+x*sy\r
bpt[1]=y0+y/sx\r
\r
e = RubberStretch()\r
e.affect()\r
-
-
+\r
+ \r
diff --git a/share/extensions/simpletransform.py b/share/extensions/simpletransform.py
--- /dev/null
@@ -0,0 +1,164 @@
+#!/usr/bin/env python\r
+'''\r
+Copyright (C) 2006 Jean-Francois Barraud, barraud@math.univ-lille1.fr\r
+\r
+This program is free software; you can redistribute it and/or modify\r
+it under the terms of the GNU General Public License as published by\r
+the Free Software Foundation; either version 2 of the License, or\r
+(at your option) any later version.\r
+\r
+This program is distributed in the hope that it will be useful,\r
+but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\r
+GNU General Public License for more details.\r
+\r
+You should have received a copy of the GNU General Public License\r
+along with this program; if not, write to the Free Software\r
+Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA\r
+barraud@math.univ-lille1.fr\r
+\r
+This code defines several functions to make handling of transform\r
+attribute easier.\r
+'''\r
+import inkex, cubicsuperpath, bezmisc, simplestyle\r
+import copy, math, re\r
+\r
+def parseTransform(transf,mat=[[1.0,0.0,0.0],[0.0,1.0,0.0]]):\r
+ if transf=="" or transf==None:\r
+ return(mat)\r
+ result=re.match("(translate|scale|rotate|skewX|skewY|matrix)\(([^)]*)\)",transf)\r
+#-- translate --\r
+ if result.group(1)=="translate":\r
+ args=result.group(2).split(",")\r
+ dx=float(args[0])\r
+ if len(args)==1:\r
+ dy=0.0\r
+ else:\r
+ dy=float(args[1])\r
+ matrix=[[1,0,dx],[0,1,dy]]\r
+#-- scale --\r
+ if result.groups(1)=="scale":\r
+ args=result.group(2).split(",")\r
+ sx=float(args[0])\r
+ if len(args)==1:\r
+ sy=sx\r
+ else:\r
+ sy=float(args[1])\r
+ matrix=[[sx,0,0],[0,sy,0]]\r
+#-- rotate --\r
+ if result.groups(1)=="rotate":\r
+ args=result.group(2).split(",")\r
+ a=float(args[0])*math.pi/180\r
+ if len(args)==1:\r
+ cx,cy=(0.0,0.0)\r
+ else:\r
+ cx,cy=args[1:]\r
+ matrix=[[math.cos(a),-math.sin(a),cx],[math.sin(a),math.cos(a),cy]]\r
+#-- skewX --\r
+ if result.groups(1)=="skewX":\r
+ a=float(result.group(2))*math.pi/180\r
+ matrix=[[1,math.tan(a),0],[0,1,0]]\r
+#-- skewX --\r
+ if result.groups(1)=="skewX":\r
+ a=float(result.group(2))*math.pi/180\r
+ matrix=[[1,0,0],[math.tan(a),1,0]]\r
+#-- matrix --\r
+ if result.group(1)=="matrix":\r
+ a11,a21,a12,a22,v1,v2=result.group(2).split(",")\r
+ matrix=[[float(a11),float(a12),float(v1)],[float(a21),float(a22),float(v2)]]\r
+ \r
+ matrix=composeTransform(mat,matrix)\r
+ if result.end()<len(transf):\r
+ return(parseTransform(transf[result.end():],matrix))\r
+ else:\r
+ return matrix\r
+\r
+def formatTransform(mat):\r
+ 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
+\r
+def composeTransform(M1,M2):\r
+ a11=M1[0][0]*M2[0][0]+M1[0][1]*M2[1][0]\r
+ a12=M1[0][0]*M2[0][1]+M1[0][1]*M2[1][1]\r
+ a21=M1[1][0]*M2[0][0]+M1[1][1]*M2[1][0]\r
+ a22=M1[1][0]*M2[0][1]+M1[1][1]*M2[1][1]\r
+\r
+ v1=M1[0][0]*M2[0][2]+M1[0][1]*M2[1][2]+M1[0][2]\r
+ v2=M1[1][0]*M2[0][2]+M1[1][1]*M2[1][2]+M1[1][2]\r
+ return [[a11,a12,v1],[a21,a22,v2]]\r
+\r
+def applyTransformToNode(mat,node):\r
+ m=parseTransform(node.get("transform"))\r
+ newtransf=formatTransform(composeTransform(mat,m))\r
+ node.set("transform", newtransf)\r
+\r
+def applyTransformToPoint(mat,pt):\r
+ x=mat[0][0]*pt[0]+mat[0][1]*pt[1]+mat[0][2]\r
+ y=mat[1][0]*pt[0]+mat[1][1]*pt[1]+mat[1][2]\r
+ pt[0]=x\r
+ pt[1]=y\r
+\r
+def applyTransformToPath(mat,path):\r
+ for comp in path:\r
+ for ctl in comp:\r
+ for pt in ctl:\r
+ applyTransformToPoint(mat,pt)\r
+\r
+def fuseTransform(node):\r
+ if node.get('d')==None:\r
+ #FIX ME: how do you raise errors?\r
+ raise AssertionError, 'can not fuse "transform" of elements that have no "d" attribute'\r
+ t = node.get("transform")\r
+ if t == None:\r
+ return\r
+ m = parseTransform(t)\r
+ d = node.get('d')\r
+ p = cubicsuperpath.parsePath(d)\r
+ applyTransformToPath(m,p)\r
+ node.set('d', cubicsuperpath.formatPath(p))\r
+ del node.attrib["transform"]\r
+\r
+####################################################################\r
+##-- Some functions to compute a rough bbox of a given list of objects.\r
+##-- this should be shipped out in an separate file...\r
+\r
+def boxunion(b1,b2):\r
+ if b1 is None:\r
+ return b2\r
+ elif b2 is None:\r
+ return b1 \r
+ else:\r
+ return((min(b1[0],b2[0]),max(b1[1],b2[1]),min(b1[2],b2[2]),max(b1[3],b2[3])))\r
+\r
+def roughBBox(path):\r
+ 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
+ for pathcomp in path:\r
+ for ctl in pathcomp:\r
+ for pt in ctl:\r
+ xmin=min(xmin,pt[0])\r
+ xMax=max(xMax,pt[0])\r
+ ymin=min(ymin,pt[1])\r
+ yMax=max(yMax,pt[1])\r
+ return xmin,xMax,ymin,yMax\r
+\r
+def computeBBox(aList,mat=[[1,0,0],[0,1,0]]):\r
+ bbox=None\r
+ for node in aList:\r
+ m = parseTransform(node.get('transform'))\r
+ m = composeTransform(mat,m)\r
+ #TODO: text not supported!\r
+ if node.get("d"):\r
+ d = node.get('d')\r
+ p = cubicsuperpath.parsePath(d)\r
+ applyTransformToPath(m,p)\r
+ bbox=boxunion(roughBBox(p),bbox)\r
+\r
+ if node.tag == inkex.addNS('use','svg') or node.tag=='use':\r
+ refid=node.get(inkex.addNS('href','xlink'))\r
+ path = '//*[@id="%s"]' % refid[1:]\r
+ refnode = node.getroottree().xpath(path,inkex.NSS)\r
+ bbox=boxunion(computeBBox(refnode,m),bbox)\r
+ \r
+ bbox=boxunion(computeBBox(node,m),bbox)\r
+ return bbox\r
+\r
+\r