diff --git a/mypy.ini b/mypy.ini deleted file mode 100644 index af3f209..0000000 --- a/mypy.ini +++ /dev/null @@ -1,21 +0,0 @@ -# Global options: - -[mypy] -python_version = 3.6 -warn_unused_configs = False -disallow_any_unimported = True -disallow_any_expr = True -disallow_any_decorated = True -disallow_any_explicit = True -disallow_any_generics = True -disallow_subclassing_any = True -disallow_untyped_calls = True -disallow_untyped_defs = True -disallow_incomplete_defs = True -check_untyped_defs = True -disallow_untyped_decorators = True -warn_return_any = True - -# Per-module options: -[mypy-tests.*] -ignore_errors = True diff --git a/pytiled_parser/__init__.py b/pytiled_parser/__init__.py index 7fd3538..3ed056d 100644 --- a/pytiled_parser/__init__.py +++ b/pytiled_parser/__init__.py @@ -1 +1,9 @@ """Parse Tiled Maps and Tilesets""" + +# pylint: disable=too-few-public-methods + +from .common_types import OrderedPair, Size +from .layer import Layer +from .map import Map +from .properties import Properties +from .tileset import TileSet diff --git a/pytiled_parser/common_types.py b/pytiled_parser/common_types.py new file mode 100644 index 0000000..95c7196 --- /dev/null +++ b/pytiled_parser/common_types.py @@ -0,0 +1,38 @@ +"""Module containing types that are common to multiple other modules.""" + +# pylint: disable=too-few-public-methods + +from typing import NamedTuple, Union + +import attr + +Color = str + + +@attr.s(auto_attribs=True) +class Template: + """FIXME TODO""" + + +class Size(NamedTuple): + """Size NamedTuple. + + Attributes: + width: The width of the object. + size: The height of the object. + """ + + width: Union[int, float] + height: Union[int, float] + + +class OrderedPair(NamedTuple): + """OrderedPair NamedTuple. + + Attributes: + x: X coordinate. + y: Y coordinate. + """ + + x: Union[int, float] + y: Union[int, float] diff --git a/pytiled_parser/layer.py b/pytiled_parser/layer.py new file mode 100644 index 0000000..76c3632 --- /dev/null +++ b/pytiled_parser/layer.py @@ -0,0 +1,85 @@ +# pylint: disable=too-few-public-methods + +from typing import List, Optional, Union + +import attr + +from .common_types import OrderedPair, Size +from .properties import Properties + + +@attr.s(auto_attribs=True, kw_only=True) +class Layer: + # FIXME:this docstring appears to be innacurate + """Class that all layers inherit from. + + Args: + id: Unique ID of the layer. Each layer that added to a map gets a unique id. + Even if a layer is deleted, no layer ever gets the same ID. + name: The name of the layer object. + tiled_objects: List of tiled_objects in the layer. + offset: Rendering offset of the layer object in pixels. + opacity: Decimal value between 0 and 1 to determine opacity. 1 is completely + opaque, 0 is completely transparent. + properties: Properties for the layer. + color: The color used to display the objects in this group. + draworder: Whether the objects are drawn according to the order of the object + elements in the object group element ('manual'), or sorted by their + y-coordinate ('topdown'). Defaults to 'topdown'. + See: + https://doc.mapeditor.org/en/stable/manual/objects/#changing-stacking-order + for more info. + """ + + id_: int + name: str + + offset: Optional[OrderedPair] + opacity: Optional[float] + properties: Optional[Properties] + + +TileLayerGrid = List[List[int]] + + +@attr.s(auto_attribs=True) +class Chunk: + """Chunk object for infinite maps. + + See: https://doc.mapeditor.org/en/stable/reference/tmx-map-format/#chunk + + Attributes: + location: Location of chunk in tiles. + width: The width of the chunk in tiles. + height: The height of the chunk in tiles. + layer_data: The global tile IDs in chunky according to row. + """ + + location: OrderedPair + width: int + height: int + chunk_data: TileLayerGrid + + +LayerData = Union[TileLayerGrid, List[Chunk]] +# The tile data for one layer. +# +# Either a 2 dimensional array of integers representing the global tile IDs +# for a TileLayerGrid, or a list of chunks for an infinite map layer. + + +@attr.s(auto_attribs=True, kw_only=True) +class TileLayer(Layer): + """Tile map layer containing tiles. + + See: https://doc.mapeditor.org/en/stable/reference/tmx-map-format/#layer + + Args: + size: The width of the layer in tiles. The same as the map width unless map is + infitite. + data: Either an 2 dimensional array of integers representing the global tile + IDs for the map layer, or a list of chunks for an infinite map. + """ + + size: Size + layer_data: LayerData diff --git a/pytiled_parser/map.py b/pytiled_parser/map.py new file mode 100644 index 0000000..eaf0b99 --- /dev/null +++ b/pytiled_parser/map.py @@ -0,0 +1,75 @@ +# pylint: disable=too-few-public-methods + +from pathlib import Path +from typing import Dict, List, NamedTuple, Optional, Union + +import attr + +from .common_types import Color, Size +from .layer import Layer +from .properties import Properties +from .tileset import TileSet + +TileSetDict = Dict[int, TileSet] + + +@attr.s(auto_attribs=True) +class Map: + """Object for storing a TMX with all associated layers and properties. + + See: https://doc.mapeditor.org/en/stable/reference/tmx-map-format/#map + + Attributes: + parent_dir: The directory the TMX file is in. Used for finding relative paths + to TSX files and other assets. + version: The TMX format version. + tiled_version: The Tiled version used to save the file. May be a date (for + snapshot builds). + orientation: Map orientation. Tiled supports "orthogonal", "isometric", + "staggered" and "hexagonal" + render_order: The order in which tiles on tile layers are rendered. Valid values + are right-down, right-up, left-down and left-up. In all cases, the map is + drawn row-by-row. (only supported for orthogonal maps at the moment) + map_size: The map width in tiles. + tile_size: The width of a tile. + infinite: If the map is infinite or not. + hex_side_length: Only for hexagonal maps. Determines the width or height + (depending on the staggered axis) of the tile's edge, in pixels. + stagger_axis: For staggered and hexagonal maps, determines which axis ("x" or + "y") is staggered. + stagger_index: For staggered and hexagonal maps, determines whether the "even" + or "odd" indexes along the staggered axis are shifted. + background_color: The background color of the map. + next_layer_id: Stores the next available ID for new layers. + next_object_id: Stores the next available ID for new objects. + tile_sets: Dict of tile sets used in this map. Key is the first GID for the + tile set. The value is a TileSet object. + layers: List of layer objects by draw order. + """ + + parent_dir: Path + tmx_file: Union[str, Path] + + version: str + tiled_version: str + orientation: str + render_order: str + map_size: Size + tile_size: Size + infinite: bool + next_layer_id: Optional[int] + next_object_id: int + + tile_sets: TileSetDict + layers: List[Layer] + + hex_side_length: Optional[int] = None + stagger_axis: Optional[int] = None + stagger_index: Optional[int] = None + background_color: Optional[Color] = None + + properties: Optional[Properties] = None + + +def parse() -> Map: + pass diff --git a/pytiled_parser/properties.py b/pytiled_parser/properties.py new file mode 100644 index 0000000..54728fd --- /dev/null +++ b/pytiled_parser/properties.py @@ -0,0 +1,12 @@ +from pathlib import Path +from typing import Dict, List, NamedTuple, Optional, Union + +from .common_types import Color + +RawProperties = List[Dict[str, Union[str, bool, int, float]]] + + +Property = Union[int, float, Path, str, bool, Color] + + +Properties = Dict[str, Property] diff --git a/pytiled_parser/template.py b/pytiled_parser/template.py new file mode 100644 index 0000000..c6d58d8 --- /dev/null +++ b/pytiled_parser/template.py @@ -0,0 +1,8 @@ +# pylint: disable=too-few-public-methods + +import attr + + +@attr.s(auto_attribs=True) +class Template: + """FIXME TODO""" diff --git a/pytiled_parser/tiled_object.py b/pytiled_parser/tiled_object.py new file mode 100644 index 0000000..9bb0449 --- /dev/null +++ b/pytiled_parser/tiled_object.py @@ -0,0 +1,231 @@ +# pylint: disable=too-few-public-methods + +from typing import Callable, Dict, List, Optional, Union + +import attr + +from pytiled_parser import OrderedPair, Size +from pytiled_parser.properties import Properties +from pytiled_parser.template import Template + + +@attr.s(auto_attribs=True, kw_only=True) +class TiledObject: + """TiledObject object. + + See: + https://doc.mapeditor.org/en/stable/reference/tmx-map-format/#object + + Attributes: + id_: Unique ID of the tiled object. Each tiled object that is placed on a map + gets a unique id. Even if an tiled object was deleted, no tiled object gets + the same ID. + gid: Global tiled object ID. + coordinates: The location of the tiled object in pixels. + size: The width of the tiled object in pixels (default: (0, 0)). + rotation: The rotation of the tiled object in degrees clockwise (default: 0). + opacity: The opacity of the tiled object. (default: 1) + name: The name of the tiled object. + type: The type of the tiled object. + properties: The properties of the TiledObject. + template: A reference to a Template tiled object FIXME + """ + + id_: int + gid: Optional[int] = None + + coordinates: OrderedPair + size: Size = Size(0, 0) + rotation: float = 0 + opacity: float = 1 + + name: Optional[str] = None + type: Optional[str] = None + + properties: Optional[Properties] = None + template: Optional[Template] = None + + +@attr.s() +class Ellipse(TiledObject): + """Elipse shape defined by a point, width, height, and rotation. + + See: https://doc.mapeditor.org/en/stable/reference/tmx-map-format/#ellipse + """ + + +@attr.s() +class Point(TiledObject): + """Point defined by a coordinate (x,y). + + See: https://doc.mapeditor.org/en/stable/reference/tmx-map-format/#point + """ + + +@attr.s(auto_attribs=True, kw_only=True) +class Polygon(TiledObject): + """Polygon shape defined by a set of connections between points. + + See: https://doc.mapeditor.org/en/stable/reference/tmx-map-format/#polygon + + Attributes: + points: FIXME + """ + + points: List[OrderedPair] + + +@attr.s(auto_attribs=True, kw_only=True) +class Polyline(TiledObject): + """Polyline defined by a set of connections between points. + + See: + https://doc.mapeditor.org/en/stable/reference/tmx-map-format/#polyline + + Attributes: + points: List of coordinates relative to the location of the object. + """ + + points: List[OrderedPair] + + +@attr.s() +class Rectangle(TiledObject): + """Rectangle shape defined by a point, width, and height. + + See: https://doc.mapeditor.org/en/stable/manual/objects/#insert-rectangle + (objects in tiled are rectangles by default, so there is no specific + documentation on the tmx-map-format page for it.) + """ + + +@attr.s(auto_attribs=True, kw_only=True) +class Text(TiledObject): + """Text object with associated settings. + + See: https://doc.mapeditor.org/en/stable/reference/tmx-map-format/#text + and https://doc.mapeditor.org/en/stable/manual/objects/#insert-text + + Attributes: + font_family: The font family used (default: "sans-serif") + font_size: The size of the font in pixels. (default: 16) + wrap: Whether word wrapping is enabled. (default: False) + color: Color of the text. (default: #000000) + bold: Whether the font is bold. (default: False) + italic: Whether the font is italic. (default: False) + underline: Whether the text is underlined. (default: False) + strike_out: Whether the text is striked-out. (default: False) + kerning: Whether kerning should be used while rendering the text. (default: + False) + horizontal_align: Horizontal alignment of the text (default: "left") + vertical_align: Vertical alignment of the text (defalt: "top") + """ + + text: str + font_family: str = "sans-serif" + font_size: int = 16 + wrap: bool = False + color: str = "#000000" + bold: bool = False + italic: bool = False + underline: bool = False + strike_out: bool = False + kerning: bool = False + horizontal_align: str = "left" + vertical_align: str = "top" + + +@attr.s(auto_attribs=True, kw_only=True) +class TileImage(TiledObject): + """Tile object + + See: https://doc.mapeditor.org/en/stable/manual/objects/#insert-tile + + Attributes: + gid: Reference to a global tile id. + """ + + gid: int + + +RawProperties = Dict[str, Union[str, int, float, bool]] + + +RawTiledObject = Dict[str, Union[str, int, float, bool, RawProperties]] + + +RawTiledObjects = List[RawTiledObject] + + +def _cast_ellipse(raw_tiled_object: RawTiledObject) -> Ellipse: + pass + + +def _cast_rectangle(raw_tiled_object: RawTiledObject) -> Rectangle: + pass + + +def _cast_point(raw_tiled_object: RawTiledObject) -> Point: + pass + + +def _cast_tile_image(raw_tiled_object: RawTiledObject) -> TileImage: + pass + + +def _cast_polygon(raw_tiled_object: RawTiledObject) -> Polygon: + pass + + +def _cast_polyline(raw_tiled_object: RawTiledObject) -> Polyline: + pass + + +def _cast_text(raw_tiled_object: RawTiledObject) -> Text: + pass + + +def _get_tiled_object_caster( + raw_tiled_object: RawTiledObject, +) -> Callable[[RawTiledObject], TiledObject]: + """ Get the caster function for the raw tiled object. + + Args: + raw_tiled_object: Raw Tiled object that is analysed to determine which caster + to return. + + Returns: + Callable[[RawTiledObject], TiledObject]: The caster function. + """ + + +def _cast_tiled_object(raw_tiled_object: RawTiledObject) -> TiledObject: + """ Cast the raw tiled object into a pytiled_parser type + + Args: + raw_tiled_object: Raw Tiled object that is to be cast. + + Returns: + TiledObject: a properly typed Tiled object. + """ + caster = _get_tiled_object_caster(raw_tiled_object) + + tiled_object = caster(raw_tiled_object) + return tiled_object + + +def cast_tiled_objects(raw_tiled_objects: RawTiledObjects) -> List[TiledObject]: + """Parses objects found in a 'objectgroup' element. + + Args: + object_elements: List of object elements to be parsed. + + Returns: + list: List of parsed tiled objects. + """ + tiled_objects: List[TiledObject] = [] + + for raw_tiled_object in raw_tiled_objects: + tiled_objects.append(_cast_tiled_object(raw_tiled_object)) + + return tiled_objects diff --git a/pytiled_parser/tileset.py b/pytiled_parser/tileset.py new file mode 100644 index 0000000..15d4523 --- /dev/null +++ b/pytiled_parser/tileset.py @@ -0,0 +1,175 @@ +# pylint: disable=too-few-public-methods +from pathlib import Path +from typing import Dict, List, NamedTuple, Optional + +import attr + +from pytiled_parser import OrderedPair, Size +from pytiled_parser.properties import Properties, Property +from pytiled_parser.tiled_object import TiledObject + + +class Grid(NamedTuple): + """Contains info for isometric maps. + + This element is only used in case of isometric orientation, and determines how tile + overlays for terrain and collision information are rendered. + + Args: + orientation: Orientation of the grid for the tiles in this tileset (orthogonal + or isometric). + width: Width of a grid cell. + height: Height of a grid cell. + """ + + orientation: str + width: int + height: int + + +@attr.s(auto_attribs=True) +class Image: + """Image object. + + pytiled_parser does not support embedded data in image elements at this time, + even though the TMX format technically does. + + Attributes: + source: The reference to the tileset image file. Note that this is a relative + path compared to FIXME + trans: Defines a specific color that is treated as transparent. + width: The image width in pixels (optional, used for tile index correction when + the image changes). + height: The image height in pixels (optional). + """ + + source: str + size: Optional[Size] = None + trans: Optional[str] = None + width: Optional[int] = None + height: Optional[int] = None + + +class Terrain(NamedTuple): + """Terrain object. + + Args: + name: The name of the terrain type. + tile: The local tile-id of the tile that represents the terrain visually. + """ + + name: str + tile: int + + +@attr.s(auto_attribs=True) +class TileTerrain: + """Defines each corner of a tile by Terrain index in + 'TileSet.terrain_types'. + + Defaults to 'None'. 'None' means that corner has no terrain. + + Attributes: + top_left: Top left terrain type. + top_right: Top right terrain type. + bottom_left: Bottom left terrain type. + bottom_right: Bottom right terrain type. + """ + + top_left: Optional[int] = None + top_right: Optional[int] = None + bottom_left: Optional[int] = None + bottom_right: Optional[int] = None + + +class Frame(NamedTuple): + """Animation Frame object. + + This is only used as a part of an animation for Tile objects. + + Args: + tile_id: The local ID of a tile within the parent tile set object. + duration: How long in milliseconds this frame should be displayed before + advancing to the next frame. + """ + + tile_id: int + duration: int + + +@attr.s(auto_attribs=True, kw_only=True) +class Tile: + # FIXME: args + """Individual tile object. + + See: https://doc.mapeditor.org/en/stable/reference/tmx-map-format/#tile + + Args: + id: The local tile ID within its tileset. + type: The type of the tile. Refers to an object type and is used by tile + objects. + terrain: Defines the terrain type of each corner of the tile. + animation: Each tile can have exactly one animation associated with it. + """ + + id_: int + type_: Optional[str] = None + terrain: Optional[TileTerrain] = None + animation: Optional[List[Frame]] = None + objectgroup: Optional[List[TiledObject]] = None + image: Optional[Image] = None + properties: Optional[List[Property]] = None + tileset: Optional["TileSet"] = None + flipped_horizontally: bool = False + flipped_diagonally: bool = False + flipped_vertically: bool = False + + +@attr.s(auto_attribs=True) +class TileSet: + """Object for storing a TSX with all associated collision data. + + Args: + name: The name of this tileset. + max_tile_size: The maximum size of a tile in this tile set in pixels. + spacing: The spacing in pixels between the tiles in this tileset (applies to + the tileset image). + margin: The margin around the tiles in this tileset (applies to the tileset + image). + tile_count: The number of tiles in this tileset. + columns: The number of tile columns in the tileset. For image collection + tilesets it is editable and is used when displaying the tileset. + grid: Only used in case of isometric orientation, and determines how tile + overlays for terrain and collision information are rendered. + tileoffset: Used to specify an offset in pixels when drawing a tile from the + tileset. When not present, no offset is applied. + image: Used for spritesheet tile sets. + terrain_types: List of of terrain types which can be referenced from the + terrain attribute of the tile object. Ordered according to the terrain + element's appearance in the TSX file. + tiles: Dict of Tile objects by Tile.id. + tsx_file: Path of the file containing the tileset, None if loaded internally + from a map + parent_dir: Path of the parent directory of the file containing the tileset, + None if loaded internally from a map + """ + + name: str + max_tile_size: Size + + spacing: Optional[int] = None + margin: Optional[int] = None + tile_count: Optional[int] = None + columns: Optional[int] = None + tile_offset: Optional[OrderedPair] = None + grid: Optional[Grid] = None + properties: Optional[Properties] = None + image: Optional[Image] = None + terrain_types: Optional[List[Terrain]] = None + tiles: Optional[Dict[int, Tile]] = None + tsx_file: Path = None + parent_dir: Path = None + + +def parse(): + pass diff --git a/setup.cfg b/setup.cfg index d115d33..dbf7dfa 100644 --- a/setup.cfg +++ b/setup.cfg @@ -59,10 +59,10 @@ warn_redundant_casts = True # Per-module options: [mypy-pytiled_parser.*] -disallow_any_unimported = False +disallow_any_unimported = True disallow_any_decorated = True disallow_any_generics = True -disallow_subclassing_any = False +disallow_subclassing_any = True disallow_untyped_calls = True disallow_untyped_defs = True disallow_incomplete_defs = True diff --git a/tests/test_data/simple_objects.json b/tests/test_data/simple_objects.json new file mode 100644 index 0000000..9e01620 --- /dev/null +++ b/tests/test_data/simple_objects.json @@ -0,0 +1,552 @@ +{ "compressionlevel":0, + "editorsettings": + { + "export": + { + "target":"." + } + }, + "height":6, + "infinite":false, + "layers":[ + { + "data":[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48], + "height":6, + "id":1, + "name":"Tile Layer 1", + "opacity":1, + "type":"tilelayer", + "visible":true, + "width":8, + "x":0, + "y":0 + }, + { + "draworder":"topdown", + "id":2, + "name":"Object Layer 1", + "objects":[ + { + "height":41.4686825053996, + "id":1, + "name":"name: rectangle", + "rotation":0, + "type":"rectangle", + "visible":true, + "width":45.3972945322269, + "x":27.7185404115039, + "y":23.571672160964 + }, + { + "height":0, + "id":2, + "name":"name: point", + "point":true, + "rotation":0, + "type":"point", + "visible":true, + "width":0, + "x":159.981811981357, + "y":82.9373650107991 + }, + { + "height":0, + "id":3, + "name":"name: point invisible", + "point":true, + "rotation":0, + "type":"point", + "visible":false, + "width":0, + "x":109.346368080027, + "y":95.8144822098443 + }, + { + "height":32.7384335568944, + "id":4, + "name":"name: rectangle - invisible", + "rotation":0, + "type":"rectangle", + "visible":true, + "width":30.9923837671934, + "x":163.910424008185, + "y":91.0128452881664 + }, + { + "height":22, + "id":5, + "name":"name: rectangle - rotated", + "rotation":10, + "type":"rectangle", + "visible":true, + "width":10, + "x":183.335227918609, + "y":23.3534159372513 + }, + { + "ellipse":true, + "height":18.5517790155735, + "id":6, + "name":"name: ellipse", + "rotation":0, + "type":"ellipse", + "visible":true, + "width":57.4013868364215, + "x":37.5400704785722, + "y":81.1913152210981 + }, + { + "ellipse":true, + "height":31.4288962146186, + "id":7, + "name":"name: ellipse - invisible", + "rotation":0, + "type":"ellipse", + "visible":true, + "width":6.32943048766625, + "x":22.6986472661134, + "y":53.9092872570194 + }, + { + "ellipse":true, + "height":24.2264408321018, + "id":8, + "name":"name: ellipse - rotated", + "rotation":111, + "type":"ellipse", + "visible":true, + "width":29.6828464249176, + "x":35.7940206888712, + "y":120.040923041946 + }, + { + "height":0, + "id":9, + "name":"name: polyline", + "polygon":[ + { + "x":0, + "y":0 + }, + { + "x":19.424803910424, + "y":27.063771740366 + }, + { + "x":19.6430601341366, + "y":3.05558713197681 + }, + { + "x":-2.61907468455156, + "y":15.9327043310219 + }, + { + "x":25.317721950665, + "y":16.3692167784472 + }], + "rotation":0, + "type":"polyline", + "visible":true, + "width":0, + "x":89.485051722178, + "y":38.6313515971354 + }, + { + "height":0, + "id":10, + "name":"name: polyline - invisible", + "polygon":[ + { + "x":0, + "y":0 + }, + { + "x":-12.8771171990451, + "y":7.63896782994203 + }, + { + "x":-14.8414232124588, + "y":-10.2580425144936 + }], + "rotation":0, + "type":"polyline", + "visible":false, + "width":0, + "x":133.791065135842, + "y":24.4446970558145 + }, + { + "height":0, + "id":11, + "name":"name: polyline - rotated", + "polygon":[ + { + "x":0, + "y":0 + }, + { + "x":-12.8771171990451, + "y":0 + }, + { + "x":-6.98419915880413, + "y":7.63896782994203 + }, + { + "x":-13.9683983176083, + "y":16.8057292258725 + }, + { + "x":3.71035580311468, + "y":15.277935659884 + }, + { + "x":-3.71035580311471, + "y":8.29373650107991 + }], + "rotation":123, + "type":"polyline", + "visible":true, + "width":0, + "x":152.779356598841, + "y":19.8613163578493 + }, + { + "height":0, + "id":12, + "name":"name: polyline - not closed", + "polyline":[ + { + "x":0, + "y":0 + }, + { + "x":-13.3136296464704, + "y":41.0321700579743 + }, + { + "x":21.3891099238377, + "y":16.8057292258725 + }], + "rotation":0, + "type":"polyline", + "visible":true, + "width":0, + "x":124.187791292486, + "y":90.1398203933159 + }, + { + "gid":79, + "height":32, + "id":13, + "name":"name: tile", + "rotation":0, + "type":"tile", + "visible":true, + "width":32, + "x":111.898147095601, + "y":48.3019211094691 + }, + { + "gid":80, + "height":32, + "id":14, + "name":"name: tile - invisible", + "rotation":0, + "type":"tile", + "visible":false, + "width":32, + "x":41.1831306127089, + "y":168.779356598841 + }, + { + "gid":2147483742, + "height":32, + "id":15, + "name":"name: tile - horizontal flipped", + "rotation":0, + "type":"tile", + "visible":true, + "width":32, + "x":197.236330567239, + "y":59.8695009662385 + }, + { + "gid":1073741918, + "height":32, + "id":16, + "name":"name: tile - vertical flipped", + "rotation":0, + "type":"tile", + "visible":true, + "width":32, + "x":32.4528816642037, + "y":60.742525861089 + }, + { + "gid":3221225558, + "height":32, + "id":17, + "name":"name: tile - both flipped", + "rotation":0, + "type":"tile", + "visible":true, + "width":32, + "x":167.553484142321, + "y":95.6635216551097 + }, + { + "gid":86, + "height":32, + "id":18, + "name":"name: tile - rotated", + "rotation":89, + "type":"tile", + "visible":true, + "width":32, + "x":85.65, + "y":142.62 + }, + { + "height":19, + "id":19, + "name":"name: text", + "rotation":0, + "text": + { + "text":"Hello World", + "wrap":true + }, + "type":"text", + "visible":true, + "width":92.375, + "x":81.7106470956008, + "y":93.2986813686484 + }, + { + "height":19, + "id":20, + "name":"name: text - invisible", + "rotation":0, + "text": + { + "text":"Hello World", + "wrap":true + }, + "type":"text", + "visible":false, + "width":92.375, + "x":8.37655592815732, + "y":112.068716607935 + }, + { + "height":19, + "id":21, + "name":"name: text - rotated", + "rotation":19, + "text": + { + "text":"Hello World", + "wrap":true + }, + "type":"text", + "visible":true, + "width":92.375, + "x":157.882069171308, + "y":78.4572581561896 + }, + { + "height":19, + "id":22, + "name":"name: text - different font", + "rotation":0, + "text": + { + "bold":true, + "fontfamily":"DejaVu Sans", + "pixelsize":19, + "text":"Hello World", + "wrap":true + }, + "type":"text", + "visible":true, + "width":92.375, + "x":2.70189411162896, + "y":101.592417869728 + }, + { + "height":19, + "id":23, + "name":"name: text - no word wrap", + "rotation":0, + "text": + { + "text":"Hello World" + }, + "type":"text", + "visible":true, + "width":92.375, + "x":9.90434949414573, + "y":154.192167784472 + }, + { + "height":19, + "id":24, + "name":"name: text - right bottom align", + "rotation":0, + "text": + { + "halign":"right", + "text":"Hello World", + "valign":"bottom", + "wrap":true + }, + "type":"", + "visible":true, + "width":92.375, + "x":151.989151131067, + "y":1.19455496191883 + }, + { + "height":19, + "id":25, + "name":"text: center center align", + "rotation":0, + "text": + { + "halign":"center", + "text":"Hello World", + "valign":"center", + "wrap":true + }, + "type":"text", + "visible":true, + "width":92.375, + "x":4.22968767761736, + "y":3.81362964647039 + }, + { + "height":19, + "id":26, + "name":"name: text - justified", + "rotation":0, + "text": + { + "halign":"justify", + "text":"Hello World", + "wrap":true + }, + "type":"text", + "visible":true, + "width":92.375, + "x":13.8329615209731, + "y":60.7785040354666 + }, + { + "height":19, + "id":27, + "name":"name: text - red", + "rotation":0, + "text": + { + "color":"#aa0000", + "text":"Hello World", + "wrap":true + }, + "type":"text", + "visible":true, + "width":92.375, + "x":96.3338140843469, + "y":130.620495623508 + }, + { + "height":0, + "id":28, + "name":"name: rectangle - no width or height", + "rotation":0, + "type":"rectangle", + "visible":true, + "width":0, + "x":131.17199045129, + "y":53.4727748095942 + }, + { + "ellipse":true, + "height":0, + "id":29, + "name":"name: ellipse - no width or height", + "rotation":0, + "type":"ellipse", + "visible":true, + "width":0, + "x":72.4610662725929, + "y":127.679890871888 + }, + { + "height":13.7501420938956, + "id":30, + "name":"name: rectangle - properties", + "properties":[ + { + "name":"bool property", + "type":"bool", + "value":false + }, + { + "name":"color property", + "type":"color", + "value":"#ffaa0000" + }, + { + "name":"file property", + "type":"file", + "value":"..\/..\/..\/..\/..\/..\/dev\/null" + }, + { + "name":"float property", + "type":"float", + "value":42.1 + }, + { + "name":"int property", + "type":"int", + "value":8675309 + }, + { + "name":"string property", + "type":"string", + "value":"pytiled_parser rulez!1!!" + }], + "rotation":0, + "type":"rectangle", + "visible":true, + "width":21.170853700125, + "x":39.0678640445606, + "y":131.826759122428 + }], + "opacity":1, + "type":"objectgroup", + "visible":true, + "x":0, + "y":0 + }], + "nextlayerid":3, + "nextobjectid":31, + "orientation":"orthogonal", + "renderorder":"right-down", + "tiledversion":"1.3.1", + "tileheight":32, + "tilesets":[ + { + "firstgid":1, + "source":"..\/..\/old_tests\/test_data\/tile_set_image_objects.tsx" + }, + { + "firstgid":49, + "source":"..\/..\/old_tests\/test_data\/tile_set_image.tsx" + }], + "tilewidth":32, + "type":"map", + "version":1.2, + "width":8 +} \ No newline at end of file diff --git a/tests/test_data/test_map_simple_offset.json b/tests/test_data/test_map_simple_offset.json new file mode 100644 index 0000000..bc3f4f5 --- /dev/null +++ b/tests/test_data/test_map_simple_offset.json @@ -0,0 +1,80 @@ +{ "compressionlevel":0, + "height":6, + "infinite":false, + "layers":[ + { + "data":[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48], + "height":6, + "id":1, + "name":"Tile Layer 1", + "offsetx":16, + "offsety":-16.42, + "opacity":1, + "type":"tilelayer", + "visible":true, + "width":8, + "x":0, + "y":0 + }], + "nextlayerid":2, + "nextobjectid":1, + "orientation":"orthogonal", + "properties":[ + { + "name":"bool property - false", + "type":"bool", + "value":false + }, + { + "name":"bool property - true", + "type":"bool", + "value":true + }, + { + "name":"color property", + "type":"color", + "value":"#ff49fcff" + }, + { + "name":"empty file", + "type":"file", + "value":"" + }, + { + "name":"empty string", + "type":"string", + "value":"" + }, + { + "name":"file_property", + "type":"file", + "value":"test_map_simple_offset.json" + }, + { + "name":"float property", + "type":"float", + "value":1.23456789 + }, + { + "name":"int property", + "type":"int", + "value":13 + }, + { + "name":"string property", + "type":"string", + "value":"Hello, World!!" + }], + "renderorder":"right-down", + "tiledversion":"1.3.1", + "tileheight":32, + "tilesets":[ + { + "firstgid":1, + "source":"..\/..\/old_tests\/test_data\/tile_set_image.tsx" + }], + "tilewidth":32, + "type":"map", + "version":1.2, + "width":8 +} \ No newline at end of file diff --git a/tests/test_tiled_object.py b/tests/test_tiled_object.py new file mode 100644 index 0000000..d0675d9 --- /dev/null +++ b/tests/test_tiled_object.py @@ -0,0 +1,82 @@ +"""Tests for objects""" +import xml.etree.ElementTree as etree +from contextlib import ExitStack as does_not_raise + +import pytest + +from pytiled_parser import tiled_object + +ELLIPSES = [] + +RECTANGLES = [ + ( + """ + {"height":41.4686825053996, + "id":1, + "name":"name: rectangle", + "rotation":0, + "type":"rectangle", + "visible":true, + "width":45.3972945322269, + "x":27.7185404115039, + "y":23.571672160964} + """, + { + "height": 41.4686825053996, + "id": 1, + "name": "name: rectangle", + "rotation": 0, + "type": "rectangle", + "visible": True, + "width": 45.3972945322269, + "x": 27.7185404115039, + "y": 23.571672160964, + }, + ), +] + +POINTS = [ + ( + """ + {"height":0, + "id":2, + "name":"name: point", + "point":true, + "rotation":0, + "type":"point", + "visible":true, + "width":0, + "x":159.981811981357, + "y":82.9373650107991} + """, + { + "height": 0, + "id": 2, + "name": "name: point", + "point": True, + "rotation": 0, + "type": "point", + "visible": True, + "width": 0, + "x": 159.981811981357, + "y": 82.9373650107991, + }, + ), +] + +TILE_IMAGES = [] + +POLYGONS = [] + +POLYLINES = [] + +TEXTS = [] + +OBJECTS = ELLIPSES + RECTANGLES + POINTS + TILE_IMAGES + POLYGONS + POLYLINES + TEXTS + + +@pytest.mark.parametrize("raw_object,expected", OBJECTS) +def test_parse_layer(raw_object, expected): + result = tiled_object._cast_tiled_object(raw_object) + + assert result == expected