Initial 2.0 refactor

This commit is contained in:
Darren Eberly
2021-11-07 02:43:32 -05:00
parent 464454b02b
commit 45e0784d3d
21 changed files with 1370 additions and 1272 deletions

View File

@@ -13,7 +13,8 @@ PyTiled Parser is not tied to any particular graphics library or game engine.
from .common_types import OrderedPair, Size
from .layer import ImageLayer, Layer, LayerGroup, ObjectLayer, TileLayer
from .parser import parse_map
from .properties import Properties
from .tiled_map import TiledMap, parse_map
from .tiled_map import TiledMap
from .tileset import Tile, Tileset
from .version import __version__

View File

@@ -8,27 +8,14 @@ See:
# pylint: disable=too-few-public-methods
import base64
import gzip
import importlib.util
import zlib
from pathlib import Path
from typing import Any, List, Optional, Union
from typing import cast as type_cast
from typing import List, Optional, Union
import attr
from typing_extensions import TypedDict
from . import properties as properties_
from . import tiled_object
from .common_types import Color, OrderedPair, Size
from .util import parse_color
zstd_spec = importlib.util.find_spec("zstd")
if zstd_spec:
import zstd # pylint: disable=import-outside-toplevel
else:
zstd = None # pylint: disable=invalid-name
from pytiled_parser.common_types import Color, OrderedPair, Size
from pytiled_parser.properties import Properties
from pytiled_parser.tiled_object import TiledObject
@attr.s(auto_attribs=True, kw_only=True)
@@ -60,7 +47,7 @@ class Layer:
id: Optional[int] = None
size: Optional[Size] = None
properties: Optional[properties_.Properties] = None
properties: Optional[Properties] = None
tint_color: Optional[Color] = None
@@ -127,7 +114,7 @@ class ObjectLayer(Layer):
for more info.
"""
tiled_objects: List[tiled_object.TiledObject]
tiled_objects: List[TiledObject]
draw_order: Optional[str] = "topdown"
@@ -162,341 +149,3 @@ class LayerGroup(Layer):
"""
layers: Optional[List[Layer]]
class RawChunk(TypedDict):
"""The keys and their types that appear in a Chunk JSON Object.
See: https://doc.mapeditor.org/en/stable/reference/json-map-format/#chunk
"""
data: Union[List[int], str]
height: int
width: int
x: int
y: int
class RawLayer(TypedDict):
"""The keys and their types that appear in a Layer JSON Object.
See: https://doc.mapeditor.org/en/stable/reference/json-map-format/#layer
"""
chunks: List[RawChunk]
compression: str
data: Union[List[int], str]
draworder: str
encoding: str
height: int
id: int
image: str
layers: List[Any]
name: str
objects: List[tiled_object.RawTiledObject]
offsetx: float
offsety: float
parallaxx: float
parallaxy: float
opacity: float
properties: List[properties_.RawProperty]
startx: int
starty: int
tintcolor: str
transparentcolor: str
type: str
visible: bool
width: int
x: int
y: int
def _convert_raw_tile_layer_data(data: List[int], layer_width: int) -> List[List[int]]:
"""Convert raw layer data into a nested lit based on the layer width
Args:
data: The data to convert
layer_width: Width of the layer
Returns:
List[List[int]]: A nested list containing the converted data
"""
tile_grid: List[List[int]] = [[]]
column_count = 0
row_count = 0
for item in data:
column_count += 1
tile_grid[row_count].append(item)
if not column_count % layer_width and column_count < len(data):
row_count += 1
tile_grid.append([])
return tile_grid
def _decode_tile_layer_data(
data: str, compression: str, layer_width: int
) -> List[List[int]]:
"""Decode Base64 Encoded tile data. Optionally supports gzip and zlib compression.
Args:
data: The base64 encoded data
compression: Either zlib, gzip, or empty. If empty no decompression is done.
Returns:
List[List[int]]: A nested list containing the decoded data
Raises:
ValueError: For an unsupported compression type.
"""
unencoded_data = base64.b64decode(data)
if compression == "zlib":
unzipped_data = zlib.decompress(unencoded_data)
elif compression == "gzip":
unzipped_data = gzip.decompress(unencoded_data)
elif compression == "zstd" and zstd is None:
raise ValueError(
"zstd compression support is not installed."
"To install use 'pip install pytiled-parser[zstd]'"
)
elif compression == "zstd":
unzipped_data = zstd.decompress(unencoded_data)
else:
unzipped_data = unencoded_data
tile_grid: List[int] = []
byte_count = 0
int_count = 0
int_value = 0
for byte in unzipped_data:
int_value += byte << (byte_count * 8)
byte_count += 1
if not byte_count % 4:
byte_count = 0
int_count += 1
tile_grid.append(int_value)
int_value = 0
return _convert_raw_tile_layer_data(tile_grid, layer_width)
def _cast_chunk(
raw_chunk: RawChunk,
encoding: Optional[str] = None,
compression: Optional[str] = None,
) -> Chunk:
"""Cast the raw_chunk to a Chunk.
Args:
raw_chunk: RawChunk to be casted to a Chunk
encoding: Encoding type. ("base64" or None)
compression: Either zlib, gzip, or empty. If empty no decompression is done.
Returns:
Chunk: The Chunk created from the raw_chunk
"""
if encoding == "base64":
assert isinstance(compression, str)
assert isinstance(raw_chunk["data"], str)
data = _decode_tile_layer_data(
raw_chunk["data"], compression, raw_chunk["width"]
)
else:
data = _convert_raw_tile_layer_data(
raw_chunk["data"], raw_chunk["width"] # type: ignore
)
chunk = Chunk(
coordinates=OrderedPair(raw_chunk["x"], raw_chunk["y"]),
size=Size(raw_chunk["width"], raw_chunk["height"]),
data=data,
)
return chunk
def _get_common_attributes(raw_layer: RawLayer) -> Layer:
"""Create a Layer containing all the attributes common to all layers.
This is to create the stub Layer object that can then be used to create the actual
specific sub-classes of Layer.
Args:
raw_layer: Raw Tiled object get common attributes from
Returns:
Layer: The attributes in common of all layers
"""
common_attributes = Layer(
name=raw_layer["name"],
opacity=raw_layer["opacity"],
visible=raw_layer["visible"],
)
# if startx is present, starty is present
if raw_layer.get("startx") is not None:
common_attributes.coordinates = OrderedPair(
raw_layer["startx"], raw_layer["starty"]
)
if raw_layer.get("id") is not None:
common_attributes.id = raw_layer["id"]
# if either width or height is present, they both are
if raw_layer.get("width") is not None:
common_attributes.size = Size(raw_layer["width"], raw_layer["height"])
if raw_layer.get("offsetx") is not None:
common_attributes.offset = OrderedPair(
raw_layer["offsetx"], raw_layer["offsety"]
)
if raw_layer.get("properties") is not None:
common_attributes.properties = properties_.cast(raw_layer["properties"])
parallax = [1.0, 1.0]
if raw_layer.get("parallaxx") is not None:
parallax[0] = raw_layer["parallaxx"]
if raw_layer.get("parallaxy") is not None:
parallax[1] = raw_layer["parallaxy"]
common_attributes.parallax_factor = OrderedPair(parallax[0], parallax[1])
if raw_layer.get("tintcolor") is not None:
common_attributes.tint_color = parse_color(raw_layer["tintcolor"])
return common_attributes
def _cast_tile_layer(raw_layer: RawLayer) -> TileLayer:
"""Cast the raw_layer to a TileLayer.
Args:
raw_layer: RawLayer to be casted to a TileLayer
Returns:
TileLayer: The TileLayer created from raw_layer
"""
tile_layer = TileLayer(**_get_common_attributes(raw_layer).__dict__)
if raw_layer.get("chunks") is not None:
tile_layer.chunks = []
for chunk in raw_layer["chunks"]:
if raw_layer.get("encoding") is not None:
tile_layer.chunks.append(
_cast_chunk(chunk, raw_layer["encoding"], raw_layer["compression"])
)
else:
tile_layer.chunks.append(_cast_chunk(chunk))
if raw_layer.get("data") is not None:
if raw_layer.get("encoding") is not None:
tile_layer.data = _decode_tile_layer_data(
data=type_cast(str, raw_layer["data"]),
compression=raw_layer["compression"],
layer_width=raw_layer["width"],
)
else:
tile_layer.data = _convert_raw_tile_layer_data(
raw_layer["data"], raw_layer["width"] # type: ignore
)
return tile_layer
def _cast_object_layer(
raw_layer: RawLayer,
parent_dir: Optional[Path] = None,
) -> ObjectLayer:
"""Cast the raw_layer to an ObjectLayer.
Args:
raw_layer: RawLayer to be casted to an ObjectLayer
Returns:
ObjectLayer: The ObjectLayer created from raw_layer
"""
tiled_objects = []
for tiled_object_ in raw_layer["objects"]:
tiled_objects.append(tiled_object.cast(tiled_object_, parent_dir))
return ObjectLayer(
tiled_objects=tiled_objects,
draw_order=raw_layer["draworder"],
**_get_common_attributes(raw_layer).__dict__,
)
def _cast_image_layer(raw_layer: RawLayer) -> ImageLayer:
"""Cast the raw_layer to a ImageLayer.
Args:
raw_layer: RawLayer to be casted to a ImageLayer
Returns:
ImageLayer: The ImageLayer created from raw_layer
"""
image_layer = ImageLayer(
image=Path(raw_layer["image"]), **_get_common_attributes(raw_layer).__dict__
)
if raw_layer.get("transparentcolor") is not None:
image_layer.transparent_color = parse_color(raw_layer["transparentcolor"])
return image_layer
def _cast_group_layer(
raw_layer: RawLayer, parent_dir: Optional[Path] = None
) -> LayerGroup:
"""Cast the raw_layer to a LayerGroup.
Args:
raw_layer: RawLayer to be casted to a LayerGroup
Returns:
LayerGroup: The LayerGroup created from raw_layer
"""
layers = []
for layer in raw_layer["layers"]:
layers.append(cast(layer, parent_dir=parent_dir))
return LayerGroup(layers=layers, **_get_common_attributes(raw_layer).__dict__)
def cast(
raw_layer: RawLayer,
parent_dir: Optional[Path] = None,
) -> Layer:
"""Cast a raw Tiled layer into a pytiled_parser type.
This function will determine the type of layer and cast accordingly.
Args:
raw_layer: Raw layer to be cast.
parent_dir: The parent directory that the map file is in.
Returns:
Layer: a properly typed Layer.
Raises:
RuntimeError: For an invalid layer type being provided
"""
type_ = raw_layer["type"]
if type_ == "objectgroup":
return _cast_object_layer(raw_layer, parent_dir)
elif type_ == "group":
return _cast_group_layer(raw_layer, parent_dir)
elif type_ == "imagelayer":
return _cast_image_layer(raw_layer)
elif type_ == "tilelayer":
return _cast_tile_layer(raw_layer)
raise RuntimeError(f"An invalid layer type of {type_} was supplied")

