From 3bb6439d7b4e57e83c3abc0b9c0deebca5163fde Mon Sep 17 00:00:00 2001 From: Sergey Vartanov Date: Thu, 10 Jun 2021 00:07:52 +0300 Subject: [PATCH] Add icon collection class. --- roentgen.py | 15 +-- roentgen/grid.py | 223 +++++++++++++++++++++++---------------------- roentgen/icon.py | 4 +- test/test_icons.py | 17 +++- 4 files changed, 138 insertions(+), 121 deletions(-) diff --git a/roentgen.py b/roentgen.py index 9656963..304c929 100644 --- a/roentgen.py +++ b/roentgen.py @@ -14,7 +14,7 @@ import svgwrite from roentgen.constructor import Constructor from roentgen.flinger import Flinger -from roentgen.grid import draw_all_icons +from roentgen.grid import IconCollection from roentgen.icon import ShapeExtractor from roentgen.mapper import ( AUTHOR_MODE, CREATION_TIME_MODE, ICONS_FILE_NAME, Painter, TAGS_FILE_NAME, @@ -158,22 +158,25 @@ def draw_element(target: str, tags_description: str): svg.write(open("test_icon.svg", "w+")) -def draw_grid() -> None: +def draw_icons() -> None: """ - Draw all possible icon shapes combinations as grid. + Draw all possible icon shapes combinations as grid in one SVG file and as + individual SVG files. """ os.makedirs("icon_set", exist_ok=True) scheme: Scheme = Scheme(Path("scheme/default.yml")) extractor: ShapeExtractor = ShapeExtractor( Path("icons/icons.svg"), Path("icons/config.json") ) - draw_all_icons(scheme, extractor, "icon_grid.svg", "icon_set") + collection: IconCollection = IconCollection.from_scheme(scheme, extractor) + collection.draw_grid(Path("icon_grid.svg")) + collection.draw_icons(Path("icon_set")) if __name__ == "__main__": if len(sys.argv) == 3 and sys.argv[1] in ["node", "way", "area"]: draw_element(sys.argv[1], sys.argv[2]) - elif len(sys.argv) == 2 and sys.argv[1] == "grid": - draw_grid() + elif len(sys.argv) == 2 and sys.argv[1] == "icons": + draw_icons() else: main(sys.argv) diff --git a/roentgen/grid.py b/roentgen/grid.py index 79fa47c..3dd56b8 100644 --- a/roentgen/grid.py +++ b/roentgen/grid.py @@ -1,7 +1,7 @@ """ Icon grid drawing. """ -from os.path import join +from pathlib import Path from typing import List, Set import numpy as np @@ -15,125 +15,130 @@ __author__ = "Sergey Vartanov" __email__ = "me@enzet.ru" -def draw_all_icons( - scheme: Scheme, extractor: ShapeExtractor, - output_file_name: str, output_directory: str, columns: int = 16, - step: float = 24, background_color: Color = Color("white"), - color: Color = Color("black") -) -> None: - """ - Draw all possible icon combinations in grid. +class IconCollection: + def __init__(self, icons): + self.icons: List[Icon] = icons - :param scheme: tag specification - :param extractor: shape extractor for icon creation - :param output_file_name: output SVG file name for icon grid - :param output_directory: path to the directory to store individual SVG files - for icons - :param columns: the number of columns in grid - :param step: horizontal and vertical distance between icons - :param background_color: background color - :param color: icon color - """ - icons: List[Icon] = [] - - def add() -> None: + @classmethod + def from_scheme( + cls, + scheme: Scheme, + extractor: ShapeExtractor, + background_color: Color = Color("white"), + color: Color = Color("black") + ) -> "IconCollection": """ - Construct icon and add it to the list. - """ - specifications = [ - ShapeSpecification.from_structure(x, extractor, scheme) - for x in current_set - ] - constructed_icon: Icon = Icon(specifications) - constructed_icon.recolor(color, white=background_color) - if constructed_icon not in icons: - icons.append(constructed_icon) + Draw all possible icon combinations in grid. - for matcher in scheme.node_matchers: - if matcher.shapes: - current_set = matcher.shapes - add() - if matcher.add_shapes: - current_set = matcher.add_shapes - add() - if not matcher.over_icon: - continue - if matcher.under_icon: + :param scheme: tag specification + :param extractor: shape extractor for icon creation + :param background_color: background color + :param color: icon color + """ + icons: List[Icon] = [] + + def add() -> None: + """ + Construct icon and add it to the list. + """ + specifications = [ + ShapeSpecification.from_structure(x, extractor, scheme) + for x in current_set + ] + constructed_icon: Icon = Icon(specifications) + constructed_icon.recolor(color, white=background_color) + if constructed_icon not in icons: + icons.append(constructed_icon) + + for matcher in scheme.node_matchers: + if matcher.shapes: + current_set = matcher.shapes + add() + if matcher.add_shapes: + current_set = matcher.add_shapes + add() + if not matcher.over_icon: + continue + if matcher.under_icon: + for icon_id in matcher.under_icon: + current_set = [icon_id] + matcher.over_icon + add() + if not (matcher.under_icon and matcher.with_icon): + continue for icon_id in matcher.under_icon: - current_set = [icon_id] + matcher.over_icon - add() - if not (matcher.under_icon and matcher.with_icon): - continue - for icon_id in matcher.under_icon: - for icon_2_id in matcher.with_icon: - current_set: List[str] = ( - [icon_id] + [icon_2_id] + matcher.over_icon - ) - add() - for icon_2_id in matcher.with_icon: - for icon_3_id in matcher.with_icon: - current_set = ( - [icon_id] + [icon_2_id] + [icon_3_id] + - matcher.over_icon + for icon_2_id in matcher.with_icon: + current_set: List[str] = ( + [icon_id] + [icon_2_id] + matcher.over_icon ) - if (icon_2_id != icon_3_id and icon_2_id != icon_id and - icon_3_id != icon_id): - add() + add() + for icon_2_id in matcher.with_icon: + for icon_3_id in matcher.with_icon: + current_set = ( + [icon_id] + [icon_2_id] + [icon_3_id] + + matcher.over_icon + ) + if (icon_2_id != icon_3_id and icon_2_id != icon_id and + icon_3_id != icon_id): + add() - specified_ids: Set[str] = set() + specified_ids: Set[str] = set() - for icon in icons: - specified_ids |= set(icon.get_shape_ids()) - print( - "Icons with no tag specification: \n " + - ", ".join(sorted(extractor.shapes.keys() - specified_ids)) + "." - ) + for icon in icons: + specified_ids |= set(icon.get_shape_ids()) + print( + "Icons with no tag specification: \n " + + ", ".join(sorted(extractor.shapes.keys() - specified_ids)) + "." + ) + return cls(icons) - for icon in icons: - icon.draw_to_file(join( - output_directory, f"{' + '.join(icon.get_names())}.svg" - )) + def draw_icons(self, output_directory: Path): + """ + :param output_directory: path to the directory to store individual SVG files + for icons + """ + for icon in self.icons: + icon.draw_to_file( + output_directory / f"{'___'.join(icon.get_shape_ids())}.svg" + ) - draw_grid( - output_file_name, sorted(icons), columns, step, - background_color=background_color - ) + def draw_grid( + self, + file_name: Path, + columns: int = 16, + step: float = 24, + background_color: Color = Color("white"), + ): + """ + Draw icons in the form of table. + :param file_name: output SVG file name + :param columns: number of columns in grid + :param step: horizontal and vertical distance between icons in grid + :param background_color: background color + """ + point: np.array = np.array((step / 2, step / 2)) + width: float = step * columns -def draw_grid( - file_name: str, icons: List[Icon], columns: int = 16, step: float = 24, - background_color: Color = Color("white") -): - """ - Draw icons in the form of table + height: int = int(int(len(self.icons) / (width / step) + 1) * step) + svg: Drawing = Drawing(str(file_name), (width, height)) + svg.add(svg.rect((0, 0), (width, height), fill=background_color.hex)) - :param file_name: output SVG file name - :param icons: list of icons - :param columns: number of columns in grid - :param step: horizontal and vertical distance between icons in grid - :param background_color: background color - """ - point: np.array = np.array((step / 2, step / 2)) - width: float = step * columns + for icon in self.icons: + icon: Icon + rectangle = svg.rect( + point - np.array((10, 10)), (20, 20), + fill=background_color.hex + ) + svg.add(rectangle) + icon.draw(svg, point) + point += np.array((step, 0)) + if point[0] > width - 8: + point[0] = step / 2 + point += np.array((0, step)) + height += step - height: int = int(int(len(icons) / (width / step) + 1) * step) - svg: Drawing = Drawing(file_name, (width, height)) - svg.add(svg.rect((0, 0), (width, height), fill=background_color.hex)) + with open(file_name, "w") as output_file: + svg.write(output_file) - for icon in icons: - icon: Icon - svg.add(svg.rect( - point - np.array((10, 10)), (20, 20), - fill=background_color.hex - )) - icon.draw(svg, point) - point += np.array((step, 0)) - if point[0] > width - 8: - point[0] = step / 2 - point += np.array((0, step)) - height += step - - print(f"Icons: {len(icons)}.") - - with open(file_name, "w") as output_file: - svg.write(output_file) + def __len__(self) -> int: + return len(self.icons) diff --git a/roentgen/icon.py b/roentgen/icon.py index 4ba5cfb..8c4f99f 100644 --- a/roentgen/icon.py +++ b/roentgen/icon.py @@ -323,13 +323,13 @@ class Icon: for shape_specification in self.shape_specifications: shape_specification.draw(svg, point, tags, outline) - def draw_to_file(self, file_name: str): + def draw_to_file(self, file_name: Path): """ Draw icon to the SVG file. :param file_name: output SVG file name """ - svg: Drawing = Drawing(file_name, (16, 16)) + svg: Drawing = Drawing(str(file_name), (16, 16)) for shape_specification in self.shape_specifications: shape_specification.draw(svg, (8, 8)) diff --git a/test/test_icons.py b/test/test_icons.py index 64c011d..4a5da1c 100644 --- a/test/test_icons.py +++ b/test/test_icons.py @@ -1,11 +1,11 @@ """ Test icon generation for nodes. """ -from os import makedirs +from pathlib import Path from typing import Dict from roentgen.icon import IconSet -from roentgen.grid import draw_all_icons +from roentgen.grid import IconCollection from test import SCHEME, SHAPE_EXTRACTOR __author__ = "Sergey Vartanov" @@ -16,8 +16,17 @@ def test_icons() -> None: """ Test grid drawing. """ - makedirs("icon_set", exist_ok=True) - draw_all_icons(SCHEME, SHAPE_EXTRACTOR, "temp.svg", "icon_set") + temp_directory: Path = Path("temp") + temp_directory.mkdir(exist_ok=True) + + set_directory: Path = temp_directory / "icon_set" + set_directory.mkdir(exist_ok=True) + + collection: IconCollection = IconCollection.from_scheme( + SCHEME, SHAPE_EXTRACTOR + ) + collection.draw_grid(temp_directory / "grid.svg") + collection.draw_icons(set_directory) def get_icon(tags: Dict[str, str]) -> IconSet: