Code

Fixed const/non-const mismatch loop.
[inkscape.git] / share / extensions / edge3d.py
1 #!/usr/bin/env python 
2 '''
3 Copyright (C) 2007 Terry Brown, terry_n_brown@yahoo.com
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.
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.
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 '''
19 import inkex, simplepath, sys, copy
20 from math import degrees, atan2
22 class Edge3d(inkex.Effect):
23     def __init__(self):
24         inkex.Effect.__init__(self)
25         opts = [('-a', '--angle', 'float', 'angle', 45.0,
26                  'angle of illumination, clockwise, 45 = upper right'),
27                 ('-d', '--stddev', 'float', 'stddev', 5.0,
28                  'stdDeviation for Gaussian Blur'),
29                 ('-H', '--blurheight', 'float', 'blurheight', 2.0,
30                  'height for Gaussian Blur'),
31                 ('-W', '--blurwidth', 'float', 'blurwidth', 2.0,
32                  'width for Gaussian Blur'),
33                 ('-s', '--shades', 'int', 'shades', 2,
34                  'shades, 2 = black and white, 3 = black, grey, white, etc.'),
35                 ('-b', '--bw', 'inkbool', 'bw', False,
36                  'black and white, create only the fully black and white wedges'),
37                 ('-p', '--thick', 'float', 'thick', 10.,
38                  'stroke-width for path pieces'),
39                 ]
40         for o in opts:
41             self.OptionParser.add_option(o[0], o[1], action="store", type=o[2],
42                                          dest=o[3], default=o[4], help=o[5])
43         self.filtId = ''
45     def angleBetween(self, start, end, angle):
46         """Return true if angle (degrees, clockwise, 0 = up/north) is between
47            angles start and end"""
48         def f(x):
49             """Maybe add 360 to x"""
50             if x < 0: return x + 360.
51             return x
52         # rotate all inputs by start, => start = 0
53         a = f(f(angle) - f(start))
54         e = f(f(end) - f(start))
55         return a < e
57     def effectX(self):
58         # size of a wedge for shade i, wedges come in pairs
59         delta = 360. / self.options.shades / 2.
61         for shade in range(0, self.options.shades):
62             if self.options.bw and shade > 0 and shade < self.options.shades-1:
63                 continue
64             self.start = [self.options.angle - delta * (shade+1)]
65             self.end = [self.options.angle - delta * (shade)]
66             self.start.append( self.options.angle + delta * (shade) )
67             self.end.append( self.options.angle + delta * (shade+1) )
68             self.makeShade(float(shade)/float(self.options.shades-1))
70     def effect(self):
71         """Check each internode to see if it's in one of the wedges
72            for the current shade.  shade is a floating point 0-1 white-black"""
73         # size of a wedge for shade i, wedges come in pairs
74         delta = 360. / self.options.shades / 2.
75         for id, node in self.selected.iteritems():
76             if node.tag == inkex.addNS('path','svg'):
77                 d = node.get('d')
78                 p = simplepath.parsePath(d)
79                 g = None
80                 for shade in range(0, self.options.shades):
81                     if (self.options.bw and shade > 0 and
82                         shade < self.options.shades - 1):
83                         continue
84                     self.start = [self.options.angle - delta * (shade+1)]
85                     self.end = [self.options.angle - delta * (shade)]
86                     self.start.append( self.options.angle + delta * (shade) )
87                     self.end.append( self.options.angle + delta * (shade+1) )
88                     level=float(shade)/float(self.options.shades-1)
89                     last = []
90                     result = []
91                     for cmd,params in p:
92                         if cmd == 'Z':
93                             last = []
94                             continue
95                         if last:
96                             a = degrees(atan2(params[-2:][0] - last[0],
97                                               params[-2:][1] - last[1]))
98                             if (self.angleBetween(self.start[0], self.end[0], a) or
99                                 self.angleBetween(self.start[1], self.end[1], a)):
100                                 result.append(('M', last))
101                                 result.append((cmd, params))
102                         last = params[-2:]
103                     if result:
104                         if g is None:
105                             g = self.getGroup(node)
106                         nn = copy.deepcopy(node)
107                         nn.set('d',simplepath.formatPath(result))
108                         
109                         col = 255 - int(255. * level)
110                         a = 'fill:none;stroke:#%02x%02x%02x;stroke-opacity:1;stroke-width:10;%s' % ((col,)*3 + (self.filtId,))
111                         nn.set('style',a)
112                         g.append(nn)
113         
114     def getGroup(self, node):
115         defs = self.document.getroot().xpath('//svg:defs', namespaces=inkex.NSS)
116         if defs:
117             defs = defs[0]
118             # make a clipped group, clip with clone of original, clipped group
119             # include original and group of paths
120             clip = inkex.etree.SubElement(defs,inkex.addNS('clipPath','svg'))
121             clip.append(copy.deepcopy(node))
122             clipId = self.uniqueId('clipPath')
123             clip.set('id', clipId)
124             clipG = inkex.etree.SubElement(node.getparent(),inkex.addNS('g','svg'))
125             g = inkex.etree.SubElement(clipG,inkex.addNS('g','svg'))
126             clipG.set('clip-path', 'url(#'+clipId+')')
127             # make a blur filter reference by the style of each path
128             filt = inkex.etree.SubElement(defs,inkex.addNS('filter','svg'))
129             filtId = self.uniqueId('filter')
130             self.filtId = 'filter:url(#%s);' % filtId
131             for k, v in [('id', filtId), ('height', str(self.options.blurheight)),
132                          ('width', str(self.options.blurwidth)),
133                          ('x', '-0.5'), ('y', '-0.5')]:
134                 filt.set(k, v)
135             fe = inkex.etree.SubElement(filt,inkex.addNS('feGaussianBlur','svg'))
136             fe.set('stdDeviation', str(self.options.stddev))
137         else:
138             # can't find defs, just group paths
139             g = inkex.etree.SubElement(node.getparent(),inkex.addNS('g','svg'))
140             g.append(node)
142         return g
144 if __name__ == '__main__':
145     e = Edge3d()
146     e.affect()
149 # vim: expandtab shiftwidth=4 tabstop=8 softtabstop=4 fileencoding=utf-8 textwidth=99