17
pytiled_parser/parser.py Normal file
View File

@@ -0,0 +1,17 @@
from pathlib import Path
from pytiled_parser.parsers.json.tiled_map import parse as json_map_parse
from pytiled_parser.tiled_map import TiledMap
def parse_map(file: Path) -> TiledMap:
"""Parse the raw Tiled map into a pytiled_parser type
Args:
file: Path to the map's JSON file
Returns:
TileSet: a properly typed TileSet.
"""
# I have no idea why, but mypy thinks this function returns "Any"
return json_map_parse(file) # type: ignore

View File

@@ -0,0 +1,364 @@
"""Layer parsing for the JSON Map Format.
"""
import base64
import gzip
import importlib.util
import zlib
from pathlib import Path
from typing import Any, List, Optional, Union, cast
from typing_extensions import TypedDict
from pytiled_parser.common_types import OrderedPair, Size
from pytiled_parser.layer import (
Chunk,
ImageLayer,
Layer,
LayerGroup,
ObjectLayer,
TileLayer,
)
from pytiled_parser.parsers.json.properties import RawProperty
from pytiled_parser.parsers.json.properties import parse as parse_properties
from pytiled_parser.parsers.json.tiled_object import RawObject
from pytiled_parser.parsers.json.tiled_object import parse as parse_object
from pytiled_parser.util import parse_color
zstd_spec = importlib.util.find_spec("zstd")
if zstd_spec:
import zstd
else:
zstd = None
class RawChunk(TypedDict):
"""The keys and their types that appear in a Tiled JSON Chunk Object.
Tiled Doc: https://doc.mapeditor.org/en/stable/reference/json-map-format/#chunk
"""
data: Union[List[int], str]
height: int
width: int
x: int
y: int
class RawLayer(TypedDict):
"""The keys and their types that appear in a Tiled JSON Layer Object.
Tiled Doc: https://doc.mapeditor.org/en/stable/reference/json-map-format/#layer
"""
chunks: List[RawChunk]
compression: str
data: Union[List[int], str]
draworder: str
encoding: str
height: int
id: int
image: str
layers: List[Any]
name: str
objects: List[RawObject]
offsetx: float
offsety: float
parallaxx: float
parallaxy: float
opacity: float
properties: List[RawProperty]
startx: int
starty: int
tintcolor: str
transparentcolor: str
type: str
visible: bool
width: int
x: int
y: int
def _convert_raw_tile_layer_data(data: List[int], layer_width: int) -> List[List[int]]:
"""Convert raw layer data into a nested lit based on the layer width
Args:
data: The data to convert
layer_width: Width of the layer
Returns:
List[List[int]]: A nested list containing the converted data
"""
tile_grid: List[List[int]] = [[]]
column_count = 0
row_count = 0
for item in data:
column_count += 1
tile_grid[row_count].append(item)
if not column_count % layer_width and column_count < len(data):
row_count += 1
tile_grid.append([])
return tile_grid
def _decode_tile_layer_data(
data: str, compression: str, layer_width: int
) -> List[List[int]]:
"""Decode Base64 Encoded tile data. Optionally supports gzip and zlib compression.
Args:
data: The base64 encoded data
compression: Either zlib, gzip, or empty. If empty no decompression is done.
Returns:
List[List[int]]: A nested list containing the decoded data
Raises:
ValueError: For an unsupported compression type.
"""
unencoded_data = base64.b64decode(data)
if compression == "zlib":
unzipped_data = zlib.decompress(unencoded_data)
elif compression == "gzip":
unzipped_data = gzip.decompress(unencoded_data)
elif compression == "zstd" and zstd is None:
raise ValueError(
"zstd compression support is not installed."
"To install use 'pip install pytiled-parser[zstd]'"
)
elif compression == "zstd":
unzipped_data = zstd.decompress(unencoded_data)
else:
unzipped_data = unencoded_data
tile_grid: List[int] = []
byte_count = 0
int_count = 0
int_value = 0
for byte in unzipped_data:
int_value += byte << (byte_count * 8)
byte_count += 1
if not byte_count % 4:
byte_count = 0
int_count += 1
tile_grid.append(int_value)
int_value = 0
return _convert_raw_tile_layer_data(tile_grid, layer_width)
def _parse_chunk(
raw_chunk: RawChunk,
encoding: Optional[str] = None,
compression: Optional[str] = None,
) -> Chunk:
"""Parse the raw_chunk to a Chunk.
Args:
raw_chunk: RawChunk to be parsed to a Chunk
encoding: Encoding type. ("base64" or None)
compression: Either zlib, gzip, or empty. If empty no decompression is done.
Returns:
Chunk: The Chunk created from the raw_chunk
"""
if encoding == "base64":
assert isinstance(compression, str)
assert isinstance(raw_chunk["data"], str)
data = _decode_tile_layer_data(
raw_chunk["data"], compression, raw_chunk["width"]
)
else:
data = _convert_raw_tile_layer_data(
raw_chunk["data"], raw_chunk["width"] # type: ignore
)
chunk = Chunk(
coordinates=OrderedPair(raw_chunk["x"], raw_chunk["y"]),
size=Size(raw_chunk["width"], raw_chunk["height"]),
data=data,
)
return chunk
def _parse_common(raw_layer: RawLayer) -> Layer:
"""Create a Layer containing all the attributes common to all layer types.
This is to create the stub Layer object that can then be used to create the actual
specific sub-classes of Layer.
Args:
raw_layer: Raw layer get common attributes from
Returns:
Layer: The attributes in common of all layer types
"""
common = Layer(
name=raw_layer["name"],
opacity=raw_layer["opacity"],
visible=raw_layer["visible"],
)
# if startx is present, starty is present
if raw_layer.get("startx") is not None:
common.coordinates = OrderedPair(raw_layer["startx"], raw_layer["starty"])
if raw_layer.get("id") is not None:
common.id = raw_layer["id"]
# if either width or height is present, they both are
if raw_layer.get("width") is not None:
common.size = Size(raw_layer["width"], raw_layer["height"])
if raw_layer.get("offsetx") is not None:
common.offset = OrderedPair(raw_layer["offsetx"], raw_layer["offsety"])
if raw_layer.get("properties") is not None:
common.properties = parse_properties(raw_layer["properties"])
parallax = [1.0, 1.0]
if raw_layer.get("parallaxx") is not None:
parallax[0] = raw_layer["parallaxx"]
if raw_layer.get("parallaxy") is not None:
parallax[1] = raw_layer["parallaxy"]
common.parallax_factor = OrderedPair(parallax[0], parallax[1])
if raw_layer.get("tintcolor") is not None:
common.tint_color = parse_color(raw_layer["tintcolor"])
return common
def _parse_tile_layer(raw_layer: RawLayer) -> TileLayer:
"""Parse the raw_layer to a TileLayer.
Args:
raw_layer: RawLayer to be parsed to a TileLayer.
Returns:
TileLayer: The TileLayer created from raw_layer
"""
tile_layer = TileLayer(**_parse_common(raw_layer).__dict__)
if raw_layer.get("chunks") is not None:
tile_layer.chunks = []
for chunk in raw_layer["chunks"]:
if raw_layer.get("encoding") is not None:
tile_layer.chunks.append(
_parse_chunk(chunk, raw_layer["encoding"], raw_layer["compression"])
)
else:
tile_layer.chunks.append(_parse_chunk(chunk))
if raw_layer.get("data") is not None:
if raw_layer.get("encoding") is not None:
tile_layer.data = _decode_tile_layer_data(
data=cast(str, raw_layer["data"]),
compression=raw_layer["compression"],
layer_width=raw_layer["width"],
)
else:
tile_layer.data = _convert_raw_tile_layer_data(
raw_layer["data"], raw_layer["width"] # type: ignore
)
return tile_layer
def _parse_object_layer(
raw_layer: RawLayer,
parent_dir: Optional[Path] = None,
) -> ObjectLayer:
"""Parse the raw_layer to an ObjectLayer.
Args:
raw_layer: RawLayer to be parsed to an ObjectLayer.
Returns:
ObjectLayer: The ObjectLayer created from raw_layer
"""
objects = []
for object_ in raw_layer["objects"]:
objects.append(parse_object(object_, parent_dir))
return ObjectLayer(
tiled_objects=objects,
draw_order=raw_layer["draworder"],
**_parse_common(raw_layer).__dict__,
)
def _parse_image_layer(raw_layer: RawLayer) -> ImageLayer:
"""Parse the raw_layer to an ImageLayer.
Args:
raw_layer: RawLayer to be parsed to an ImageLayer.
Returns:
ImageLayer: The ImageLayer created from raw_layer
"""
image_layer = ImageLayer(
image=Path(raw_layer["image"]), **_parse_common(raw_layer).__dict__
)
if raw_layer.get("transparentcolor") is not None:
image_layer.transparent_color = parse_color(raw_layer["transparentcolor"])
return image_layer
def _parse_group_layer(
raw_layer: RawLayer, parent_dir: Optional[Path] = None
) -> LayerGroup:
"""Parse the raw_layer to a LayerGroup.
Args:
raw_layer: RawLayer to be parsed to a LayerGroup.
Returns:
LayerGroup: The LayerGroup created from raw_layer
"""
layers = []
for layer in raw_layer["layers"]:
layers.append(parse(layer, parent_dir=parent_dir))
return LayerGroup(layers=layers, **_parse_common(raw_layer).__dict__)
def parse(
raw_layer: RawLayer,
parent_dir: Optional[Path] = None,
) -> Layer:
"""Parse a raw Layer into a pytiled_parser object.
This function will determine the type of layer and parse accordingly.
Args:
raw_layer: Raw layer to be parsed.
parent_dir: The parent directory that the map file is in.
Returns:
Layer: A parsed Layer.
Raises:
RuntimeError: For an invalid layer type being provided
"""
type_ = raw_layer["type"]
if type_ == "objectgroup":
return _parse_object_layer(raw_layer, parent_dir)
elif type_ == "group":
return _parse_group_layer(raw_layer, parent_dir)
elif type_ == "imagelayer":
return _parse_image_layer(raw_layer)
elif type_ == "tilelayer":
return _parse_tile_layer(raw_layer)
raise RuntimeError(f"An invalid layer type of {type_} was supplied")

