Skip to main content

lib3mf_core/parser/
displacement_parser.rs

1use crate::error::{Lib3mfError, Result};
2use crate::model::{
3    Channel, Displacement2D, DisplacementMesh, DisplacementTriangle, FilterMode, GradientVector,
4    NormalVector, ResourceId, TileStyle, Vertex,
5};
6use crate::parser::xml_parser::{XmlParser, get_attribute_f32, get_attribute_u32};
7use quick_xml::events::Event;
8use std::io::BufRead;
9
10/// Parses a `<displacementmesh>` element and its contents into a `DisplacementMesh`.
11pub fn parse_displacement_mesh<R: BufRead>(parser: &mut XmlParser<R>) -> Result<DisplacementMesh> {
12    let mut vertices = Vec::new();
13    let mut triangles = Vec::new();
14    let mut normals = Vec::new();
15    let mut gradients = Vec::new();
16
17    loop {
18        match parser.read_next_event()? {
19            Event::Start(e) => {
20                let local_name = e.local_name();
21                match local_name.as_ref() {
22                    b"vertices" => {
23                        vertices = parse_displacement_vertices(parser)?;
24                    }
25                    b"triangles" => {
26                        triangles = parse_displacement_triangles(parser)?;
27                    }
28                    b"normvectors" => {
29                        normals = parse_normal_vectors(parser)?;
30                    }
31                    b"disp2dgroups" => {
32                        // Parse displacement 2D groups which contain gradient vectors
33                        gradients = parse_disp2d_groups(parser)?;
34                    }
35                    _ => {}
36                }
37            }
38            Event::End(e) if e.local_name().as_ref() == b"displacementmesh" => break,
39            Event::Eof => {
40                return Err(Lib3mfError::Validation(
41                    "Unexpected EOF in displacementmesh".to_string(),
42                ));
43            }
44            _ => {}
45        }
46    }
47
48    Ok(DisplacementMesh {
49        vertices,
50        triangles,
51        normals,
52        gradients: if gradients.is_empty() {
53            None
54        } else {
55            Some(gradients)
56        },
57    })
58}
59
60fn parse_displacement_vertices<R: BufRead>(parser: &mut XmlParser<R>) -> Result<Vec<Vertex>> {
61    let mut vertices = Vec::new();
62    loop {
63        match parser.read_next_event()? {
64            Event::Start(e) | Event::Empty(e) if e.local_name().as_ref() == b"vertex" => {
65                let x = get_attribute_f32(&e, b"x")?;
66                let y = get_attribute_f32(&e, b"y")?;
67                let z = get_attribute_f32(&e, b"z")?;
68                vertices.push(Vertex { x, y, z });
69            }
70            Event::End(e) if e.local_name().as_ref() == b"vertices" => break,
71            Event::Eof => {
72                return Err(Lib3mfError::Validation(
73                    "Unexpected EOF in vertices".to_string(),
74                ));
75            }
76            _ => {}
77        }
78    }
79    Ok(vertices)
80}
81
82fn parse_displacement_triangles<R: BufRead>(
83    parser: &mut XmlParser<R>,
84) -> Result<Vec<DisplacementTriangle>> {
85    let mut triangles = Vec::new();
86    loop {
87        match parser.read_next_event()? {
88            Event::Start(e) | Event::Empty(e) if e.local_name().as_ref() == b"triangle" => {
89                let v1 = get_attribute_u32(&e, b"v1")?;
90                let v2 = get_attribute_u32(&e, b"v2")?;
91                let v3 = get_attribute_u32(&e, b"v3")?;
92                let d1 = get_attribute_u32(&e, b"d1").ok();
93                let d2 = get_attribute_u32(&e, b"d2").ok();
94                let d3 = get_attribute_u32(&e, b"d3").ok();
95                let p1 = get_attribute_u32(&e, b"p1").ok();
96                let p2 = get_attribute_u32(&e, b"p2").ok();
97                let p3 = get_attribute_u32(&e, b"p3").ok();
98                let pid = get_attribute_u32(&e, b"pid").ok();
99
100                triangles.push(DisplacementTriangle {
101                    v1,
102                    v2,
103                    v3,
104                    d1,
105                    d2,
106                    d3,
107                    p1,
108                    p2,
109                    p3,
110                    pid,
111                });
112            }
113            Event::End(e) if e.local_name().as_ref() == b"triangles" => break,
114            Event::Eof => {
115                return Err(Lib3mfError::Validation(
116                    "Unexpected EOF in triangles".to_string(),
117                ));
118            }
119            _ => {}
120        }
121    }
122    Ok(triangles)
123}
124
125fn parse_normal_vectors<R: BufRead>(parser: &mut XmlParser<R>) -> Result<Vec<NormalVector>> {
126    let mut normals = Vec::new();
127    loop {
128        match parser.read_next_event()? {
129            Event::Start(e) | Event::Empty(e) if e.local_name().as_ref() == b"normvector" => {
130                let nx = get_attribute_f32(&e, b"nx")?;
131                let ny = get_attribute_f32(&e, b"ny")?;
132                let nz = get_attribute_f32(&e, b"nz")?;
133                normals.push(NormalVector { nx, ny, nz });
134            }
135            Event::End(e) if e.local_name().as_ref() == b"normvectors" => break,
136            Event::Eof => {
137                return Err(Lib3mfError::Validation(
138                    "Unexpected EOF in normvectors".to_string(),
139                ));
140            }
141            _ => {}
142        }
143    }
144    Ok(normals)
145}
146
147fn parse_disp2d_groups<R: BufRead>(parser: &mut XmlParser<R>) -> Result<Vec<GradientVector>> {
148    let mut gradients = Vec::new();
149    loop {
150        match parser.read_next_event()? {
151            Event::Start(e) => {
152                if e.local_name().as_ref() == b"disp2dgroup" {
153                    // Parse gradient vectors within the group
154                    loop {
155                        match parser.read_next_event()? {
156                            Event::Start(grad_e) | Event::Empty(grad_e)
157                                if grad_e.local_name().as_ref() == b"gradient" =>
158                            {
159                                let gu = get_attribute_f32(&grad_e, b"gu")?;
160                                let gv = get_attribute_f32(&grad_e, b"gv")?;
161                                gradients.push(GradientVector { gu, gv });
162                            }
163                            Event::End(end_e) if end_e.local_name().as_ref() == b"disp2dgroup" => {
164                                break;
165                            }
166                            Event::Eof => {
167                                return Err(Lib3mfError::Validation(
168                                    "Unexpected EOF in disp2dgroup".to_string(),
169                                ));
170                            }
171                            _ => {}
172                        }
173                    }
174                }
175            }
176            Event::End(e) if e.local_name().as_ref() == b"disp2dgroups" => break,
177            Event::Eof => {
178                return Err(Lib3mfError::Validation(
179                    "Unexpected EOF in disp2dgroups".to_string(),
180                ));
181            }
182            _ => {}
183        }
184    }
185    Ok(gradients)
186}
187
188/// Parses a `<displacement2d>` element (with pre-parsed attributes) into a `Displacement2D`.
189#[allow(clippy::too_many_arguments)]
190pub fn parse_displacement_2d<R: BufRead>(
191    parser: &mut XmlParser<R>,
192    id: ResourceId,
193    path: String,
194    channel: Channel,
195    tile_style: TileStyle,
196    filter: FilterMode,
197    height: f32,
198    offset: f32,
199) -> Result<Displacement2D> {
200    // Skip to closing tag (displacement2d is typically empty element)
201    loop {
202        match parser.read_next_event()? {
203            Event::End(e) if e.local_name().as_ref() == b"displacement2d" => break,
204            Event::Eof => {
205                return Err(Lib3mfError::Validation(
206                    "Unexpected EOF in displacement2d".to_string(),
207                ));
208            }
209            _ => {}
210        }
211    }
212
213    Ok(Displacement2D {
214        id,
215        path,
216        channel,
217        tile_style,
218        filter,
219        height,
220        offset,
221    })
222}