1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
use crate::*;
use std::{
    error::Error,
    fs::File,
    io::Write as IoWrite,
    path::{Path, PathBuf},
};

impl Polyhedron {
    /// Write the polyhedron to a
    /// [Wavefront OBJ](https://en.wikipedia.org/wiki/Wavefront_.obj_file)
    /// file.
    ///
    /// The [`name`](Polyhedron::name()) of the polyhedron is appended
    /// to the given `destination` and postfixed with the extension
    /// `.obj`.
    ///
    /// Depending on the target coordinate system (left- or right
    /// handed) the mesh’s winding order can be reversed with the
    /// `reverse_face_winding` flag.
    ///
    /// The return value, on success, is the final, complete path of
    /// the OBJ file.
    #[cfg(feature = "obj")]
    pub fn write_obj(
        &self,
        destination: &Path,
        reverse_winding: bool,
    ) -> Result<PathBuf, Box<dyn Error>> {
        let path = destination.join(format!("polyhedron-{}.obj", self.name));
        let mut file = File::create(path.clone())?;

        writeln!(file, "o {}", self.name)?;

        for vertex in &self.positions {
            writeln!(file, "v {} {} {}", vertex.x, vertex.y, vertex.z)?;
        }

        match reverse_winding {
            true => {
                for face in &self.face_index {
                    write!(file, "f")?;
                    for vertex_index in face.iter().rev() {
                        write!(file, " {}", vertex_index + 1)?;
                    }
                    writeln!(file)?;
                }
            }
            false => {
                for face in &self.face_index {
                    write!(file, "f")?;
                    for vertex_index in face {
                        write!(file, " {}", vertex_index + 1)?;
                    }
                    writeln!(file)?;
                }
            }
        };

        file.flush()?;

        Ok(path)
    }

    pub fn read_obj(
        source: &Path,
        reverse_winding: bool,
    ) -> Result<Self, tobj::LoadError> {
        let (geometry, _) =
            tobj::load_obj(source, &tobj::OFFLINE_RENDERING_LOAD_OPTIONS)?;

        Ok(Polyhedron {
            face_index: {
                let mut index = 0;
                geometry[0]
                    .mesh
                    .face_arities
                    .iter()
                    .map(|&face_arity| {
                        assert!(0 != face_arity);
                        let face_arity = face_arity as usize;
                        let mut face_indices = geometry[0].mesh.indices
                            [index..index + face_arity]
                            .to_vec();
                        if reverse_winding {
                            face_indices.reverse();
                        }
                        index += face_arity;

                        face_indices
                    })
                    .collect()
            },
            positions: geometry[0]
                .mesh
                .positions
                .iter()
                .array_chunks::<3>()
                .map(|p| Point::new(*p[0], *p[1], *p[2]))
                .collect(),
            name: geometry[0].name.clone(),
            ..Default::default()
        })
    }
}