diff options
| -rw-r--r-- | lib/tiles.py | 136 | ||||
| -rw-r--r-- | tiles/diamond_square.svg | 119 | ||||
| -rw-r--r-- | tiles/hexagon.svg | 116 |
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> |
