summaryrefslogtreecommitdiffstats
path: root/share/extensions/pathmodifier.py
diff options
context:
space:
mode:
Diffstat (limited to 'share/extensions/pathmodifier.py')
-rw-r--r--share/extensions/pathmodifier.py614
1 files changed, 307 insertions, 307 deletions
diff --git a/share/extensions/pathmodifier.py b/share/extensions/pathmodifier.py
index 76b9d77b1..bfc72f074 100644
--- a/share/extensions/pathmodifier.py
+++ b/share/extensions/pathmodifier.py
@@ -1,307 +1,307 @@
-#!/usr/bin/env python
-'''
-Copyright (C) 2006 Jean-Francois Barraud, barraud@math.univ-lille1.fr
-
-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
-the Free Software Foundation; either version 2 of the License, or
-(at your option) any later version.
-
-This program is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-GNU General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with this program; if not, write to the Free Software
-Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
-barraud@math.univ-lille1.fr
-
-This code defines a basic class (PathModifier) of effects whose purpose is
-to somehow deform given objects: one common tasks for all such effect is to
-convert shapes, groups, clones to paths. The class has several functions to
-make this (more or less!) easy.
-As an exemple, a second class (Diffeo) is derived from it,
-to implement deformations of the form X=f(x,y), Y=g(x,y)...
-
-TODO: Several handy functions are defined, that might in fact be of general
-interest and that should be shipped out in separate files...
-'''
-import inkex, cubicsuperpath, bezmisc, simplestyle
-from simpletransform import *
-import copy, math, re, random
-
-####################################################################
-##-- zOrder computation...
-##-- this should be shipped out in a separate file. inkex.py?
-
-def zSort(inNode,idList):
- sortedList=[]
- theid = inNode.get("id")
- if theid in idList:
- sortedList.append(theid)
- for child in inNode:
- if len(sortedList)==len(idList):
- break
- sortedList+=zSort(child,idList)
- return sortedList
-
-
-class PathModifier(inkex.Effect):
- def __init__(self):
- inkex.Effect.__init__(self)
-
-##################################
-#-- Selectionlists manipulation --
-##################################
-
- def duplicateNodes(self, aList):
- clones={}
- for id,node in aList.iteritems():
- clone=copy.deepcopy(node)
- #!!!--> should it be given an id?
- #seems to work without this!?!
- myid = node.tag.split('}')[-1]
- clone.set("id", self.uniqueId(myid))
- node.getparent().append(clone)
- clones[clone.get("id")]=clone
- return(clones)
-
- def uniqueId(self, prefix):
- id="%s%04i"%(prefix,random.randint(0,9999))
- while len(self.document.getroot().xpath('//*[@id="%s"]' % id,namespaces=inkex.NSS)):
- id="%s%04i"%(prefix,random.randint(0,9999))
- return(id)
-
- def expandGroups(self,aList,transferTransform=True):
- for id, node in aList.items():
- if node.tag == inkex.addNS('g','svg') or node.tag=='g':
- mat=parseTransform(node.get("transform"))
- for child in node:
- if transferTransform:
- applyTransformToNode(mat,child)
- aList.update(self.expandGroups({child.get('id'):child}))
- if transferTransform and node.get("transform"):
- del node.attrib["transform"]
- del aList[id]
- return(aList)
-
- def expandGroupsUnlinkClones(self,aList,transferTransform=True,doReplace=True):
- for id in aList.keys()[:]:
- node=aList[id]
- if node.tag == inkex.addNS('g','svg') or node.tag=='g':
- self.expandGroups(aList,transferTransform)
- self.expandGroupsUnlinkClones(aList,transferTransform,doReplace)
- #Hum... not very efficient if there are many clones of groups...
-
- elif node.tag == inkex.addNS('use','svg') or node.tag=='use':
- refnode=self.refNode(node)
- newnode=self.unlinkClone(node,doReplace)
- del aList[id]
-
- style = simplestyle.parseStyle(node.get('style') or "")
- refstyle=simplestyle.parseStyle(refnode.get('style') or "")
- style.update(refstyle)
- newnode.set('style',simplestyle.formatStyle(style))
-
- newid=newnode.get('id')
- aList.update(self.expandGroupsUnlinkClones({newid:newnode},transferTransform,doReplace))
- return aList
-
- def recursNewIds(self,node):
- if node.get('id'):
- node.set('id',self.uniqueId(node.tag))
- for child in node:
- self.recursNewIds(child)
-
- def refNode(self,node):
- if node.get(inkex.addNS('href','xlink')):
- refid=node.get(inkex.addNS('href','xlink'))
- path = '//*[@id="%s"]' % refid[1:]
- newNode = self.document.getroot().xpath(path, namespaces=inkex.NSS)[0]
- return newNode
- else:
- raise AssertionError, "Trying to follow empty xlink.href attribute."
-
- def unlinkClone(self,node,doReplace):
- if node.tag == inkex.addNS('use','svg') or node.tag=='use':
- newNode = copy.deepcopy(self.refNode(node))
- self.recursNewIds(newNode)
- applyTransformToNode(parseTransform(node.get('transform')),newNode)
-
- if doReplace:
- parent=node.getparent()
- parent.insert(parent.index(node),newNode)
- parent.remove(node)
-
- return newNode
- else:
- raise AssertionError, "Only clones can be unlinked..."
-
-
-
-################################
-#-- Object conversion ----------
-################################
-
- def rectToPath(self,node,doReplace=True):
- if node.tag == inkex.addNS('rect','svg'):
- x =float(node.get('x'))
- y =float(node.get('y'))
- #FIXME: no exception anymore and sometimes just one
- try:
- rx=float(node.get('rx'))
- ry=float(node.get('ry'))
- except:
- rx=0
- ry=0
- w =float(node.get('width' ))
- h =float(node.get('height'))
- d ='M %f,%f '%(x+rx,y)
- d+='L %f,%f '%(x+w-rx,y)
- d+='A %f,%f,%i,%i,%i,%f,%f '%(rx,ry,0,0,1,x+w,y+ry)
- d+='L %f,%f '%(x+w,y+h-ry)
- d+='A %f,%f,%i,%i,%i,%f,%f '%(rx,ry,0,0,1,x+w-rx,y+h)
- d+='L %f,%f '%(x+rx,y+h)
- d+='A %f,%f,%i,%i,%i,%f,%f '%(rx,ry,0,0,1,x,y+h-ry)
- d+='L %f,%f '%(x,y+ry)
- d+='A %f,%f,%i,%i,%i,%f,%f '%(rx,ry,0,0,1,x+rx,y)
-
- newnode=inkex.etree.Element('path')
- newnode.set('d',d)
- newnode.set('id', self.uniqueId('path'))
- newnode.set('style',node.get('style'))
- nnt = node.get('transform')
- if nnt:
- newnode.set('transform',nnt)
- fuseTransform(newnode)
- if doReplace:
- parent=node.getparent()
- parent.insert(parent.index(node),newnode)
- parent.remove(node)
- return newnode
-
- def groupToPath(self,node,doReplace=True):
- if node.tag == inkex.addNS('g','svg'):
- newNode = inkex.etree.SubElement(self.current_layer,inkex.addNS('path','svg'))
-
- newstyle = simplestyle.parseStyle(node.get('style') or "")
- newp = []
- for child in node:
- childstyle = simplestyle.parseStyle(child.get('style') or "")
- childstyle.update(newstyle)
- newstyle.update(childstyle)
- childAsPath = self.objectToPath(child,False)
- newp += cubicsuperpath.parsePath(childAsPath.get('d'))
- newNode.set('d',cubicsuperpath.formatPath(newp))
- newNode.set('style',simplestyle.formatStyle(newstyle))
-
- self.current_layer.remove(newNode)
- if doReplace:
- parent=node.getparent()
- parent.insert(parent.index(node),newNode)
- parent.remove(node)
-
- return newNode
- else:
- raise AssertionError
-
- def objectToPath(self,node,doReplace=True):
- #--TODO: support other object types!!!!
- #--TODO: make sure cubicsuperpath supports A and Q commands...
- if node.tag == inkex.addNS('rect','svg'):
- return(self.rectToPath(node,doReplace))
- if node.tag == inkex.addNS('g','svg'):
- return(self.groupToPath(node,doReplace))
- elif node.tag == inkex.addNS('path','svg') or node.tag == 'path':
- #remove inkscape attributes, otherwise any modif of 'd' will be discarded!
- for attName in node.attrib.keys():
- if ("sodipodi" in attName) or ("inkscape" in attName):
- del node.attrib[attName]
- fuseTransform(node)
- return node
- elif node.tag == inkex.addNS('use','svg') or node.tag == 'use':
- newNode = self.unlinkClone(node,doReplace)
- return self.objectToPath(newNode,doReplace)
- else:
- inkex.debug("Please first convert objects to paths!...(got '%s')"%node.tag)
- return None
-
- def objectsToPaths(self,aList,doReplace=True):
- newSelection={}
- for id,node in aList.items():
- newnode=self.objectToPath(node,doReplace)
- del aList[id]
- aList[newnode.get('id')]=newnode
-
-
-################################
-#-- Action ----------
-################################
-
- #-- overwrite this method in subclasses...
- def effect(self):
- #self.duplicateNodes(self.selected)
- #self.expandGroupsUnlinkClones(self.selected, True)
- self.objectsToPaths(self.selected, True)
- self.bbox=computeBBox(self.selected.values())
- for id, node in self.selected.iteritems():
- if node.tag == inkex.addNS('path','svg'):
- d = node.get('d')
- p = cubicsuperpath.parsePath(d)
-
- #do what ever you want with p!
-
- node.set('d',cubicsuperpath.formatPath(p))
-
-
-class Diffeo(PathModifier):
- def __init__(self):
- inkex.Effect.__init__(self)
-
- def applyDiffeo(self,bpt,vects=()):
- '''
- bpt is a base point and for v in vectors, v'=v-p is a tangent vector at bpt.
- Defaults to identity!
- '''
- for v in vects:
- v[0]-=bpt[0]
- v[1]-=bpt[1]
-
- #-- your transformations go here:
- #x,y=bpt
- #bpt[0]=f(x,y)
- #bpt[1]=g(x,y)
- #for v in vects:
- # vx,vy=v
- # v[0]=df/dx(x,y)*vx+df/dy(x,y)*vy
- # v[1]=dg/dx(x,y)*vx+dg/dy(x,y)*vy
- #
- #-- !caution! y-axis is pointing downward!
-
- for v in vects:
- v[0]+=bpt[0]
- v[1]+=bpt[1]
-
-
- def effect(self):
- #self.duplicateNodes(self.selected)
- self.expandGroupsUnlinkClones(self.selected, True)
- self.expandGroups(self.selected, True)
- self.objectsToPaths(self.selected, True)
- self.bbox=computeBBox(self.selected.values())
- for id, node in self.selected.iteritems():
- if node.tag == inkex.addNS('path','svg') or node.tag=='path':
- d = node.get('d')
- p = cubicsuperpath.parsePath(d)
-
- for sub in p:
- for ctlpt in sub:
- self.applyDiffeo(ctlpt[1],(ctlpt[0],ctlpt[2]))
-
- node.set('d',cubicsuperpath.formatPath(p))
-
-#e = Diffeo()
-#e.affect()
-
-
+#!/usr/bin/env python
+'''
+Copyright (C) 2006 Jean-Francois Barraud, barraud@math.univ-lille1.fr
+
+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
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+barraud@math.univ-lille1.fr
+
+This code defines a basic class (PathModifier) of effects whose purpose is
+to somehow deform given objects: one common tasks for all such effect is to
+convert shapes, groups, clones to paths. The class has several functions to
+make this (more or less!) easy.
+As an exemple, a second class (Diffeo) is derived from it,
+to implement deformations of the form X=f(x,y), Y=g(x,y)...
+
+TODO: Several handy functions are defined, that might in fact be of general
+interest and that should be shipped out in separate files...
+'''
+import inkex, cubicsuperpath, bezmisc, simplestyle
+from simpletransform import *
+import copy, math, re, random
+
+####################################################################
+##-- zOrder computation...
+##-- this should be shipped out in a separate file. inkex.py?
+
+def zSort(inNode,idList):
+ sortedList=[]
+ theid = inNode.get("id")
+ if theid in idList:
+ sortedList.append(theid)
+ for child in inNode:
+ if len(sortedList)==len(idList):
+ break
+ sortedList+=zSort(child,idList)
+ return sortedList
+
+
+class PathModifier(inkex.Effect):
+ def __init__(self):
+ inkex.Effect.__init__(self)
+
+##################################
+#-- Selectionlists manipulation --
+##################################
+
+ def duplicateNodes(self, aList):
+ clones={}
+ for id,node in aList.iteritems():
+ clone=copy.deepcopy(node)
+ #!!!--> should it be given an id?
+ #seems to work without this!?!
+ myid = node.tag.split('}')[-1]
+ clone.set("id", self.uniqueId(myid))
+ node.getparent().append(clone)
+ clones[clone.get("id")]=clone
+ return(clones)
+
+ def uniqueId(self, prefix):
+ id="%s%04i"%(prefix,random.randint(0,9999))
+ while len(self.document.getroot().xpath('//*[@id="%s"]' % id,namespaces=inkex.NSS)):
+ id="%s%04i"%(prefix,random.randint(0,9999))
+ return(id)
+
+ def expandGroups(self,aList,transferTransform=True):
+ for id, node in aList.items():
+ if node.tag == inkex.addNS('g','svg') or node.tag=='g':
+ mat=parseTransform(node.get("transform"))
+ for child in node:
+ if transferTransform:
+ applyTransformToNode(mat,child)
+ aList.update(self.expandGroups({child.get('id'):child}))
+ if transferTransform and node.get("transform"):
+ del node.attrib["transform"]
+ del aList[id]
+ return(aList)
+
+ def expandGroupsUnlinkClones(self,aList,transferTransform=True,doReplace=True):
+ for id in aList.keys()[:]:
+ node=aList[id]
+ if node.tag == inkex.addNS('g','svg') or node.tag=='g':
+ self.expandGroups(aList,transferTransform)
+ self.expandGroupsUnlinkClones(aList,transferTransform,doReplace)
+ #Hum... not very efficient if there are many clones of groups...
+
+ elif node.tag == inkex.addNS('use','svg') or node.tag=='use':
+ refnode=self.refNode(node)
+ newnode=self.unlinkClone(node,doReplace)
+ del aList[id]
+
+ style = simplestyle.parseStyle(node.get('style') or "")
+ refstyle=simplestyle.parseStyle(refnode.get('style') or "")
+ style.update(refstyle)
+ newnode.set('style',simplestyle.formatStyle(style))
+
+ newid=newnode.get('id')
+ aList.update(self.expandGroupsUnlinkClones({newid:newnode},transferTransform,doReplace))
+ return aList
+
+ def recursNewIds(self,node):
+ if node.get('id'):
+ node.set('id',self.uniqueId(node.tag))
+ for child in node:
+ self.recursNewIds(child)
+
+ def refNode(self,node):
+ if node.get(inkex.addNS('href','xlink')):
+ refid=node.get(inkex.addNS('href','xlink'))
+ path = '//*[@id="%s"]' % refid[1:]
+ newNode = self.document.getroot().xpath(path, namespaces=inkex.NSS)[0]
+ return newNode
+ else:
+ raise AssertionError, "Trying to follow empty xlink.href attribute."
+
+ def unlinkClone(self,node,doReplace):
+ if node.tag == inkex.addNS('use','svg') or node.tag=='use':
+ newNode = copy.deepcopy(self.refNode(node))
+ self.recursNewIds(newNode)
+ applyTransformToNode(parseTransform(node.get('transform')),newNode)
+
+ if doReplace:
+ parent=node.getparent()
+ parent.insert(parent.index(node),newNode)
+ parent.remove(node)
+
+ return newNode
+ else:
+ raise AssertionError, "Only clones can be unlinked..."
+
+
+
+################################
+#-- Object conversion ----------
+################################
+
+ def rectToPath(self,node,doReplace=True):
+ if node.tag == inkex.addNS('rect','svg'):
+ x =float(node.get('x'))
+ y =float(node.get('y'))
+ #FIXME: no exception anymore and sometimes just one
+ try:
+ rx=float(node.get('rx'))
+ ry=float(node.get('ry'))
+ except:
+ rx=0
+ ry=0
+ w =float(node.get('width' ))
+ h =float(node.get('height'))
+ d ='M %f,%f '%(x+rx,y)
+ d+='L %f,%f '%(x+w-rx,y)
+ d+='A %f,%f,%i,%i,%i,%f,%f '%(rx,ry,0,0,1,x+w,y+ry)
+ d+='L %f,%f '%(x+w,y+h-ry)
+ d+='A %f,%f,%i,%i,%i,%f,%f '%(rx,ry,0,0,1,x+w-rx,y+h)
+ d+='L %f,%f '%(x+rx,y+h)
+ d+='A %f,%f,%i,%i,%i,%f,%f '%(rx,ry,0,0,1,x,y+h-ry)
+ d+='L %f,%f '%(x,y+ry)
+ d+='A %f,%f,%i,%i,%i,%f,%f '%(rx,ry,0,0,1,x+rx,y)
+
+ newnode=inkex.etree.Element('path')
+ newnode.set('d',d)
+ newnode.set('id', self.uniqueId('path'))
+ newnode.set('style',node.get('style'))
+ nnt = node.get('transform')
+ if nnt:
+ newnode.set('transform',nnt)
+ fuseTransform(newnode)
+ if doReplace:
+ parent=node.getparent()
+ parent.insert(parent.index(node),newnode)
+ parent.remove(node)
+ return newnode
+
+ def groupToPath(self,node,doReplace=True):
+ if node.tag == inkex.addNS('g','svg'):
+ newNode = inkex.etree.SubElement(self.current_layer,inkex.addNS('path','svg'))
+
+ newstyle = simplestyle.parseStyle(node.get('style') or "")
+ newp = []
+ for child in node:
+ childstyle = simplestyle.parseStyle(child.get('style') or "")
+ childstyle.update(newstyle)
+ newstyle.update(childstyle)
+ childAsPath = self.objectToPath(child,False)
+ newp += cubicsuperpath.parsePath(childAsPath.get('d'))
+ newNode.set('d',cubicsuperpath.formatPath(newp))
+ newNode.set('style',simplestyle.formatStyle(newstyle))
+
+ self.current_layer.remove(newNode)
+ if doReplace:
+ parent=node.getparent()
+ parent.insert(parent.index(node),newNode)
+ parent.remove(node)
+
+ return newNode
+ else:
+ raise AssertionError
+
+ def objectToPath(self,node,doReplace=True):
+ #--TODO: support other object types!!!!
+ #--TODO: make sure cubicsuperpath supports A and Q commands...
+ if node.tag == inkex.addNS('rect','svg'):
+ return(self.rectToPath(node,doReplace))
+ if node.tag == inkex.addNS('g','svg'):
+ return(self.groupToPath(node,doReplace))
+ elif node.tag == inkex.addNS('path','svg') or node.tag == 'path':
+ #remove inkscape attributes, otherwise any modif of 'd' will be discarded!
+ for attName in node.attrib.keys():
+ if ("sodipodi" in attName) or ("inkscape" in attName):
+ del node.attrib[attName]
+ fuseTransform(node)
+ return node
+ elif node.tag == inkex.addNS('use','svg') or node.tag == 'use':
+ newNode = self.unlinkClone(node,doReplace)
+ return self.objectToPath(newNode,doReplace)
+ else:
+ inkex.debug("Please first convert objects to paths!...(got '%s')"%node.tag)
+ return None
+
+ def objectsToPaths(self,aList,doReplace=True):
+ newSelection={}
+ for id,node in aList.items():
+ newnode=self.objectToPath(node,doReplace)
+ del aList[id]
+ aList[newnode.get('id')]=newnode
+
+
+################################
+#-- Action ----------
+################################
+
+ #-- overwrite this method in subclasses...
+ def effect(self):
+ #self.duplicateNodes(self.selected)
+ #self.expandGroupsUnlinkClones(self.selected, True)
+ self.objectsToPaths(self.selected, True)
+ self.bbox=computeBBox(self.selected.values())
+ for id, node in self.selected.iteritems():
+ if node.tag == inkex.addNS('path','svg'):
+ d = node.get('d')
+ p = cubicsuperpath.parsePath(d)
+
+ #do what ever you want with p!
+
+ node.set('d',cubicsuperpath.formatPath(p))
+
+
+class Diffeo(PathModifier):
+ def __init__(self):
+ inkex.Effect.__init__(self)
+
+ def applyDiffeo(self,bpt,vects=()):
+ '''
+ bpt is a base point and for v in vectors, v'=v-p is a tangent vector at bpt.
+ Defaults to identity!
+ '''
+ for v in vects:
+ v[0]-=bpt[0]
+ v[1]-=bpt[1]
+
+ #-- your transformations go here:
+ #x,y=bpt
+ #bpt[0]=f(x,y)
+ #bpt[1]=g(x,y)
+ #for v in vects:
+ # vx,vy=v
+ # v[0]=df/dx(x,y)*vx+df/dy(x,y)*vy
+ # v[1]=dg/dx(x,y)*vx+dg/dy(x,y)*vy
+ #
+ #-- !caution! y-axis is pointing downward!
+
+ for v in vects:
+ v[0]+=bpt[0]
+ v[1]+=bpt[1]
+
+
+ def effect(self):
+ #self.duplicateNodes(self.selected)
+ self.expandGroupsUnlinkClones(self.selected, True)
+ self.expandGroups(self.selected, True)
+ self.objectsToPaths(self.selected, True)
+ self.bbox=computeBBox(self.selected.values())
+ for id, node in self.selected.iteritems():
+ if node.tag == inkex.addNS('path','svg') or node.tag=='path':
+ d = node.get('d')
+ p = cubicsuperpath.parsePath(d)
+
+ for sub in p:
+ for ctlpt in sub:
+ self.applyDiffeo(ctlpt[1],(ctlpt[0],ctlpt[2]))
+
+ node.set('d',cubicsuperpath.formatPath(p))
+
+#e = Diffeo()
+#e.affect()
+
+