View File

@@ -0,0 +1,48 @@
"""Property parsing for the JSON Map Format
"""
from pathlib import Path
from typing import List, Union, cast
from typing_extensions import TypedDict
from pytiled_parser.properties import Properties, Property
from pytiled_parser.util import parse_color
RawValue = Union[float, str, bool]
class RawProperty(TypedDict):
"""The keys and their values that appear in a Tiled JSON Property Object.
Tiled Docs: https://doc.mapeditor.org/en/stable/reference/json-map-format/#property
"""
name: str
type: str
value: RawValue
def parse(raw_properties: List[RawProperty]) -> Properties:
"""Parse a list of `RawProperty` objects into `Properties`.
Args:
raw_properties: The list of `RawProperty` objects to parse.
Returns:
Properties: The parsed `Property` objects.
"""
final: Properties = {}
value: Property
for raw_property in raw_properties:
if raw_property["type"] == "file":
value = Path(cast(str, raw_property["value"]))
elif raw_property["type"] == "color":
value = parse_color(cast(str, raw_property["value"]))
else:
value = raw_property["value"]
final[raw_property["name"]] = value
return final

View File

@@ -0,0 +1,153 @@
import json
from pathlib import Path
from typing import List, Union, cast
from typing_extensions import TypedDict
from pytiled_parser.common_types import Size
from pytiled_parser.parsers.json.layer import RawLayer
from pytiled_parser.parsers.json.layer import parse as parse_layer
from pytiled_parser.parsers.json.properties import RawProperty
from pytiled_parser.parsers.json.properties import parse as parse_properties
from pytiled_parser.parsers.json.tileset import RawTileSet
from pytiled_parser.parsers.json.tileset import parse as parse_tileset
from pytiled_parser.tiled_map import TiledMap, TilesetDict
from pytiled_parser.util import parse_color
class RawTilesetMapping(TypedDict):
firstgid: int
source: str
class RawTiledMap(TypedDict):
"""The keys and their types that appear in a Tiled JSON Map Object.
Tiled Docs: https://doc.mapeditor.org/en/stable/reference/json-map-format/#map
"""
backgroundcolor: str
compressionlevel: int
height: int
hexsidelength: int
infinite: bool
layers: List[RawLayer]
nextlayerid: int
nextobjectid: int
orientation: str
properties: List[RawProperty]
renderorder: str
staggeraxis: str
staggerindex: str
tiledversion: str
tileheight: int
tilesets: List[RawTilesetMapping]
tilewidth: int
type: str
version: Union[str, float]
width: int
def parse(file: Path) -> TiledMap:
"""Parse the raw Tiled map into a pytiled_parser type.
Args:
file: Path to the map file.
Returns:
TiledMap: A parsed TiledMap.
"""
with open(file) as map_file:
raw_tiled_map = json.load(map_file)
parent_dir = file.parent
raw_tilesets: List[Union[RawTileSet, RawTilesetMapping]] = raw_tiled_map["tilesets"]
tilesets: TilesetDict = {}
for raw_tileset in raw_tilesets:
if raw_tileset.get("source") is not None:
# Is an external Tileset
tileset_path = Path(parent_dir / raw_tileset["source"])
with open(tileset_path) as raw_tileset_file:
tilesets[raw_tileset["firstgid"]] = parse_tileset(
json.load(raw_tileset_file),
raw_tileset["firstgid"],
external_path=tileset_path.parent,
)
else:
# Is an embedded Tileset
raw_tileset = cast(RawTileSet, raw_tileset)
tilesets[raw_tileset["firstgid"]] = parse_tileset(
raw_tileset, raw_tileset["firstgid"]
)
if isinstance(raw_tiled_map["version"], float):
version = str(raw_tiled_map["version"])
else:
version = raw_tiled_map["version"]
# `map` is a built-in function
map_ = TiledMap(
map_file=file,
infinite=raw_tiled_map["infinite"],
layers=[parse_layer(layer_, parent_dir) for layer_ in raw_tiled_map["layers"]],
map_size=Size(raw_tiled_map["width"], raw_tiled_map["height"]),
next_layer_id=raw_tiled_map["nextlayerid"],
next_object_id=raw_tiled_map["nextobjectid"],
orientation=raw_tiled_map["orientation"],
render_order=raw_tiled_map["renderorder"],
tiled_version=raw_tiled_map["tiledversion"],
tile_size=Size(raw_tiled_map["tilewidth"], raw_tiled_map["tileheight"]),
tilesets=tilesets,
version=version,
)
layers = [layer for layer in map_.layers if hasattr(layer, "tiled_objects")]
for my_layer in layers:
for tiled_object in my_layer.tiled_objects: # type: ignore
if hasattr(tiled_object, "new_tileset"):
if tiled_object.new_tileset:
already_loaded = None
for val in map_.tilesets.values():
if val.name == tiled_object.new_tileset["name"]:
already_loaded = val
break
if not already_loaded:
highest_firstgid = max(map_.tilesets.keys())
last_tileset_count = map_.tilesets[highest_firstgid].tile_count
new_firstgid = highest_firstgid + last_tileset_count
map_.tilesets[new_firstgid] = parse_tileset(
tiled_object.new_tileset,
new_firstgid,
tiled_object.new_tileset_path,
)
tiled_object.gid = tiled_object.gid + (new_firstgid - 1)
else:
tiled_object.gid = tiled_object.gid + (
already_loaded.firstgid - 1
)
tiled_object.new_tileset = None
tiled_object.new_tileset_path = None
if raw_tiled_map.get("backgroundcolor") is not None:
map_.background_color = parse_color(raw_tiled_map["backgroundcolor"])
if raw_tiled_map.get("hexsidelength") is not None:
map_.hex_side_length = raw_tiled_map["hexsidelength"]
if raw_tiled_map.get("properties") is not None:
map_.properties = parse_properties(raw_tiled_map["properties"])
if raw_tiled_map.get("staggeraxis") is not None:
map_.stagger_axis = raw_tiled_map["staggeraxis"]
if raw_tiled_map.get("staggerindex") is not None:
map_.stagger_index = raw_tiled_map["staggerindex"]
return map_

View File

