Skip to main content

draco_oxide_core/mesh/
mod.rs

1pub mod builder;
2pub mod ds;
3
4use crate::attribute::{Attribute, AttributeType, ComponentDataType};
5use crate::types::{Float, Vector};
6use crate::types::{NdVector, PointIdx};
7use crate::utils::geom::point_to_face_distance_3d;
8
9/// Represents a 3D mesh.
10/// It consists of a list of faces, where each face is defined by three vertex indices,
11/// and a list of attributes ([Attribute]) that can be associated with the mesh.
12#[derive(Clone, Debug)]
13pub struct Mesh {
14    /// The faces as point-index triples.
15    pub faces: Vec<[PointIdx; 3]>,
16    /// The attributes attached to the mesh.
17    pub attributes: Vec<Attribute>,
18
19    name: String,
20}
21
22impl Default for Mesh {
23    fn default() -> Self {
24        Self::new()
25    }
26}
27
28impl Mesh {
29    /// Returns the attributes of the mesh.
30    pub fn get_attributes(&self) -> &[Attribute] {
31        &self.attributes
32    }
33
34    /// Returns the faces of the mesh as point-index triples.
35    pub fn get_faces(&self) -> &[[PointIdx; 3]] {
36        &self.faces
37    }
38
39    /// Returns the attributes of the mesh mutably.
40    pub fn get_attributes_mut(&mut self) -> &mut [Attribute] {
41        &mut self.attributes
42    }
43
44    /// Returns mutable references to the attributes at the given indices.
45    ///
46    /// The indices must be pairwise distinct; duplicate indices would produce
47    /// two mutable references to the same attribute. Panics if any index is
48    /// out of bounds.
49    pub fn get_attributes_mut_by_indices<'a>(
50        &'a mut self,
51        indices: &[usize],
52    ) -> Vec<&'a mut Attribute> {
53        let out = indices
54            .iter()
55            .map(|i| &mut self.attributes[*i] as *mut Attribute)
56            .collect::<Vec<_>>();
57
58        // Safety: the indices are pairwise distinct per the documented
59        // contract, so the resulting mutable references do not alias.
60        unsafe {
61            let out = out.to_vec();
62            std::mem::transmute::<Vec<*mut Attribute>, Vec<&mut Attribute>>(out)
63        }
64    }
65
66    /// Returns the name of the mesh, or an empty string if unset.
67    pub fn get_name(&self) -> &str {
68        &self.name
69    }
70
71    /// Sets the name of the mesh.
72    pub fn set_name(&mut self, name: &str) {
73        self.name = name.to_owned();
74    }
75
76    /// Creates an empty mesh with no faces and no attributes.
77    pub fn new() -> Self {
78        Self {
79            faces: Vec::new(),
80            attributes: Vec::new(),
81
82            name: String::new(),
83        }
84    }
85
86    /// Computes a symmetric point-to-surface L2 distance between the position
87    /// attributes of `self` and `other`, normalized by the total number of
88    /// points. Panics if a position attribute does not have three float
89    /// components.
90    pub fn diff_l2_norm(&self, other: &Self) -> f64 {
91        let pos_att_iter = self
92            .attributes
93            .iter()
94            .enumerate()
95            .filter(|(_, att)| att.get_attribute_type() == AttributeType::Position);
96        let other_pos_att_iter = other
97            .attributes
98            .iter()
99            .enumerate()
100            .filter(|(_, att)| att.get_attribute_type() == AttributeType::Position);
101
102        let mut num_points = 0;
103        let mut sum_of_squared_dist = 0.0;
104        for ((_, pos_att), (_, other_pos_att)) in pos_att_iter.zip(other_pos_att_iter) {
105            if pos_att.get_num_components() != 3 {
106                panic!("Position attribute must have 3 components, but the first mesh has {} components", pos_att.get_num_components());
107            }
108
109            // Faces are now stored directly in the mesh
110            let faces = &self.faces;
111            let other_faces = &other.faces;
112
113            num_points += pos_att.len();
114            num_points += other_pos_att.len();
115            sum_of_squared_dist +=
116                sum_of_squared_dist_unpack_datatype(pos_att, faces, other_pos_att, other_faces);
117        }
118
119        sum_of_squared_dist.sqrt() / num_points as f64
120    }
121}
122
123fn sum_of_squared_dist_unpack_datatype(
124    position_att: &Attribute,
125    faces: &[[PointIdx; 3]],
126    other_position_att: &Attribute,
127    other_faces: &[[PointIdx; 3]],
128) -> f64 {
129    // Safety:
130    // 1. The number of components is checked to be 3.
131    // 2. The component type is checked to be f32 or f64.
132    unsafe {
133        match position_att.get_component_type() {
134            ComponentDataType::F32 => sum_of_squared_dist_impl::<f32>(
135                position_att,
136                faces,
137                other_position_att,
138                other_faces,
139            ) as f64,
140            ComponentDataType::F64 => sum_of_squared_dist_impl::<f64>(
141                position_att,
142                faces,
143                other_position_att,
144                other_faces,
145            ),
146            _ => panic!("Position Attribute is not of type f32 or f64"),
147        }
148    }
149}
150
151// # Safety: it must be safe to cast the first argument to &[Data]
152unsafe fn sum_of_squared_dist_impl<F>(
153    self_pos_att: &Attribute,
154    self_faces: &[[PointIdx; 3]],
155    other_pos_att: &Attribute,
156    other_faces: &[[PointIdx; 3]],
157) -> F
158where
159    F: Float,
160    NdVector<3, F>: Vector<3, Component = F>,
161{
162    assert!(
163        other_pos_att.get_component_type() == self_pos_att.get_component_type(),
164        "Component types must match, but the first mesh has {:?} and the second mesh has {:?}",
165        self_pos_att.get_component_type(),
166        other_pos_att.get_component_type()
167    );
168
169    if other_pos_att.get_num_components() != 3 {
170        panic!(
171            "Position attribute must have 3 components, but the second mesh has {} components",
172            other_pos_att.get_num_components()
173        );
174    }
175
176    // Faces reference points; resolve them to unique-value indices so the value
177    // slices below can be indexed directly (the point-to-value map need not be
178    // the identity).
179    let resolve = |att: &Attribute, faces: &[[PointIdx; 3]]| -> Vec<[usize; 3]> {
180        faces
181            .iter()
182            .map(|f| {
183                [
184                    usize::from(att.get_unique_val_idx(f[0])),
185                    usize::from(att.get_unique_val_idx(f[1])),
186                    usize::from(att.get_unique_val_idx(f[2])),
187                ]
188            })
189            .collect()
190    };
191    let self_face_vals = resolve(self_pos_att, self_faces);
192    let other_face_vals = resolve(other_pos_att, other_faces);
193
194    // Safety: upheld
195    let self_pos_att = self_pos_att.unique_vals_as_slice_unchecked::<NdVector<3, F>>();
196    // Satety: Just checked
197    let other_pos_att = unsafe { other_pos_att.unique_vals_as_slice_unchecked::<NdVector<3, F>>() };
198
199    let mut sum_of_squared_dist = F::zero();
200    for pos in self_pos_att.iter() {
201        let min_dist = min_dist_point_to_faces(*pos, &other_face_vals, other_pos_att);
202        sum_of_squared_dist += min_dist * min_dist;
203    }
204    for pos in other_pos_att.iter() {
205        let min_dist = min_dist_point_to_faces(*pos, &self_face_vals, self_pos_att);
206        sum_of_squared_dist += min_dist * min_dist;
207    }
208
209    sum_of_squared_dist.sqrt()
210}
211
212fn min_dist_point_to_faces<F>(
213    p: NdVector<3, F>,
214    face_vals: &[[usize; 3]],
215    pos_att: &[NdVector<3, F>],
216) -> F
217where
218    F: Float,
219{
220    let mut min_dist = F::MAX_VALUE;
221    for face in face_vals {
222        let v0 = pos_att[face[0]];
223        let v1 = pos_att[face[1]];
224        let v2 = pos_att[face[2]];
225        let dist = point_to_face_distance_3d(p, [v0, v1, v2]);
226        if dist < min_dist {
227            min_dist = dist;
228        }
229    }
230    min_dist
231}