Skip to main content

bunny_codec/obj/
iter.rs

1use std::str::Lines;
2
3use bunny_mesh::Triangle32;
4
5use super::{
6    face_indices_are_valid, parse_face_line, parse_vertex_line, statement_kind, ObjError, ObjVertex,
7};
8
9/// Forward iterator over decoded OBJ vertex records.
10#[derive(Clone, Debug)]
11pub struct ObjVertices<'a> {
12    lines: Lines<'a>,
13}
14
15impl<'a> ObjVertices<'a> {
16    pub(super) fn new(source: &'a str) -> Self {
17        Self { lines: source.lines() }
18    }
19}
20
21impl Iterator for ObjVertices<'_> {
22    type Item = Result<ObjVertex, ObjError>;
23
24    fn next(&mut self) -> Option<Self::Item> {
25        next_record(&mut self.lines, "v").map(|line| line.and_then(parse_vertex_line))
26    }
27}
28
29/// Forward iterator over decoded OBJ triangle face records.
30#[derive(Clone, Debug)]
31pub struct ObjTriangles<'a> {
32    lines: Lines<'a>,
33    vertex_count: usize,
34}
35
36impl<'a> ObjTriangles<'a> {
37    pub(super) fn new(source: &'a str, vertex_count: usize) -> Self {
38        Self { lines: source.lines(), vertex_count }
39    }
40}
41
42impl Iterator for ObjTriangles<'_> {
43    type Item = Result<Triangle32, ObjError>;
44
45    fn next(&mut self) -> Option<Self::Item> {
46        next_record(&mut self.lines, "f").map(|line| {
47            line.and_then(parse_face_line)
48                .and_then(|triangle| validate_iter_triangle(triangle, self.vertex_count))
49        })
50    }
51}
52
53pub(super) fn find_record<'a>(source: &'a str, kind: &str, index: usize) -> Option<&'a str> {
54    let mut lines = source.lines();
55    let mut found = 0;
56    while let Some(record) = next_record(&mut lines, kind) {
57        let Ok(line) = record else {
58            return None;
59        };
60        if found == index {
61            return Some(line);
62        }
63        found += 1;
64    }
65    None
66}
67
68fn next_record<'a>(lines: &mut Lines<'a>, kind: &str) -> Option<Result<&'a str, ObjError>> {
69    for line in lines {
70        match statement_kind(line) {
71            Ok(Some(record_kind)) if record_kind == kind => return Some(Ok(line)),
72            Ok(Some(_) | None) => {}
73            Err(error) => return Some(Err(error)),
74        }
75    }
76    None
77}
78
79fn validate_iter_triangle(
80    triangle: Triangle32,
81    vertex_count: usize,
82) -> Result<Triangle32, ObjError> {
83    if face_indices_are_valid(triangle, vertex_count) {
84        Ok(triangle)
85    } else {
86        Err(ObjError::IndexOutOfBounds)
87    }
88}