@@ -0,0 +1,321 @@
"""Object parsing for the JSON Map Format.
"""
import json
from pathlib import Path
from typing import Any, Callable, Dict, List, Optional
from typing_extensions import TypedDict
from pytiled_parser.common_types import OrderedPair, Size
from pytiled_parser.parsers.json.properties import RawProperty
from pytiled_parser.parsers.json.properties import parse as parse_properties
from pytiled_parser.tiled_object import (
Ellipse,
Point,
Polygon,
Polyline,
Rectangle,
Text,
Tile,
TiledObject,
)
from pytiled_parser.util import parse_color
class RawText(TypedDict):
"""The keys and their types that appear in a Tiled JSON Text Object.
Tiled Doc: https://doc.mapeditor.org/en/stable/reference/json-map-format/#text-example
"""
text: str
color: str
fontfamily: str
pixelsize: float # this is `font_size` in Text
bold: bool
italic: bool
strikeout: bool
underline: bool
kerning: bool
halign: str
valign: str
wrap: bool
class RawObject(TypedDict):
"""The keys and their types that appear in a Tiled JSON Object.
Tiled Doc: https://doc.mapeditor.org/en/stable/reference/json-map-format/#object
"""
id: int
gid: int
template: str
x: float
y: float
width: float
height: float
rotation: float
visible: bool
name: str
type: str
properties: List[RawProperty]
ellipse: bool
point: bool
polygon: List[Dict[str, float]]
polyline: List[Dict[str, float]]
text: RawText
def _parse_common(raw_object: RawObject) -> TiledObject:
"""Create an Object containing all the attributes common to all types of objects.
Args:
raw_object: Raw object to get common attributes from
Returns:
Object: The attributes in common of all types of objects
"""
common = TiledObject(
id=raw_object["id"],
coordinates=OrderedPair(raw_object["x"], raw_object["y"]),
visible=raw_object["visible"],
size=Size(raw_object["width"], raw_object["height"]),
rotation=raw_object["rotation"],
name=raw_object["name"],
type=raw_object["type"],
)
if raw_object.get("properties") is not None:
common.properties = parse_properties(raw_object["properties"])
return common
def _parse_ellipse(raw_object: RawObject) -> Ellipse:
"""Parse the raw object into an Ellipse.
Args:
raw_object: Raw object to be parsed to an Ellipse
Returns:
Ellipse: The Ellipse object created from the raw object
"""
return Ellipse(**_parse_common(raw_object).__dict__)
def _parse_rectangle(raw_object: RawObject) -> Rectangle:
"""Parse the raw object into a Rectangle.
Args:
raw_object: Raw object to be parsed to a Rectangle
Returns:
Rectangle: The Rectangle object created from the raw object
"""
return Rectangle(**_parse_common(raw_object).__dict__)
def _parse_point(raw_object: RawObject) -> Point:
"""Parse the raw object into a Point.
Args:
raw_object: Raw object to be parsed to a Point
Returns:
Point: The Point object created from the raw object
"""
return Point(**_parse_common(raw_object).__dict__)
def _parse_polygon(raw_object: RawObject) -> Polygon:
"""Parse the raw object into a Polygon.
Args:
raw_object: Raw object to be parsed to a Polygon
Returns:
Polygon: The Polygon object created from the raw object
"""
polygon = []
for point in raw_object["polygon"]:
polygon.append(OrderedPair(point["x"], point["y"]))
return Polygon(points=polygon, **_parse_common(raw_object).__dict__)
def _parse_polyline(raw_object: RawObject) -> Polyline:
"""Parse the raw object into a Polyline.
Args:
raw_object: Raw object to be parsed to a Polyline
Returns:
Polyline: The Polyline object created from the raw object
"""
polyline = []
for point in raw_object["polyline"]:
polyline.append(OrderedPair(point["x"], point["y"]))
return Polyline(points=polyline, **_parse_common(raw_object).__dict__)
def _parse_tile(
raw_object: RawObject,
new_tileset: Optional[Dict[str, Any]] = None,
new_tileset_path: Optional[Path] = None,
) -> Tile:
"""Parse the raw object into a Tile.
Args:
raw_object: Raw object to be parsed to a Tile
Returns:
Tile: The Tile object created from the raw object
"""
gid = raw_object["gid"]
return Tile(
gid=gid,
new_tileset=new_tileset,
new_tileset_path=new_tileset_path,
**_parse_common(raw_object).__dict__
)
def _parse_text(raw_object: RawObject) -> Text:
"""Parse the raw object into Text.
Args:
raw_object: Raw object to be parsed to a Text
Returns:
Text: The Text object created from the raw object
"""
# required attributes
raw_text: RawText = raw_object["text"]
text = raw_text["text"]
# create base Text object
text_object = Text(text=text, **_parse_common(raw_object).__dict__)
# optional attributes
if raw_text.get("color") is not None:
text_object.color = parse_color(raw_text["color"])
if raw_text.get("fontfamily") is not None:
text_object.font_family = raw_text["fontfamily"]
if raw_text.get("pixelsize") is not None:
text_object.font_size = raw_text["pixelsize"]
if raw_text.get("bold") is not None:
text_object.bold = raw_text["bold"]
if raw_text.get("italic") is not None:
text_object.italic = raw_text["italic"]
if raw_text.get("kerning") is not None:
text_object.kerning = raw_text["kerning"]
if raw_text.get("strikeout") is not None:
text_object.strike_out = raw_text["strikeout"]
if raw_text.get("underline") is not None:
text_object.underline = raw_text["underline"]
if raw_text.get("halign") is not None:
text_object.horizontal_align = raw_text["halign"]
if raw_text.get("valign") is not None:
text_object.vertical_align = raw_text["valign"]
if raw_text.get("wrap") is not None:
text_object.wrap = raw_text["wrap"]
return text_object
def _get_parser(raw_object: RawObject) -> Callable[[RawObject], TiledObject]:
"""Get the parser function for a given raw object.
Only used internally by the JSON parser.
Args:
raw_object: Raw object that is analyzed to determine the parser function.
Returns:
Callable[[RawObject], Object]: The parser function.
"""
if raw_object.get("ellipse"):
return _parse_ellipse
if raw_object.get("point"):
return _parse_point
if raw_object.get("gid"):
# Only tile objects have the `gid` key
return _parse_tile
if raw_object.get("polygon"):
return _parse_polygon
if raw_object.get("polyline"):
return _parse_polyline
if raw_object.get("text"):
return _parse_text
# If it's none of the above, rectangle is the only one left.
# Rectangle is the only object which has no special properties to signify that.
return _parse_rectangle
def parse(
raw_object: RawObject,
parent_dir: Optional[Path] = None,
) -> TiledObject:
"""Parse the raw object into a pytiled_parser version
Args:
raw_object: Raw object that is to be cast.
parent_dir: The parent directory that the map file is in.
Returns:
Object: A parsed Object.
Raises:
RuntimeError: When a parameter that is conditionally required was not sent.
"""
new_tileset = None
new_tileset_path = None
if raw_object.get("template"):
if not parent_dir:
raise RuntimeError(
"A parent directory must be specified when using object templates."
)
template_path = Path(parent_dir / raw_object["template"])
with open(template_path) as raw_template_file:
template = json.load(raw_template_file)
if "tileset" in template:
tileset_path = Path(
template_path.parent / template["tileset"]["source"]
)
with open(tileset_path) as raw_tileset_file:
new_tileset = json.load(raw_tileset_file)
new_tileset_path = tileset_path.parent
loaded_template = template["object"]
for key in loaded_template:
if key != "id":
raw_object[key] = loaded_template[key] # type: ignore
if raw_object.get("gid"):
return _parse_tile(raw_object, new_tileset, new_tileset_path)
return _get_parser(raw_object)(raw_object)

View File

@@ -0,0 +1,272 @@
from pathlib import Path
from typing import List, Optional, Union
from typing_extensions import TypedDict
from pytiled_parser.common_types import OrderedPair
from pytiled_parser.parsers.json.layer import RawLayer
from pytiled_parser.parsers.json.layer import parse as parse_layer
from pytiled_parser.parsers.json.properties import RawProperty
from pytiled_parser.parsers.json.properties import parse as parse_properties
from pytiled_parser.parsers.json.wang_set import RawWangSet
from pytiled_parser.parsers.json.wang_set import parse as parse_wangset
from pytiled_parser.tileset import Frame, Grid, Tile, Tileset, Transformations
from pytiled_parser.util import parse_color
class RawFrame(TypedDict):
"""The keys and their types that appear in a Frame JSON Object."""
duration: int
tileid: int
class RawTileOffset(TypedDict):
"""The keys and their types that appear in a TileOffset JSON Object."""
x: int
y: int
class RawTransformations(TypedDict):
"""The keys and their types that appear in a Transformations JSON Object."""
hflip: bool
vflip: bool
rotate: bool
preferuntransformed: bool
class RawTile(TypedDict):
"""The keys and their types that appear in a Tile JSON Object."""
animation: List[RawFrame]
id: int
image: str
imageheight: int
imagewidth: int
opacity: float
properties: List[RawProperty]
objectgroup: RawLayer
type: str
class RawGrid(TypedDict):
"""The keys and their types that appear in a Grid JSON Object."""
height: int
width: int
orientation: str
class RawTileSet(TypedDict):
"""The keys and their types that appear in a TileSet JSON Object."""
backgroundcolor: str
columns: int
firstgid: int
grid: RawGrid
image: str
imageheight: int
imagewidth: int
margin: int
name: str
properties: List[RawProperty]
source: str
spacing: int
tilecount: int
tiledversion: str
tileheight: int
tileoffset: RawTileOffset
tiles: List[RawTile]
tilewidth: int
transparentcolor: str
transformations: RawTransformations
version: Union[str, float]
wangsets: List[RawWangSet]
def _parse_frame(raw_frame: RawFrame) -> Frame:
"""Parse the raw_frame to a Frame.
Args:
raw_frame: RawFrame to be parsed to a Frame
Returns:
Frame: The Frame created from the raw_frame
"""
return Frame(duration=raw_frame["duration"], tile_id=raw_frame["tileid"])
def _parse_tile_offset(raw_tile_offset: RawTileOffset) -> OrderedPair:
"""Parse the raw_tile_offset to an OrderedPair.
Args:
raw_tile_offset: RawTileOffset to be parsed to an OrderedPair
Returns:
OrderedPair: The OrderedPair created from the raw_tile_offset
"""
return OrderedPair(raw_tile_offset["x"], raw_tile_offset["y"])
def _parse_transformations(raw_transformations: RawTransformations) -> Transformations:
"""Parse the raw_transformations to a Transformations object.
Args:
raw_transformations: RawTransformations to be parsed to a Transformations
Returns:
Transformations: The Transformations created from the raw_transformations
"""
return Transformations(
hflip=raw_transformations["hflip"],
vflip=raw_transformations["vflip"],
rotate=raw_transformations["rotate"],
prefer_untransformed=raw_transformations["preferuntransformed"],
)
def _parse_grid(raw_grid: RawGrid) -> Grid:
"""Parse the raw_grid to a Grid object.
Args:
raw_grid: RawGrid to be parsed to a Grid
Returns:
Grid: The Grid created from the raw_grid
"""
return Grid(
orientation=raw_grid["orientation"],
width=raw_grid["width"],
height=raw_grid["height"],
)
def _parse_tile(raw_tile: RawTile, external_path: Optional[Path] = None) -> Tile:
"""Parse the raw_tile to a Tile object.
Args:
raw_tile: RawTile to be parsed to a Tile
Returns:
Tile: The Tile created from the raw_tile
"""
id_ = raw_tile["id"]
tile = Tile(id=id_)
if raw_tile.get("animation") is not None:
tile.animation = []
for frame in raw_tile["animation"]:
tile.animation.append(_parse_frame(frame))
if raw_tile.get("objectgroup") is not None:
tile.objects = parse_layer(raw_tile["objectgroup"])
if raw_tile.get("properties") is not None:
tile.properties = parse_properties(raw_tile["properties"])
if raw_tile.get("image") is not None:
if external_path:
tile.image = Path(external_path / raw_tile["image"]).absolute().resolve()
else:
tile.image = Path(raw_tile["image"])
if raw_tile.get("imagewidth") is not None:
tile.image_width = raw_tile["imagewidth"]
if raw_tile.get("imageheight") is not None:
tile.image_height = raw_tile["imageheight"]
if raw_tile.get("type") is not None:
tile.type = raw_tile["type"]
return tile
def parse(
raw_tileset: RawTileSet,
firstgid: int,
external_path: Optional[Path] = None,
) -> Tileset:
"""Parse the raw tileset into a pytiled_parser type
Args:
raw_tileset: Raw Tileset to be parsed.
firstgid: GID corresponding the first tile in the set.
external_path: The path to the tileset if it is not an embedded one.
Returns:
TileSet: a properly typed TileSet.
"""
tileset = Tileset(
name=raw_tileset["name"],
tile_count=raw_tileset["tilecount"],
tile_width=raw_tileset["tilewidth"],
tile_height=raw_tileset["tileheight"],
columns=raw_tileset["columns"],
spacing=raw_tileset["spacing"],
margin=raw_tileset["margin"],
firstgid=firstgid,
)
if raw_tileset.get("version") is not None:
if isinstance(raw_tileset["version"], float):
tileset.version = str(raw_tileset["version"])
else:
tileset.version = raw_tileset["version"]
if raw_tileset.get("tiledversion") is not None:
tileset.tiled_version = raw_tileset["tiledversion"]
if raw_tileset.get("image") is not None:
if external_path:
tileset.image = (
Path(external_path / raw_tileset["image"]).absolute().resolve()
)
else:
tileset.image = Path(raw_tileset["image"])
if raw_tileset.get("imagewidth") is not None:
tileset.image_width = raw_tileset["imagewidth"]
if raw_tileset.get("imageheight") is not None:
tileset.image_height = raw_tileset["imageheight"]
if raw_tileset.get("backgroundcolor") is not None:
tileset.background_color = parse_color(raw_tileset["backgroundcolor"])
if raw_tileset.get("tileoffset") is not None:
tileset.tile_offset = _parse_tile_offset(raw_tileset["tileoffset"])
if raw_tileset.get("transparentcolor") is not None:
tileset.transparent_color = parse_color(raw_tileset["transparentcolor"])
if raw_tileset.get("grid") is not None:
tileset.grid = _parse_grid(raw_tileset["grid"])
if raw_tileset.get("properties") is not None:
tileset.properties = parse_properties(raw_tileset["properties"])
if raw_tileset.get("tiles") is not None:
tiles = {}
for raw_tile in raw_tileset["tiles"]:
tiles[raw_tile["id"]] = _parse_tile(raw_tile, external_path=external_path)
tileset.tiles = tiles
if raw_tileset.get("wangsets") is not None:
wangsets = []
for raw_wangset in raw_tileset["wangsets"]:
wangsets.append(parse_wangset(raw_wangset))
tileset.wang_sets = wangsets
if raw_tileset.get("transformations") is not None:
tileset.transformations = _parse_transformations(raw_tileset["transformations"])
return tileset

