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
use crate::*;
use rustc_hash::FxHashMap as HashMap;
use std::fmt::Debug;
use std::hash::Hash;

impl<V: Copy + Hash + Debug + Eq, A: Attributes<V>> PolygonMesh<V, A> {
    /// Contract attributes and expand polygon.
    ///
    /// # Examples
    /// ```
    /// use truck_polymesh::*;
    /// let polygon = PolygonMesh::new(
    ///     StandardAttributes {
    ///         positions: vec![
    ///             Point3::new(0.0, 0.0, 0.0),
    ///             Point3::new(1.0, 0.0, 0.0),
    ///             Point3::new(0.0, 1.0, 0.0),
    ///             Point3::new(1.0, 1.0, 0.0),
    ///         ],
    ///         normals: vec![
    ///             Vector3::new(0.0, 0.0, 1.0),
    ///             Vector3::new(0.0, 0.0, -1.0),
    ///         ],
    ///         ..Default::default()
    ///     },
    ///     Faces::from_iter(&[
    ///         &[(0, None, Some(0)), (1, None, Some(0)), (2, None, Some(0))],
    ///         &[(3, None, Some(1)), (1, None, Some(1)), (2, None, Some(1))],
    ///     ])
    /// );
    /// let expands = polygon.expands(|attr| (attr.position, attr.normal.unwrap()));
    /// assert_eq!(
    ///     expands,
    ///     PolygonMesh::<usize, Vec<(Point3, Vector3)>>::new(
    ///         vec![
    ///            (Point3::new(0.0, 0.0, 0.0), Vector3::new(0.0, 0.0, 1.0)),
    ///            (Point3::new(1.0, 0.0, 0.0), Vector3::new(0.0, 0.0, 1.0)),
    ///            (Point3::new(0.0, 1.0, 0.0), Vector3::new(0.0, 0.0, 1.0)),
    ///            (Point3::new(1.0, 1.0, 0.0), Vector3::new(0.0, 0.0, -1.0)),
    ///            (Point3::new(1.0, 0.0, 0.0), Vector3::new(0.0, 0.0, -1.0)),
    ///            (Point3::new(0.0, 1.0, 0.0), Vector3::new(0.0, 0.0, -1.0)),
    ///         ],
    ///         Faces::from_iter(&[[0, 1, 2], [3, 4, 5]]),
    ///     )
    /// );
    /// ```
    pub fn expands<T: Copy>(
        &self,
        contraction: impl Fn(A::Output) -> T,
    ) -> PolygonMesh<usize, Vec<T>> {
        let mut vec = Vec::<T>::new();
        let mut vertex_map = HashMap::<V, usize>::default();
        let faces: Faces<usize> = self
            .face_iter()
            .map(|face| {
                face.iter()
                    .cloned()
                    .map(|vertex| {
                        *vertex_map.entry(vertex).or_insert_with(|| {
                            let idx = vec.len();
                            vec.push(contraction(self.attributes.get(vertex).unwrap()));
                            idx
                        })
                    })
                    .collect::<Vec<_>>()
            })
            .collect();
        PolygonMesh::new(vec, faces)
    }
}