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