View File

@@ -0,0 +1,104 @@
from typing import List
from typing_extensions import TypedDict
from pytiled_parser.parsers.json.properties import RawProperty
from pytiled_parser.parsers.json.properties import parse as parse_properties
from pytiled_parser.util import parse_color
from pytiled_parser.wang_set import WangColor, WangSet, WangTile
class RawWangTile(TypedDict):
"""The keys and their types that appear in a Wang Tile JSON Object."""
tileid: int
# Tiled stores these IDs as a list represented like so:
# [top, top_right, right, bottom_right, bottom, bottom_left, left, top_left]
wangid: List[int]
class RawWangColor(TypedDict):
"""The keys and their types that appear in a Wang Color JSON Object."""
color: str
name: str
probability: float
tile: int
properties: List[RawProperty]
class RawWangSet(TypedDict):
"""The keys and their types that appear in a Wang Set JSON Object."""
colors: List[RawWangColor]
name: str
properties: List[RawProperty]
tile: int
type: str
wangtiles: List[RawWangTile]
def _parse_wang_tile(raw_wang_tile: RawWangTile) -> WangTile:
"""Parse the raw wang tile into a pytiled_parser type
Args:
raw_wang_tile: RawWangTile to be parsed.
Returns:
WangTile: A properly typed WangTile.
"""
return WangTile(tile_id=raw_wang_tile["tileid"], wang_id=raw_wang_tile["wangid"])
def _parse_wang_color(raw_wang_color: RawWangColor) -> WangColor:
"""Parse the raw wang color into a pytiled_parser type
Args:
raw_wang_color: RawWangColor to be parsed.
Returns:
WangColor: A properly typed WangColor.
"""
wang_color = WangColor(
name=raw_wang_color["name"],
color=parse_color(raw_wang_color["color"]),
tile=raw_wang_color["tile"],
probability=raw_wang_color["probability"],
)
if raw_wang_color.get("properties") is not None:
wang_color.properties = parse_properties(raw_wang_color["properties"])
return wang_color
def parse(raw_wangset: RawWangSet) -> WangSet:
"""Parse the raw wangset into a pytiled_parser type
Args:
raw_wangset: Raw Wangset to be parsed.
Returns:
WangSet: A properly typed WangSet.
"""
colors = []
for raw_wang_color in raw_wangset["colors"]:
colors.append(_parse_wang_color(raw_wang_color))
tiles = {}
for raw_wang_tile in raw_wangset["wangtiles"]:
tiles[raw_wang_tile["tileid"]] = _parse_wang_tile(raw_wang_tile)
wangset = WangSet(
name=raw_wangset["name"],
tile=raw_wangset["tile"],
wang_type=raw_wangset["type"],
wang_colors=colors,
wang_tiles=tiles,
)
if raw_wangset.get("properties") is not None:
wangset.properties = parse_properties(raw_wangset["properties"])
return wangset

View File

@@ -1,55 +1,18 @@
"""Properties Module
This module casts raw properties from Tiled maps into a dictionary of
properly typed Properties.
This module defines types for Property objects.
For more about properties in Tiled maps see the below link:
https://doc.mapeditor.org/en/stable/manual/custom-properties/
The types defined in this module get added to other objects
such as Layers, Maps, Objects, etc
"""
from pathlib import Path
from typing import Dict, List, Union
from typing import cast as type_cast
from typing_extensions import TypedDict
from typing import Dict, Union
from .common_types import Color
from .util import parse_color
Property = Union[float, Path, str, bool, Color]
Properties = Dict[str, Property]
RawValue = Union[float, str, bool]
class RawProperty(TypedDict):
"""A dictionary of raw properties."""
name: str
type: str
value: RawValue
def cast(raw_properties: List[RawProperty]) -> Properties:
"""Cast a list of `RawProperty`s into `Properties`
Args:
raw_properties: The list of `RawProperty`s to cast.
Returns:
Properties: The casted `Properties`.
"""
final: Properties = {}
value: Property
for property_ in raw_properties:
if property_["type"] == "file":
value = Path(type_cast(str, property_["value"]))
elif property_["type"] == "color":
value = parse_color(type_cast(str, property_["value"]))
else:
value = property_["value"]
final[property_["name"]] = value
return final

View File

@@ -1,19 +1,12 @@
# pylint: disable=too-few-public-methods
import json
from pathlib import Path
from typing import Dict, List, Optional, Union
from typing import cast as typing_cast
from typing import Dict, List, Optional
import attr
from typing_extensions import TypedDict
from . import layer, properties, tileset
from .common_types import Color, Size
from .layer import Layer, RawLayer
from .properties import Properties, RawProperty
from .tileset import RawTileSet, Tileset
from .util import parse_color
from pytiled_parser.common_types import Color, Size
from pytiled_parser.layer import Layer
from pytiled_parser.properties import Properties
from pytiled_parser.tileset import Tileset
TilesetDict = Dict[int, Tileset]
@@ -68,146 +61,3 @@ class TiledMap:
hex_side_length: Optional[int] = None
stagger_axis: Optional[str] = None
stagger_index: Optional[str] = None
class _RawTilesetMapping(TypedDict):
""" The way that tilesets are stored in the Tiled JSON formatted map."""
firstgid: int
source: str
class _RawTiledMap(TypedDict):
"""The keys and their types that appear in a Tiled JSON Map.
Keys:
compressionlevel: not documented - https://github.com/bjorn/tiled/issues/2815
"""
backgroundcolor: str
compressionlevel: int
height: int
hexsidelength: int
infinite: bool
layers: List[RawLayer]
nextlayerid: int
nextobjectid: int
orientation: str
properties: List[RawProperty]
renderorder: str
staggeraxis: str
staggerindex: str
tiledversion: str
tileheight: int
tilesets: List[_RawTilesetMapping]
tilewidth: int
type: str
version: Union[str, float]
width: int
def parse_map(file: Path) -> TiledMap:
"""Parse the raw Tiled map into a pytiled_parser type
Args:
file: Path to the map's JSON file
Returns:
TileSet: a properly typed TileSet.
"""
with open(file) as map_file:
raw_tiled_map = json.load(map_file)
parent_dir = file.parent
raw_tilesets: List[Union[RawTileSet, _RawTilesetMapping]] = raw_tiled_map[
"tilesets"
]
tilesets: TilesetDict = {}
for raw_tileset in raw_tilesets:
if raw_tileset.get("source") is not None:
# Is an external Tileset
tileset_path = Path(parent_dir / raw_tileset["source"])
with open(tileset_path) as raw_tileset_file:
tilesets[raw_tileset["firstgid"]] = tileset.cast(
json.load(raw_tileset_file),
raw_tileset["firstgid"],
external_path=tileset_path.parent,
)
else:
# Is an embedded Tileset
raw_tileset = typing_cast(RawTileSet, raw_tileset)
tilesets[raw_tileset["firstgid"]] = tileset.cast(
raw_tileset, raw_tileset["firstgid"]
)
if isinstance(raw_tiled_map["version"], float):
version = str(raw_tiled_map["version"])
else:
version = raw_tiled_map["version"]
# `map` is a built-in function
map_ = TiledMap(
map_file=file,
infinite=raw_tiled_map["infinite"],
layers=[layer.cast(layer_, parent_dir) for layer_ in raw_tiled_map["layers"]],
map_size=Size(raw_tiled_map["width"], raw_tiled_map["height"]),
next_layer_id=raw_tiled_map["nextlayerid"],
next_object_id=raw_tiled_map["nextobjectid"],
orientation=raw_tiled_map["orientation"],
render_order=raw_tiled_map["renderorder"],
tiled_version=raw_tiled_map["tiledversion"],
tile_size=Size(raw_tiled_map["tilewidth"], raw_tiled_map["tileheight"]),
tilesets=tilesets,
version=version,
)
layers = [layer for layer in map_.layers if hasattr(layer, "tiled_objects")]
for my_layer in layers:
for tiled_object in my_layer.tiled_objects: # type: ignore
if hasattr(tiled_object, "new_tileset"):
if tiled_object.new_tileset:
already_loaded = None
for val in map_.tilesets.values():
if val.name == tiled_object.new_tileset["name"]:
already_loaded = val
break
if not already_loaded:
highest_firstgid = max(map_.tilesets.keys())
last_tileset_count = map_.tilesets[highest_firstgid].tile_count
new_firstgid = highest_firstgid + last_tileset_count
map_.tilesets[new_firstgid] = tileset.cast(
tiled_object.new_tileset,
new_firstgid,
tiled_object.new_tileset_path,
)
tiled_object.gid = tiled_object.gid + (new_firstgid - 1)
else:
tiled_object.gid = tiled_object.gid + (
already_loaded.firstgid - 1
)
tiled_object.new_tileset = None
tiled_object.new_tileset_path = None
if raw_tiled_map.get("backgroundcolor") is not None:
map_.background_color = parse_color(raw_tiled_map["backgroundcolor"])
if raw_tiled_map.get("hexsidelength") is not None:
map_.hex_side_length = raw_tiled_map["hexsidelength"]
if raw_tiled_map.get("properties") is not None:
map_.properties = properties.cast(raw_tiled_map["properties"])
if raw_tiled_map.get("staggeraxis") is not None:
map_.stagger_axis = raw_tiled_map["staggeraxis"]
if raw_tiled_map.get("staggerindex") is not None:
map_.stagger_index = raw_tiled_map["staggerindex"]
return map_

View File

