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
23
import random, inkex, simplestyle, gettext, voronoi
27
from subprocess import Popen, PIPE
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))
33
def clip_line(x1, y1, x2, y2, w, h):
39
y1 = (y1*x2 - y2*x1)/(x2 - x1)
42
y2 = (y1*x2 - y2*x1)/(x2 - x1)
45
y1 = y1 + (w - x1)*(y2 - y1)/(x2 - x1)
48
y2 = y1 + (w - x1)*(y2 - y1)/(x2 - x1)
54
if x1 == x2 and y1 == y2:
57
x1 = (x1*y2 - x2*y1)/(y2 - y1)
60
x2 = (x1*y2 - x2*y1)/(y2 - y1)
63
x1 = x1 + (h - y1)*(x2 - x1)/(y2 - y1)
66
x2 = x1 + (h - y1)*(x2 - x1)/(y2 - y1)
68
return [x1, y1, x2, y2]
70
class Pattern(inkex.Effect):
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)")
83
if not self.options.ids:
84
inkex.errormsg(_("Please select an object"))
86
q = {'x':0,'y':0,'width':0,'height':0} # query the bounding box of ids[0]
87
for query in q.keys():
88
p = Popen('inkscape --query-%s --query-id=%s "%s"' % (query, self.options.ids[0], self.args[-1]), shell=True, stdout=PIPE, stderr=PIPE)
90
q[query] = float(p.stdout.read())
91
defs = self.xpathSingle('/svg:svg//svg:defs')
92
pattern = inkex.etree.SubElement(defs ,inkex.addNS('pattern','svg'))
93
pattern.set('id', 'Voronoi' + str(random.randint(1, 9999)))
94
pattern.set('width', str(q['width']))
95
pattern.set('height', str(q['height']))
96
pattern.set('patternTransform', 'translate(%s,%s)' % (q['x'], q['y']))
97
pattern.set('patternUnits', 'userSpaceOnUse')
99
# generate random pattern of points
100
c = voronoi.Context()
102
b = float(self.options.border) # width of border
103
for i in range(int(q['width']*q['height']/self.options.size/self.options.size)):
104
x = random.random()*q['width']
105
y = random.random()*q['height']
106
if b > 0: # duplicate border area
107
pts.append(voronoi.Site(x, y))
109
pts.append(voronoi.Site(x + q['width'], y))
111
pts.append(voronoi.Site(x + q['width'], y + q['height']))
112
if y > q['height'] - b:
113
pts.append(voronoi.Site(x + q['width'], y - q['height']))
114
if x > q['width'] - b:
115
pts.append(voronoi.Site(x - q['width'], y))
117
pts.append(voronoi.Site(x - q['width'], y + q['height']))
118
if y > q['height'] - b:
119
pts.append(voronoi.Site(x - q['width'], y - q['height']))
121
pts.append(voronoi.Site(x, y + q['height']))
122
if y > q['height'] - b:
123
pts.append(voronoi.Site(x, y - q['height']))
124
elif x > -b and y > -b and x < q['width'] + b and y < q['height'] + b:
125
pts.append(voronoi.Site(x, y)) # leave border area blank
126
# dot = inkex.etree.SubElement(pattern, inkex.addNS('rect','svg'))
127
# dot.set('x', str(x-1))
128
# dot.set('y', str(y-1))
129
# dot.set('width', '2')
130
# dot.set('height', '2')
132
inkex.errormsg("Please choose a larger object, or smaller cell size")
135
# plot Voronoi diagram
136
sl = voronoi.SiteList(pts)
137
voronoi.voronoi(sl, c)
139
if edge[1] >= 0 and edge[2] >= 0: # two vertices
140
[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'])
141
elif edge[1] >= 0: # only one vertex
142
if c.lines[edge[0]][1] == 0: # vertical line
143
xtemp = c.lines[edge[0]][2]/c.lines[edge[0]][0]
144
if c.vertices[edge[1]][1] > q['height']/2:
150
ytemp = (c.lines[edge[0]][2] - q['width']*c.lines[edge[0]][0])/c.lines[edge[0]][1]
151
[x1, y1, x2, y2] = clip_line(c.vertices[edge[1]][0], c.vertices[edge[1]][1], xtemp, ytemp, q['width'], q['height'])
152
elif edge[2] >= 0: # only one vertex
153
if c.lines[edge[0]][1] == 0: # vertical line
154
xtemp = c.lines[edge[0]][2]/c.lines[edge[0]][0]
155
if c.vertices[edge[2]][1] > q['height']/2:
161
ytemp = c.lines[edge[0]][2]/c.lines[edge[0]][1]
162
[x1, y1, x2, y2] = clip_line(xtemp, ytemp, c.vertices[edge[2]][0], c.vertices[edge[2]][1], q['width'], q['height'])
163
if x1 or x2 or y1 or y2:
164
path = 'M %f,%f %f,%f' % (x1, y1, x2, y2)
165
attribs = {'d': path, 'style': 'stroke:#000000'}
166
inkex.etree.SubElement(pattern, inkex.addNS('path', 'svg'), attribs)
168
# link selected object to pattern
169
obj = self.selected[self.options.ids[0]]
171
if obj.attrib.has_key('style'):
172
style = simplestyle.parseStyle(obj.attrib['style'])
173
style['fill'] = 'url(#%s)' % pattern.get('id')
174
obj.attrib['style'] = simplestyle.formatStyle(style)
175
if obj.tag == inkex.addNS('g', 'svg'):
178
if node.attrib.has_key('style'):
179
style = simplestyle.parseStyle(node.attrib['style'])
180
style['fill'] = 'url(#%s)' % pattern.get('id')
181
node.attrib['style'] = simplestyle.formatStyle(style)
183
if __name__ == '__main__':
187
# vim: expandtab shiftwidth=4 tabstop=8 softtabstop=4 encoding=utf-8 textwidth=99