1#![forbid(unsafe_code)]
2#![doc = include_str!("../README.md")]
3
4#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
6pub struct JointId(pub usize);
7
8#[derive(Debug, Clone, Copy, PartialEq)]
10pub struct Bar {
11 start: usize,
12 end: usize,
13 length: f64,
14}
15
16impl Bar {
17 #[must_use]
19 pub const fn new(start: usize, end: usize, length: f64) -> Option<Self> {
20 if start != end && length.is_finite() && length > 0.0 {
21 Some(Self { start, end, length })
22 } else {
23 None
24 }
25 }
26
27 #[must_use]
29 pub const fn endpoints(self) -> (usize, usize) {
30 (self.start, self.end)
31 }
32
33 #[must_use]
35 pub const fn length(self) -> f64 {
36 self.length
37 }
38}
39
40#[derive(Debug, Clone, PartialEq)]
42pub struct Linkage {
43 joint_count: usize,
44 bars: Vec<Bar>,
45}
46
47impl Linkage {
48 #[must_use]
50 pub fn new(joint_count: usize, bars: Vec<Bar>) -> Option<Self> {
51 if joint_count > 0 {
52 Some(Self { joint_count, bars })
53 } else {
54 None
55 }
56 }
57
58 #[must_use]
60 pub const fn joint_count(&self) -> usize {
61 self.joint_count
62 }
63
64 #[must_use]
66 pub fn bars(&self) -> &[Bar] {
67 &self.bars
68 }
69}
70
71#[cfg(test)]
72mod tests {
73 use super::{Bar, JointId, Linkage};
74
75 #[test]
76 fn stores_linkage_records() {
77 let bar = Bar::new(0, 1, 2.0).expect("valid bar");
78 let linkage = Linkage::new(4, vec![bar]).expect("valid linkage");
79
80 assert_eq!(JointId(2).0, 2);
81 assert_eq!(bar.endpoints(), (0, 1));
82 assert_eq!(bar.length(), 2.0);
83 assert_eq!(linkage.joint_count(), 4);
84 assert_eq!(linkage.bars(), &[bar]);
85 }
86}