@@ -1,14 +1,11 @@
# pylint: disable=too-few-public-methods
import json
from pathlib import Path
from typing import Any, Callable, Dict, List, Optional, Union
from typing import Any, Dict, List, Optional
import attr
from typing_extensions import TypedDict
from . import properties as properties_
from .common_types import Color, OrderedPair, Size
from .util import parse_color
@attr.s(auto_attribs=True, kw_only=True)
@@ -150,300 +147,3 @@ class Tile(TiledObject):
gid: int
new_tileset: Optional[Dict[str, Any]] = None
new_tileset_path: Optional[Path] = None
class RawTextDict(TypedDict):
""" The keys and their types that appear in a Text JSON Object."""
text: str
color: str
fontfamily: str
pixelsize: float # this is `font_size` in Text
bold: bool
italic: bool
strikeout: bool
underline: bool
kerning: bool
halign: str
valign: str
wrap: bool
class RawTiledObject(TypedDict):
""" The keys and their types that appear in a Tiled JSON Object."""
id: int
gid: int
template: str
x: float
y: float
width: float
height: float
rotation: float
visible: bool
name: str
type: str
properties: List[properties_.RawProperty]
ellipse: bool
point: bool
polygon: List[Dict[str, float]]
polyline: List[Dict[str, float]]
text: Dict[str, Union[float, str]]
RawTiledObjects = List[RawTiledObject]
def _get_common_attributes(raw_tiled_object: RawTiledObject) -> TiledObject:
"""Create a TiledObject containing all the attributes common to all tiled objects
Args:
raw_tiled_object: Raw Tiled object get common attributes from
Returns:
TiledObject: The attributes in common of all Tiled Objects
"""
common_attributes = TiledObject(
id=raw_tiled_object["id"],
coordinates=OrderedPair(raw_tiled_object["x"], raw_tiled_object["y"]),
visible=raw_tiled_object["visible"],
size=Size(raw_tiled_object["width"], raw_tiled_object["height"]),
rotation=raw_tiled_object["rotation"],
name=raw_tiled_object["name"],
type=raw_tiled_object["type"],
)
if raw_tiled_object.get("properties") is not None:
common_attributes.properties = properties_.cast(raw_tiled_object["properties"])
return common_attributes
def _cast_ellipse(raw_tiled_object: RawTiledObject) -> Ellipse:
"""Cast the raw_tiled_object to an Ellipse object.
Args:
raw_tiled_object: Raw Tiled object to be casted to an Ellipse
Returns:
Ellipse: The Ellipse object created from the raw_tiled_object
"""
return Ellipse(**_get_common_attributes(raw_tiled_object).__dict__)
def _cast_rectangle(raw_tiled_object: RawTiledObject) -> Rectangle:
"""Cast the raw_tiled_object to a Rectangle object.
Args:
raw_tiled_object: Raw Tiled object to be casted to a Rectangle
Returns:
Rectangle: The Rectangle object created from the raw_tiled_object
"""
return Rectangle(**_get_common_attributes(raw_tiled_object).__dict__)
def _cast_point(raw_tiled_object: RawTiledObject) -> Point:
"""Cast the raw_tiled_object to a Point object.
Args:
raw_tiled_object: Raw Tiled object to be casted to a Point
Returns:
Point: The Point object created from the raw_tiled_object
"""
return Point(**_get_common_attributes(raw_tiled_object).__dict__)
def _cast_tile(
raw_tiled_object: RawTiledObject,
new_tileset: Optional[Dict[str, Any]] = None,
new_tileset_path: Optional[Path] = None,
) -> Tile:
"""Cast the raw_tiled_object to a Tile object.
Args:
raw_tiled_object: Raw Tiled object to be casted to a Tile
Returns:
Tile: The Tile object created from the raw_tiled_object
"""
gid = raw_tiled_object["gid"]
return Tile(
gid=gid,
new_tileset=new_tileset,
new_tileset_path=new_tileset_path,
**_get_common_attributes(raw_tiled_object).__dict__
)
def _cast_polygon(raw_tiled_object: RawTiledObject) -> Polygon:
"""Cast the raw_tiled_object to a Polygon object.
Args:
raw_tiled_object: Raw Tiled object to be casted to a Polygon
Returns:
Polygon: The Polygon object created from the raw_tiled_object
"""
polygon = []
for point in raw_tiled_object["polygon"]:
polygon.append(OrderedPair(point["x"], point["y"]))
return Polygon(points=polygon, **_get_common_attributes(raw_tiled_object).__dict__)
def _cast_polyline(raw_tiled_object: RawTiledObject) -> Polyline:
"""Cast the raw_tiled_object to a Polyline object.
Args:
raw_tiled_object: Raw Tiled Object to be casted to a Polyline
Returns:
Polyline: The Polyline object created from the raw_tiled_object
"""
polyline = []
for point in raw_tiled_object["polyline"]:
polyline.append(OrderedPair(point["x"], point["y"]))
return Polyline(
points=polyline, **_get_common_attributes(raw_tiled_object).__dict__
)
def _cast_text(raw_tiled_object: RawTiledObject) -> Text:
"""Cast the raw_tiled_object to a Text object.
Args:
raw_tiled_object: Raw Tiled object to be casted to a Text object
Returns:
Text: The Text object created from the raw_tiled_object
"""
# required attributes
raw_text_dict: RawTextDict = raw_tiled_object["text"]
text = raw_text_dict["text"]
# create base Text object
text_object = Text(text=text, **_get_common_attributes(raw_tiled_object).__dict__)
# optional attributes
if raw_text_dict.get("color") is not None:
text_object.color = parse_color(raw_text_dict["color"])
if raw_text_dict.get("fontfamily") is not None:
text_object.font_family = raw_text_dict["fontfamily"]
if raw_text_dict.get("pixelsize") is not None:
text_object.font_size = raw_text_dict["pixelsize"]
if raw_text_dict.get("bold") is not None:
text_object.bold = raw_text_dict["bold"]
if raw_text_dict.get("italic") is not None:
text_object.italic = raw_text_dict["italic"]
if raw_text_dict.get("kerning") is not None:
text_object.kerning = raw_text_dict["kerning"]
if raw_text_dict.get("strikeout") is not None:
text_object.strike_out = raw_text_dict["strikeout"]
if raw_text_dict.get("underline") is not None:
text_object.underline = raw_text_dict["underline"]
if raw_text_dict.get("halign") is not None:
text_object.horizontal_align = raw_text_dict["halign"]
if raw_text_dict.get("valign") is not None:
text_object.vertical_align = raw_text_dict["valign"]
if raw_text_dict.get("wrap") is not None:
text_object.wrap = raw_text_dict["wrap"]
return text_object
def _get_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.
"""
if raw_tiled_object.get("ellipse"):
return _cast_ellipse
if raw_tiled_object.get("point"):
return _cast_point
if raw_tiled_object.get("gid"):
# Only Tile objects have the `gid` key (I think)
return _cast_tile
if raw_tiled_object.get("polygon"):
return _cast_polygon
if raw_tiled_object.get("polyline"):
return _cast_polyline
if raw_tiled_object.get("text"):
return _cast_text
return _cast_rectangle
def cast(
raw_tiled_object: RawTiledObject,
parent_dir: Optional[Path] = None,
) -> TiledObject:
"""Cast the raw tiled object into a pytiled_parser type
Args:
raw_tiled_object: Raw Tiled object that is to be cast.
parent_dir: The parent directory that the map file is in.
Returns:
TiledObject: a properly typed Tiled object.
Raises:
RuntimeError: When a required parameter was not sent based on a condition.
"""
new_tileset = None
new_tileset_path = None
if raw_tiled_object.get("template"):
if not parent_dir:
raise RuntimeError(
"A parent directory must be specified when using object templates"
)
template_path = Path(parent_dir / raw_tiled_object["template"])
with open(template_path) as raw_template_file:
template = json.load(raw_template_file)
if "tileset" in template:
tileset_path = Path(
template_path.parent / template["tileset"]["source"]
)
with open(tileset_path) as raw_tileset_file:
new_tileset = json.load(raw_tileset_file)
new_tileset_path = tileset_path.parent
loaded_template = template["object"]
for key in loaded_template:
if key != "id":
raw_tiled_object[key] = loaded_template[key] # type: ignore
if raw_tiled_object.get("gid"):
return _cast_tile(raw_tiled_object, new_tileset, new_tileset_path)
return _get_caster(raw_tiled_object)(raw_tiled_object)

View File

@@ -1,16 +1,13 @@
# pylint: disable=too-few-public-methods
from pathlib import Path
from typing import Dict, List, NamedTuple, Optional, Union
from typing import Dict, List, NamedTuple, Optional
import attr
from typing_extensions import TypedDict
from . import layer
from . import properties as properties_
from .common_types import Color, OrderedPair
from .util import parse_color
from .wang_set import RawWangSet, WangSet
from .wang_set import cast as cast_wangset
from .wang_set import WangSet
class Grid(NamedTuple):
@@ -153,261 +150,3 @@ class Tileset:
properties: Optional[properties_.Properties] = None
tiles: Optional[Dict[int, Tile]] = None
wang_sets: Optional[List[WangSet]] = None
class RawFrame(TypedDict):
""" The keys and their types that appear in a Frame JSON Object."""
duration: int
tileid: int
class RawTileOffset(TypedDict):
""" The keys and their types that appear in a TileOffset JSON Object."""
x: int
y: int
class RawTransformations(TypedDict):
""" The keys and their types that appear in a Transformations JSON Object."""
hflip: bool
vflip: bool
rotate: bool
preferuntransformed: bool
class RawTile(TypedDict):
""" The keys and their types that appear in a Tile JSON Object."""
animation: List[RawFrame]
id: int
image: str
imageheight: int
imagewidth: int
opacity: float
properties: List[properties_.RawProperty]
objectgroup: layer.RawLayer
type: str
class RawGrid(TypedDict):
""" The keys and their types that appear in a Grid JSON Object."""
height: int
width: int
orientation: str
class RawTileSet(TypedDict):
""" The keys and their types that appear in a TileSet JSON Object."""
backgroundcolor: str
columns: int
firstgid: int
grid: RawGrid
image: str
imageheight: int
imagewidth: int
margin: int
name: str
properties: List[properties_.RawProperty]
source: str
spacing: int
tilecount: int
tiledversion: str
tileheight: int
tileoffset: RawTileOffset
tiles: List[RawTile]
tilewidth: int
transparentcolor: str
transformations: RawTransformations
version: Union[str, float]
wangsets: List[RawWangSet]
def _cast_frame(raw_frame: RawFrame) -> Frame:
"""Cast the raw_frame to a Frame.
Args:
raw_frame: RawFrame to be casted to a Frame
Returns:
Frame: The Frame created from the raw_frame
"""
return Frame(duration=raw_frame["duration"], tile_id=raw_frame["tileid"])
def _cast_tile_offset(raw_tile_offset: RawTileOffset) -> OrderedPair:
"""Cast the raw_tile_offset to an OrderedPair.
Args:
raw_tile_offset: RawTileOffset to be casted to an OrderedPair
Returns:
OrderedPair: The OrderedPair created from the raw_tile_offset
"""
return OrderedPair(raw_tile_offset["x"], raw_tile_offset["y"])
def _cast_tile(raw_tile: RawTile, external_path: Optional[Path] = None) -> Tile:
"""Cast the raw_tile to a Tile object.
Args:
raw_tile: RawTile to be casted to a Tile
Returns:
Tile: The Tile created from the raw_tile
"""
id_ = raw_tile["id"]
tile = Tile(id=id_)
if raw_tile.get("animation") is not None:
tile.animation = []
for frame in raw_tile["animation"]:
tile.animation.append(_cast_frame(frame))
if raw_tile.get("objectgroup") is not None:
tile.objects = layer.cast(raw_tile["objectgroup"])
if raw_tile.get("properties") is not None:
tile.properties = properties_.cast(raw_tile["properties"])
if raw_tile.get("image") is not None:
if external_path:
tile.image = Path(external_path / raw_tile["image"]).absolute().resolve()
else:
tile.image = Path(raw_tile["image"])
if raw_tile.get("imagewidth") is not None:
tile.image_width = raw_tile["imagewidth"]
if raw_tile.get("imageheight") is not None:
tile.image_height = raw_tile["imageheight"]
if raw_tile.get("type") is not None:
tile.type = raw_tile["type"]
return tile
def _cast_transformations(raw_transformations: RawTransformations) -> Transformations:
"""Cast the raw_transformations to a Transformations object.
Args:
raw_transformations: RawTransformations to be casted to a Transformations
Returns:
Transformations: The Transformations created from the raw_transformations
"""
return Transformations(
hflip=raw_transformations["hflip"],
vflip=raw_transformations["vflip"],
rotate=raw_transformations["rotate"],
prefer_untransformed=raw_transformations["preferuntransformed"],
)
def _cast_grid(raw_grid: RawGrid) -> Grid:
"""Cast the raw_grid to a Grid object.
Args:
raw_grid: RawGrid to be casted to a Grid
Returns:
Grid: The Grid created from the raw_grid
"""
return Grid(
orientation=raw_grid["orientation"],
width=raw_grid["width"],
height=raw_grid["height"],
)
def cast(
raw_tileset: RawTileSet,
firstgid: int,
external_path: Optional[Path] = None,
) -> Tileset:
"""Cast the raw tileset into a pytiled_parser type
Args:
raw_tileset: Raw Tileset to be cast.
firstgid: GID corresponding the first tile in the set.
external_path: The path to the tileset if it is not an embedded one.
Returns:
TileSet: a properly typed TileSet.
"""
tileset = Tileset(
name=raw_tileset["name"],
tile_count=raw_tileset["tilecount"],
tile_width=raw_tileset["tilewidth"],
tile_height=raw_tileset["tileheight"],
columns=raw_tileset["columns"],
spacing=raw_tileset["spacing"],
margin=raw_tileset["margin"],
firstgid=firstgid,
)
if raw_tileset.get("version") is not None:
if isinstance(raw_tileset["version"], float):
tileset.version = str(raw_tileset["version"])
else:
tileset.version = raw_tileset["version"]
if raw_tileset.get("tiledversion") is not None:
tileset.tiled_version = raw_tileset["tiledversion"]
if raw_tileset.get("image") is not None:
if external_path:
tileset.image = (
Path(external_path / raw_tileset["image"]).absolute().resolve()
)
else:
tileset.image = Path(raw_tileset["image"])
if raw_tileset.get("imagewidth") is not None:
tileset.image_width = raw_tileset["imagewidth"]
if raw_tileset.get("imageheight") is not None:
tileset.image_height = raw_tileset["imageheight"]
if raw_tileset.get("backgroundcolor") is not None:
tileset.background_color = parse_color(raw_tileset["backgroundcolor"])
if raw_tileset.get("tileoffset") is not None:
tileset.tile_offset = _cast_tile_offset(raw_tileset["tileoffset"])
if raw_tileset.get("transparentcolor") is not None:
tileset.transparent_color = parse_color(raw_tileset["transparentcolor"])
if raw_tileset.get("grid") is not None:
tileset.grid = _cast_grid(raw_tileset["grid"])
if raw_tileset.get("properties") is not None:
tileset.properties = properties_.cast(raw_tileset["properties"])
if raw_tileset.get("tiles") is not None:
tiles = {}
for raw_tile in raw_tileset["tiles"]:
tiles[raw_tile["id"]] = _cast_tile(raw_tile, external_path=external_path)
tileset.tiles = tiles
if raw_tileset.get("wangsets") is not None:
wangsets = []
for raw_wangset in raw_tileset["wangsets"]:
wangsets.append(cast_wangset(raw_wangset))
tileset.wang_sets = wangsets
if raw_tileset.get("transformations") is not None:
tileset.transformations = _cast_transformations(raw_tileset["transformations"])
return tileset

View File

@@ -1,3 +1,3 @@
"""pytiled_parser version"""
__version__ = "1.5.4"
__version__ = "2.0.0-beta"

View File

@@ -1,11 +1,9 @@
from typing import Dict, List, Optional
import attr
from typing_extensions import TypedDict
from . import properties as properties_
from .common_types import Color
from .util import parse_color
from pytiled_parser.common_types import Color
from pytiled_parser.properties import Properties
@attr.s(auto_attribs=True)
@@ -22,7 +20,7 @@ class WangColor:
name: str
probability: float
tile: int
properties: Optional[properties_.Properties] = None
properties: Optional[Properties] = None
@attr.s(auto_attribs=True)
@@ -33,100 +31,4 @@ class WangSet:
wang_type: str
wang_tiles: Dict[int, WangTile]
wang_colors: List[WangColor]
properties: Optional[properties_.Properties] = None
class RawWangTile(TypedDict):
""" The keys and their types that appear in a Wang Tile JSON Object."""
tileid: int
# Tiled stores these IDs as a list represented like so:
# [top, top_right, right, bottom_right, bottom, bottom_left, left, top_left]
wangid: List[int]
class RawWangColor(TypedDict):
""" The keys and their types that appear in a Wang Color JSON Object."""
color: str
name: str
probability: float
tile: int
properties: List[properties_.RawProperty]
class RawWangSet(TypedDict):
""" The keys and their types that appear in a Wang Set JSON Object."""
colors: List[RawWangColor]
name: str
properties: List[properties_.RawProperty]
tile: int
type: str
wangtiles: List[RawWangTile]
def _cast_wang_tile(raw_wang_tile: RawWangTile) -> WangTile:
"""Cast the raw wang tile into a pytiled_parser type
Args:
raw_wang_tile: RawWangTile to be cast.
Returns:
WangTile: A properly typed WangTile.
"""
return WangTile(tile_id=raw_wang_tile["tileid"], wang_id=raw_wang_tile["wangid"])
def _cast_wang_color(raw_wang_color: RawWangColor) -> WangColor:
"""Cast the raw wang color into a pytiled_parser type
Args:
raw_wang_color: RawWangColor to be cast.
Returns:
WangColor: A properly typed WangColor.
"""
wang_color = WangColor(
name=raw_wang_color["name"],
color=parse_color(raw_wang_color["color"]),
tile=raw_wang_color["tile"],
probability=raw_wang_color["probability"],
)
if raw_wang_color.get("properties") is not None:
wang_color.properties = properties_.cast(raw_wang_color["properties"])
return wang_color
def cast(raw_wangset: RawWangSet) -> WangSet:
"""Cast the raw wangset into a pytiled_parser type
Args:
raw_wangset: Raw Wangset to be cast.
Returns:
WangSet: A properly typed WangSet.
"""
colors = []
for raw_wang_color in raw_wangset["colors"]:
colors.append(_cast_wang_color(raw_wang_color))
tiles = {}
for raw_wang_tile in raw_wangset["wangtiles"]:
tiles[raw_wang_tile["tileid"]] = _cast_wang_tile(raw_wang_tile)
wangset = WangSet(
name=raw_wangset["name"],
tile=raw_wangset["tile"],
wang_type=raw_wangset["type"],
wang_colors=colors,
wang_tiles=tiles,
)
if raw_wangset.get("properties") is not None:
wangset.properties = properties_.cast(raw_wangset["properties"])
return wangset
properties: Optional[Properties] = None

View File

@@ -8,8 +8,9 @@ from typing import List
import attr
from typing_extensions import TypedDict
from .common_types import OrderedPair, Size
from .tiled_map import TiledMap, parse_map
from pytiled_parser.common_types import OrderedPair, Size
from pytiled_parser.parser import parse_map
from pytiled_parser.tiled_map import TiledMap
@attr.s(auto_attribs=True)
@@ -55,7 +56,7 @@ class RawWorld(TypedDict):
onlyShowAdjacentMaps: bool
def _cast_world_map(raw_world_map: RawWorldMap, map_file: Path) -> WorldMap:
def _parse_world_map(raw_world_map: RawWorldMap, map_file: Path) -> WorldMap:
"""Parse the RawWorldMap into a WorldMap.
Args:
@@ -94,7 +95,7 @@ def parse_world(file: Path) -> World:
if raw_world.get("maps"):
for raw_map in raw_world["maps"]:
map_path = Path(parent_dir / raw_map["fileName"])
maps.append(_cast_world_map(raw_map, map_path))
maps.append(_parse_world_map(raw_map, map_path))
if raw_world.get("patterns"):
for raw_pattern in raw_world["patterns"]:
@@ -131,7 +132,7 @@ def parse_world(file: Path) -> World:
}
map_path = Path(parent_dir / map_file)
maps.append(_cast_world_map(raw_world_map, map_path))
maps.append(_parse_world_map(raw_world_map, map_path))
world = World(maps=maps)

