Another project
1use bone_solver::{ConstraintSystem, LineHandle, PointHandle, Residual, decompose};
2use bone_types::{Parameter, ParameterIndex, ParentIndex, ResidualIndex};
3
4fn p(i: u32) -> ParameterIndex {
5 ParameterIndex::new(i)
6}
7
8fn parent(i: u32) -> ParentIndex {
9 ParentIndex::new(i)
10}
11
12fn point(x: u32, y: u32) -> PointHandle {
13 PointHandle { x: p(x), y: p(y) }
14}
15
16fn parameters(values: &[f64]) -> Vec<Parameter> {
17 values.iter().copied().map(Parameter::new).collect()
18}
19
20#[test]
21fn disjoint_residuals_yield_separate_components() {
22 let system = ConstraintSystem::new(
23 parameters(&[0.0, 0.0, 1.0, 0.0, 10.0, 0.0, 11.0, 0.0]),
24 vec![
25 Residual::Horizontal(LineHandle {
26 a: point(0, 1),
27 b: point(2, 3),
28 }),
29 Residual::Horizontal(LineHandle {
30 a: point(4, 5),
31 b: point(6, 7),
32 }),
33 ],
34 );
35 let decomp = decompose(&system);
36 assert_eq!(decomp.components().len(), 2);
37 let first = &decomp.components()[0];
38 let second = &decomp.components()[1];
39 assert_eq!(
40 first
41 .parameters()
42 .iter()
43 .map(|p| p.value())
44 .collect::<Vec<_>>(),
45 vec![0, 1, 2, 3]
46 );
47 assert_eq!(
48 second
49 .parameters()
50 .iter()
51 .map(|p| p.value())
52 .collect::<Vec<_>>(),
53 vec![4, 5, 6, 7]
54 );
55 assert_eq!(first.residual_parents(), &[parent(0)]);
56 assert_eq!(second.residual_parents(), &[parent(1)]);
57 assert_eq!(first.residual_rows(&system), vec![ResidualIndex::new(0)]);
58 assert_eq!(second.residual_rows(&system), vec![ResidualIndex::new(1)]);
59}
60
61#[test]
62fn shared_parameter_merges_components() {
63 let system = ConstraintSystem::new(
64 parameters(&[0.0, 0.0, 1.0, 0.0, 2.0, 0.0]),
65 vec![
66 Residual::Horizontal(LineHandle {
67 a: point(0, 1),
68 b: point(2, 3),
69 }),
70 Residual::Horizontal(LineHandle {
71 a: point(2, 3),
72 b: point(4, 5),
73 }),
74 ],
75 );
76 let decomp = decompose(&system);
77 assert_eq!(decomp.components().len(), 1);
78 let only = &decomp.components()[0];
79 assert_eq!(only.parameters().len(), 6);
80 assert_eq!(only.residual_parents(), &[parent(0), parent(1)]);
81}
82
83#[test]
84fn isolated_parameter_forms_its_own_component() {
85 let system = ConstraintSystem::new(
86 parameters(&[0.0, 0.0, 3.0]),
87 vec![Residual::Pin {
88 param: p(0),
89 target: 1.0,
90 }],
91 );
92 let decomp = decompose(&system);
93 assert_eq!(decomp.components().len(), 3);
94 decomp
95 .components()
96 .iter()
97 .enumerate()
98 .for_each(|(i, comp)| {
99 let Ok(iv) = u32::try_from(i) else {
100 unreachable!()
101 };
102 assert_eq!(comp.parameters(), &[ParameterIndex::new(iv)]);
103 });
104 assert_eq!(decomp.components()[0].residual_parents(), &[parent(0)]);
105 assert!(decomp.components()[1].residual_parents().is_empty());
106 assert!(decomp.components()[2].residual_parents().is_empty());
107}