import streetData from '../data/streetCoordinates.json'; class StreetCoordinates { constructor() { this.data = streetData; } /** * Konvertiert relative Koordinaten (0-1) zu absoluten Koordinaten * @param {number} relativeX - Relative X-Koordinate (0-1) * @param {number} relativeY - Relative Y-Koordinate (0-1) * @param {number} tileSize - Größe des Tiles in Pixeln * @returns {Object} Absolute Koordinaten {x, y} */ toAbsolute(relativeX, relativeY, tileSize) { return { x: Math.round(relativeX * tileSize), y: Math.round(relativeY * tileSize) }; } /** * Konvertiert absolute Koordinaten zu relativen Koordinaten (0-1) * @param {number} absoluteX - Absolute X-Koordinate * @param {number} absoluteY - Absolute Y-Koordinate * @param {number} tileSize - Größe des Tiles in Pixeln * @returns {Object} Relative Koordinaten {x, y} */ toRelative(absoluteX, absoluteY, tileSize) { return { x: absoluteX / tileSize, y: absoluteY / tileSize }; } /** * Gibt die Straßenregionen für einen Tile-Typ zurück * @param {string} tileType - Typ des Tiles (z.B. 'cornerBottomRight') * @returns {Array} Array von Polygonen mit relativen Koordinaten (0-1) */ getDriveableRegions(tileType) { const tile = this.data.tiles[tileType]; if (!tile) { console.warn(`Tile type '${tileType}' not found`); return []; } // Gib die relativen Koordinaten (0-1) direkt zurück return tile.regions; } /** * Prüft, ob ein Punkt innerhalb der fahrbaren Bereiche liegt * @param {number} x - X-Koordinate des Punktes (relativ 0-1) * @param {number} y - Y-Koordinate des Punktes (relativ 0-1) * @param {string} tileType - Typ des Tiles * @returns {boolean} True wenn der Punkt fahrbar ist */ isPointDriveable(x, y, tileType) { const regions = this.getDriveableRegions(tileType); for (const region of regions) { if (this.isPointInPolygon(x, y, region)) { return true; } } return false; } /** * Prüft, ob ein Punkt innerhalb eines Polygons liegt (Ray Casting Algorithm) * @param {number} x - X-Koordinate des Punktes * @param {number} y - Y-Koordinate des Punktes * @param {Array} polygon - Array von {x, y} Punkten * @returns {boolean} True wenn der Punkt im Polygon liegt */ isPointInPolygon(x, y, polygon) { let inside = false; for (let i = 0, j = polygon.length - 1; i < polygon.length; j = i++) { if (((polygon[i].y > y) !== (polygon[j].y > y)) && (x < (polygon[j].x - polygon[i].x) * (y - polygon[i].y) / (polygon[j].y - polygon[i].y) + polygon[i].x)) { inside = !inside; } } return inside; } /** * Zeichnet die Straßenregionen auf einem Canvas * @param {CanvasRenderingContext2D} ctx - Canvas 2D Context * @param {string} tileType - Typ des Tiles * @param {number} tileSize - Größe des Tiles in Pixeln * @param {number} offsetX - X-Offset für das Tile * @param {number} offsetY - Y-Offset für das Tile */ drawDriveableRegions(ctx, tileType, tileSize, offsetX = 0, offsetY = 0) { const regions = this.getDriveableRegions(tileType); ctx.fillStyle = '#f0f0f0'; // Straßenfarbe ctx.strokeStyle = '#333'; ctx.lineWidth = 1; for (const region of regions) { ctx.beginPath(); // Konvertiere relative Koordinaten zu absoluten für das Zeichnen const startX = region[0].x * tileSize + offsetX; const startY = region[0].y * tileSize + offsetY; ctx.moveTo(startX, startY); for (let i = 1; i < region.length; i++) { const x = region[i].x * tileSize + offsetX; const y = region[i].y * tileSize + offsetY; ctx.lineTo(x, y); } ctx.closePath(); ctx.fill(); ctx.stroke(); } } /** * Gibt alle verfügbaren Tile-Typen zurück * @returns {Array} Array von Tile-Typen */ getAvailableTileTypes() { return Object.keys(this.data.tiles); } /** * Gibt die ursprüngliche Tile-Größe zurück * @returns {number} Ursprüngliche Tile-Größe in Pixeln */ getOriginalTileSize() { return this.data.tileSize; } } // Singleton-Instanz const streetCoordinates = new StreetCoordinates(); export default streetCoordinates;