View File

@@ -44,7 +44,7 @@ tests =
pytest
pytest-cov
black
pylint
flake8
mypy
isort<5,>=4.2.5
@@ -104,3 +104,7 @@ strict_optional = True
[mypy-tests.*]
ignore_errors = True
[flake8]
max-line-length = 88
exclude = .git,__pycache__,__init__.py,.mypy_cache,.pytest_cache

View File

@@ -6,7 +6,7 @@ from pathlib import Path
import pytest
from pytiled_parser import layer
from pytiled_parser.parsers.json.layer import parse
TESTS_DIR = Path(os.path.dirname(os.path.abspath(__file__)))
TEST_DATA = TESTS_DIR / "test_data"
@@ -39,6 +39,6 @@ def test_layer_integration(layer_test):
with open(raw_layers_path) as raw_layers_file:
raw_layers = json.load(raw_layers_file)["layers"]
layers = [layer.cast(raw_layer) for raw_layer in raw_layers]
layers = [parse(raw_layer) for raw_layer in raw_layers]
assert layers == expected.EXPECTED

View File

@@ -5,7 +5,7 @@ from pathlib import Path
import pytest
from pytiled_parser import tiled_map
from pytiled_parser import parse_map
TESTS_DIR = Path(os.path.dirname(os.path.abspath(__file__)))
TEST_DATA = TESTS_DIR / "test_data"
@@ -31,7 +31,7 @@ def test_map_integration(map_test):
raw_maps_path = map_test / "map.json"
casted_map = tiled_map.parse_map(raw_maps_path)
casted_map = parse_map(raw_maps_path)
expected.EXPECTED.map_file = casted_map.map_file
assert casted_map == expected.EXPECTED

