1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
//! Used for reading SBML layout specifications.

use crate::sbml::import::{child_tags, read_unique_child, SBML_LAYOUT};
use roxmltree::Node;
use std::collections::HashMap;

/// A very simple representation of the SBML layout XML object.
///
/// If contains dimensions of the layout and a mapping
/// from SBML ids to coordinates in such layout.
#[derive(Clone, Debug, PartialEq)]
pub struct SbmlLayout {
    pub dimensions: (f64, f64),
    pub glyphs: HashMap<String, (f64, f64)>,
}

pub fn read_sbml_layout(model: Node) -> Result<SbmlLayout, String> {
    let layout_list = read_unique_child(model, (SBML_LAYOUT, "listOfLayouts"))?;

    let layouts = child_tags(layout_list, (SBML_LAYOUT, "layout"));

    if layouts.is_empty() {
        return Err("No layout found.".to_string());
    }

    // If there are multiple layouts, just pick the first one.

    let mut result = SbmlLayout {
        dimensions: (0.0, 0.0),
        glyphs: HashMap::new(),
    };

    let layout = layouts[0];

    let dimensions = read_unique_child(layout, (SBML_LAYOUT, "dimensions")).ok();

    if let Some(dimensions) = dimensions {
        if let Some(width) = dimensions.attribute((SBML_LAYOUT, "width")) {
            result.dimensions.0 = str_to_f64(width)?;
        }
        if let Some(height) = dimensions.attribute((SBML_LAYOUT, "height")) {
            result.dimensions.1 = str_to_f64(height)?;
        }
    }

    let glyph_list =
        read_unique_child(layout, (SBML_LAYOUT, "listOfAdditionalGraphicalObjects")).ok();
    if let Some(glyph_list) = glyph_list {
        let glyphs = child_tags(glyph_list, (SBML_LAYOUT, "generalGlyph"));
        for glyph in glyphs {
            if let Some(reference) = glyph.attribute((SBML_LAYOUT, "reference")) {
                let bounding_box = read_unique_child(glyph, (SBML_LAYOUT, "boundingBox")).ok();

                if let Some(bounding_box) = bounding_box {
                    let position = read_unique_child(bounding_box, (SBML_LAYOUT, "position")).ok();

                    if let Some(position) = position {
                        let mut coordinates = (0.0, 0.0);
                        if let Some(x) = position.attribute((SBML_LAYOUT, "x")) {
                            coordinates.0 = str_to_f64(x)?;
                        }
                        if let Some(y) = position.attribute((SBML_LAYOUT, "y")) {
                            coordinates.1 = str_to_f64(y)?;
                        }

                        result.glyphs.insert(reference.to_string(), coordinates);
                    }
                }
            }
        }
    }

    Ok(result)
}

fn str_to_f64(str: &str) -> Result<f64, String> {
    if let Ok(value) = str.parse::<f64>() {
        Ok(value)
    } else {
        Err(format!("Invalid numeric value: {}.", str))
    }
}