summaryrefslogtreecommitdiffstats
path: root/share/extensions/generate_voronoi.py
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 18:24:48 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 18:24:48 +0000
commitcca66b9ec4e494c1d919bff0f71a820d8afab1fa (patch)
tree146f39ded1c938019e1ed42d30923c2ac9e86789 /share/extensions/generate_voronoi.py
parentInitial commit. (diff)
downloadinkscape-cca66b9ec4e494c1d919bff0f71a820d8afab1fa.tar.xz
inkscape-cca66b9ec4e494c1d919bff0f71a820d8afab1fa.zip
Adding upstream version 1.2.2.upstream/1.2.2upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'share/extensions/generate_voronoi.py')
-rwxr-xr-xshare/extensions/generate_voronoi.py203
1 files changed, 203 insertions, 0 deletions
diff --git a/share/extensions/generate_voronoi.py b/share/extensions/generate_voronoi.py
new file mode 100755
index 0000000..317d07b
--- /dev/null
+++ b/share/extensions/generate_voronoi.py
@@ -0,0 +1,203 @@
+#!/usr/bin/env python
+# coding=utf-8
+#
+# Copyright (C) 2010 Alvin Penner, penner@vaxxine.com
+#
+# - Voronoi Diagram algorithm and C code by Steven Fortune, 1987, http://ect.bell-labs.com/who/sjf/
+# - Python translation to file voronoi.py by Bill Simons, 2005, http://www.oxfish.com/
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+#
+
+import random
+
+import inkex
+from inkex import PathElement, Pattern
+
+import voronoi
+
+
+def clip_line(x1, y1, x2, y2, w, h):
+ if x1 < 0 and x2 < 0:
+ return [0, 0, 0, 0]
+ if x1 > w and x2 > w:
+ return [0, 0, 0, 0]
+ if x1 < 0:
+ y1 = (y1 * x2 - y2 * x1) / (x2 - x1)
+ x1 = 0
+ if x2 < 0:
+ y2 = (y1 * x2 - y2 * x1) / (x2 - x1)
+ x2 = 0
+ if x1 > w:
+ y1 = y1 + (w - x1) * (y2 - y1) / (x2 - x1)
+ x1 = w
+ if x2 > w:
+ y2 = y1 + (w - x1) * (y2 - y1) / (x2 - x1)
+ x2 = w
+ if y1 < 0 and y2 < 0:
+ return [0, 0, 0, 0]
+ if y1 > h and y2 > h:
+ return [0, 0, 0, 0]
+ if x1 == x2 and y1 == y2:
+ return [0, 0, 0, 0]
+ if y1 < 0:
+ x1 = (x1 * y2 - x2 * y1) / (y2 - y1)
+ y1 = 0
+ if y2 < 0:
+ x2 = (x1 * y2 - x2 * y1) / (y2 - y1)
+ y2 = 0
+ if y1 > h:
+ x1 = x1 + (h - y1) * (x2 - x1) / (y2 - y1)
+ y1 = h
+ if y2 > h:
+ x2 = x1 + (h - y1) * (x2 - x1) / (y2 - y1)
+ y2 = h
+ return [x1, y1, x2, y2]
+
+
+class GenerateVoronoi(inkex.EffectExtension):
+ def add_arguments(self, pars):
+ pars.add_argument("--tab")
+ pars.add_argument(
+ "--size", type=int, default=10, help="Average size of cell (px)"
+ )
+ pars.add_argument("--border", type=int, default=0, help="Size of Border (px)")
+
+ def effect(self):
+ if not self.options.ids:
+ return inkex.errormsg(_("Please select an object"))
+ scale = self.svg.unittouu("1px") # convert to document units
+ self.options.size *= scale
+ self.options.border *= scale
+ obj = self.svg.selection.first()
+ bbox = obj.bounding_box()
+ mat = obj.composed_transform().matrix
+ pattern = self.svg.defs.add(Pattern())
+ pattern.set_random_id("Voronoi")
+ pattern.set("width", str(bbox.width))
+ pattern.set("height", str(bbox.height))
+ pattern.set("patternUnits", "userSpaceOnUse")
+ pattern.patternTransform.add_translate(
+ bbox.left - mat[0][2], bbox.top - mat[1][2]
+ )
+
+ # generate random pattern of points
+ c = voronoi.Context()
+ pts = []
+ b = float(self.options.border) # width of border
+ for i in range(
+ int(bbox.width * bbox.height / self.options.size / self.options.size)
+ ):
+ x = random.random() * bbox.width
+ y = random.random() * bbox.height
+ if b > 0: # duplicate border area
+ pts.append(voronoi.Site(x, y))
+ if x < b:
+ pts.append(voronoi.Site(x + bbox.width, y))
+ if y < b:
+ pts.append(voronoi.Site(x + bbox.width, y + bbox.height))
+ if y > bbox.height - b:
+ pts.append(voronoi.Site(x + bbox.width, y - bbox.height))
+ if x > bbox.width - b:
+ pts.append(voronoi.Site(x - bbox.width, y))
+ if y < b:
+ pts.append(voronoi.Site(x - bbox.width, y + bbox.height))
+ if y > bbox.height - b:
+ pts.append(voronoi.Site(x - bbox.width, y - bbox.height))
+ if y < b:
+ pts.append(voronoi.Site(x, y + bbox.height))
+ if y > bbox.height - b:
+ pts.append(voronoi.Site(x, y - bbox.height))
+ elif x > -b and y > -b and x < bbox.width + b and y < bbox.height + b:
+ pts.append(voronoi.Site(x, y)) # leave border area blank
+ # dot = pattern.add(inkex.Rectangle())
+ # dot.set('x', str(x-1))
+ # dot.set('y', str(y-1))
+ # dot.set('width', '2')
+ # dot.set('height', '2')
+ if len(pts) < 3:
+ return inkex.errormsg("Please choose a larger object, or smaller cell size")
+
+ # plot Voronoi diagram
+ sl = voronoi.SiteList(pts)
+ voronoi.voronoi(sl, c)
+ path = ""
+ for edge in c.edges:
+ if edge[1] >= 0 and edge[2] >= 0: # two vertices
+ [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],
+ bbox.width,
+ bbox.height,
+ )
+ elif edge[1] >= 0: # only one vertex
+ if c.lines[edge[0]][1] == 0: # vertical line
+ xtemp = c.lines[edge[0]][2] / c.lines[edge[0]][0]
+ if c.vertices[edge[1]][1] > bbox.height / 2:
+ ytemp = bbox.height
+ else:
+ ytemp = 0
+ else:
+ xtemp = bbox.width
+ ytemp = (
+ c.lines[edge[0]][2] - bbox.width * c.lines[edge[0]][0]
+ ) / c.lines[edge[0]][1]
+ [x1, y1, x2, y2] = clip_line(
+ c.vertices[edge[1]][0],
+ c.vertices[edge[1]][1],
+ xtemp,
+ ytemp,
+ bbox.width,
+ bbox.height,
+ )
+ elif edge[2] >= 0: # only one vertex
+ if edge[0] >= len(c.lines):
+ xtemp = 0
+ ytemp = 0
+ elif c.lines[edge[0]][1] == 0: # vertical line
+ xtemp = c.lines[edge[0]][2] / c.lines[edge[0]][0]
+ if c.vertices[edge[2]][1] > bbox.height / 2:
+ ytemp = bbox.height
+ else:
+ ytemp = 0
+ else:
+ xtemp = 0
+ ytemp = c.lines[edge[0]][2] / c.lines[edge[0]][1]
+ [x1, y1, x2, y2] = clip_line(
+ xtemp,
+ ytemp,
+ c.vertices[edge[2]][0],
+ c.vertices[edge[2]][1],
+ bbox.width,
+ bbox.height,
+ )
+ if x1 or x2 or y1 or y2:
+ path += "M %.3f,%.3f %.3f,%.3f " % (x1, y1, x2, y2)
+
+ patternstyle = {"stroke": "#000000", "stroke-width": str(scale)}
+ attribs = {"d": path, "style": str(inkex.Style(patternstyle))}
+ pattern.append(PathElement(**attribs))
+
+ # link selected object to pattern
+ obj.style["fill"] = pattern
+ if isinstance(obj, inkex.Group):
+ for node in obj:
+ node.style["fill"] = pattern
+
+
+if __name__ == "__main__":
+ GenerateVoronoi().run()