dae_parser/api/
source.rs

1//! Contains functions for reading data out of a [`Source`].
2
3use super::*;
4
5/// A trait implemented on marker types like [`XYZ`] to allow for strict typing of input kinds.
6pub trait InputKind {
7    /// The array type that this reader is expecting. Must be one of the types in [`ArrayElement`].
8    type Array: ArrayKind;
9    /// The associated reader type.
10    type Reader: SourceRead<Self>;
11
12    /// Constructs a new reader object from the given [`Accessor`].
13    fn new_reader(&self, acc: &Accessor) -> Option<Self::Reader>;
14}
15
16/// A trait for [`Source`] readers, which are used via the [`SourceReader`]
17/// type returned from [`Source::reader`]. Users can implement this trait
18/// on their own type to customize reading, or use [`XYZ`] and [`ST`]
19/// for the common cases.
20pub trait SourceRead<K: InputKind + ?Sized>: Sized {
21    /// The output value. This must be `Clone` because outputs are reused in some mesh kinds,
22    /// like [`TriStrips`].
23    type Output: Clone;
24
25    /// Given the array data for a single vertex, extract the values into [`Output`](Self::Output).
26    /// The length of `data` will be the [`stride`](Accessor::stride) of the accessor.
27    ///
28    /// Readers should be stateless, because the output of a `load` call may be cached and reused
29    /// instead of calling `load` again.
30    fn load(&self, data: &[<K::Array as ArrayKind>::Elem]) -> Self::Output;
31}
32
33/// The output of [`SourceReader::map`].
34#[derive(Copy, Clone, Debug)]
35pub struct Map<R, F> {
36    reader: R,
37    f: F,
38}
39
40impl<K: InputKind, R: SourceRead<K>, O: Clone, F: Fn(R::Output) -> O> SourceRead<K> for Map<R, F> {
41    type Output = O;
42
43    fn load(&self, data: &[<K::Array as ArrayKind>::Elem]) -> Self::Output {
44        (self.f)(self.reader.load(data))
45    }
46}
47
48impl Accessor {
49    /// Get the offset for a parameter with the given name and type.
50    pub fn param_offset(&self, name: &str, ty: &str) -> Option<usize> {
51        let pos = self
52            .param
53            .iter()
54            .position(|p| p.name.as_deref() == Some(name) && p.ty == ty)?;
55        Some(self.offset + pos)
56    }
57}
58
59/// A `SourceReader` is responsible for preparing a [`Source`] for reading, by doing some up front
60/// calulation in `Source::reader` to determine how the data will be sliced. Once it is ready,
61/// [`SourceReader::get`] is used to access values from the array.
62/// [`SourceReader`] also implements [`Iterator`], so it can be used in for loops.
63#[derive(Clone, Debug)]
64pub struct SourceReader<'a, K: InputKind, R = <K as InputKind>::Reader> {
65    kind: K,
66    array: &'a [<K::Array as ArrayKind>::Elem],
67    stride: usize,
68    len: usize,
69    reader: R,
70}
71
72impl Source {
73    /// Construct a new [`SourceReader`] for this source, which can be used to access elements
74    /// of the stored array. The input reader can be a user struct, or one of the pre-built readers
75    /// [`XYZReader`] and [`STReader`] by passing the marker types
76    /// [`XYZ`] or [`ST`] to [`Source::reader`].
77    pub fn reader<K: InputKind>(&self, kind: K) -> Option<SourceReader<'_, K>> {
78        let arr = self.array.as_ref()?;
79        if matches!((arr.id(), &self.accessor.source), (Some(id), Url::Fragment(s)) if s == id) {
80            let array = K::Array::from_array_element(arr)?;
81            debug_assert!(self.accessor.count * self.accessor.stride == array.len());
82            Some(SourceReader {
83                reader: kind.new_reader(&self.accessor)?,
84                kind,
85                array,
86                stride: self.accessor.stride,
87                len: self.accessor.count,
88            })
89        } else {
90            None // Reading external arrays is not supported
91        }
92    }
93}
94
95impl<'a, K: InputKind, R: SourceRead<K>> SourceReader<'a, K, R> {
96    /// Map a function on a source reader.
97    pub fn map_reader<S: SourceRead<K>>(self, f: impl FnOnce(R) -> S) -> SourceReader<'a, K, S> {
98        SourceReader {
99            kind: self.kind,
100            array: self.array,
101            stride: self.stride,
102            len: self.len,
103            reader: f(self.reader),
104        }
105    }
106    /// Map a function on a source reader.
107    pub fn map<O: Clone, F: Fn(R::Output) -> O>(self, f: F) -> SourceReader<'a, K, Map<R, F>> {
108        self.map_reader(|reader| Map { reader, f })
109    }
110
111    /// Return a specified element of the array.
112    pub fn get(&self, i: usize) -> R::Output {
113        let elems = &self.array[i * self.stride..][..self.stride];
114        self.reader.load(elems)
115    }
116
117    /// Advances the iterator by `n` elements. See [`Iterator::advance_by`].
118    pub fn advance_by(&mut self, n: usize) -> Result<(), usize> {
119        if n <= self.len {
120            self.array = &self.array[n * self.stride..];
121            Ok(())
122        } else {
123            Err(self.len)
124        }
125    }
126
127    /// Advances the iterator from the back by `n` elements.
128    /// See [`DoubleEndedIterator::advance_back_by`].
129    pub fn advance_back_by(&mut self, n: usize) -> Result<(), usize> {
130        if n <= self.len {
131            self.array = &self.array[..self.array.len() - n * self.stride];
132            Ok(())
133        } else {
134            Err(self.len)
135        }
136    }
137}
138
139impl<'a, K: InputKind, R: SourceRead<K>> ExactSizeIterator for SourceReader<'a, K, R> {
140    fn len(&self) -> usize {
141        self.len
142    }
143}
144
145impl<'a, K: InputKind, R: SourceRead<K>> DoubleEndedIterator for SourceReader<'a, K, R> {
146    fn next_back(&mut self) -> Option<Self::Item> {
147        self.len = self.len.checked_sub(1)?;
148        let (left, right) = self.array.split_at(self.len);
149        self.array = left;
150        Some(self.reader.load(right))
151    }
152
153    fn nth_back(&mut self, n: usize) -> Option<Self::Item> {
154        self.advance_back_by(n).ok()?;
155        self.next_back()
156    }
157}
158
159impl<'a, K: InputKind, R: SourceRead<K>> Iterator for SourceReader<'a, K, R> {
160    type Item = R::Output;
161
162    fn next(&mut self) -> Option<Self::Item> {
163        self.len = self.len.checked_sub(1)?;
164        let (left, right) = self.array.split_at(self.stride);
165        self.array = right;
166        Some(self.reader.load(left))
167    }
168
169    fn size_hint(&self) -> (usize, Option<usize>) {
170        (self.len, Some(self.len))
171    }
172
173    fn count(self) -> usize {
174        self.len
175    }
176
177    fn last(mut self) -> Option<Self::Item> {
178        self.next_back()
179    }
180
181    fn nth(&mut self, n: usize) -> Option<Self::Item> {
182        self.advance_by(n).ok()?;
183        self.next()
184    }
185}
186
187/// A marker type for inputs with `name="X"`, `"Y"`, `"Z"`.
188/// This is used in Collada to store vertex position data, and normal data.
189#[derive(Copy, Clone, Debug, Default)]
190pub struct XYZ;
191
192impl InputKind for XYZ {
193    type Array = FloatArray;
194    type Reader = XYZReader;
195    fn new_reader(&self, acc: &Accessor) -> Option<Self::Reader> {
196        Some(XYZReader {
197            x: acc.param_offset("X", "float")?,
198            y: acc.param_offset("Y", "float")?,
199            z: acc.param_offset("Z", "float")?,
200        })
201    }
202}
203
204/// A reader which accesses fields with `name="X"`, `"Y"`, `"Z"` in the source array.
205/// This is used in Collada to store vertex position data, and normal data.
206#[derive(Copy, Clone, Debug)]
207pub struct XYZReader {
208    x: usize,
209    y: usize,
210    z: usize,
211}
212
213impl SourceRead<XYZ> for XYZReader {
214    type Output = [f32; 3];
215    fn load(&self, data: &[f32]) -> Self::Output {
216        [data[self.x], data[self.y], data[self.z]]
217    }
218}
219
220/// A marker type for inputs with `name="S"`, `"T"`.
221/// Collada uses `S` and `T` to denote texture coordinates (usually called "UV"),
222/// because `"U"` and `"V"` are used for generic parameters.
223#[derive(Copy, Clone, Debug, Default)]
224pub struct ST;
225
226impl InputKind for ST {
227    type Array = FloatArray;
228    type Reader = STReader;
229    fn new_reader(&self, acc: &Accessor) -> Option<Self::Reader> {
230        Some(STReader {
231            s: acc.param_offset("S", "float")?,
232            t: acc.param_offset("T", "float")?,
233        })
234    }
235}
236
237/// A reader which accesses fields with `name="S"`, `"T"` in the source array.
238/// Collada uses `S` and `T` to denote texture coordinates (usually called "UV"),
239/// because `"U"` and `"V"` are used for generic parameters.
240#[derive(Copy, Clone, Debug)]
241pub struct STReader {
242    s: usize,
243    t: usize,
244}
245
246impl SourceRead<ST> for STReader {
247    type Output = [f32; 2];
248    fn load(&self, data: &[f32]) -> Self::Output {
249        [data[self.s], data[self.t]]
250    }
251}