Code

Extensions. XAML export improvements.
[inkscape.git] / share / extensions / generate_voronoi.py
1 #!/usr/bin/env python
2 """
3 Copyright (C) 2010 Alvin Penner, penner@vaxxine.com
5 - Voronoi Diagram algorithm and C code by Steven Fortune, 1987, http://ect.bell-labs.com/who/sjf/
6 - Python translation to file voronoi.py by Bill Simons, 2005, http://www.oxfish.com/
8 This program is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 2 of the License, or
11 (at your option) any later version.
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16 GNU General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with this program; if not, write to the Free Software
20 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
22 """
23 import random, inkex, simplestyle, gettext, voronoi
24 _ = gettext.gettext
26 try:
27     from subprocess import Popen, PIPE
28 except:
29     inkex.errormsg(_("Failed to import the subprocess module. Please report this as a bug at : https://bugs.launchpad.net/inkscape."))
30     inkex.errormsg("Python version is : " + str(inkex.sys.version_info))
31     exit()
33 def clip_line(x1, y1, x2, y2, w, h):
34     if x1 < 0 and x2 < 0:
35         return [0, 0, 0, 0]
36     if x1 > w and x2 > w:
37         return [0, 0, 0, 0]
38     if x1 < 0:
39         y1 = (y1*x2 - y2*x1)/(x2 - x1)
40         x1 = 0
41     if x2 < 0:
42         y2 = (y1*x2 - y2*x1)/(x2 - x1)
43         x2 = 0
44     if x1 > w:
45         y1 = y1 + (w - x1)*(y2 - y1)/(x2 - x1)
46         x1 = w
47     if x2 > w:
48         y2 = y1 + (w - x1)*(y2 - y1)/(x2 - x1)
49         x2 = w
50     if y1 < 0 and y2 < 0:
51         return [0, 0, 0, 0]
52     if y1 > h and y2 > h:
53         return [0, 0, 0, 0]
54     if x1 == x2 and y1 == y2:
55         return [0, 0, 0, 0]
56     if y1 < 0:
57         x1 = (x1*y2 - x2*y1)/(y2 - y1)
58         y1 = 0
59     if y2 < 0:
60         x2 = (x1*y2 - x2*y1)/(y2 - y1)
61         y2 = 0
62     if y1 > h:
63         x1 = x1 + (h - y1)*(x2 - x1)/(y2 - y1)
64         y1 = h
65     if y2 > h:
66         x2 = x1 + (h - y1)*(x2 - x1)/(y2 - y1)
67         y2 = h
68     return [x1, y1, x2, y2]
70 class Pattern(inkex.Effect):
71     def __init__(self):
72         inkex.Effect.__init__(self)
73         self.OptionParser.add_option("--size",
74                         action="store", type="int", 
75                         dest="size", default=10,
76                         help="Average size of cell (px)")
77         self.OptionParser.add_option("--border",
78                         action="store", type="int", 
79                         dest="border", default=0,
80                         help="Size of Border (px)")
81         self.OptionParser.add_option("--tab",
82                         action="store", type="string",
83                         dest="tab",
84                         help="The selected UI-tab when OK was pressed")
86     def effect(self):
87         if not self.options.ids:
88             inkex.errormsg(_("Please select an object"))
89             exit()
90         q = {'x':0,'y':0,'width':0,'height':0}  # query the bounding box of ids[0]
91         for query in q.keys():
92             p = Popen('inkscape --query-%s --query-id=%s "%s"' % (query, self.options.ids[0], self.args[-1]), shell=True, stdout=PIPE, stderr=PIPE)
93             rc = p.wait()
94             q[query] = float(p.stdout.read())
95         defs = self.xpathSingle('/svg:svg//svg:defs')
96         pattern = inkex.etree.SubElement(defs ,inkex.addNS('pattern','svg'))
97         pattern.set('id', 'Voronoi' + str(random.randint(1, 9999)))
98         pattern.set('width', str(q['width']))
99         pattern.set('height', str(q['height']))
100         pattern.set('patternTransform', 'translate(%s,%s)' % (q['x'], q['y']))
101         pattern.set('patternUnits', 'userSpaceOnUse')
103         # generate random pattern of points
104         c = voronoi.Context()
105         pts = []
106         b = float(self.options.border)          # width of border
107         for i in range(int(q['width']*q['height']/self.options.size/self.options.size)):
108             x = random.random()*q['width']
109             y = random.random()*q['height']
110             if b > 0:                           # duplicate border area
111                 pts.append(voronoi.Site(x, y))
112                 if x < b:
113                     pts.append(voronoi.Site(x + q['width'], y))
114                     if y < b:
115                         pts.append(voronoi.Site(x + q['width'], y + q['height']))
116                     if y > q['height'] - b:
117                         pts.append(voronoi.Site(x + q['width'], y - q['height']))
118                 if x > q['width'] - b:
119                     pts.append(voronoi.Site(x - q['width'], y))
120                     if y < b:
121                         pts.append(voronoi.Site(x - q['width'], y + q['height']))
122                     if y > q['height'] - b:
123                         pts.append(voronoi.Site(x - q['width'], y - q['height']))
124                 if y < b:
125                     pts.append(voronoi.Site(x, y + q['height']))
126                 if y > q['height'] - b:
127                     pts.append(voronoi.Site(x, y - q['height']))
128             elif x > -b and y > -b and x < q['width'] + b and y < q['height'] + b:
129                 pts.append(voronoi.Site(x, y))  # leave border area blank
130             # dot = inkex.etree.SubElement(pattern, inkex.addNS('rect','svg'))
131             # dot.set('x', str(x-1))
132             # dot.set('y', str(y-1))
133             # dot.set('width', '2')
134             # dot.set('height', '2')
135         if len(pts) < 3:
136             inkex.errormsg("Please choose a larger object, or smaller cell size")
137             exit()
139         # plot Voronoi diagram
140         sl = voronoi.SiteList(pts)
141         voronoi.voronoi(sl, c)
142         path = ""
143         for edge in c.edges:
144             if edge[1] >= 0 and edge[2] >= 0:       # two vertices
145                 [x1, y1, x2, y2] = clip_line(c.vertices[edge[1]][0], c.vertices[edge[1]][1], c.vertices[edge[2]][0], c.vertices[edge[2]][1], q['width'], q['height'])
146             elif edge[1] >= 0:                      # only one vertex
147                 if c.lines[edge[0]][1] == 0:        # vertical line
148                     xtemp = c.lines[edge[0]][2]/c.lines[edge[0]][0]
149                     if c.vertices[edge[1]][1] > q['height']/2:
150                         ytemp = q['height']
151                     else:
152                         ytemp = 0
153                 else:
154                     xtemp = q['width']
155                     ytemp = (c.lines[edge[0]][2] - q['width']*c.lines[edge[0]][0])/c.lines[edge[0]][1]
156                 [x1, y1, x2, y2] = clip_line(c.vertices[edge[1]][0], c.vertices[edge[1]][1], xtemp, ytemp, q['width'], q['height'])
157             elif edge[2] >= 0:                      # only one vertex
158                 if c.lines[edge[0]][1] == 0:        # vertical line
159                     xtemp = c.lines[edge[0]][2]/c.lines[edge[0]][0]
160                     if c.vertices[edge[2]][1] > q['height']/2:
161                         ytemp = q['height']
162                     else:
163                         ytemp = 0
164                 else:
165                     xtemp = 0
166                     ytemp = c.lines[edge[0]][2]/c.lines[edge[0]][1]
167                 [x1, y1, x2, y2] = clip_line(xtemp, ytemp, c.vertices[edge[2]][0], c.vertices[edge[2]][1], q['width'], q['height'])
168             if x1 or x2 or y1 or y2:
169                 path += 'M %.3f,%.3f %.3f,%.3f ' % (x1, y1, x2, y2)
171         attribs = {'d': path, 'style': 'stroke:#000000'}
172         inkex.etree.SubElement(pattern, inkex.addNS('path', 'svg'), attribs)
174         # link selected object to pattern
175         obj = self.selected[self.options.ids[0]]
176         style = {}
177         if obj.attrib.has_key('style'):
178             style = simplestyle.parseStyle(obj.attrib['style'])
179         style['fill'] = 'url(#%s)' % pattern.get('id')
180         obj.attrib['style'] = simplestyle.formatStyle(style)
181         if obj.tag == inkex.addNS('g', 'svg'):
182             for node in obj:
183                 style = {}
184                 if node.attrib.has_key('style'):
185                     style = simplestyle.parseStyle(node.attrib['style'])
186                 style['fill'] = 'url(#%s)' % pattern.get('id')
187                 node.attrib['style'] = simplestyle.formatStyle(style)
189 if __name__ == '__main__':
190     e = Pattern()
191     e.affect()
193 # vim: expandtab shiftwidth=4 tabstop=8 softtabstop=4 fileencoding=utf-8 textwidth=99