root/gdadin/gdadin.py

Revision 1433, 27.0 kB (checked in by alpt, 5 months ago)

README completed

  • Property svn:executable set to
Line 
1 #!/usr/bin/env python
2 #
3 #                                  Gdadin
4 #                 GUI to Draw Algorithms Designed with Inkscape
5 #
6
7 '''
8 Copyright (C) 2005 Andrea Lo Pumo aka AlpT <alpt@freaknet.org>
9
10 This source code is free software; you can redistribute it and/or
11 modify it under the terms of the GNU General Public License as published
12 by the Free Software Foundation; either version 2 of the License,
13 or (at your option) any later version.
14
15 This source code is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
18 Please refer to the GNU Public License for more details.
19
20 You should have received a copy of the GNU Public License along with
21 this source code; if not, write to:
22 Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
23 '''
24
25 # TODO: use threads if possible. (Multi-core CPUs)
26 # TODO: check the memory available and halt the rendering when it becomes
27 #       insufficient
28 # TODO: musical shapes
29
30 import inkex, T, stylevariation
31 import random, copy, sys
32 from math import sqrt
33 #import pdb
34
35 HSBO_PRECISION=1000.0
36
37 def weighted_random_index(weights):
38         '''Given a list of weights, chooses a random weighted integer
39             from [0, len(weights)-1]'''
40         weights = [ int(w*100) for w in weights ]
41         wsum = int(sum(weights))
42         wi = random.randint(1, wsum)
43         i=ws=0
44         for w in weights:
45                 ws+=w
46                 if wi <= ws: break
47                 i+=1
48         return i
49
50 class Gdadin(inkex.Effect):
51         def svg_nodefromid(self, id):
52                 return self.document.xpath('//*[@id="%s"]' % id, namespaces=inkex.NSS)[0]
53
54         def svg_prefixfromtag(self, name):
55                 if '{' in name and '}' in name:
56                         prefix = name.split('}')[-1]
57                 else:
58                         prefix = name[0]
59                 return prefix
60
61         def svg_appendnewnode(self, node, name, attrib={}):
62                 if not attrib or 'id' not in attrib:
63                         attrib['id'] = self.newid(self.svg_prefixfromtag(name))
64                 return inkex.etree.SubElement(node, name, attrib)
65
66         def svg_appendnode(self, node, child):
67                 node.append(child)
68
69         def svg_getattr(self, node, attribname):
70                 #Note: returns None if the attribute doesn't exist
71                 return node.get(attribname)
72
73         def svg_setattr(self, node, attribname, value):
74                 return node.set(attribname, value)
75
76         def svg_remattr(self, node, attribname):
77                 del node.attrib[attribname]
78
79         def svg_remnode(self, node):
80                 parent=node.getparent()
81                 parent.remove(node)
82
83         def svg_subnodeslist(self, node, id=None):
84                 return [ child for child in node if not id or child.get('id') == id ]
85
86         def svg_nodename(self, node):
87                 return node.tag
88
89         def svg_appendnewlink(self, node, hrefid):
90                  self.svg_appendnewnode(node, inkex.addNS('use', 'svg'),
91                                         { inkex.addNS('href','xlink'):'#'+hrefid })
92
93         def newid(self, prefix):
94                 #Note: 2**53 is the maximum of randint
95                 while 1:
96                         id = prefix+str(random.randint(0, 2**53))
97                         if not id in self.doc_ids:
98                                 self.doc_ids[id]=1
99                                 return id
100
101         def __init__(self):
102                 inkex.Effect.__init__(self)
103
104                 self.rdepth_counter = {}
105                 self.shape_dimension = {}
106                 self.gdadin_shapeids = None
107                 self.cache_nodebyname = {}
108                 self.cache_idbyname = {}
109                 self.shape_bbox={}
110                 self.render_groups=[]
111
112                 inkex.NSS[u'gdadin']=u'http://www.inkscape.org/namespaces/inkscape'
113
114                 self.OptionParser.add_option("--shapename",
115                                                 action="store", type="string",
116                                                 dest="shapename", default="shape"+str(random.randint(0, 2**32-1)),
117                                                 help="Shape's name")
118                 self.OptionParser.add_option("--shapeweight",
119                                                 action="store", type="float",
120                                                 dest="shapeweight", default=1.0,
121                                                 help="Shape's weight. Used for random calls."
122
123                 self.OptionParser.add_option("--scprdepth",
124                                                 action="store", type="int",
125                                                 dest="scprdepth", default=1,
126                                                 help="Recursion depth (0 is infinite)")
127                 ## Hue
128                 self.OptionParser.add_option("--scphue",
129                                                 action="store", type="int",
130                                                 dest="scphue", default=0,
131                                                 help="Relative hue change")
132                 self.OptionParser.add_option("--scphuefloor",
133                                                 action="store", type="int",
134                                                 dest="scphuefloor", default=-1,
135                                                 help="Minimum hue value (<0 is disabled)")
136                 self.OptionParser.add_option("--scphueceil",
137                                                 action="store", type="int",
138                                                 dest="scphueceil", default=-1,
139                                                 help="Maximum hue value (<0 is disabled)")
140                 ## Saturation
141                 self.OptionParser.add_option("--scpsat",
142                                                 action="store", type="int",
143                                                 dest="scpsat", default=0,
144                                                 help="Relative saturation change")
145                 self.OptionParser.add_option("--scpsatfloor",
146                                                 action="store", type="int",
147                                                 dest="scpsatfloor", default=-1,
148                                                 help="Minimum sat value (<0 is disabled)")
149                 self.OptionParser.add_option("--scpsatceil",
150                                                 action="store", type="int",
151                                                 dest="scpsatceil", default=-1,
152                                                 help="Maximum sat value (<0 is disabled)")
153
154                 ## Brightness
155                 self.OptionParser.add_option("--scpbri",
156                                                 action="store", type="int",
157                                                 dest="scpbri", default=0,
158                                                 help="Relative brightness change")
159                 self.OptionParser.add_option("--scpbrifloor",
160                                                 action="store", type="int",
161                                                 dest="scpbrifloor", default=-1,
162                                                 help="Minimum bri value (<0 is disabled)")
163                 self.OptionParser.add_option("--scpbriceil",
164                                                 action="store", type="int",
165                                                 dest="scpbriceil", default=-1,
166                                                 help="Maximum bri value (<0 is disabled)")
167                
168                 ## Opacity
169                 self.OptionParser.add_option("--scpopa",
170                                                 action="store", type="int",
171                                                 dest="scpopa", default=0,
172                                                 help="Relative opacity change")
173                 self.OptionParser.add_option("--scpopafloor",
174                                                 action="store", type="int",
175                                                 dest="scpopafloor", default=0,
176                                                 help="Minimum opa value (<0 is disabled)")
177                 self.OptionParser.add_option("--scpopaceil",
178                                                 action="store", type="int",
179                                                 dest="scpopaceil", default=1000,
180                                                 help="Maximum opa value (<0 is disabled)")
181
182
183                 chars = ''.join([chr(i) for i in range(65,91)+range(48,58)])
184                 randomstring = ''.join([random.choice(chars) for i in range(9)])
185                 self.OptionParser.add_option("--renderseed",
186                                                 action="store", type="string",
187                                                 dest="renderseed", default=randomstring,
188                                                 help="Random seed")
189                 self.OptionParser.add_option("--rendermaxshapes",
190                                                 action="store", type="int",
191                                                 dest="rendermaxshapes", default=0,
192                                                 help="Maximum number of shapes (0 is infinite)")
193
194         def effect(self):
195                 pass
196        
197         def set_gdadin_shapeids(self):
198                 if self.gdadin_shapeids:
199                         return self.gdadin_shapeids
200
201                 defs = self.xpathSingle('/svg:svg//svg:defs')
202                 if defs == None:
203                         defs = self.svg_appendnewnode(self.document.getroot(), inkex.addNS('defs','svg'))
204                 self.gdadin_shapeids = defs.find(inkex.addNS('shapeids','gdadin'))
205                 if self.gdadin_shapeids == None:
206                         self.gdadin_shapeids = self.svg_appendnewnode(defs, inkex.addNS('shapeids','gdadin'))
207                 else:
208                         self.rem_old_shapeids()
209                 return self.gdadin_shapeids
210
211         def get_gdadin_shapeids(self):
212                 if self.gdadin_shapeids == None:
213                         inkex.debug('WARNING: No shape has been defined yet.')
214                         return None
215                 return self.gdadin_shapeids
216
217         def add_shapeid(self, sname, sid, sweight):
218                 '''Adds the shape id `sid' in the gdadin_shapeids node'''
219                
220                 gs = self.get_gdadin_shapeids()
221                 gss = self.svg_subnodeslist(gs, sname)
222                 if not gss:
223                         n = self.svg_appendnewnode(self.get_gdadin_shapeids(), inkex.addNS('shape','gdadin'),
224                                                         {'id':sname, inkex.addNS('shapeid','gdadin'):sid,
225                                                                 inkex.addNS('weight','gdadin'): str(sweight)})
226                 else:
227                         n = gss[0]
228                         self.svg_setattr(n, inkex.addNS('shapeid','gdadin'), self.svg_getattr(n, inkex.addNS('shapeid','gdadin')) + ';' + sid)
229                         self.svg_setattr(n, inkex.addNS('weight','gdadin'), self.svg_getattr(n, inkex.addNS('weight','gdadin')) + ';' + str(sweight))
230
231         def get_gss(self, sname):
232                 # Note: an exception occurs if the shape isn't found
233                 return self.svg_subnodeslist(self.get_gdadin_shapeids(), sname)[0]
234
235         def get_allshapeid(self, sname, gss=None):
236                 # Note: an exception occurs if the shape isn't found
237                 if gss == None:
238                         gss = self.get_gss(sname)
239                 return self.svg_getattr(gss, inkex.addNS('shapeid','gdadin')).split(';')
240
241         def get_allshapeweight(self, sname, gss=None):
242                 # Note: an exception occurs if the shape isn't found
243                 if gss == None:
244                         gss = self.get_gss(sname)
245                 return [float(n) for n in self.svg_getattr(gss, inkex.addNS('weight','gdadin')).split(';')]
246
247         def rem_old_shapeids(self):
248                 for gss in self.svg_subnodeslist(self.get_gdadin_shapeids()):
249                         gshapeids = self.get_allshapeid(None, gss)
250                         weights = [ str(w) for w in self.get_allshapeweight(None, gss) ]
251                         d = dict(zip(gshapeids, weights))
252
253                         def is_node_alive(id):
254                                 try:
255                                         self.svg_nodefromid(id)
256                                         return True
257                                 except:
258                                         return False
259                        
260                         for id in d.keys():
261                                 if not is_node_alive(id):
262                                         del d[id]
263
264                         if not len(d):
265                                 # the shape has been completely removed
266                                 self.svg_remnode(gss)
267                         else:
268                                 self.svg_setattr(gss, inkex.addNS('shapeid','gdadin'), ';'.join(d.keys()))
269                                 self.svg_setattr(gss, inkex.addNS('weight','gdadin'), ';'.join(d.values()))
270
271         def init_shape_cache(self):
272                 allnodes = self.svg_subnodeslist(self.get_gdadin_shapeids())
273                 cache_shapenames = [self.svg_getattr(sn, 'id') for sn in allnodes]
274
275                 cache_shapeids     = [self.get_allshapeid(sname) for sname in cache_shapenames]
276                 cache_shapeweights = [self.get_allshapeweight(sname) for sname in cache_shapenames]
277                 cache_shapenodes = [[ self.svg_nodefromid(sid) for sid in sids ] for sids in cache_shapeids]
278
279                 self.cache_nodebyname = dict(zip(cache_shapenames, cache_shapenodes))
280                 self.cache_idbyname = dict(zip(cache_shapenames,  cache_shapeids))
281                 self.cache_weightbyname = dict(zip(cache_shapenames, cache_shapeweights))
282
283
284
285         def get_shapenode(self, sname):
286                 # Note: an exception occurs if the shape isn't found
287                 if not self.cache_nodebyname:
288                         self.init_shape_cache()
289
290                 return self.cache_nodebyname[sname][weighted_random_index(self.cache_weightbyname[sname])]
291
292         def at_least_one(self):
293                 if len(self.selected) < 1:
294                        inkex.debug('Nothing to do: you have to select at least one object.')
295                        return 1
296                 return 0
297
298         def defshape(self):
299                if self.at_least_one(): return
300
301                self.set_gdadin_shapeids()
302
303                # create the shape group
304                firstnode=self.selected.items()[0][1]
305                '''
306                if len(self.selected) == 1 and self.svg_getattr(firstnode, inkex.addNS('shape','gdadin')):
307                        sgroup = firstnode
308                        del self.selected[self.selected.items()[0][0]]
309                else:
310                '''
311                sgroup = self.svg_appendnewnode(firstnode.getparent(),
312                                                  inkex.addNS('g','svg'),
313                                                  {inkex.addNS('shape','gdadin'): self.options.shapename})
314
315                for id, node in self.selected.iteritems():
316 #approach1                       self.svg_appendnode(sgroup, node)
317 #approach2                       self.svg_appendnewlink(sgroup, id)
318 #approach3
319                       cn = copy.deepcopy(node)
320                       self.svg_setattr(cn, 'id', self.newid(self.svg_prefixfromtag(cn.tag)))
321                       self.svg_appendnode(sgroup, cn)
322
323                # Update the shapes index
324                sgroupid=self.svg_getattr(sgroup, 'id')
325                self.add_shapeid(self.options.shapename, sgroupid, self.options.shapeweight)
326
327         def foreach_selected(self, f, args=None):
328                 for id, node in self.selected.iteritems():
329                         shapename = self.svg_getattr(node, inkex.addNS('shape','gdadin'))
330                         if not shapename:
331                                 inkex.debug('WARNING: one of the selected objects isn\'t a defined shape (use Gdadin -> Define Shape).  I\'ll ignore it.')
332                                 continue
333            
334                         if args:
335                                 f(id, node, shapename, *args)
336                         else:
337                                 f(id, node, shapename)
338
339         def setshapecallparams(self):
340                 if self.at_least_one(): return
341
342                 self.set_gdadin_shapeids()
343                
344                 def setallattr(id, node, shapename):
345                         self.svg_setattr(node, inkex.addNS('rdepth','gdadin'), str(self.options.scprdepth))
346
347                         self.svg_setattr(node, inkex.addNS('hue','gdadin'), str(self.options.scphue/HSBO_PRECISION))
348                         self.svg_setattr(node, inkex.addNS('huefloor','gdadin'), str(self.options.scphuefloor/HSBO_PRECISION))
349                         self.svg_setattr(node, inkex.addNS('hueceil','gdadin'), str(self.options.scphueceil/HSBO_PRECISION))
350
351                         self.svg_setattr(node, inkex.addNS('sat','gdadin'), str(self.options.scpsat/HSBO_PRECISION))
352                         self.svg_setattr(node, inkex.addNS('satfloor','gdadin'), str(self.options.scpsatfloor/HSBO_PRECISION))
353                         self.svg_setattr(node, inkex.addNS('satceil','gdadin'), str(self.options.scpsatceil/HSBO_PRECISION))
354
355                         self.svg_setattr(node, inkex.addNS('bri','gdadin'), str(self.options.scpbri/HSBO_PRECISION))
356                         self.svg_setattr(node, inkex.addNS('brifloor','gdadin'), str(self.options.scpbrifloor/HSBO_PRECISION))
357                         self.svg_setattr(node, inkex.addNS('briceil','gdadin'), str(self.options.scpbriceil/HSBO_PRECISION))
358
359                         self.svg_setattr(node, inkex.addNS('opa','gdadin'), str(self.options.scpopa/HSBO_PRECISION))
360                         self.svg_setattr(node, inkex.addNS('opafloor','gdadin'), str(self.options.scpopafloor/HSBO_PRECISION))
361                         self.svg_setattr(node, inkex.addNS('opaceil','gdadin'), str(self.options.scpopaceil/HSBO_PRECISION))
362
363                 self.foreach_selected(setallattr)
364
365         def apply_hsbo(self, node, pnode, rdata):
366                 stylevariation.apply_colmod(self, node, (rdata['hue'], rdata['sat'], rdata['bri']), rdata['opa'], rdata)
367
368         def foreach_subshape(self, rootnode, f, args=None):
369                 for parentnode in self.svg_subnodeslist(rootnode):
370                         childsname = self.svg_getattr(parentnode, inkex.addNS('shape','gdadin'))
371                         if not childsname:
372                                 continue
373                         try:
374                                 childsnode = self.get_shapenode(childsname)
375                         except:
376                                 continue
377 #                                sys.exit('ERROR: one of the defined shape isn\'t available (has it been deleted?). Aborting.')
378
379                         if args:
380                                 f(rootnode, parentnode, childsname, childsnode, *args)
381                         else:
382                                 f(rootnode, parentnode, childsname, childsnode)
383
384         def compute_shape_bbox(self, id, node, shapename):
385                 self.shape_bbox[id] = T.computeBBox(node)
386                 return self.shape_bbox[id]
387         def bbox_diagonal_length(self, bbox):
388                 x1,y1,x2,y2=(bbox[0], bbox[2], bbox[1], bbox[3])
389                 return sqrt((x1-x2)**2+(y1-y2)**2)
390         def bbox_apply_mat(self, bbox, mat):
391                 A=[0,0]
392                 B=[0,0]
393                 A[0],A[1],B[0],B[1]=(bbox[0], bbox[2], bbox[1], bbox[3])
394                 T.applyTransformToPoint(mat, A)
395                 T.applyTransformToPoint(mat, B)
396                 return min(A[0], B[0]), max(A[0], B[0]), min(A[1], B[1]), max(A[1], B[1])
397
398         def check_shape_size(self, id, mat):
399                 '''Returns 1 if the shape is too small to be seen'''
400                 bbox = self.bbox_apply_mat(self.shape_bbox[id], mat)
401                 if self.bbox_diagonal_length(bbox) < 1:
402                         return 1
403                 return 0
404
405         def adjust_document_dim(self):
406                 bbox = T.computeBBox(self.render_groups)
407                 width = bbox[1] - bbox[0] + 100
408                 height = bbox[3] - bbox[2] + 100
409                 translateX = -bbox[0]+50
410                 translateY= -bbox[2]+50
411
412                 root = self.document.getroot()
413                 self.svg_setattr(root, 'width', str(width))
414                 self.svg_setattr(root, 'height', str(height))
415                
416                 T.applyTransformToNode([[1,0,translateX],[0,1,translateY]], self.current_layer)
417        
418         def add_rendergroup(self, id, node, shapename):
419                 rgroup = self.svg_appendnewnode(node.getparent(), inkex.addNS('g','svg'),
420                                                 {inkex.addNS('render','gdadin'):'1',
421                                                 inkex.addNS('seed', 'gdadin'): self.options.renderseed}
422                                                 )
423                 self.svg_appendnode(rgroup, node)
424                 self.render_groups.append(rgroup)
425
426         def render(self):
427                 if self.at_least_one(): return
428                
429                 random.seed(self.options.renderseed)
430
431                 self.set_gdadin_shapeids()
432                
433                 self.render_stack=[]
434                
435                 recursedata = { 'firstid' : None,
436                                 'mat'     : None,
437                                 'hue'     : 0.0,
438                                 'sat'     : 0.0,
439                                 'bri'     : 0.0,
440                                 'opa'     : 0.0,
441
442                                 'hf'      : -1,
443                                 'hc'      : -1,
444                                 'sf'      : -1,
445                                 'sc'      : -1,
446                                 'bf'      : -1,
447                                 'bc'      : -1,
448                                 'of'      : 0,
449                                 'oc'      : 1
450                               }
451
452                 def doit(id, node, shapename, rdata):
453                         self.compute_shape_bbox(id, node, shapename)
454                         self.add_rendergroup(id, node, shapename)
455                         self.render_shape(id, node, shapename, rdata)
456                 self.foreach_selected(doit, (recursedata,))
457                
458                 nshapes=0
459                 maxnshapes=int(self.options.rendermaxshapes)
460                 while len(self.render_stack):
461                         if maxnshapes and nshapes > maxnshapes:
462                                 inkex.debug('Maximum number of shapes reached ('+str(maxnshapes)+')')
463                                 break
464                         args=self.render_stack.pop(0)
465                         self._render_shape(*args)
466                         nshapes+=1
467        
468                 if nshapes:
469                         self.adjust_document_dim()
470
471         def render_shape(self, id, node, shapename, rdata):
472                 self.render_stack.append((id, node, shapename, rdata))
473        
474         def _render_shape(self, id, node, shapename, rdata):
475                 #Note: `shapename' is not used
476                 if rdata['firstid'] == None: rdata['firstid'] = id
477
478                 ## Affine transform
479                 nmat = T.parseTransform(node.get('transform'))
480                 if rdata['mat']:
481                         rdata['mat'] = T.composeTransform(rdata['mat'], nmat)
482                         T.writeTransformToNode(rdata['mat'], node)
483                 else:
484                         rdata['mat'] = nmat
485
486                 if self.check_shape_size(rdata['firstid'], rdata['mat']):
487                         return #ok, let's stop
488
489                 self.foreach_subshape(node, self.call_shape, (rdata,))
490        
491         def check_rdepth(self, parentnode):
492                 pid = self.svg_getattr(parentnode, 'id')
493                 try:
494                         rdepth_max = int(self.svg_getattr(parentnode, inkex.addNS('rdepth','gdadin')))
495                 except:
496                         #no depth to care about
497                         return 1 # go on
498
499                 if not pid in self.rdepth_counter:
500                         self.rdepth_counter[pid]=0
501                 if rdepth_max and self.rdepth_counter[pid] >= rdepth_max-1:
502                         return 0 # stop
503                 self.rdepth_counter[pid]+=1
504                 return 1 # go on
505
506         def call_shape(self, rootnode, parentnode, childsname, childsnode, rdata):
507                 '''Inside `rootnode', substitute the `parentnode' with
508                 `childsnode', but keep the transformation attribute'''
509                
510                 if not self.check_rdepth(parentnode):
511                         return
512
513                 nrdata = dict(rdata)
514
515                 childid = self.svg_getattr(childsnode, 'id')
516                 if childid in self.rdepth_counter:
517                         self.rdepth_counter[self.svg_getattr(childsnode, 'id')]+=1
518
519                 newchild = copy.deepcopy(childsnode)
520                 self.svg_setattr(newchild, 'id', self.newid(self.svg_prefixfromtag(childsnode.tag)))
521                 try:
522                         self.svg_remattr(newchild, inkex.addNS('shape','gdadin'))
523                         self.svg_remattr(newchild, 'transform')
524                 except:
525                         pass
526                 #inkex.debug('calling ' + childsname + ' by ' + parentnode.get('id') +' in '+rootnode.get('id')+' newchild is '+newchild.get('id'))
527
528                 ### Calculate the various transformations.
529                 pmat = T.parseTransform(self.svg_getattr(parentnode, 'transform'))
530                 nrdata['mat'] = T.composeTransform(nrdata['mat'], pmat)
531
532                 ## Hue, Saturation, Brightness, Opacity
533                 try:
534                         nrdata['hue']+=float(self.svg_getattr(parentnode, inkex.addNS('hue','gdadin')))
535                         nrdata['sat']+=float(self.svg_getattr(parentnode, inkex.addNS('sat','gdadin')))
536                         nrdata['bri']+=float(self.svg_getattr(parentnode, inkex.addNS('bri','gdadin')))
537                         nrdata['opa']+=float(self.svg_getattr(parentnode, inkex.addNS('opa','gdadin')))
538                         nrdata['hf'] = float(self.svg_getattr(parentnode, inkex.addNS('huefloor','gdadin')))
539                         nrdata['hc'] = float(self.svg_getattr(parentnode, inkex.addNS('hueceil','gdadin')))
540                         nrdata['sf'] = float(self.svg_getattr(parentnode, inkex.addNS('satfloor','gdadin')))
541                         nrdata['sc'] = float(self.svg_getattr(parentnode, inkex.addNS('satceil','gdadin')))
542                         nrdata['bf'] = float(self.svg_getattr(parentnode, inkex.addNS('brifloor','gdadin')))
543                         nrdata['bc'] = float(self.svg_getattr(parentnode, inkex.addNS('briceil','gdadin')))
544                         nrdata['of'] = float(self.svg_getattr(parentnode, inkex.addNS('opafloor','gdadin')))
545                         nrdata['oc'] = float(self.svg_getattr(parentnode, inkex.addNS('opaceil','gdadin')))
546                 except:
547                         pass
548                 ###
549
550                 if nrdata['hue'] or nrdata['sat'] or nrdata['bri'] or nrdata['opa']:
551                         self.apply_hsbo(newchild, parentnode, nrdata)
552                         hsbo = 1
553                 else:
554                         hsbo = 0
555
556                 # xlinkify
557                 if not hsbo:
558                         for sn in self.svg_subnodeslist(newchild):
559                                 if not self.svg_getattr(sn, inkex.addNS('shape','gdadin')):
560                                         self.xlinkify(newchild, sn)
561
562                 self.svg_appendnode(rootnode.getparent(), newchild)
563                 if self.svg_getattr(rootnode, 'id') != nrdata['firstid']:
564                         self.svg_remnode(parentnode)
565
566                 # Recurse!
567                 self.render_shape(None, newchild, None, nrdata)
568
569         def xlinkify(self, node, subnode):
570                  id = self.svg_getattr(subnode, 'id')
571                  self.svg_appendnewlink(node, id)
572                  self.svg_remnode(subnode)
573
574 if __name__ == '__main__':
575         e = Gdadin()
576         e.affect()
Note: See TracBrowser for help on using the browser.