home *** CD-ROM | disk | FTP | other *** search
/ Chip 2011 November / CHIP_2011_11.iso / Programy / Narzedzia / Inkscape / Inkscape-0.48.2-1-win32.exe / share / extensions / pathmodifier.py < prev    next >
Text File  |  2011-07-08  |  12KB  |  311 lines

  1. #!/usr/bin/env python
  2. '''
  3. Copyright (C) 2006 Jean-Francois Barraud, barraud@math.univ-lille1.fr
  4.  
  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.
  9.  
  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.
  14.  
  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
  19.  
  20. This code defines a basic class (PathModifier) of effects whose purpose is
  21. to somehow deform given objects: one common tasks for all such effect is to
  22. convert shapes, groups, clones to paths. The class has several functions to
  23. make this (more or less!) easy.
  24. As an exemple, a second class (Diffeo) is derived from it,
  25. to implement deformations of the form X=f(x,y), Y=g(x,y)...
  26.  
  27. TODO: Several handy functions are defined, that might in fact be of general
  28. interest and that should be shipped out in separate files...
  29. '''
  30. import inkex, cubicsuperpath, bezmisc, simplestyle
  31. from simpletransform import *
  32. import copy, math, re, random
  33. import gettext
  34. _ = gettext.gettext
  35.  
  36. ####################################################################
  37. ##-- zOrder computation...
  38. ##-- this should be shipped out in a separate file. inkex.py?
  39.  
  40. def zSort(inNode,idList):
  41.     sortedList=[]
  42.     theid = inNode.get("id")
  43.     if theid in idList:
  44.         sortedList.append(theid)
  45.     for child in inNode:
  46.         if len(sortedList)==len(idList):
  47.             break
  48.         sortedList+=zSort(child,idList)
  49.     return sortedList
  50.  
  51.  
  52. class PathModifier(inkex.Effect):
  53.     def __init__(self):
  54.         inkex.Effect.__init__(self)
  55.  
  56. ##################################
  57. #-- Selectionlists manipulation --
  58. ##################################
  59.  
  60.     def duplicateNodes(self, aList):
  61.         clones={}
  62.         for id,node in aList.iteritems():
  63.             clone=copy.deepcopy(node)
  64.             #!!!--> should it be given an id?
  65.             #seems to work without this!?!
  66.             myid = node.tag.split('}')[-1]
  67.             clone.set("id", self.uniqueId(myid))
  68.             node.getparent().append(clone)
  69.             clones[clone.get("id")]=clone
  70.         return(clones)
  71.  
  72.     def uniqueId(self, prefix):
  73.         id="%s%04i"%(prefix,random.randint(0,9999))
  74.         while len(self.document.getroot().xpath('//*[@id="%s"]' % id,namespaces=inkex.NSS)):
  75.             id="%s%04i"%(prefix,random.randint(0,9999))
  76.         return(id)
  77.  
  78.     def expandGroups(self,aList,transferTransform=True):
  79.         for id, node in aList.items():      
  80.             if node.tag == inkex.addNS('g','svg') or node.tag=='g':
  81.                 mat=parseTransform(node.get("transform"))
  82.                 for child in node:
  83.                     if transferTransform:
  84.                         applyTransformToNode(mat,child)
  85.                     aList.update(self.expandGroups({child.get('id'):child}))
  86.                 if transferTransform and node.get("transform"):
  87.                     del node.attrib["transform"]
  88.                 del aList[id]
  89.         return(aList)
  90.  
  91.     def expandGroupsUnlinkClones(self,aList,transferTransform=True,doReplace=True):
  92.         for id in aList.keys()[:]:     
  93.             node=aList[id]
  94.             if node.tag == inkex.addNS('g','svg') or node.tag=='g':
  95.                 self.expandGroups(aList,transferTransform)
  96.                 self.expandGroupsUnlinkClones(aList,transferTransform,doReplace)
  97.                 #Hum... not very efficient if there are many clones of groups...
  98.  
  99.             elif node.tag == inkex.addNS('use','svg') or node.tag=='use':
  100.                 refnode=self.refNode(node)
  101.                 newnode=self.unlinkClone(node,doReplace)
  102.                 del aList[id]
  103.  
  104.                 style = simplestyle.parseStyle(node.get('style') or "")
  105.                 refstyle=simplestyle.parseStyle(refnode.get('style') or "")
  106.                 style.update(refstyle)
  107.                 newnode.set('style',simplestyle.formatStyle(style))
  108.  
  109.                 newid=newnode.get('id')
  110.                 aList.update(self.expandGroupsUnlinkClones({newid:newnode},transferTransform,doReplace))
  111.         return aList
  112.     
  113.     def recursNewIds(self,node):
  114.         if node.get('id'):
  115.             node.set('id',self.uniqueId(node.tag))
  116.         for child in node:
  117.             self.recursNewIds(child)
  118.             
  119.     def refNode(self,node):
  120.         if node.get(inkex.addNS('href','xlink')):
  121.             refid=node.get(inkex.addNS('href','xlink'))
  122.             path = '//*[@id="%s"]' % refid[1:]
  123.             newNode = self.document.getroot().xpath(path, namespaces=inkex.NSS)[0]
  124.             return newNode
  125.         else:
  126.             raise AssertionError, "Trying to follow empty xlink.href attribute."
  127.  
  128.     def unlinkClone(self,node,doReplace):
  129.         if node.tag == inkex.addNS('use','svg') or node.tag=='use':
  130.             newNode = copy.deepcopy(self.refNode(node))
  131.             self.recursNewIds(newNode)
  132.             applyTransformToNode(parseTransform(node.get('transform')),newNode)
  133.  
  134.             if doReplace:
  135.                 parent=node.getparent()
  136.                 parent.insert(parent.index(node),newNode)
  137.                 parent.remove(node)
  138.  
  139.             return newNode
  140.         else:
  141.             raise AssertionError, "Only clones can be unlinked..."
  142.  
  143.  
  144.  
  145. ################################
  146. #-- Object conversion ----------
  147. ################################
  148.  
  149.     def rectToPath(self,node,doReplace=True):
  150.         if node.tag == inkex.addNS('rect','svg'):
  151.             x =float(node.get('x'))
  152.             y =float(node.get('y'))
  153.             #FIXME: no exception anymore and sometimes just one
  154.             try:
  155.                 rx=float(node.get('rx'))
  156.                 ry=float(node.get('ry'))
  157.             except:
  158.                 rx=0
  159.                 ry=0
  160.             w =float(node.get('width' ))
  161.             h =float(node.get('height'))
  162.             d ='M %f,%f '%(x+rx,y)
  163.             d+='L %f,%f '%(x+w-rx,y)
  164.             d+='A %f,%f,%i,%i,%i,%f,%f '%(rx,ry,0,0,1,x+w,y+ry)
  165.             d+='L %f,%f '%(x+w,y+h-ry)
  166.             d+='A %f,%f,%i,%i,%i,%f,%f '%(rx,ry,0,0,1,x+w-rx,y+h)
  167.             d+='L %f,%f '%(x+rx,y+h)
  168.             d+='A %f,%f,%i,%i,%i,%f,%f '%(rx,ry,0,0,1,x,y+h-ry)
  169.             d+='L %f,%f '%(x,y+ry)
  170.             d+='A %f,%f,%i,%i,%i,%f,%f '%(rx,ry,0,0,1,x+rx,y)
  171.  
  172.             newnode=inkex.etree.Element('path')
  173.             newnode.set('d',d)
  174.             newnode.set('id', self.uniqueId('path'))
  175.             newnode.set('style',node.get('style'))
  176.             nnt = node.get('transform')
  177.             if nnt:
  178.                 newnode.set('transform',nnt)
  179.                 fuseTransform(newnode)
  180.             if doReplace:
  181.                 parent=node.getparent()
  182.                 parent.insert(parent.index(node),newnode)
  183.                 parent.remove(node)
  184.             return newnode
  185.  
  186.     def groupToPath(self,node,doReplace=True):
  187.         if node.tag == inkex.addNS('g','svg'):
  188.             newNode = inkex.etree.SubElement(self.current_layer,inkex.addNS('path','svg'))    
  189.  
  190.             newstyle = simplestyle.parseStyle(node.get('style') or "")
  191.             newp = []
  192.             for child in node:
  193.                 childstyle = simplestyle.parseStyle(child.get('style') or "")
  194.                 childstyle.update(newstyle)
  195.                 newstyle.update(childstyle)
  196.                 childAsPath = self.objectToPath(child,False)
  197.                 newp += cubicsuperpath.parsePath(childAsPath.get('d'))
  198.             newNode.set('d',cubicsuperpath.formatPath(newp))
  199.             newNode.set('style',simplestyle.formatStyle(newstyle))
  200.  
  201.             self.current_layer.remove(newNode)
  202.             if doReplace:
  203.                 parent=node.getparent()
  204.                 parent.insert(parent.index(node),newNode)
  205.                 parent.remove(node)
  206.  
  207.             return newNode
  208.         else:
  209.             raise AssertionError
  210.         
  211.     def objectToPath(self,node,doReplace=True):
  212.         #--TODO: support other object types!!!!
  213.         #--TODO: make sure cubicsuperpath supports A and Q commands... 
  214.         if node.tag == inkex.addNS('rect','svg'):
  215.             return(self.rectToPath(node,doReplace))
  216.         if node.tag == inkex.addNS('g','svg'):
  217.             return(self.groupToPath(node,doReplace))
  218.         elif node.tag == inkex.addNS('path','svg') or node.tag == 'path':
  219.             #remove inkscape attributes, otherwise any modif of 'd' will be discarded!
  220.             for attName in node.attrib.keys():
  221.                 if ("sodipodi" in attName) or ("inkscape" in attName):
  222.                     del node.attrib[attName]
  223.             fuseTransform(node)
  224.             return node
  225.         elif node.tag == inkex.addNS('use','svg') or node.tag == 'use':
  226.             newNode = self.unlinkClone(node,doReplace)
  227.             return self.objectToPath(newNode,doReplace)
  228.         else:
  229.             inkex.errormsg(_("Please first convert objects to paths!  (Got [%s].)") % node.tag)
  230.             return None
  231.  
  232.     def objectsToPaths(self,aList,doReplace=True):
  233.         newSelection={}
  234.         for id,node in aList.items():
  235.             newnode=self.objectToPath(node,doReplace)
  236.             del aList[id]
  237.             aList[newnode.get('id')]=newnode
  238.  
  239.  
  240. ################################
  241. #-- Action ----------
  242. ################################
  243.         
  244.     #-- overwrite this method in subclasses...
  245.     def effect(self):
  246.         #self.duplicateNodes(self.selected)
  247.         #self.expandGroupsUnlinkClones(self.selected, True)
  248.         self.objectsToPaths(self.selected, True)
  249.         self.bbox=computeBBox(self.selected.values())
  250.         for id, node in self.selected.iteritems():
  251.             if node.tag == inkex.addNS('path','svg'):
  252.                 d = node.get('d')
  253.                 p = cubicsuperpath.parsePath(d)
  254.  
  255.                 #do what ever you want with p!
  256.  
  257.                 node.set('d',cubicsuperpath.formatPath(p))
  258.  
  259.  
  260. class Diffeo(PathModifier):
  261.     def __init__(self):
  262.         inkex.Effect.__init__(self)
  263.  
  264.     def applyDiffeo(self,bpt,vects=()):
  265.         '''
  266.         bpt is a base point and for v in vectors, v'=v-p is a tangent vector at bpt. 
  267.         Defaults to identity!
  268.         '''
  269.         for v in vects:
  270.             v[0]-=bpt[0]
  271.             v[1]-=bpt[1]
  272.  
  273.         #-- your transformations go here:
  274.         #x,y=bpt
  275.         #bpt[0]=f(x,y)
  276.         #bpt[1]=g(x,y)
  277.         #for v in vects:
  278.         #    vx,vy=v
  279.         #    v[0]=df/dx(x,y)*vx+df/dy(x,y)*vy
  280.         #    v[1]=dg/dx(x,y)*vx+dg/dy(x,y)*vy
  281.         #
  282.         #-- !caution! y-axis is pointing downward!
  283.  
  284.         for v in vects:
  285.             v[0]+=bpt[0]
  286.             v[1]+=bpt[1]
  287.  
  288.  
  289.     def effect(self):
  290.         #self.duplicateNodes(self.selected)
  291.         self.expandGroupsUnlinkClones(self.selected, True)
  292.         self.expandGroups(self.selected, True)
  293.         self.objectsToPaths(self.selected, True)
  294.         self.bbox=computeBBox(self.selected.values())
  295.         for id, node in self.selected.iteritems():
  296.             if node.tag == inkex.addNS('path','svg') or node.tag=='path':
  297.                 d = node.get('d')
  298.                 p = cubicsuperpath.parsePath(d)
  299.  
  300.                 for sub in p:
  301.                     for ctlpt in sub:
  302.                         self.applyDiffeo(ctlpt[1],(ctlpt[0],ctlpt[2]))
  303.  
  304.                 node.set('d',cubicsuperpath.formatPath(p))
  305.  
  306. #e = Diffeo()
  307. #e.affect()
  308.  
  309.     
  310. # vim: expandtab shiftwidth=4 tabstop=8 softtabstop=4 encoding=utf-8 textwidth=99
  311.