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