View File

@@ -5,7 +5,17 @@ from pathlib import Path
import pytest
from pytiled_parser import common_types, tiled_object
from pytiled_parser import common_types
from pytiled_parser.parsers.json.tiled_object import parse
from pytiled_parser.tiled_object import (
Ellipse,
Point,
Polygon,
Polyline,
Rectangle,
Text,
Tile,
)
ELLIPSES = [
(
@@ -23,7 +33,7 @@ ELLIPSES = [
"y":81.1913152210981
}
""",
tiled_object.Ellipse(
Ellipse(
id=6,
size=common_types.Size(57.4013868364215, 18.5517790155735),
name="name: ellipse",
@@ -48,7 +58,7 @@ ELLIPSES = [
"y":53.9092872570194
}
""",
tiled_object.Ellipse(
Ellipse(
id=7,
size=common_types.Size(6.32943048766625, 31.4288962146186),
name="name: ellipse - invisible",
@@ -73,7 +83,7 @@ ELLIPSES = [
"y":120.040923041946
}
""",
tiled_object.Ellipse(
Ellipse(
id=8,
size=common_types.Size(29.6828464249176, 24.2264408321018),
name="name: ellipse - rotated",
@@ -98,7 +108,7 @@ ELLIPSES = [
"y":127.679890871888
}
""",
tiled_object.Ellipse(
Ellipse(
id=29,
name="name: ellipse - no width or height",
rotation=0,
@@ -124,7 +134,7 @@ RECTANGLES = [
"y":23.571672160964
}
""",
tiled_object.Rectangle(
Rectangle(
id=1,
size=common_types.Size(45.3972945322269, 41.4686825053996),
name="name: rectangle",
@@ -148,7 +158,7 @@ RECTANGLES = [
"y":91.0128452881664
}
""",
tiled_object.Rectangle(
Rectangle(
id=4,
size=common_types.Size(30.9923837671934, 32.7384335568944),
name="name: rectangle - invisible",
@@ -172,7 +182,7 @@ RECTANGLES = [
"y":23.3534159372513
}
""",
tiled_object.Rectangle(
Rectangle(
id=5,
size=common_types.Size(10, 22),
name="name: rectangle - rotated",
@@ -196,7 +206,7 @@ RECTANGLES = [
"y":53.4727748095942
}
""",
tiled_object.Rectangle(
Rectangle(
id=28,
size=common_types.Size(0, 0),
name="name: rectangle - no width or height",
@@ -251,7 +261,7 @@ RECTANGLES = [
"y":131.826759122428
}
""",
tiled_object.Rectangle(
Rectangle(
id=30,
size=common_types.Size(21.170853700125, 13.7501420938956),
name="name: rectangle - properties",
@@ -287,7 +297,7 @@ POINTS = [
"y":82.9373650107991
}
""",
tiled_object.Point(
Point(
id=2,
name="name: point",
rotation=0,
@@ -311,7 +321,7 @@ POINTS = [
"y":95.8144822098443
}
""",
tiled_object.Point(
Point(
id=3,
name="name: point invisible",
rotation=0,
@@ -338,7 +348,7 @@ TILES = [
"y":48.3019211094691
}
""",
tiled_object.Tile(
Tile(
id=13,
size=common_types.Size(32, 32),
name="name: tile",
@@ -364,7 +374,7 @@ TILES = [
"y":168.779356598841
}
""",
tiled_object.Tile(
Tile(
id=14,
size=common_types.Size(32, 32),
name="name: tile - invisible",
@@ -390,7 +400,7 @@ TILES = [
"y":59.8695009662385
}
""",
tiled_object.Tile(
Tile(
id=15,
size=common_types.Size(32, 32),
name="name: tile - horizontal flipped",
@@ -416,7 +426,7 @@ TILES = [
"y":60.742525861089
}
""",
tiled_object.Tile(
Tile(
id=16,
size=common_types.Size(32, 32),
name="name: tile - vertical flipped",
@@ -442,7 +452,7 @@ TILES = [
"y":95.6635216551097
}
""",
tiled_object.Tile(
Tile(
id=17,
size=common_types.Size(32, 32),
name="name: tile - both flipped",
@@ -468,7 +478,7 @@ TILES = [
"y":142.62
}
""",
tiled_object.Tile(
Tile(
id=18,
size=common_types.Size(32, 32),
name="name: tile - rotated",
@@ -517,7 +527,7 @@ POLYGONS = [
"y":38.6313515971354
}
""",
tiled_object.Polygon(
Polygon(
id=9,
name="name: polygon",
points=[
@@ -560,7 +570,7 @@ POLYGONS = [
"y":24.4446970558145
}
""",
tiled_object.Polygon(
Polygon(
id=10,
name="name: polygon - invisible",
points=[
@@ -613,7 +623,7 @@ POLYGONS = [
"y":19.8613163578493
}
""",
tiled_object.Polygon(
Polygon(
id=11,
name="name: polygon - rotated",
points=[
@@ -660,7 +670,7 @@ POLYLINES = [
"y":90.1398203933159
}
""",
tiled_object.Polyline(
Polyline(
id=12,
name="name: polyline",
points=[
@@ -701,7 +711,7 @@ POLYLINES = [
"y":163.333333333333
}
""",
tiled_object.Polyline(
Polyline(
id=31,
name="name: polyline - invisible",
points=[
@@ -742,7 +752,7 @@ POLYLINES = [
"y":128.666666666667
}
""",
tiled_object.Polyline(
Polyline(
id=32,
name="name: polyline - rotated",
points=[
@@ -778,7 +788,7 @@ TEXTS = [
"y":93.2986813686484
}
""",
tiled_object.Text(
Text(
id=19,
name="name: text",
text="Hello World",
@@ -809,7 +819,7 @@ TEXTS = [
"y":112.068716607935
}
""",
tiled_object.Text(
Text(
id=20,
name="name: text - invisible",
text="Hello World",
@@ -840,7 +850,7 @@ TEXTS = [
"y":78.4572581561896
}
""",
tiled_object.Text(
Text(
id=21,
name="name: text - rotated",
text="Hello World",
@@ -874,7 +884,7 @@ TEXTS = [
"y":101.592417869728
}
""",
tiled_object.Text(
Text(
id=22,
name="name: text - different font",
text="Hello World",
@@ -907,7 +917,7 @@ TEXTS = [
"y":154.192167784472
}
""",
tiled_object.Text(
Text(
id=23,
name="name: text - no word wrap",
text="Hello World",
@@ -939,7 +949,7 @@ TEXTS = [
"y":1.19455496191883
}
""",
tiled_object.Text(
Text(
id=24,
name="name: text - right bottom align",
text="Hello World",
@@ -973,7 +983,7 @@ TEXTS = [
"y": 3.81362964647039
}
""",
tiled_object.Text(
Text(
id=25,
name="text: center center align",
rotation=0,
@@ -1006,7 +1016,7 @@ TEXTS = [
"y": 60.7785040354666
}
""",
tiled_object.Text(
Text(
id=26,
name="name: text - justified",
rotation=0,
@@ -1038,7 +1048,7 @@ TEXTS = [
"y": 130.620495623508
}
""",
tiled_object.Text(
Text(
id=27,
name="name: text - red",
rotation=0,
@@ -1075,7 +1085,7 @@ TEXTS = [
"y":22
}
""",
tiled_object.Text(
Text(
id=31,
name="name: text - font options",
rotation=0,
@@ -1100,7 +1110,7 @@ OBJECTS = ELLIPSES + RECTANGLES + POINTS + TILES + POLYGONS + POLYLINES + TEXTS
@pytest.mark.parametrize("raw_object_json,expected", OBJECTS)
def test_parse_layer(raw_object_json, expected):
raw_object = json.loads(raw_object_json)
result = tiled_object.cast(raw_object)
result = parse(raw_object)
assert result == expected
@@ -1118,4 +1128,4 @@ def test_parse_no_parent_dir():
json_object = json.loads(raw_object)
with pytest.raises(RuntimeError):
tiled_object.cast(json_object)
parse(json_object)

View File

@@ -6,7 +6,7 @@ from pathlib import Path
import pytest
from pytiled_parser import tileset
from pytiled_parser.parsers.json.tileset import parse
TESTS_DIR = Path(os.path.dirname(os.path.abspath(__file__)))
TEST_DATA = TESTS_DIR / "test_data"
@@ -39,6 +39,6 @@ def test_tilesets_integration(tileset_dir):
raw_tileset_path = tileset_dir / "tileset.json"
with open(raw_tileset_path) as raw_tileset:
tileset_ = tileset.cast(json.loads(raw_tileset.read()), 1)
tileset_ = parse(json.loads(raw_tileset.read()), 1)
assert tileset_ == expected.EXPECTED