1#![allow(dead_code)]
4
5#[derive(Debug, Clone)]
8pub struct OpenSimBody {
9 pub name: String,
10 pub mass: f64,
11 pub inertia: [f64; 6],
12 pub com: [f64; 3],
13}
14
15#[derive(Debug, Clone)]
16pub struct OpenSimJoint {
17 pub name: String,
18 pub parent_body: String,
19 pub child_body: String,
20 pub location_in_parent: [f64; 3],
21 pub location_in_child: [f64; 3],
22}
23
24#[derive(Debug, Clone)]
25pub struct OpenSimMuscle {
26 pub name: String,
27 pub origin_body: String,
28 pub insertion_body: String,
29 pub optimal_fiber_length: f64,
30 pub tendon_slack_length: f64,
31 pub max_isometric_force: f64,
32}
33
34#[derive(Debug, Clone)]
35pub struct OpenSimModel {
36 pub name: String,
37 pub bodies: Vec<OpenSimBody>,
38 pub joints: Vec<OpenSimJoint>,
39 pub muscles: Vec<OpenSimMuscle>,
40}
41
42pub fn new_opensim_model(name: &str) -> OpenSimModel {
43 OpenSimModel {
44 name: name.to_string(),
45 bodies: Vec::new(),
46 joints: Vec::new(),
47 muscles: Vec::new(),
48 }
49}
50
51pub fn add_opensim_body(model: &mut OpenSimModel, name: &str, mass: f64) {
52 model.bodies.push(OpenSimBody {
53 name: name.to_string(),
54 mass,
55 inertia: [0.0; 6],
56 com: [0.0; 3],
57 });
58}
59
60pub fn add_opensim_joint(model: &mut OpenSimModel, name: &str, parent: &str, child: &str) {
61 model.joints.push(OpenSimJoint {
62 name: name.to_string(),
63 parent_body: parent.to_string(),
64 child_body: child.to_string(),
65 location_in_parent: [0.0; 3],
66 location_in_child: [0.0; 3],
67 });
68}
69
70pub fn add_opensim_muscle(
71 model: &mut OpenSimModel,
72 name: &str,
73 origin: &str,
74 insertion: &str,
75 opt_fiber_len: f64,
76 tendon_slack: f64,
77 max_force: f64,
78) {
79 model.muscles.push(OpenSimMuscle {
80 name: name.to_string(),
81 origin_body: origin.to_string(),
82 insertion_body: insertion.to_string(),
83 optimal_fiber_length: opt_fiber_len,
84 tendon_slack_length: tendon_slack,
85 max_isometric_force: max_force,
86 });
87}
88
89pub fn render_opensim_xml(model: &OpenSimModel) -> String {
90 let mut s = format!("<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n<OpenSimDocument Version=\"40000\">\n<Model name=\"{}\">\n", model.name);
91 s.push_str("<BodySet>\n<objects>\n");
92 for b in &model.bodies {
93 s.push_str(&format!(
94 " <Body name=\"{}\">\n <mass>{}</mass>\n </Body>\n",
95 b.name, b.mass
96 ));
97 }
98 s.push_str("</objects></BodySet>\n");
99 s.push_str("<JointSet>\n<objects>\n");
100 for j in &model.joints {
101 s.push_str(&format!(" <PinJoint name=\"{}\">\n <parent_frame>{}</parent_frame>\n <child_frame>{}</child_frame>\n </PinJoint>\n",
102 j.name, j.parent_body, j.child_body));
103 }
104 s.push_str("</objects></JointSet>\n");
105 s.push_str("<ForceSet>\n<objects>\n");
106 for m in &model.muscles {
107 s.push_str(&format!(" <Millard2012EquilibriumMuscle name=\"{}\">\n <optimal_fiber_length>{}</optimal_fiber_length>\n <max_isometric_force>{}</max_isometric_force>\n </Millard2012EquilibriumMuscle>\n",
108 m.name, m.optimal_fiber_length, m.max_isometric_force));
109 }
110 s.push_str("</objects></ForceSet>\n</Model>\n</OpenSimDocument>\n");
111 s
112}
113
114pub fn export_opensim(model: &OpenSimModel) -> Vec<u8> {
115 render_opensim_xml(model).into_bytes()
116}
117pub fn opensim_body_count(model: &OpenSimModel) -> usize {
118 model.bodies.len()
119}
120pub fn opensim_muscle_count(model: &OpenSimModel) -> usize {
121 model.muscles.len()
122}
123pub fn validate_opensim_model(model: &OpenSimModel) -> bool {
124 !model.name.is_empty() && !model.bodies.is_empty()
125}
126pub fn opensim_size_bytes(model: &OpenSimModel) -> usize {
127 render_opensim_xml(model).len()
128}
129
130pub fn default_biped_opensim_model() -> OpenSimModel {
131 let mut m = new_opensim_model("BipedModel");
132 add_opensim_body(&mut m, "pelvis", 10.0);
133 add_opensim_body(&mut m, "femur_r", 8.0);
134 add_opensim_joint(&mut m, "hip_r", "pelvis", "femur_r");
135 add_opensim_muscle(&mut m, "rect_fem_r", "pelvis", "femur_r", 0.08, 0.35, 800.0);
136 m
137}
138
139#[cfg(test)]
140mod tests {
141 use super::*;
142
143 #[test]
144 fn test_new_opensim_model() {
145 let m = new_opensim_model("Test");
146 assert_eq!(m.name, "Test");
147 }
148
149 #[test]
150 fn test_add_body() {
151 let mut m = new_opensim_model("T");
152 add_opensim_body(&mut m, "pelvis", 10.0);
153 assert_eq!(opensim_body_count(&m), 1);
154 }
155
156 #[test]
157 fn test_render_contains_opensim_tag() {
158 let m = new_opensim_model("T");
159 let s = render_opensim_xml(&m);
160 assert!(s.contains("OpenSimDocument"));
161 }
162
163 #[test]
164 fn test_export_opensim_nonempty() {
165 let m = new_opensim_model("T");
166 assert!(!export_opensim(&m).is_empty());
167 }
168
169 #[test]
170 fn test_validate_opensim_model() {
171 let mut m = new_opensim_model("T");
172 add_opensim_body(&mut m, "b", 1.0);
173 assert!(validate_opensim_model(&m));
174 }
175
176 #[test]
177 fn test_default_biped() {
178 let m = default_biped_opensim_model();
179 assert!(opensim_body_count(&m) >= 2);
180 assert!(opensim_muscle_count(&m) >= 1);
181 }
182
183 #[test]
184 fn test_opensim_size_bytes() {
185 let m = new_opensim_model("T");
186 assert!(opensim_size_bytes(&m) > 0);
187 }
188
189 #[test]
190 fn test_muscle_in_render() {
191 let mut m = new_opensim_model("T");
192 add_opensim_body(&mut m, "b1", 1.0);
193 add_opensim_body(&mut m, "b2", 1.0);
194 add_opensim_muscle(&mut m, "muscle1", "b1", "b2", 0.1, 0.2, 500.0);
195 let s = render_opensim_xml(&m);
196 assert!(s.contains("muscle1"));
197 }
198}