TinyRobotics
Loading...
Searching...
No Matches
GridBitMap.h
1#pragma once
2#include <vector>
3
4#include "IMap.h"
5#include "TinyRobotics/coordinates/Coordinate.h"
6#include "TinyRobotics/utils/AllocatorPSRAM.h"
7#include "TinyRobotics/utils/Common.h"
8#include <TinyRobotics/serialize/MapSerializer.h>
9
10namespace tinyrobotics {
11
12/**
13 * @class GridBitMap
14 * @ingroup maps
15 * @brief A grid map using two bit vectors to represent CellState efficiently.
16 *
17 * The GridMapBitVector class models the environment as a regular grid of cells,
18 * each storing a state (FREE, OCCUPIED, UNKNOWN) using two std::vector<bool>
19 * for memory efficiency. So each cell occupies only 2 buts!
20 *
21 * CellState encoding:
22 * - 00: FREE
23 * - 01: OCCUPIED
24 * - 10: UNKNOWN
25 *
26 * @tparam T Numeric type for coordinates (default: float)
27 */
28
29template <typename T = DistanceM>
30class GridBitMap : public IMap<T> {
31 public:
32 struct Cell {
33 size_t cx;
34 size_t cy;
35 };
36
37 GridBitMap() = default;
38 GridBitMap(int xCount, int yCount, float resolutionM)
39 : xCount(xCount), yCount(yCount), resolution(resolutionM) {
40 resize(xCount, yCount);
41 }
42 GridBitMap(int xCount, int yCount, Distance resolution)
43 : xCount(xCount), yCount(yCount) {
44 this->resolution = resolution.getValue(DistanceUnit::M);
45 resize(xCount, yCount);
46 }
47
48 void resize(int newXCount, int newYCount) {
49 xCount = newXCount;
50 yCount = newYCount;
51 occupied.resize(xCount * yCount, false);
52 free.resize(xCount * yCount, false);
53 }
54
55 int getXCount() const { return xCount; }
56
57 int getYCount() const { return yCount; }
58
59 float getResolution() const { return resolution; }
60
61 // World to cell conversion
62 bool worldToCell(float wx, float wy, Cell& cell) const {
63 cell.cx = static_cast<int>((wx - origin.x) / resolution);
64 cell.cy = static_cast<int>((wy - origin.y) / resolution);
65 return (cell.cx >= 0 && cell.cx < xCount && cell.cy >= 0 &&
66 cell.cy < yCount);
67 }
68
69 // Cell to world (center of cell)
70 void cellToWorld(int cx, int cy, float& wx, float& wy) const {
71 wx = origin.x + (cx + 0.5f) * resolution;
72 wy = origin.y + (cy + 0.5f) * resolution;
73 }
74
75 // Get cell state by index
76 bool getCell(int cx, int cy, CellState& result) const {
77 if (cx < 0 || cx >= xCount || cy < 0 || cy >= yCount) return false;
78 size_t idx = cy * xCount + cx;
79 if (occupied[idx]) {
80 result = CellState::OCCUPIED;
81 } else if (free[idx]) {
82 result = CellState::FREE;
83 } else {
84 result = CellState::UNKNOWN;
85 }
86 return true;
87 }
88
89 // Set cell state by index
90 void setCell(int cx, int cy, CellState value) {
91 if (cx < 0 || cx >= xCount || cy < 0 || cy >= yCount) return;
92 size_t idx = cy * xCount + cx;
93 switch (value) {
94 case CellState::FREE:
95 occupied[idx] = false;
96 free[idx] = true;
97 break;
98 case CellState::OCCUPIED:
99 occupied[idx] = true;
100 free[idx] = false;
101 break;
102 case CellState::UNKNOWN:
103 occupied[idx] = false;
104 free[idx] = false;
105 break;
106 }
107 }
108
109 // Get cell state by coordinate
110 bool getCell(const Coordinate<T>& coord, CellState& result) const {
111 Cell cell;
112 if (worldToCell(coord.x, coord.y, cell)) {
113 return getCell(cell.cx, cell.cy, result);
114 }
115 return false;
116 }
117
118 // Set cell state by coordinate
119 void setCell(const Coordinate<T>& coord, CellState value) {
120 Cell cell;
121 if (worldToCell(coord.x, coord.y, cell)) {
122 setCell(cell.cx, cell.cy, value);
123 }
124 }
125
126 /**
127 * @brief Check if a coordinate is within the map bounds.
128 * @param coord The coordinate to check.
129 * @return true if the coordinate is inside the map, false otherwise.
130 */
131 bool isValid(const Coordinate<T>& coord) const {
132 int cx, cy;
133 cx = static_cast<int>((coord.x - origin.x) / resolution);
134 cy = static_cast<int>((coord.y - origin.y) / resolution);
135 return cx >= 0 && cx < xCount && cy >= 0 && cy < yCount;
136 }
137
138 /// Get world coordinates of neighboring cells (for pathfinding or navigation)
140 std::vector<Coordinate<T>> neighbors;
141 for (auto& cell : getNeighborCells(from)) {
142 Coordinate<T> neighbor;
143 cellToWorld(cell.cx, cell.cy, neighbor.x, neighbor.y);
144 neighbors.push_back(neighbor);
145 }
146 return neighbors;
147 }
148
149 /// Convert cell indices to world coordinates (returns Coordinate<T>)
150 Coordinate<T> toWorld(int cx, int cy) const override {
151 float wx, wy;
152 cellToWorld(cx, cy, wx, wy);
153 return Coordinate<T>(wx, wy);
154 }
155
156 /// Write map to output
158 return serializer.write(*this, out);
159 }
160
161 /// Read map from input
163 return serializer.read(*this, in);
164 }
165
166
167 protected:
168 int xCount = 0;
169 int yCount = 0;
170 float resolution = 0.1f;
171 Coordinate<T> origin;
172 std::vector<bool> occupied;
173 std::vector<bool> free;
174 // Serialization
175 GridMapSerializer<GridBitMap, CellState, T> serializer;
176
177 /// Determine all neighboring cells (8-connected) for a given cell coordinate.
179 Cell cell;
180 worldToCell(from.x, from.y, cell);
181 int cx = static_cast<int>(cell.cx);
182 int cy = static_cast<int>(cell.cy);
183 std::vector<Cell> neighbors;
184 if (cx < xCount - 1)
185 neighbors.push_back(
186 {static_cast<size_t>(cx + 1), static_cast<size_t>(cy)});
187 if (cx > 0)
188 neighbors.push_back(
189 {static_cast<size_t>(cx - 1), static_cast<size_t>(cy)});
190 if (cy < yCount - 1)
191 neighbors.push_back(
192 {static_cast<size_t>(cx), static_cast<size_t>(cy + 1)});
193 if (cy > 0)
194 neighbors.push_back(
195 {static_cast<size_t>(cx), static_cast<size_t>(cy - 1)});
196
197 if (cx < xCount - 1 && cy < yCount - 1)
198 neighbors.push_back(
199 {static_cast<size_t>(cx + 1), static_cast<size_t>(cy + 1)});
200 if (cx > 0 && cy < yCount - 1)
201 neighbors.push_back(
202 {static_cast<size_t>(cx - 1), static_cast<size_t>(cy + 1)});
203 if (cx < xCount - 1 && cy > 0)
204 neighbors.push_back(
205 {static_cast<size_t>(cx + 1), static_cast<size_t>(cy - 1)});
206 if (cx > 0 && cy > 0)
207 neighbors.push_back(
208 {static_cast<size_t>(cx - 1), static_cast<size_t>(cy - 1)});
209 return neighbors;
210 }
211};
212
213} // namespace tinyrobotics
A generic 3D coordinate class for robotics, navigation, and spatial calculations.
Definition: Coordinate.h:57
Represents a distance measurement with unit conversion support.
Definition: Distance.h:40
A grid map using two bit vectors to represent CellState efficiently.
Definition: GridBitMap.h:30
size_t readFrom(Stream &in)
Read map from input.
Definition: GridBitMap.h:162
size_t writeTo(Print &out)
Write map to output.
Definition: GridBitMap.h:157
Coordinate< T > toWorld(int cx, int cy) const override
Convert cell indices to world coordinates (returns Coordinate<T>)
Definition: GridBitMap.h:150
int getXCount() const
Get the number of cells in the X direction.
Definition: GridBitMap.h:55
std::vector< Coordinate<> > getNeighbors(Coordinate< T > from) const
Get world coordinates of neighboring cells (for pathfinding or navigation)
Definition: GridBitMap.h:139
bool isValid(const Coordinate< T > &coord) const
Check if a coordinate is within the map bounds.
Definition: GridBitMap.h:131
int getYCount() const
Get the number of cells in the Y direction.
Definition: GridBitMap.h:57
bool getCell(int cx, int cy, CellState &result) const
Get the state of a cell by integer indices.
Definition: GridBitMap.h:76
std::vector< Cell > getNeighborCells(const Coordinate< T > from) const
Determine all neighboring cells (8-connected) for a given cell coordinate.
Definition: GridBitMap.h:178
float getResolution() const
Get the map resolution (cell size in meters).
Definition: GridBitMap.h:59
Utility class for serializing and deserializing grid maps in CSV format.
Definition: MapSerializer.h:42
Abstract interface for 2D grid maps and occupancy maps in TinyRobotics.
Definition: IMap.h:79
DistanceUnit
Supported distance units for conversion and representation.
Definition: Distance.h:10
CellState
Cell state for occupancy grid mapping (e.g., UNKNOWN, FREE, OCCUPIED).
Definition: Common.h:32
Definition: GridBitMap.h:32