using RobotNet10.Shared.Geometry; namespace RobotNet10.Shared.Localization; /// /// Occupancy grid for representing map as a 2D grid of cells /// Compatible with ROS navigation_msgs/OccupancyGrid /// public class OccupancyGrid { /// /// Resolution of the grid (meters per pixel) /// public double Resolution { get; set; } /// /// Width of the grid (number of cells) /// public int Width { get; set; } /// /// Height of the grid (number of cells) /// public int Height { get; set; } /// /// Origin of the grid in map frame (bottom-left corner) /// public Pose Origin { get; set; } /// /// Grid data: -1 = Unknown, 0-100 = Probability of occupancy (0 = free, 100 = occupied) /// Stored in row-major order (index = y * width + x) /// public sbyte[] Data { get; set; } /// /// Creates a new empty occupancy grid /// public OccupancyGrid() { Resolution = 0.05; Width = 0; Height = 0; Origin = new Pose(); Data = []; } /// /// Creates a new occupancy grid with specified dimensions /// public OccupancyGrid(double resolution, int width, int height, Pose origin) { Resolution = resolution; Width = width; Height = height; Origin = origin; Data = new sbyte[width * height]; // Initialize all cells as unknown (-1) Array.Fill(Data, (sbyte)-1); } /// /// Gets the occupancy value at the specified cell coordinates /// /// X coordinate (0 to Width-1) /// Y coordinate (0 to Height-1) /// -1 = Unknown, 0-100 = Occupancy probability public sbyte GetCell(int x, int y) { if (x < 0 || x >= Width || y < 0 || y >= Height) { return -1; // Out of bounds = unknown } return Data[y * Width + x]; } /// /// Sets the occupancy value at the specified cell coordinates /// /// X coordinate (0 to Width-1) /// Y coordinate (0 to Height-1) /// -1 = Unknown, 0-100 = Occupancy probability public void SetCell(int x, int y, sbyte value) { if (x < 0 || x >= Width || y < 0 || y >= Height) { return; // Out of bounds, ignore } Data[y * Width + x] = value; } /// /// Converts world coordinates (meters) to grid cell coordinates /// /// X coordinate in meters /// Y coordinate in meters /// Cell coordinates (x, y) public (int x, int y) WorldToGrid(double worldX, double worldY) { // Convert world coordinates relative to origin var relativeX = worldX - Origin.Position.X; var relativeY = worldY - Origin.Position.Y; // Convert to grid coordinates var gridX = (int)Math.Floor(relativeX / Resolution); var gridY = (int)Math.Floor(relativeY / Resolution); return (gridX, gridY); } /// /// Converts grid cell coordinates to world coordinates (meters) /// /// X coordinate in grid cells /// Y coordinate in grid cells /// World coordinates (x, y) in meters public (double x, double y) GridToWorld(int gridX, int gridY) { var worldX = Origin.Position.X + (gridX * Resolution); var worldY = Origin.Position.Y + (gridY * Resolution); return (worldX, worldY); } /// /// Gets the bounds of the grid in world coordinates /// public BoundingBox GetBounds() { var (minX, minY) = GridToWorld(0, 0); var (maxX, maxY) = GridToWorld(Width, Height); return new BoundingBox { MinX = Math.Min(minX, maxX), MinY = Math.Min(minY, maxY), MaxX = Math.Max(minX, maxX), MaxY = Math.Max(minY, maxY) }; } } /// /// Bounding box for representing map bounds /// public class BoundingBox { public double MinX { get; set; } public double MinY { get; set; } public double MaxX { get; set; } public double MaxY { get; set; } }