Skip to main content

waymark_tilecache/
tile_cache_data.rs

1//! Tile cache data structures and serialization
2//!
3//! This module defines the data structures used for tile caching
4//! and provides serialization/deserialization functionality.
5
6use crate::error::TileCacheError;
7
8/// Magic number for tile cache data
9const TILECACHE_MAGIC: u32 = 0x4C494554; // 'TILE' in ASCII
10
11/// Version number for tile cache data format
12const TILECACHE_VERSION: u32 = 1;
13
14/// Tile cache layer header
15#[derive(Debug, Clone)]
16#[cfg_attr(
17    feature = "serialization",
18    derive(serde::Serialize, serde::Deserialize)
19)]
20pub struct TileCacheLayerHeader {
21    /// Magic number for validation
22    pub(crate) magic: u32,
23    /// Version of the tile cache format
24    pub(crate) version: u32,
25    /// Tile position X
26    pub(crate) tx: i32,
27    /// Tile position Y
28    pub(crate) ty: i32,
29    /// Tile layer
30    pub(crate) tlayer: i32,
31    /// Bounding box minimum
32    pub(crate) bmin: [f32; 3],
33    /// Bounding box maximum
34    pub(crate) bmax: [f32; 3],
35    /// Height of the layer (in cells)
36    pub(crate) hmin: u16,
37    /// Height of the layer (in cells)
38    pub(crate) hmax: u16,
39    /// Width of the layer (in cells)
40    pub(crate) width: u8,
41    /// Height of the layer (in cells)
42    pub(crate) height: u8,
43    /// Minimum region id
44    pub(crate) minx: u8,
45    /// Maximum region id
46    pub(crate) maxx: u8,
47    /// Minimum region id
48    pub(crate) miny: u8,
49    /// Maximum region id
50    pub(crate) maxy: u8,
51}
52
53impl Default for TileCacheLayerHeader {
54    fn default() -> Self {
55        Self::new()
56    }
57}
58
59impl TileCacheLayerHeader {
60    /// Creates a new tile cache layer header
61    pub fn new() -> Self {
62        Self {
63            magic: TILECACHE_MAGIC,
64            version: TILECACHE_VERSION,
65            tx: 0,
66            ty: 0,
67            tlayer: 0,
68            bmin: [0.0; 3],
69            bmax: [0.0; 3],
70            hmin: 0,
71            hmax: 0,
72            width: 0,
73            height: 0,
74            minx: 0,
75            maxx: 0,
76            miny: 0,
77            maxy: 0,
78        }
79    }
80
81    /// Validates the header
82    pub fn validate(&self) -> Result<(), TileCacheError> {
83        if self.magic != TILECACHE_MAGIC {
84            return Err(TileCacheError::InvalidParam);
85        }
86        if self.version != TILECACHE_VERSION {
87            return Err(TileCacheError::InvalidParam);
88        }
89        Ok(())
90    }
91
92    /// Serializes the header to bytes
93    pub fn to_bytes(&self) -> Vec<u8> {
94        let mut bytes = Vec::with_capacity(56);
95
96        // Write magic and version
97        bytes.extend_from_slice(&self.magic.to_le_bytes());
98        bytes.extend_from_slice(&self.version.to_le_bytes());
99
100        // Write tile position
101        bytes.extend_from_slice(&self.tx.to_le_bytes());
102        bytes.extend_from_slice(&self.ty.to_le_bytes());
103        bytes.extend_from_slice(&self.tlayer.to_le_bytes());
104
105        // Write bounding box
106        for i in 0..3 {
107            bytes.extend_from_slice(&self.bmin[i].to_le_bytes());
108        }
109        for i in 0..3 {
110            bytes.extend_from_slice(&self.bmax[i].to_le_bytes());
111        }
112
113        // Write dimensions
114        bytes.extend_from_slice(&self.hmin.to_le_bytes());
115        bytes.extend_from_slice(&self.hmax.to_le_bytes());
116        bytes.push(self.width);
117        bytes.push(self.height);
118        bytes.push(self.minx);
119        bytes.push(self.maxx);
120        bytes.push(self.miny);
121        bytes.push(self.maxy);
122
123        bytes
124    }
125
126    /// Deserializes the header from bytes
127    pub fn from_bytes(data: &[u8]) -> Result<Self, TileCacheError> {
128        if data.len() < 54 {
129            return Err(TileCacheError::InvalidParam);
130        }
131
132        let mut offset = 0;
133
134        // Read magic and version
135        let magic = u32::from_le_bytes([
136            data[offset],
137            data[offset + 1],
138            data[offset + 2],
139            data[offset + 3],
140        ]);
141        offset += 4;
142        let version = u32::from_le_bytes([
143            data[offset],
144            data[offset + 1],
145            data[offset + 2],
146            data[offset + 3],
147        ]);
148        offset += 4;
149
150        // Read tile position
151        let tx = i32::from_le_bytes([
152            data[offset],
153            data[offset + 1],
154            data[offset + 2],
155            data[offset + 3],
156        ]);
157        offset += 4;
158        let ty = i32::from_le_bytes([
159            data[offset],
160            data[offset + 1],
161            data[offset + 2],
162            data[offset + 3],
163        ]);
164        offset += 4;
165        let tlayer = i32::from_le_bytes([
166            data[offset],
167            data[offset + 1],
168            data[offset + 2],
169            data[offset + 3],
170        ]);
171        offset += 4;
172
173        // Read bounding box
174        let mut bmin = [0.0f32; 3];
175        for item in &mut bmin {
176            *item = f32::from_le_bytes([
177                data[offset],
178                data[offset + 1],
179                data[offset + 2],
180                data[offset + 3],
181            ]);
182            offset += 4;
183        }
184        let mut bmax = [0.0f32; 3];
185        for item in &mut bmax {
186            *item = f32::from_le_bytes([
187                data[offset],
188                data[offset + 1],
189                data[offset + 2],
190                data[offset + 3],
191            ]);
192            offset += 4;
193        }
194
195        // Read dimensions
196        let hmin = u16::from_le_bytes([data[offset], data[offset + 1]]);
197        offset += 2;
198        let hmax = u16::from_le_bytes([data[offset], data[offset + 1]]);
199        offset += 2;
200        let width = data[offset];
201        offset += 1;
202        let height = data[offset];
203        offset += 1;
204        let minx = data[offset];
205        offset += 1;
206        let maxx = data[offset];
207        offset += 1;
208        let miny = data[offset];
209        offset += 1;
210        let maxy = data[offset];
211
212        let header = Self {
213            magic,
214            version,
215            tx,
216            ty,
217            tlayer,
218            bmin,
219            bmax,
220            hmin,
221            hmax,
222            width,
223            height,
224            minx,
225            maxx,
226            miny,
227            maxy,
228        };
229
230        header.validate()?;
231        Ok(header)
232    }
233
234    /// Returns the magic number.
235    pub fn magic(&self) -> u32 {
236        self.magic
237    }
238
239    /// Returns the format version.
240    pub fn version(&self) -> u32 {
241        self.version
242    }
243
244    /// Returns the tile X position.
245    pub fn tx(&self) -> i32 {
246        self.tx
247    }
248
249    /// Returns the tile Y position.
250    pub fn ty(&self) -> i32 {
251        self.ty
252    }
253
254    /// Returns the tile layer index.
255    pub fn tlayer(&self) -> i32 {
256        self.tlayer
257    }
258
259    /// Returns the bounding box minimum.
260    pub fn bmin(&self) -> [f32; 3] {
261        self.bmin
262    }
263
264    /// Returns the bounding box maximum.
265    pub fn bmax(&self) -> [f32; 3] {
266        self.bmax
267    }
268
269    /// Returns the minimum height (in cells).
270    pub fn hmin(&self) -> u16 {
271        self.hmin
272    }
273
274    /// Returns the maximum height (in cells).
275    pub fn hmax(&self) -> u16 {
276        self.hmax
277    }
278
279    /// Returns the layer width (in cells).
280    pub fn width(&self) -> u8 {
281        self.width
282    }
283
284    /// Returns the layer height (in cells).
285    pub fn height(&self) -> u8 {
286        self.height
287    }
288
289    /// Returns the minimum X region bound.
290    pub fn minx(&self) -> u8 {
291        self.minx
292    }
293
294    /// Returns the maximum X region bound.
295    pub fn maxx(&self) -> u8 {
296        self.maxx
297    }
298
299    /// Returns the minimum Y region bound.
300    pub fn miny(&self) -> u8 {
301        self.miny
302    }
303
304    /// Returns the maximum Y region bound.
305    pub fn maxy(&self) -> u8 {
306        self.maxy
307    }
308}
309
310/// Compressed tile cache layer data
311#[derive(Debug, Clone)]
312pub struct TileCacheLayer {
313    /// Header information
314    pub header: TileCacheLayerHeader,
315    /// Compressed region data
316    pub regons: Vec<u8>,
317    /// Compressed area data
318    pub areas: Vec<u8>,
319    /// Compressed connection data
320    pub cons: Vec<u8>,
321}
322
323impl TileCacheLayer {
324    /// Creates a new tile cache layer
325    pub fn new(header: TileCacheLayerHeader) -> Self {
326        Self {
327            header,
328            regons: Vec::new(),
329            areas: Vec::new(),
330            cons: Vec::new(),
331        }
332    }
333
334    /// Serializes the layer to bytes (uncompressed)
335    pub fn to_bytes(&self) -> Vec<u8> {
336        let mut bytes = Vec::new();
337
338        // Write header
339        bytes.extend_from_slice(&self.header.to_bytes());
340
341        // Write data lengths
342        bytes.extend_from_slice(&(self.regons.len() as u32).to_le_bytes());
343        bytes.extend_from_slice(&(self.areas.len() as u32).to_le_bytes());
344        bytes.extend_from_slice(&(self.cons.len() as u32).to_le_bytes());
345
346        // Write data
347        bytes.extend_from_slice(&self.regons);
348        bytes.extend_from_slice(&self.areas);
349        bytes.extend_from_slice(&self.cons);
350
351        bytes
352    }
353
354    /// Deserializes the layer from bytes (uncompressed)
355    pub fn from_bytes(data: &[u8]) -> Result<Self, TileCacheError> {
356        if data.len() < 66 {
357            // Header (54) + 3 lengths (12)
358            return Err(TileCacheError::InvalidParam);
359        }
360
361        let header = TileCacheLayerHeader::from_bytes(data)?;
362        let mut offset = 54; // Header size
363
364        // Read data lengths
365        let regons_len = u32::from_le_bytes([
366            data[offset],
367            data[offset + 1],
368            data[offset + 2],
369            data[offset + 3],
370        ]) as usize;
371        offset += 4;
372        let areas_len = u32::from_le_bytes([
373            data[offset],
374            data[offset + 1],
375            data[offset + 2],
376            data[offset + 3],
377        ]) as usize;
378        offset += 4;
379        let cons_len = u32::from_le_bytes([
380            data[offset],
381            data[offset + 1],
382            data[offset + 2],
383            data[offset + 3],
384        ]) as usize;
385        offset += 4;
386
387        // Validate lengths
388        if offset + regons_len + areas_len + cons_len > data.len() {
389            return Err(TileCacheError::InvalidParam);
390        }
391
392        // Read data
393        let regons = data[offset..offset + regons_len].to_vec();
394        offset += regons_len;
395        let areas = data[offset..offset + areas_len].to_vec();
396        offset += areas_len;
397        let cons = data[offset..offset + cons_len].to_vec();
398
399        Ok(Self {
400            header,
401            regons,
402            areas,
403            cons,
404        })
405    }
406}
407
408/// Tile cache builder configuration
409#[derive(Debug, Clone)]
410#[non_exhaustive]
411pub struct TileCacheBuilderConfig {
412    /// Cell size
413    pub cs: f32,
414    /// Cell height
415    pub ch: f32,
416    /// Agent height
417    pub walkable_height: i32,
418    /// Agent radius
419    pub walkable_radius: i32,
420    /// Agent max climb
421    pub walkable_climb: i32,
422    /// Maximum edge length
423    pub max_edge_len: f32,
424    /// Maximum simplification error
425    pub max_simplification_error: f32,
426    /// Minimum region size
427    pub min_region_area: i32,
428    /// Region merge area
429    pub merge_region_area: i32,
430    /// Maximum vertices per polygon
431    pub max_verts_per_poly: i32,
432}
433
434impl Default for TileCacheBuilderConfig {
435    fn default() -> Self {
436        Self {
437            cs: 0.3,
438            ch: 0.2,
439            walkable_height: 20,
440            walkable_radius: 6,
441            walkable_climb: 9,
442            max_edge_len: 12.0,
443            max_simplification_error: 1.3,
444            min_region_area: 8,
445            merge_region_area: 20,
446            max_verts_per_poly: 6,
447        }
448    }
449}