61 lines
2.2 KiB
Python
61 lines
2.2 KiB
Python
"""Umrechnung zwischen gtadb-Weltkoordinaten, Kacheln und Mosaikpixeln.
|
||
|
||
Spiegelt src/world/coords.js. Beide Seiten müssen dieselben Werte liefern.
|
||
"""
|
||
import re
|
||
|
||
TILE_PX = 256
|
||
WORLD_HALF = 16384
|
||
CHUNK_SIZE = 250
|
||
SLICE = {"x0": -1000, "x1": 2000, "y0": -500, "y1": 2500}
|
||
|
||
|
||
def tile_for_world(x: float, y: float) -> tuple[int, int]:
|
||
return (int((WORLD_HALF - y) // TILE_PX), int((x + WORLD_HALF) // TILE_PX))
|
||
|
||
|
||
def slice_tiles() -> list[tuple[int, int]]:
|
||
r0, c0 = tile_for_world(SLICE["x0"], SLICE["y1"])
|
||
r1, c1 = tile_for_world(SLICE["x1"] - 1, SLICE["y0"] + 1)
|
||
return [(r, c) for r in range(r0, r1 + 1) for c in range(c0, c1 + 1)]
|
||
|
||
|
||
def mosaic_origin_px() -> tuple[int, int]:
|
||
"""Pixelversatz der Ausschnittsecke innerhalb des ungeschnittenen Kachelrasters."""
|
||
r0, c0 = tile_for_world(SLICE["x0"], SLICE["y1"])
|
||
return (SLICE["x0"] + WORLD_HALF - c0 * TILE_PX, (WORLD_HALF - SLICE["y1"]) - r0 * TILE_PX)
|
||
|
||
|
||
def pixel_to_world(px: int, py: int) -> tuple[float, float]:
|
||
return (SLICE["x0"] + px, SLICE["y1"] - py)
|
||
|
||
|
||
CHUNKS_PER_SIDE = (SLICE["x1"] - SLICE["x0"]) // CHUNK_SIZE # 12
|
||
CHUNK_COLUMNS = "ABCDEFGHIJKL"
|
||
# [0-9] statt \d: in Python passt \d auch auf andere Ziffernzeichen wie ١ oder 1, in JavaScript nicht.
|
||
_CHUNK_NAME = re.compile(r"([A-La-l])([1-9][0-9]?)")
|
||
|
||
|
||
def _is_index(k) -> bool:
|
||
return isinstance(k, int) and not isinstance(k, bool) and 0 <= k < CHUNKS_PER_SIDE
|
||
|
||
|
||
def chunk_name(i: int, j: int) -> str | None:
|
||
"""Planquadrat-Name wie src/world/coords.js: Spalte A–L aus i, Zeile 1–12 aus j. A1 = Nordwest."""
|
||
if not (_is_index(i) and _is_index(j)):
|
||
return None
|
||
return f"{CHUNK_COLUMNS[i]}{j + 1}"
|
||
|
||
|
||
def parse_chunk_name(name: str) -> tuple[int, int] | None:
|
||
"""Umkehrung von chunk_name. Akzeptiert Kleinschreibung, None außerhalb A1–L12."""
|
||
match = _CHUNK_NAME.fullmatch(str(name))
|
||
if not match or int(match.group(2)) > CHUNKS_PER_SIDE:
|
||
return None
|
||
return CHUNK_COLUMNS.index(match.group(1).upper()), int(match.group(2)) - 1
|
||
|
||
|
||
def chunk_for_world(x: float, y: float) -> tuple[int, int]:
|
||
"""Spiegel von chunkForWorld in src/world/coords.js: i nach Osten, j von der Nordkante nach Süden."""
|
||
return int((x - SLICE["x0"]) // CHUNK_SIZE), int((SLICE["y1"] - y) // CHUNK_SIZE)
|