summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--lib/tiles.py136
-rw-r--r--tiles/diamond_square.svg119
-rw-r--r--tiles/hexagon.svg116
3 files changed, 371 insertions, 0 deletions
diff --git a/lib/tiles.py b/lib/tiles.py
new file mode 100644
index 00000000..34097f67
--- /dev/null
+++ b/lib/tiles.py
@@ -0,0 +1,136 @@
+import inkex
+from math import ceil, floor
+from networkx import Graph
+import os
+from shapely.geometry import LineString
+from shapely.prepared import prep
+
+from .svg import apply_transforms
+from .utils import get_bundled_dir, guess_inkscape_config_path, Point
+from random import random
+
+
+class Tile:
+ def __init__(self, path):
+ self._load_tile(path)
+
+ def _load_tile(self, tile_path):
+ tile_svg = inkex.load_svg(tile_path)
+ self.name = self._get_name(tile_path)
+ self._load_paths(tile_svg)
+ self._load_dimensions(tile_svg)
+ self._load_buffer_size(tile_svg)
+ self._load_parallelogram(tile_svg)
+
+ def __repr__(self):
+ return f"Tile({self.name}, {self.shift0}, {self.shift1})"
+
+ __str__ = __repr__
+
+ def _get_name(self, tile_path):
+ return os.path.splitext(os.path.basename(tile_path))[0]
+
+ def _load_paths(self, tile_svg):
+ path_elements = tile_svg.findall('.//svg:path', namespaces=inkex.NSS)
+ self.tile = self._path_elements_to_line_strings(path_elements)
+ # self.center, ignore, ignore = self._get_center_and_dimensions(self.tile)
+
+ def _load_dimensions(self, tile_svg):
+ svg_element = tile_svg.getroot()
+ self.width = svg_element.viewport_width
+ self.height = svg_element.viewport_height
+
+ def _load_buffer_size(self, tile_svg):
+ circle_elements = tile_svg.findall('.//svg:circle', namespaces=inkex.NSS)
+ if circle_elements:
+ self.buffer_size = circle_elements[0].radius
+ else:
+ self.buffer_size = 0
+
+ def _load_parallelogram(self, tile_svg):
+ parallelogram_elements = tile_svg.findall(".//svg:*[@class='para']", namespaces=inkex.NSS)
+ if parallelogram_elements:
+ path_element = parallelogram_elements[0]
+ path = apply_transforms(path_element.get_path(), path_element)
+ subpaths = path.to_superpath()
+ subpath = subpaths[0]
+ points = [Point.from_tuple(p[1]) for p in subpath]
+ self.shift0 = points[1] - points[0]
+ self.shift1 = points[2] - points[1]
+ else:
+ self.shift0 = Point(self.width, 0)
+ self.shift1 = Point(0, self.height)
+
+ def _path_elements_to_line_strings(self, path_elements):
+ lines = []
+ for path_element in path_elements:
+ path = apply_transforms(path_element.get_path(), path_element)
+ for subpath in path.to_superpath():
+ # We only care about the endpoints of each subpath. They're
+ # supposed to be simple line segments.
+ lines.append([Point.from_tuple(subpath[0][1]), Point.from_tuple(subpath[-1][1])])
+
+ return lines
+
+ def _get_center_and_dimensions(self, shape):
+ min_x, min_y, max_x, max_y = shape.bounds
+ center = Point((max_x + min_x) / 2, (max_y + min_y) / 2)
+ width = max_x - min_x
+ height = max_y - min_y
+
+ return center, width, height
+
+ def translate_tile(self, shift):
+ translated_tile = []
+
+ for start, end in self.tile:
+ start += shift
+ end += shift
+ translated_tile.append((start.as_int().as_tuple(), end.as_int().as_tuple()))
+
+ return translated_tile
+
+ def to_graph(self, shape, only_inside=True, pad=True):
+ """Apply this tile to a shape, repeating as necessary.
+
+ Return value:
+ networkx.Graph with edges corresponding to lines in the pattern.
+ Each edge has an attribute 'line_string' with the LineString
+ representation of this edge.
+ """
+ shape_center, shape_width, shape_height = self._get_center_and_dimensions(shape)
+ shape_diagonal = (shape_width ** 2 + shape_height ** 2) ** 0.5
+ graph = Graph()
+
+ if pad:
+ shape = shape.buffer(-self.buffer_size)
+
+ prepared_shape = prep(shape)
+
+ tiles0 = ceil(shape_diagonal / self.shift0.length()) + 2
+ tiles1 = ceil(shape_diagonal / self.shift1.length()) + 2
+ for repeat0 in range(floor(-tiles0 / 2), ceil(tiles0 / 2)):
+ for repeat1 in range(floor(-tiles1 / 2), ceil(tiles1 / 2)):
+ shift0 = repeat0 * self.shift0 + shape_center
+ shift1 = repeat1 * self.shift1 + shape_center
+ this_tile = self.translate_tile(shift0 + shift1)
+ for line in this_tile:
+ line_string = LineString(line)
+ if not only_inside or prepared_shape.contains(line_string):
+ graph.add_edge(line[0], line[1], line_string=line_string, weight=random() + 0.1)
+
+ return graph
+
+
+def all_tile_paths():
+ return [os.path.join(guess_inkscape_config_path(), 'tiles'),
+ get_bundled_dir('tiles')]
+
+
+def all_tiles():
+ for tile_dir in all_tile_paths():
+ try:
+ for tile_file in sorted(os.listdir(tile_dir)):
+ yield Tile(os.path.join(tile_dir, tile_file))
+ except FileNotFoundError:
+ pass
diff --git a/tiles/diamond_square.svg b/tiles/diamond_square.svg
new file mode 100644
index 00000000..aea71d3a
--- /dev/null
+++ b/tiles/diamond_square.svg
@@ -0,0 +1,119 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+ width="15mm"
+ height="15mm"
+ viewBox="0 0 56.692914 56.692914"
+ id="svg8375"
+ version="1.1"
+ inkscape:version="1.2 (1:1.2.1+202207142221+cd75a1ee6d)"
+ sodipodi:docname="diamond_square.svg"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:dc="http://purl.org/dc/elements/1.1/">
+ <defs
+ id="defs8377" />
+ <sodipodi:namedview
+ id="base"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ inkscape:pageopacity="0.0"
+ inkscape:pageshadow="2"
+ inkscape:zoom="6.5412668"
+ inkscape:cx="45.021861"
+ inkscape:cy="42.499413"
+ inkscape:document-units="mm"
+ inkscape:current-layer="layer1"
+ showgrid="false"
+ units="mm"
+ inkscape:window-width="1366"
+ inkscape:window-height="705"
+ inkscape:window-x="-4"
+ inkscape:window-y="-4"
+ inkscape:window-maximized="1"
+ inkscape:showpageshadow="2"
+ inkscape:pagecheckerboard="0"
+ inkscape:deskcolor="#d1d1d1" />
+ <metadata
+ id="metadata8380">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <g
+ inkscape:label="Layer 1"
+ inkscape:groupmode="layer"
+ id="layer1">
+ <path
+ style="fill:#000000;stroke:#30ff00;stroke-width:1.13386;stop-color:#000000"
+ d="M 0,0 H 18.897638"
+ id="path315"
+ sodipodi:nodetypes="cc" />
+ <path
+ style="fill:#000000;stroke:#30ff00;stroke-width:1.13386;stop-color:#000000"
+ d="M 0,18.897638 H 18.897638"
+ id="path315-3"
+ sodipodi:nodetypes="cc" />
+ <path
+ style="fill:#000000;stroke:#30ff00;stroke-width:1.13386;stop-color:#000000"
+ d="M 18.897638,0 V 18.897638"
+ id="path1017"
+ sodipodi:nodetypes="cc" />
+ <path
+ style="fill:#000000;stroke:#30ff00;stroke-width:1.13386;stop-color:#000000"
+ d="M 0,0 V 18.897638"
+ id="path1095"
+ sodipodi:nodetypes="cc" />
+ <path
+ style="fill:#000000;stroke:#30ff00;stroke-width:1.13386;stop-color:#000000"
+ d="M 28.346457,28.346457 H 47.244095"
+ id="path315-6"
+ sodipodi:nodetypes="cc" />
+ <path
+ style="fill:#000000;stroke:#30ff00;stroke-width:1.13386;stop-color:#000000"
+ d="M 28.346457,47.244095 H 47.244095"
+ id="path315-3-7"
+ sodipodi:nodetypes="cc" />
+ <path
+ style="fill:#000000;stroke:#30ff00;stroke-width:1.13386;stop-color:#000000"
+ d="M 47.244095,28.346457 V 47.244095"
+ id="path1017-5"
+ sodipodi:nodetypes="cc" />
+ <path
+ style="fill:#000000;stroke:#30ff00;stroke-width:1.13386;stop-color:#000000"
+ d="M 28.346457,28.346457 V 47.244095"
+ id="path1095-3"
+ sodipodi:nodetypes="cc" />
+ <path
+ style="fill:#000000;stroke:#30ff00;stroke-width:1.13386;stop-color:#000000"
+ d="m 18.897638,18.897638 9.448819,9.448819"
+ id="path1097"
+ sodipodi:nodetypes="cc" />
+ <path
+ style="fill:#000000;stroke:#30ff00;stroke-width:1.13386;stop-color:#000000"
+ d="m 28.346457,47.244095 -9.448819,9.448819"
+ id="path1192"
+ sodipodi:nodetypes="cc" />
+ <path
+ style="fill:#000000;stroke:#30ff00;stroke-width:1.13386;stop-color:#000000"
+ d="m 47.244095,47.244095 9.448819,9.448819"
+ id="path1539"
+ sodipodi:nodetypes="cc" />
+ <path
+ style="fill:#000000;stroke:#30ff00;stroke-width:1.13386;stop-color:#000000"
+ d="m 47.244095,28.346457 9.448819,-9.448819"
+ id="path1541"
+ sodipodi:nodetypes="cc" />
+ </g>
+</svg>
diff --git a/tiles/hexagon.svg b/tiles/hexagon.svg
new file mode 100644
index 00000000..ec6faf13
--- /dev/null
+++ b/tiles/hexagon.svg
@@ -0,0 +1,116 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+ width="30mm"
+ height="34.6410162mm"
+ viewBox="0 0 113.38582677 130.9266754"
+ id="svg8375"
+ version="1.1"
+ inkscape:version="1.2 (1:1.2.1+202207142221+cd75a1ee6d)"
+ sodipodi:docname="hexagon.svg"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:dc="http://purl.org/dc/elements/1.1/">
+ <defs
+ id="defs8377" />
+ <sodipodi:namedview
+ id="base"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ inkscape:pageopacity="0.0"
+ inkscape:pageshadow="2"
+ inkscape:zoom="5.682637"
+ inkscape:cx="40.562155"
+ inkscape:cy="63.526845"
+ inkscape:document-units="mm"
+ inkscape:current-layer="layer1"
+ showgrid="false"
+ units="mm"
+ inkscape:window-width="1366"
+ inkscape:window-height="705"
+ inkscape:window-x="-4"
+ inkscape:window-y="-4"
+ inkscape:window-maximized="1"
+ inkscape:showpageshadow="2"
+ inkscape:pagecheckerboard="0"
+ inkscape:deskcolor="#d1d1d1" />
+ <metadata
+ id="metadata8380">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <g
+ inkscape:label="Layer 1"
+ inkscape:groupmode="layer"
+ id="layer1"
+ transform="translate(43.621862,47.396329)">
+ <path
+ style="display:inline;vector-effect:none;fill:none;fill-rule:evenodd;stroke:#0000ff;stroke-width:1.03832"
+ d="M -24.724225,18.068867 -43.621862,-14.665621"
+ id="path463" />
+ <path
+ style="display:inline;vector-effect:none;fill:none;fill-rule:evenodd;stroke:#0000ff;stroke-width:1.03832"
+ d="M 13.07105,18.068867 H -24.724225"
+ id="path461" />
+ <path
+ style="display:inline;vector-effect:none;fill:none;fill-rule:evenodd;stroke:#0000ff;stroke-width:1.03832"
+ d="M 31.968688,-14.665621 13.07105,18.068867"
+ id="path58963" />
+ <path
+ style="display:inline;vector-effect:none;fill:none;fill-rule:evenodd;stroke:#0000ff;stroke-width:1.03832"
+ d="m -24.724225,-47.396329 -18.897637,32.730708"
+ id="path457" />
+ <path
+ style="display:inline;vector-effect:none;fill:none;fill-rule:evenodd;stroke:#0000ff;stroke-width:1.03832"
+ d="M 13.07105,-47.396329 H -24.724225"
+ id="path455" />
+ <path
+ style="display:inline;vector-effect:none;fill:none;fill-rule:evenodd;stroke:#0000ff;stroke-width:1.03832"
+ d="M 31.968688,-14.665621 13.07105,-47.396329"
+ id="path453"
+ sodipodi:nodetypes="cc" />
+ <path
+ style="display:inline;vector-effect:none;fill:none;fill-rule:evenodd;stroke:#0000ff;stroke-width:1.03832"
+ d="M 69.763962,-14.665621 H 31.968688"
+ id="path58961"
+ sodipodi:nodetypes="cc" />
+ <path
+ style="display:inline;vector-effect:none;fill:none;fill-rule:evenodd;stroke:#0000ff;stroke-width:1.03832"
+ d="M 31.968688,50.799575 H 69.763962"
+ id="path449" />
+ <path
+ style="display:inline;vector-effect:none;fill:none;fill-rule:evenodd;stroke:#0000ff;stroke-width:1.03832"
+ d="M 13.07105,18.068867 31.968688,50.799575"
+ id="path58542" />
+ <path
+ style="display:inline;vector-effect:none;fill:none;fill-rule:evenodd;stroke:#0000ff;stroke-width:1.03832"
+ d="m -43.621862,50.799575 18.897637,32.730707"
+ id="path445" />
+ <path
+ style="display:inline;vector-effect:none;fill:none;fill-rule:evenodd;stroke:#0000ff;stroke-width:1.03832"
+ d="M -24.724225,18.068867 -43.621862,50.799575"
+ id="path58987" />
+ <path
+ style="display:inline;vector-effect:none;fill:none;fill-rule:evenodd;stroke:#0000ff;stroke-width:1.03832"
+ d="M 13.07105,83.530282 31.968688,50.799575"
+ id="path58981" />
+ <circle
+ style="opacity:1;vector-effect:none;fill:none;fill-opacity:1;fill-rule:evenodd;stroke:#ff0000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+ id="path1901"
+ cx="-24.811083"
+ cy="-47.329594"
+ r="18.897638" />
+ </g>
+</svg>