Skip to main content

lib3mf_core/parser/
material_parser.rs

1use crate::error::{Lib3mfError, Result};
2use crate::model::{
3    BaseMaterial, BaseMaterialsGroup, BlendMethod, Color, ColorGroup, Composite,
4    CompositeMaterials, Multi, MultiProperties, ResourceId, Texture2DCoord, Texture2DGroup,
5};
6use crate::parser::xml_parser::{XmlParser, get_attribute};
7use quick_xml::events::Event;
8use std::io::BufRead;
9
10// ... existing code ...
11
12/// Parses a `<texture2dgroup>` element into a `Texture2DGroup`.
13pub fn parse_texture_2d_group<R: BufRead>(
14    parser: &mut XmlParser<R>,
15    id: ResourceId,
16    texture_id: ResourceId,
17) -> Result<Texture2DGroup> {
18    let mut coords = Vec::new();
19
20    loop {
21        match parser.read_next_event()? {
22            Event::Start(e) | Event::Empty(e) if e.local_name().as_ref() == b"tex2coord" => {
23                let u = get_attribute(&e, b"u")
24                    .ok_or_else(|| Lib3mfError::Validation("tex2coord missing u".to_string()))?
25                    .parse::<f32>()
26                    .map_err(|_| Lib3mfError::Validation("Invalid u value".to_string()))?;
27                let v = get_attribute(&e, b"v")
28                    .ok_or_else(|| Lib3mfError::Validation("tex2coord missing v".to_string()))?
29                    .parse::<f32>()
30                    .map_err(|_| Lib3mfError::Validation("Invalid v value".to_string()))?;
31                coords.push(Texture2DCoord { u, v });
32            }
33            Event::End(e) if e.local_name().as_ref() == b"texture2dgroup" => break,
34            Event::Eof => {
35                return Err(Lib3mfError::Validation(
36                    "Unexpected EOF in texture2dgroup".to_string(),
37                ));
38            }
39            _ => {}
40        }
41    }
42
43    Ok(Texture2DGroup {
44        id,
45        texture_id,
46        coords,
47    })
48}
49
50/// Parses a `<compositematerials>` element into a `CompositeMaterials`.
51pub fn parse_composite_materials<R: BufRead>(
52    parser: &mut XmlParser<R>,
53    id: ResourceId,
54    base_material_id: ResourceId,
55    indices: Vec<u32>,
56) -> Result<CompositeMaterials> {
57    let mut composites = Vec::new();
58
59    loop {
60        match parser.read_next_event()? {
61            Event::Start(e) | Event::Empty(e) if e.local_name().as_ref() == b"composite" => {
62                let values_str = get_attribute(&e, b"values").ok_or_else(|| {
63                    Lib3mfError::Validation("composite missing values".to_string())
64                })?;
65                let values = values_str
66                    .split_whitespace()
67                    .map(|s| {
68                        s.parse::<f32>().map_err(|_| {
69                            Lib3mfError::Validation("Invalid composite value".to_string())
70                        })
71                    })
72                    .collect::<Result<Vec<f32>>>()?;
73                composites.push(Composite { values });
74            }
75            Event::End(e) if e.local_name().as_ref() == b"compositematerials" => break,
76            Event::Eof => {
77                return Err(Lib3mfError::Validation(
78                    "Unexpected EOF in compositematerials".to_string(),
79                ));
80            }
81            _ => {}
82        }
83    }
84
85    Ok(CompositeMaterials {
86        id,
87        base_material_id,
88        indices,
89        composites,
90    })
91}
92
93/// Parses a `<multiproperties>` element into a `MultiProperties`.
94pub fn parse_multi_properties<R: BufRead>(
95    parser: &mut XmlParser<R>,
96    id: ResourceId,
97    pids: Vec<ResourceId>,
98    blend_methods: Vec<BlendMethod>,
99) -> Result<MultiProperties> {
100    let mut multis = Vec::new();
101
102    loop {
103        match parser.read_next_event()? {
104            Event::Start(e) | Event::Empty(e) if e.local_name().as_ref() == b"multi" => {
105                let pindices_str = get_attribute(&e, b"pindices")
106                    .ok_or_else(|| Lib3mfError::Validation("multi missing pindices".to_string()))?;
107                let pindices = pindices_str
108                    .split_whitespace()
109                    .map(|s| {
110                        s.parse::<u32>().map_err(|_| {
111                            Lib3mfError::Validation("Invalid pindex value".to_string())
112                        })
113                    })
114                    .collect::<Result<Vec<u32>>>()?;
115                multis.push(Multi { pindices });
116            }
117            Event::End(e) if e.local_name().as_ref() == b"multiproperties" => break,
118            Event::Eof => {
119                return Err(Lib3mfError::Validation(
120                    "Unexpected EOF in multiproperties".to_string(),
121                ));
122            }
123            _ => {}
124        }
125    }
126
127    Ok(MultiProperties {
128        id,
129        pids,
130        blend_methods,
131        multis,
132    })
133}
134
135/// Parses a `<basematerials>` element into a `BaseMaterialsGroup`.
136pub fn parse_base_materials<R: BufRead>(
137    parser: &mut XmlParser<R>,
138    id: ResourceId,
139) -> Result<BaseMaterialsGroup> {
140    let mut materials = Vec::new();
141
142    loop {
143        match parser.read_next_event()? {
144            Event::Start(e) | Event::Empty(e) if e.local_name().as_ref() == b"base" => {
145                let name = get_attribute(&e, b"name").ok_or_else(|| {
146                    Lib3mfError::Validation("base element missing 'name' attribute".to_string())
147                })?;
148                let color_hex = get_attribute(&e, b"displaycolor").ok_or_else(|| {
149                    Lib3mfError::Validation(
150                        "base element missing 'displaycolor' attribute".to_string(),
151                    )
152                })?;
153                let display_color = Color::from_hex(&color_hex).ok_or_else(|| {
154                    Lib3mfError::Validation(format!("Invalid color format: {}", color_hex))
155                })?;
156
157                materials.push(BaseMaterial {
158                    name: name.into_owned(),
159                    display_color,
160                });
161            }
162            Event::End(e) if e.local_name().as_ref() == b"basematerials" => break,
163            Event::Eof => {
164                return Err(Lib3mfError::Validation(
165                    "Unexpected EOF in basematerials".to_string(),
166                ));
167            }
168            _ => {}
169        }
170    }
171
172    Ok(BaseMaterialsGroup { id, materials })
173}
174
175/// Parses a `<colorgroup>` element into a `ColorGroup`.
176pub fn parse_color_group<R: BufRead>(
177    parser: &mut XmlParser<R>,
178    id: ResourceId,
179) -> Result<ColorGroup> {
180    let mut colors = Vec::new();
181
182    loop {
183        match parser.read_next_event()? {
184            Event::Start(e) | Event::Empty(e) if e.local_name().as_ref() == b"color" => {
185                let color_hex = get_attribute(&e, b"color").ok_or_else(|| {
186                    Lib3mfError::Validation("color element missing 'color' attribute".to_string())
187                })?;
188                let color = Color::from_hex(&color_hex).ok_or_else(|| {
189                    Lib3mfError::Validation(format!("Invalid color format: {}", color_hex))
190                })?;
191                colors.push(color);
192            }
193            Event::End(e) if e.local_name().as_ref() == b"colorgroup" => break,
194            Event::Eof => {
195                return Err(Lib3mfError::Validation(
196                    "Unexpected EOF in colorgroup".to_string(),
197                ));
198            }
199            _ => {}
200        }
201    }
202
203    Ok(ColorGroup { id, colors })
204}