~valavanisalex/ubuntu/precise/inkscape/fix-943984

« back to all changes in this revision

Viewing changes to inkscape-0.47pre1/share/extensions/pathmodifier.py

  • Committer: Bazaar Package Importer
  • Author(s): Bryce Harrington
  • Date: 2009-07-02 17:09:45 UTC
  • mfrom: (1.1.9 upstream)
  • Revision ID: james.westby@ubuntu.com-20090702170945-nn6d6zswovbwju1t
Tags: 0.47~pre1-0ubuntu1
* New upstream release.
  - Don't constrain maximization on small resolution devices (pre0)
    (LP: #348842)
  - Fixes segfault on startup (pre0)
    (LP: #391149)

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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