lib3mf_core/parser/
component_parser.rs

1use crate::error::{Lib3mfError, Result};
2use crate::model::{Component, Components};
3use crate::parser::xml_parser::{XmlParser, get_attribute, get_attribute_u32};
4use glam::Mat4;
5use quick_xml::events::Event;
6use std::io::BufRead;
7use std::str::FromStr;
8
9pub fn parse_components<R: BufRead>(parser: &mut XmlParser<R>) -> Result<Components> {
10    let mut components = Vec::new();
11
12    loop {
13        match parser.read_next_event()? {
14            Event::Start(e) | Event::Empty(e) if e.name().as_ref() == b"component" => {
15                let object_id = crate::model::ResourceId(get_attribute_u32(&e, b"objectid")?);
16                let path = get_attribute(&e, b"path")
17                    .or_else(|| get_attribute(&e, b"p:path"))
18                    .map(|s| s.into_owned());
19                let uuid = crate::parser::xml_parser::get_attribute_uuid(&e)?;
20                let transform = if let Some(s) = get_attribute(&e, b"transform") {
21                    parse_transform(&s)?
22                } else {
23                    Mat4::IDENTITY
24                };
25                components.push(Component {
26                    object_id,
27                    path,
28                    uuid,
29                    transform,
30                });
31            }
32            Event::End(e) if e.name().as_ref() == b"components" => break,
33            Event::Eof => {
34                return Err(Lib3mfError::Validation(
35                    "Unexpected EOF in components".to_string(),
36                ));
37            }
38            _ => {}
39        }
40    }
41
42    Ok(Components { components })
43}
44
45pub fn parse_transform(s: &str) -> Result<Mat4> {
46    let parts: Vec<&str> = s.split_whitespace().collect();
47    if parts.len() != 12 {
48        return Err(Lib3mfError::Validation(format!(
49            "Invalid transform matrix: expected 12 values, got {}",
50            parts.len()
51        )));
52    }
53
54    let p: Result<Vec<f32>> = parts
55        .iter()
56        .map(|v| {
57            f32::from_str(v)
58                .map_err(|_| Lib3mfError::Validation(format!("Invalid float in transform: {}", v)))
59        })
60        .collect();
61    let p = p?;
62
63    // 3MF uses column-major order 4x3 matrix, last column is 0,0,0,1
64    Ok(Mat4::from_cols_array(&[
65        p[0], p[1], p[2], 0.0, p[3], p[4], p[5], 0.0, p[6], p[7], p[8], 0.0, p[9], p[10], p[11],
66        1.0,
67    ]))
68}