1use schemars::JsonSchema;
5use serde::{Deserialize, Serialize};
6use std::collections::BTreeMap;
7
8#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize, JsonSchema)]
10#[serde(transparent)]
11pub struct ComponentId(pub String);
12
13#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize, JsonSchema)]
15#[serde(transparent)]
16pub struct OccurrenceId(pub String);
17
18#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize, JsonSchema)]
20#[serde(transparent)]
21pub struct JointId(pub String);
22
23#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
25#[serde(rename_all = "snake_case")]
26pub enum ComponentKind {
27 Part,
29 Group,
31 LinkGroup,
33 Object,
35}
36
37#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema)]
39pub struct Component {
40 pub id: ComponentId,
42 pub kind: ComponentKind,
44 #[serde(default, skip_serializing_if = "Option::is_none")]
46 pub source_name: Option<String>,
47 #[serde(default, skip_serializing_if = "Option::is_none")]
49 pub label: Option<String>,
50 #[serde(default, skip_serializing_if = "Option::is_none")]
52 pub description: Option<String>,
53 #[serde(default, skip_serializing_if = "Option::is_none")]
55 pub part_number: Option<String>,
56 #[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
58 pub bom_properties: BTreeMap<String, String>,
59 #[serde(default, skip_serializing_if = "Option::is_none")]
61 pub parent: Option<ComponentId>,
62 #[serde(default = "identity_transform")]
64 pub local_transform: [[f64; 4]; 4],
65 #[serde(default = "identity_transform")]
67 pub resolved_transform: [[f64; 4]; 4],
68 #[serde(default, skip_serializing_if = "Vec::is_empty")]
70 pub components: Vec<ComponentId>,
71 #[serde(default, skip_serializing_if = "Vec::is_empty")]
73 pub occurrences: Vec<OccurrenceId>,
74 #[serde(default, skip_serializing_if = "Option::is_none")]
76 pub native_ref: Option<String>,
77}
78
79fn identity_transform() -> [[f64; 4]; 4] {
80 [
81 [1.0, 0.0, 0.0, 0.0],
82 [0.0, 1.0, 0.0, 0.0],
83 [0.0, 0.0, 1.0, 0.0],
84 [0.0, 0.0, 0.0, 1.0],
85 ]
86}
87
88#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
90#[serde(tag = "scope", rename_all = "snake_case")]
91pub enum ComponentReference {
92 Local {
94 component: ComponentId,
96 },
97 External {
99 document: ExternalDocumentReference,
101 #[serde(default, skip_serializing_if = "Option::is_none")]
103 object: Option<String>,
104 },
105 Unresolved,
107}
108
109#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
111pub struct ExternalDocumentReference {
112 #[serde(default, skip_serializing_if = "Option::is_none")]
114 pub path: Option<String>,
115 #[serde(default, skip_serializing_if = "Option::is_none")]
117 pub document_id: Option<String>,
118 pub resolution: ExternalResolution,
120}
121
122#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
124#[serde(rename_all = "snake_case")]
125pub enum ExternalResolution {
126 Unresolved,
128 MissingReference,
130}
131
132#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
134#[serde(tag = "policy", content = "native_policy", rename_all = "snake_case")]
135pub enum CopyOnChangePolicy {
136 Disabled,
138 Enabled,
140 Owned,
142 Tracking,
144 Native(String),
146}
147
148#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema)]
150pub struct Occurrence {
151 pub id: OccurrenceId,
153 pub prototype: ComponentReference,
155 #[serde(default, skip_serializing_if = "Option::is_none")]
157 pub parent: Option<ComponentId>,
158 #[serde(default, skip_serializing_if = "Option::is_none")]
160 pub array_index: Option<u32>,
161 pub local_transform: [[f64; 4]; 4],
163 #[serde(default = "identity_transform")]
165 pub prototype_transform: [[f64; 4]; 4],
166 pub resolved_transform: [[f64; 4]; 4],
168 pub scale: [f64; 3],
170 #[serde(default, skip_serializing_if = "Vec::is_empty")]
172 pub linked_subelements: Vec<String>,
173 #[serde(default, skip_serializing_if = "Option::is_none")]
175 pub visible: Option<bool>,
176 #[serde(default, skip_serializing_if = "Option::is_none")]
178 pub element_component: Option<ComponentId>,
179 #[serde(default, skip_serializing_if = "Option::is_none")]
181 pub claim_child: Option<bool>,
182 #[serde(default, skip_serializing_if = "Option::is_none")]
184 pub copy_on_change: Option<CopyOnChangePolicy>,
185 #[serde(default, skip_serializing_if = "Option::is_none")]
187 pub copy_on_change_source: Option<ComponentId>,
188 #[serde(default, skip_serializing_if = "Option::is_none")]
190 pub copy_on_change_group: Option<ComponentId>,
191 #[serde(default, skip_serializing_if = "Option::is_none")]
193 pub copy_on_change_touched: Option<bool>,
194 #[serde(default, skip_serializing_if = "Option::is_none")]
196 pub link_transform: Option<bool>,
197 #[serde(default, skip_serializing_if = "Option::is_none")]
199 pub native_ref: Option<String>,
200}
201
202#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
204#[serde(tag = "kind", content = "native_kind", rename_all = "snake_case")]
205pub enum JointKind {
206 Fixed,
208 Revolute,
210 Slider,
212 Cylindrical,
214 Ball,
216 Distance,
218 Parallel,
220 Perpendicular,
222 Angle,
224 RackPinion,
226 Screw,
228 Gears,
230 Belt,
232 Grounded,
234 Native(String),
236}
237
238#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
240pub struct JointOperand {
241 #[serde(default, skip_serializing_if = "Option::is_none")]
243 pub component: Option<ComponentId>,
244 #[serde(default, skip_serializing_if = "Option::is_none")]
246 pub external_document: Option<ExternalDocumentReference>,
247 #[serde(default, skip_serializing_if = "Option::is_none")]
249 pub object: Option<String>,
250 #[serde(default, skip_serializing_if = "Vec::is_empty")]
252 pub subelements: Vec<String>,
253}
254
255#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema)]
257pub struct JointLimits {
258 #[serde(default, skip_serializing_if = "Option::is_none")]
260 pub minimum: Option<f64>,
261 #[serde(default, skip_serializing_if = "Option::is_none")]
263 pub maximum: Option<f64>,
264}
265
266#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema)]
268pub struct AssemblyJoint {
269 pub id: JointId,
271 pub kind: JointKind,
273 pub operands: Vec<JointOperand>,
275 pub frames: Vec<[[f64; 4]; 4]>,
277 #[serde(default, skip_serializing_if = "Vec::is_empty")]
279 pub offset_frames: Vec<[[f64; 4]; 4]>,
280 pub suppressed: bool,
282 pub detached: [bool; 2],
284 #[serde(default, skip_serializing_if = "Option::is_none")]
286 pub angle: Option<f64>,
287 #[serde(default, skip_serializing_if = "Option::is_none")]
289 pub distance: Option<f64>,
290 #[serde(default, skip_serializing_if = "Option::is_none")]
292 pub distance2: Option<f64>,
293 #[serde(default, skip_serializing_if = "Option::is_none")]
295 pub angular_limits: Option<JointLimits>,
296 #[serde(default, skip_serializing_if = "Option::is_none")]
298 pub linear_limits: Option<JointLimits>,
299 #[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
301 pub properties: BTreeMap<String, String>,
302 #[serde(default, skip_serializing_if = "Option::is_none")]
304 pub native_ref: Option<String>,
305}