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