# Coordinate Mapping ## Metadata | Field | Value | |---|---| | Status | Implemented | | Target/work package | `M01-FND-COORDS-001`, `M01-FND-COORD-TILE-AXIS-002` | | Owners | Foundation/domain | | Last verified | Worktree `work/sindo-main-codex/m01-coordinate-tile-axis-fix`, 2026-07-13 | | Profiles/capabilities | All profiles; contract version 2 | ## Purpose Provide scene-free value objects and one pure `CoordinateMapper` for positions, ADT grid ownership and world-facing yaw. The module makes every source space explicit before renderer, network, gameplay and authoring consumers migrate. ## Non-goals - Reading ADT, packet, SQL or scene data. - Constructing `Vector3`, `Transform3D`, Node or Resource values. - Correcting M2/WMO local axes or MDDF/MODF Euler rotations. - Selecting maps, validating terrain height or owning streaming focus. - Persisting coordinate values in this work package. ## Context and boundaries ```mermaid flowchart LR Server[Server / movement X,Y,Z] --> ServerPosition[ServerWorldPosition] ADT[MDDF/MODF X,height,Z] --> AdtPosition[AdtPlacementPosition] ServerPosition --> Mapper[CoordinateMapper] AdtPosition --> Mapper Mapper --> Canonical[CanonicalWowWorldPosition] Canonical --> Tile[AdtTileCoordinate + AdtTileLocalPosition] Canonical --> GodotPosition[GodotWorldPosition] GodotPosition --> Adapter[Future scene/render adapter] Adapter --> Vector[Vector3 at boundary] Scene[Node / Camera3D / scene lookup] -. forbidden .-> Mapper ``` Allowed dependencies: - GDScript scalar math and `RefCounted` lifetime; - other value objects in `src/domain/coordinates`; - callers in network, gameplay, render and server-data adapters. Forbidden dependencies: - `Node`, `Resource`, SceneTree, Camera3D and global scene lookup; - packet buffers, SQL rows, asset parsers and renderer state; - `Vector3` as a public mapper input or output; - model-specific placement corrections. ## Public API | Symbol | Kind | Purpose | Thread/lifetime | Errors | |---|---|---|---|---| | `CanonicalWowWorldPosition` | Immutable value | Client/server X/Y/Z in yards | Any thread; caller-owned reference | No bounds validation | | `ServerWorldPosition` | Immutable value | Server/wire X/Y/Z in yards | Any thread; caller-owned reference | No adapter validation | | `AdtPlacementPosition` | Immutable value | MDDF/MODF X/height/Z fields | Any thread; caller-owned reference | No file validation | | `GodotWorldPosition` | Immutable value | Godot east/up/south scalars | Any thread; caller-owned reference | Does not create `Vector3` | | `AdtTileCoordinate` | Immutable value | `Map_tileX_tileY.adt` indices | Any thread; caller-owned reference | `is_within_map_grid()` reports invalid indices | | `AdtTileLocalPosition` | Immutable value | East/height/south offsets from tile north-west corner | Any thread; caller-owned reference | Values are intentionally unclamped | | `AdtChunkCoordinate` | Immutable value | MCNK X-east/Y-south indices inside one tile | Any thread; caller-owned reference | `is_within_map_grid()` reports invalid indices | | `CanonicalWowWorldYaw` | Immutable value | Canonical world yaw in radians | Any thread; caller-owned reference | Constructor does not normalize | | `ServerWorldYaw` | Immutable value | Movement orientation in radians | Any thread; caller-owned reference | Constructor does not normalize | | `GodotWorldYaw` | Immutable value | Godot Y-axis world yaw in radians | Any thread; caller-owned reference | Constructor does not normalize | | `CoordinateMapper.server_to_canonical` / inverse | Pure static methods | Preserve server X/Y/Z as canonical | Synchronous; new result value | Null input is a caller contract violation | | `CoordinateMapper.canonical_to_godot` / inverse | Pure static methods | Apply Y-up renderer basis and map offset | Synchronous; new result value | Non-finite scalars propagate | | `CoordinateMapper.canonical_to_adt_placement` / inverse | Pure static methods | Convert ADT placement field basis | Synchronous; new result value | Non-finite scalars propagate | | `CoordinateMapper.canonical_to_adt_tile` | Pure static method | Compute unclamped filename indices | Synchronous; new result value | Out-of-map result remains invalid | | `CoordinateMapper.canonical_to_adt_tile_local` / inverse | Pure static methods | Split/reconstruct tile-local position | Synchronous; new result value | Caller validates tile ownership | | `CoordinateMapper.adt_tile_local_to_chunk` | Pure static method | Compute unclamped MCNK indices | Synchronous; new result value | Boundary/out-of-range remains diagnosable | | World-yaw mapper methods | Pure static methods | Normalize and cross the world-basis boundary | Synchronous; new result value | Non-finite input is unsupported | ## Inputs and outputs | Direction | Contract/data | Producer | Consumer | Ownership | Thread/lifetime | |---|---|---|---|---|---| | Input | `ServerWorldPosition`, `ServerWorldYaw` | Future protocol/server adapter | `CoordinateMapper` | Immutable caller value | Any thread, call duration | | Input | `AdtPlacementPosition` | Future ADT adapter | `CoordinateMapper` | Immutable caller value | Any thread, call duration | | Input | `CanonicalWowWorldPosition`, `CanonicalWowWorldYaw` | Gameplay/content/domain | `CoordinateMapper` | Immutable caller value | Any thread, call duration | | Output | `CanonicalWowWorldPosition` | `CoordinateMapper` | Gameplay/content/adapters | New immutable value | Caller-owned reference | | Output | `AdtTileCoordinate`, `AdtTileLocalPosition`, `AdtChunkCoordinate` | `CoordinateMapper` | Streaming/content adapters | New immutable values | Caller-owned references | | Output | `GodotWorldPosition`, `GodotWorldYaw` | `CoordinateMapper` | Future render/scene adapter | New immutable values | Caller-owned references | Side effects: - none; - no scene mutation, file/cache write, network send, database access or logging. ## Data flow ```mermaid flowchart LR Server[ServerWorldPosition X,Y,Z] -->|identity components| Canonical[CanonicalWowWorldPosition] ADT[AdtPlacementPosition X,height,Z] -->|half extent - horizontal| Canonical Canonical -->|tileX from Y; tileY from X| Tile[AdtTileCoordinate] Canonical -->|north-west edge offsets| Local[AdtTileLocalPosition] Local -->|floor offset / chunk size| Chunk[AdtChunkCoordinate] Canonical -->|half extent - Y; Z; half extent - X| Godot[GodotWorldPosition] ServerYaw[ServerWorldYaw] -->|normalize only| CanonicalYaw[CanonicalWowWorldYaw] CanonicalYaw -->|same numeric world angle| GodotYaw[GodotWorldYaw] ``` ## Lifecycle/state The module is stateless. Each call reads immutable scalar inputs and returns a new value object. There is no initialization, shutdown, cancellation or retry state machine. ## Main sequence No cross-layer asynchronous sequence occurs inside this module. Producers parse or validate their external format first, call the mapper synchronously, and then pass only typed results across the next boundary. ## Ownership, threading and resources - Value objects own only scalar fields and, for chunks, one immutable tile reference. - Callers own returned `RefCounted` references. - Methods do not mutate inputs or shared state and are safe for worker use. - No Node, RID, file, socket, database or cache resource is acquired. ## Errors, cancellation and recovery | Failure | Detection | Behavior | Diagnostic | Recovery | |---|---|---|---|---| | Tile/chunk outside standard grid | `is_within_map_grid()` | Unclamped indices are preserved | Caller reports source and map context | Reject or apply explicit map policy | | Tile-local value outside half-open tile | Consumer boundary validation | Value is preserved | Caller reports source coordinate | Recompute ownership or reject malformed input | | Null value object | Static typing/call failure | Programming contract violation | GDScript error with call site | Fix producer; no fallback value | | NaN/infinite scalar | Adapter validation | Arithmetic propagates invalid value | Adapter must report source field | Reject malformed external input | There is no cancellation because every conversion is constant-time scalar math. ## Configuration and capabilities | Setting/capability | Default | Profile | Runtime mutable | Effect | |---|---|---|---|---| | Contract version | 2 | All | No | Corrected filename/chunk axes in ADR 0001 | | Godot units per WoW yard | 1 | `Blizzlike335` and current tools | No | Preserves current renderer scale | | ADT grid | 64 tiles/axis, 16 chunks/tile | WotLK 3.3.5a | No | Defines index and boundary math | ## Persistence, cache and migration This package adds no persisted format, cache or migration. Existing dictionaries, native `Vector3` payloads and renderer conversion helpers remain compatible. Consumer migration is ordered after this contract and must remove old manual conversions only after its producer/consumer tests pass. Any future serialized coordinate must store a schema version and explicit space; raw three-number arrays are not an accepted new persistence contract. ## Diagnostics and observability - Logs: none in production mapper. - Metrics: none; conversion cost is constant and outside frame-budget concern. - Debug views: headless contract probe prints one PASS summary. - Correlation IDs: supplied by external adapters if an invalid source is reported. ## Verification - Unit/contract tests: `src/tools/verify_coordinate_mapper.gd`. - Integration/E2E: consumer migration and `StreamingFocus` packages remain pending. - Fidelity evidence: five build-12340 camera positions from the M00 manifest map within `0.002` yard; independent reference formulas are recorded in ADR 0001. - Performance budgets: constant-time arithmetic; no scene or file access. - Manual diagnostics: not required for the pure contract; renderer checkpoints are required when consumers migrate. ## Extension points - Add typed map IDs around positions without changing axis semantics. - Add a format-specific placement-orientation adapter with its own fixtures. - Add serialization only with explicit space/version fields and migrations. - Add consumer adapters that construct engine-native vectors at their boundary. ## Capability status | Capability | Status | Evidence | Gap/next step | |---|---|---|---| | Canonical/server/Godot position mapping | Implemented | Headless round-trip and five M00 golden points | Migrate producers/consumers | | ADT placement mapping | Implemented | Synthetic/direct round-trip fixtures | Compare native parsed placements during migration | | ADT tile/local/chunk ownership | Implemented | Center/extent boundaries plus raw `Azeroth_31_49.adt` filename and MCNK direction evidence | Migrate streaming consumers | | World-facing yaw mapping | Implemented | Cardinal/wrap round-trip fixtures | Placement Euler remains separate | | Scene-free production contract | Implemented | Source boundary and headless test | Add dependency search gate later in M01 | | Visual/server-spawn integration | Planned | M01 target criteria | Add golden server spawn after adapter migration | ## Known gaps and risks - Existing native loaders and renderer scripts still contain manual conversions. - Contract version 1 briefly transposed ADT filename/chunk axes; version 2 fixes the defect before any consumer migration. - MDDF/MODF rotation comments and implementations disagree across existing paths; this package deliberately does not generalize them into world yaw. - Finite-number validation belongs to external format adapters and is not yet implemented. - Arithmetic agreement does not prove terrain, placement, camera direction or visual parity. ## Source map | Path | Responsibility | |---|---| | `src/domain/coordinates/coordinate_mapper.gd` | Constants and all coordinate-axis conversions | | `src/domain/coordinates/*_position.gd` | Immutable typed position values | | `src/domain/coordinates/adt_tile_coordinate.gd` | ADT filename tile indices and bounds | | `src/domain/coordinates/adt_chunk_coordinate.gd` | MCNK indices and bounds | | `src/domain/coordinates/*_yaw.gd` | Immutable typed world-facing yaw values | | `src/tools/verify_coordinate_mapper.gd` | Headless fixtures and boundary regressions | | `docs/adr/0001-canonical-world-coordinates.md` | Normative axes, units, boundaries and rollout decision | ## Related decisions and references - ADR: [`../adr/0001-canonical-world-coordinates.md`](../adr/0001-canonical-world-coordinates.md) - M00 evidence: [`../M00_FINAL_PAIRED_EVIDENCE.md`](../M00_FINAL_PAIRED_EVIDENCE.md) - Architecture: [`../ARCHITECTURE.md`](../ARCHITECTURE.md) - Testing rules: [`../TESTING.md`](../TESTING.md)