1use std::sync::{Arc, RwLock, Weak};
2
3use itertools::Itertools;
4use nalgebra::{vector, Matrix3};
5
6use crate::{
7 cluster_objects::kinematic_data_tree::KinematicDataTree,
8 identifiers::GroupIDChanger,
9 joint::{joint_data, joint_tranform_mode::JointTransformMode, Joint, JointType},
10 link::{
11 builder::{BuildLink, LinkBuilder},
12 Link, LinkShapeData,
13 },
14 transform::{Mirror, MirrorUpdater, Transform},
15 utils::{ArcLock, WeakLock},
16};
17
18pub trait BuildJoint: Into<JointBuilder> {
19 fn build(
21 self,
22 tree: Weak<KinematicDataTree>,
23 parent_link: WeakLock<Link>,
24 child_link: ArcLock<Link>,
25 parent_shape_data: LinkShapeData,
26 ) -> ArcLock<Joint>;
27}
28
29pub(crate) trait BuildJointChain {
31 fn build_chain(
32 self,
33 tree: &Weak<KinematicDataTree>,
34 parent_link: &WeakLock<Link>,
35 parent_shape_data: LinkShapeData,
36 ) -> ArcLock<Joint>;
37}
38
39#[derive(Debug, PartialEq, Clone, Default)]
40pub struct JointBuilder {
41 pub(crate) name: String,
42 pub(crate) joint_type: JointType,
43 pub(crate) transform: JointTransformMode,
48 pub(crate) child: Option<LinkBuilder>,
49
50 pub(crate) axis: Option<(f32, f32, f32)>,
53 pub(crate) calibration: joint_data::CalibrationData,
54 pub(crate) dynamics: joint_data::DynamicsData,
55 pub(crate) limit: Option<joint_data::LimitData>,
56 pub(crate) mimic: Option<joint_data::MimicBuilderData>,
57 pub(crate) safety_controller: Option<joint_data::SafetyControllerData>,
58}
59
60impl JointBuilder {
62 pub fn new(name: impl Into<String>, joint_type: JointType) -> Self {
63 Self {
64 name: name.into(),
65 joint_type,
66 ..Default::default()
67 }
68 }
69
70 pub fn add_origin_offset(mut self, offset: (f32, f32, f32)) -> Self {
72 match &mut self.transform {
73 JointTransformMode::Direct(transform) => transform.translation = Some(offset),
74 JointTransformMode::FigureItOut(_) => todo!("Don't know how to do this"),
75 };
76 self
77 }
78
79 pub fn add_origin_rotation(mut self, rotation: (f32, f32, f32)) -> Self {
81 match &mut self.transform {
82 JointTransformMode::Direct(tranform) => tranform.rotation = Some(rotation),
83 JointTransformMode::FigureItOut(_) => todo!("Don't know how to do this yet"),
84 }
85 self
86 }
87
88 pub fn set_transform_simple(&mut self, transform: Transform) {
89 self.transform = JointTransformMode::Direct(transform);
90 }
91
92 #[inline]
95 pub(crate) fn with_transform<JTM>(&mut self, transform: JTM)
96 where
97 JTM: Into<JointTransformMode>,
98 {
99 self.transform = transform.into();
100 }
101
102 #[inline]
105 pub fn with_axis(&mut self, axis: (f32, f32, f32)) {
106 self.axis = Some(axis);
107 }
108
109 #[inline]
111 pub(crate) fn with_calibration_data(&mut self, calibration_data: joint_data::CalibrationData) {
112 self.calibration = calibration_data;
113 }
114
115 #[inline]
117 pub(crate) fn with_dynamics_data(&mut self, dynamics_data: joint_data::DynamicsData) {
118 self.dynamics = dynamics_data;
119 }
120
121 #[inline]
123 pub(crate) fn with_limit_data(&mut self, limit_data: joint_data::LimitData) {
124 self.limit = Some(limit_data);
125 }
126
127 #[inline]
129 pub(crate) fn with_mimic_data(&mut self, mimic_data: joint_data::MimicBuilderData) {
130 self.mimic = Some(mimic_data);
131 }
132
133 #[inline]
135 pub(crate) fn with_safety_controller(
136 &mut self,
137 safety_controller_data: joint_data::SafetyControllerData,
138 ) {
139 self.safety_controller = Some(safety_controller_data);
140 }
141}
142
143impl JointBuilder {
146 pub fn name(&self) -> &String {
147 &self.name
148 }
149
150 pub fn joint_type(&self) -> &JointType {
151 &self.joint_type
152 }
153
154 pub fn transform(&self) -> Option<&Transform> {
155 match &self.transform {
157 JointTransformMode::Direct(transform) => match transform.contains_some() {
158 true => Some(transform),
159 false => None,
160 },
161 JointTransformMode::FigureItOut(_) => todo!("?"),
162 }
163 }
164
165 pub fn transform_mut(&mut self) -> Option<&mut Transform> {
166 match &mut self.transform {
168 JointTransformMode::Direct(transform) => match transform.contains_some() {
169 true => Some(transform),
170 false => None,
171 },
172 JointTransformMode::FigureItOut(_) => todo!("?"),
173 }
174 }
175
176 pub fn child(&self) -> Option<&LinkBuilder> {
179 self.child.as_ref()
180 }
181
182 pub fn axis(&self) -> Option<(f32, f32, f32)> {
183 self.axis
184 }
185
186 pub fn axis_mut(&mut self) -> Option<&mut (f32, f32, f32)> {
187 self.axis.as_mut()
188 }
189
190 pub fn calibration(&self) -> &joint_data::CalibrationData {
191 &self.calibration
192 }
193
194 pub fn calibration_mut(&mut self) -> &mut joint_data::CalibrationData {
195 &mut self.calibration
196 }
197
198 pub fn dynamics(&self) -> &joint_data::DynamicsData {
199 &self.dynamics
200 }
201
202 pub fn dynamics_mut(&mut self) -> &mut joint_data::DynamicsData {
203 &mut self.dynamics
204 }
205
206 pub fn limit(&self) -> Option<&joint_data::LimitData> {
207 self.limit.as_ref()
208 }
209
210 pub fn limit_mut(&mut self) -> &mut Option<joint_data::LimitData> {
211 &mut self.limit
212 }
213
214 pub fn mimic(&self) -> Option<&joint_data::MimicBuilderData> {
215 self.mimic.as_ref()
216 }
217
218 pub fn mimic_mut(&mut self) -> &mut Option<joint_data::MimicBuilderData> {
219 &mut self.mimic
220 }
221
222 pub fn safety_controller(&self) -> Option<&joint_data::SafetyControllerData> {
223 self.safety_controller.as_ref()
224 }
225
226 pub fn safety_controller_mut(&mut self) -> &mut Option<joint_data::SafetyControllerData> {
227 &mut self.safety_controller
228 }
229}
230
231impl Mirror for JointBuilder {
232 fn mirrored(&self, mirror_matrix: &Matrix3<f32>) -> Self {
233 let (transform, new_mirror_matrix) = self.transform.mirrored_update_matrix(mirror_matrix);
234 Self {
235 name: self.name.clone(), joint_type: self.joint_type,
237 transform,
238 child: self
239 .child
240 .as_ref()
241 .map(|link_builder| link_builder.mirrored(&new_mirror_matrix)),
242 axis: match (self.joint_type, self.axis) {
243 (JointType::Fixed | JointType::Floating, _) => None, (_, Some((x, y, z))) => Some(
245 (new_mirror_matrix * vector![x, y, z] * -1.)
246 .normalize()
248 .iter()
249 .copied()
250 .collect_tuple()
251 .unwrap(),
253 ),
254 (
255 JointType::Revolute
256 | JointType::Continuous
257 | JointType::Prismatic
258 | JointType::Planar,
259 None,
260 ) => Some(
261 (new_mirror_matrix * vector![1., 0., 0.] * -1.)
262 .normalize() .iter()
264 .copied()
265 .collect_tuple()
266 .unwrap(), ),
268 },
269 calibration: self.calibration, dynamics: self.dynamics, limit: self.limit, mimic: self.mimic.as_ref().cloned(), safety_controller: self.safety_controller, }
275 }
276}
277
278impl BuildJoint for JointBuilder {
279 fn build(
280 self,
281 tree: Weak<KinematicDataTree>,
282 parent_link: WeakLock<Link>,
283 child_link: ArcLock<Link>,
284 parent_link_size_data: LinkShapeData,
285 ) -> ArcLock<Joint> {
286 let joint = Arc::new_cyclic(|me| -> RwLock<Joint> {
287 RwLock::new(Joint {
288 name: self.name,
289 tree: Weak::clone(&tree),
290 parent_link,
291 child_link,
292 joint_type: self.joint_type,
293 transform: self.transform.apply(parent_link_size_data),
294 axis: self.axis,
295 calibration: self.calibration,
296 dynamics: self.dynamics,
297 limit: self.limit,
298 mimic: self.mimic.map(|mimic| mimic.to_mimic_data(&tree)),
299 safety_controller: self.safety_controller,
300 me: Weak::clone(me),
301 })
302 });
303
304 tree.upgrade().unwrap().try_add_joint(&joint).unwrap(); joint
306 }
307}
308
309impl BuildJointChain for JointBuilder {
310 fn build_chain(
311 self,
312 tree: &Weak<KinematicDataTree>,
313 parent_link: &WeakLock<Link>,
314 parent_shape_data: LinkShapeData,
315 ) -> ArcLock<Joint> {
316 #[cfg(any(feature = "logging", test))]
317 log::trace!("Building a Joint[name ='{}']", self.name);
318
319 Arc::new_cyclic(|me| {
320 RwLock::new(Joint {
321 name: self.name,
322 tree: Weak::clone(tree),
323 parent_link: Weak::clone(parent_link),
324 child_link: self.child.expect("When Building Kinematic Branches Joints should have a child link, since a Joint only makes sense when attachted to a Parent and a Child").build_chain(tree, me),
326 joint_type: self.joint_type,
327 transform: self.transform.apply(parent_shape_data),
328 axis: self.axis,
329 calibration: self.calibration,
330 dynamics: self.dynamics,
331 limit: self.limit,
332 mimic: self
333 .mimic
334 .map(|mimic| mimic.to_mimic_data(tree)),
335 safety_controller: self.safety_controller,
336 me: Weak::clone(me),
337 })
338 })
339 }
340}
341
342impl GroupIDChanger for JointBuilder {
343 unsafe fn change_group_id_unchecked(&mut self, new_group_id: &str) {
344 self.name.change_group_id_unchecked(new_group_id);
345
346 if let Some(link_builder) = self.child.as_mut() {
347 link_builder.change_group_id_unchecked(new_group_id);
348 }
349 }
350
351 fn apply_group_id(&mut self) {
352 self.name.apply_group_id();
353
354 if let Some(link_builder) = self.child.as_mut() {
355 link_builder.apply_group_id();
356 }
357 }
358}
359
360#[cfg(test)]
361mod tests {
362 use super::{JointBuilder, JointType};
363 use test_log::test;
364
365 mod group_id_changer {
366 use super::{test, JointBuilder, JointType};
367 use crate::identifiers::{GroupIDChanger, GroupIDError};
368
369 #[test]
370 fn change_group_id_unchecked_simple() {
371 #[inline]
372 fn test(
373 name: impl Into<String>,
374 joint_type: JointType,
375 new_group_id: &str,
376 result: &str,
377 ) {
378 let mut joint_builder = JointBuilder::new(name, joint_type);
379 unsafe {
380 joint_builder.change_group_id_unchecked(new_group_id);
381 }
382 assert_eq!(joint_builder.name, result)
383 }
384
385 test(
386 "leg_[[M09da]]_joint_1",
387 JointType::Fixed,
388 "C10df",
389 "leg_[[C10df]]_joint_1",
390 );
391
392 test(
393 "leg_[[M09da]]_joint_1",
394 JointType::Fixed,
395 "",
396 "leg_[[]]_joint_1",
397 );
398
399 test(
400 "leg_[[M09da]]_joint_1",
401 JointType::Fixed,
402 "[[tsst",
403 "leg_[[[[tsst]]_joint_1",
404 );
405
406 test(
407 "leg_[[M09da]]_joint_1",
408 JointType::Fixed,
409 "tsst]]",
410 "leg_[[tsst]]]]_joint_1",
411 );
412 }
413
414 #[test]
415 #[ignore = "TODO"]
416 fn test_change_group_id_unchecked_advanced() {
417 todo!("Chained things")
418 }
419
420 #[test]
421 fn change_group_id_simple() {
422 #[inline]
423 fn test(
424 name: impl Into<String>,
425 joint_type: JointType,
426 group_id: &str,
427 change_result: Result<(), GroupIDError>,
428 final_name: &str,
429 ) {
430 let mut joint_builder = JointBuilder::new(name, joint_type);
431 assert_eq!(joint_builder.change_group_id(group_id), change_result);
432 assert_eq!(joint_builder.name, final_name)
433 }
434
435 test(
436 "leg_[[M09da]]_joint_1",
437 JointType::Fixed,
438 "C10df",
439 Ok(()),
440 "leg_[[C10df]]_joint_1",
441 );
442 test(
443 "leg_[[M09da]]_joint_1",
444 JointType::Fixed,
445 "",
446 Err(GroupIDError::new_empty()),
447 "leg_[[M09da]]_joint_1",
448 );
449 test(
450 "leg_[[M09da]]_joint_1",
451 JointType::Fixed,
452 "[[tsst",
453 Err(GroupIDError::new_open("[[tsst")),
454 "leg_[[M09da]]_joint_1",
455 );
456 test(
457 "leg_[[M09da]]_joint_1",
458 JointType::Fixed,
459 "tsst]]",
460 Err(GroupIDError::new_close("tsst]]")),
461 "leg_[[M09da]]_joint_1",
462 );
463 }
464
465 #[test]
466 #[ignore = "TODO"]
467 fn change_group_id_advanced() {
468 todo!()
469 }
470
471 #[test]
472 fn apply_group_id_simple() {
473 #[inline]
474 fn test(name: impl Into<String>, joint_type: JointType, final_name: &str) {
475 let mut joint_builder = JointBuilder::new(name, joint_type);
476 joint_builder.apply_group_id();
477 assert_eq!(joint_builder.name, final_name)
478 }
479
480 test(
481 "leg_[[M09da]]_joint_1",
482 JointType::Fixed,
483 "leg_M09da_joint_1",
484 );
485 test(
486 "leg_[[M09daf_joint_1",
487 JointType::Fixed,
488 "leg_[[M09daf_joint_1",
489 );
490 test(
491 "leg_sM09da]]_joint_1",
492 JointType::Fixed,
493 "leg_sM09da]]_joint_1",
494 );
495 test(
496 "leg_[\\[M09da]\\]_joint_1",
497 JointType::Fixed,
498 "leg_[[M09da]]_joint_1",
499 );
500 }
501
502 #[test]
503 #[ignore = "TODO"]
504 fn apply_group_id_advanced() {
505 todo!()
506 }
507 }
508}