21 pub angle : Angle, |
21 pub angle : Angle, |
22 pub z : f64 |
22 pub z : f64 |
23 } |
23 } |
24 |
24 |
25 impl CylCoords { |
25 impl CylCoords { |
|
26 /// Convert to cartesian coordinates. |
26 #[inline] |
27 #[inline] |
27 pub fn to_cartesian(&self) -> Loc<f64, 3> { |
28 pub fn to_cartesian(&self) -> Loc<f64, 3> { |
28 let &CylCoords{r, angle, z} = self; |
29 let &CylCoords{r, angle, z} = self; |
29 [r * angle.cos(), r * angle.sin(), z].into() |
30 [r * angle.cos(), r * angle.sin(), z].into() |
30 } |
31 } |
31 |
32 |
|
33 /// Form cylindrical coordinates from cartesian coordinates. |
32 #[inline] |
34 #[inline] |
33 #[allow(dead_code)] |
35 #[allow(dead_code)] |
34 pub fn from_cartesian(coords : Loc<f64, 3>) -> Self { |
36 pub fn from_cartesian(coords : Loc<f64, 3>) -> Self { |
35 let [x, y, z] = coords.into(); |
37 let [x, y, z] = coords.into(); |
36 let r = (x*x + y*y).sqrt(); |
38 let r = (x*x + y*y).sqrt(); |
224 Top, |
228 Top, |
225 Bottom, |
229 Bottom, |
226 Side, |
230 Side, |
227 } |
231 } |
228 |
232 |
|
233 /// Point on a specific [`Cylinder`] |
229 #[derive(Clone, Debug, PartialEq)] |
234 #[derive(Clone, Debug, PartialEq)] |
230 pub struct OnCylinder<'a> { |
235 pub struct OnCylinder<'a> { |
|
236 /// Face and coordinates of the point |
|
237 point : Point, |
|
238 /// The specific cylinder the `point` lies on. |
231 cylinder : &'a Cylinder, |
239 cylinder : &'a Cylinder, |
232 point : Point, |
|
233 } |
240 } |
234 |
241 |
235 |
242 |
236 /// Cylinder configuration |
243 /// Cylinder configuration |
237 #[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)] |
244 #[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)] |
238 pub struct CylinderConfig { |
245 pub struct CylinderConfig { |
|
246 /// Number of Newton iterates two take to solve geodesics. |
239 pub newton_iters : usize, |
247 pub newton_iters : usize, |
240 } |
248 } |
241 |
249 |
242 impl Default for CylinderConfig { |
250 impl Default for CylinderConfig { |
243 fn default() -> Self { |
251 fn default() -> Self { |
288 } |
297 } |
289 |
298 |
290 // Tangent vector |
299 // Tangent vector |
291 type Tangent = Loc<f64, 2>; |
300 type Tangent = Loc<f64, 2>; |
292 |
301 |
|
302 /// Goes through list of potential tangents (corresponding to several geodesics taking |
|
303 /// different paths), and retuns the shortest tangent vector and the corresponding length. |
293 #[inline] |
304 #[inline] |
294 fn best_tangent<I>(tangents : I) -> (Tangent, f64) |
305 fn best_tangent<I>(tangents : I) -> (Tangent, f64) |
295 where I : IntoIterator<Item = Tangent> { |
306 where I : IntoIterator<Item = Tangent> { |
296 tangents.into_iter() |
307 tangents.into_iter() |
297 .map(|t| (t, t.norm(L2))) |
308 .map(|t| (t, t.norm(L2))) |
318 Point::Bottom(cap) => { cap.cyl_coords(self.bottom_z()) }, |
330 Point::Bottom(cap) => { cap.cyl_coords(self.bottom_z()) }, |
319 Point::Side(side) => { side.cyl_coords(self.radius) }, |
331 Point::Side(side) => { side.cyl_coords(self.radius) }, |
320 } |
332 } |
321 } |
333 } |
322 |
334 |
|
335 /// Returns the z coordinate of the top (cap) of the cylinder. |
323 #[inline] |
336 #[inline] |
324 pub fn top_z(&self) -> f64 { |
337 pub fn top_z(&self) -> f64 { |
325 self.height / 2.0 |
338 self.height / 2.0 |
326 } |
339 } |
327 |
340 |
|
341 /// Returns the z coordinate of the bottom (cap) of the cylinder. |
328 #[inline] |
342 #[inline] |
329 pub fn bottom_z(&self) -> f64 { |
343 pub fn bottom_z(&self) -> f64 { |
330 -self.height / 2.0 |
344 -self.height / 2.0 |
331 } |
345 } |
332 |
346 |
333 /// Find angle where a geodesic from `side` to `(cap, z)` crosses the cap edge. |
347 /// Find angle where a geodesic from `side` to `(cap, z)` crosses the cap edge. |
334 /// |
348 /// |
335 /// Uses `newton_sym1x1`. |
349 /// Uses [`newton_sym1x1`]. |
336 fn side_cap_crossing( |
350 fn side_cap_crossing( |
337 &self, |
351 &self, |
338 side : &SidePoint, |
352 side : &SidePoint, |
339 cap : &CapPoint, z : f64, // `z` is the z coordinate of cap |
353 cap : &CapPoint, z : f64, // `z` is the z coordinate of cap |
340 ) -> Angle { |
354 ) -> Angle { |
413 |
427 |
414 /// Find angles where the geodesic passing through the side from `cap1` at height `z_1` |
428 /// Find angles where the geodesic passing through the side from `cap1` at height `z_1` |
415 /// to `cap2` at height `z_2` crosses the cap edges. |
429 /// to `cap2` at height `z_2` crosses the cap edges. |
416 /// **Panics if `cap2.angle < cap1.angle`.** |
430 /// **Panics if `cap2.angle < cap1.angle`.** |
417 /// |
431 /// |
418 /// Uses `newton_sym2x2`. |
432 /// Uses [`newton_sym2x2`]. |
419 fn cap_side_cap_crossing( |
433 fn cap_side_cap_crossing( |
420 &self, |
434 &self, |
421 cap1 : &CapPoint, z_1 : f64, |
435 cap1 : &CapPoint, z_1 : f64, |
422 cap2 : &CapPoint, z_2 : f64, |
436 cap2 : &CapPoint, z_2 : f64, |
423 init_by_cap2 : bool |
437 init_by_cap2 : bool |
478 }; |
492 }; |
479 let [α_1, α_2] = newton_sym2x2(f, α_init, self.config.newton_iters); |
493 let [α_1, α_2] = newton_sym2x2(f, α_init, self.config.newton_iters); |
480 (normalise_angle(φ_1 + α_1), normalise_angle(φ_2 - α_2)) |
494 (normalise_angle(φ_1 + α_1), normalise_angle(φ_2 - α_2)) |
481 } |
495 } |
482 |
496 |
|
497 /// Calculates the log between points on a `cap` and a `side` of the cylinder, in this direction. |
|
498 /// The `z` coordinate of the cap and an indication whether it is a `top` or bottom cap must |
|
499 /// also be given. |
483 fn cap_side_log( |
500 fn cap_side_log( |
484 &self, |
501 &self, |
485 cap : &CapPoint, (z, top) : (f64, bool), |
502 cap : &CapPoint, (z, top) : (f64, bool), |
486 side : &SidePoint |
503 side : &SidePoint |
487 ) -> Tangent { |
504 ) -> Tangent { |
594 (t1/n)*(n + t2.norm(L2) + t3.norm(L2)) |
617 (t1/n)*(n + t2.norm(L2) + t3.norm(L2)) |
595 } |
618 } |
596 } |
619 } |
597 } |
620 } |
598 |
621 |
|
622 /// Calculates the log between points on two caps of the cylinder, assuming the corresonding |
|
623 /// geodesic crosses the side. The heights of the caps and indications whether they are |
|
624 /// top caps, must also be given. |
599 fn cap_side_cap_log( |
625 fn cap_side_cap_log( |
600 &self, |
626 &self, |
601 cap1 : &CapPoint, (z1, top1) : (f64, bool), |
627 cap1 : &CapPoint, (z1, top1) : (f64, bool), |
602 cap2 : &CapPoint, (z2, top2) : (f64, bool), |
628 cap2 : &CapPoint, (z2, top2) : (f64, bool), |
603 init_by_cap2 : bool, |
629 init_by_cap2 : bool, |
642 (t1/n)*(n + t2.norm(L2) + t3.norm(L2)) |
668 (t1/n)*(n + t2.norm(L2) + t3.norm(L2)) |
643 } |
669 } |
644 } |
670 } |
645 } |
671 } |
646 |
672 |
647 /// Calculates both the logarithmic map and distance to another point |
673 /// Calculates both the logarithmic map and distance between two points. |
648 fn log_dist(&self, source : &Point, destination : &Point) -> (Tangent, f64) { |
674 fn log_dist(&self, source : &Point, destination : &Point) -> (Tangent, f64) { |
649 use Point::*; |
675 use Point::*; |
650 match (source, destination) { |
676 match (source, destination) { |
651 (Top(cap1), Top(cap2)) => { |
677 (Top(cap1), Top(cap2)) => { |
652 best_tangent([cap1.log(cap2)]) |
678 best_tangent([cap1.log(cap2)]) |
704 ]) |
730 ]) |
705 }, |
731 }, |
706 } |
732 } |
707 } |
733 } |
708 |
734 |
|
735 /// Calculates the exponential map of `point` in the direction `t` until the next edge. |
|
736 /// If `t` was fully exhausted before reaching an edge, the second return value is [`None`], |
|
737 /// otherwise it is a [`Some`] of the remaining tangent, translated at the returned point. |
709 #[allow(unreachable_code)] |
738 #[allow(unreachable_code)] |
710 #[allow(unused_variables)] |
739 #[allow(unused_variables)] |
711 fn partial_exp(&self, point : Point, t : Tangent) -> (Point, Option<Tangent>) { |
740 fn partial_exp(&self, point : Point, t : Tangent) -> (Point, Option<Tangent>) { |
712 match point { |
741 match point { |
713 Point::Top(cap) => { |
742 Point::Top(cap) => { |
929 radius : 1.0, |
959 radius : 1.0, |
930 height : 1.0, |
960 height : 1.0, |
931 config : CylinderConfig { newton_iters : 20 }, |
961 config : CylinderConfig { newton_iters : 20 }, |
932 }; |
962 }; |
933 |
963 |
|
964 /// Tests for correct distance calculation beween two points on the same cap. |
934 #[test] |
965 #[test] |
935 fn intra_cap_log_dist() { |
966 fn intra_cap_dist() { |
936 let π = f64::PI; |
967 let π = f64::PI; |
937 let p1 = CYL.on_top(0.0, 0.5); |
968 let p1 = CYL.on_top(0.0, 0.5); |
938 let p2 = CYL.on_top(π, 0.5); |
969 let p2 = CYL.on_top(π, 0.5); |
939 let p3 = CYL.on_top(π/2.0, 0.5); |
970 let p3 = CYL.on_top(π/2.0, 0.5); |
940 |
971 |
941 check_distance!(p1.dist_to(&p2), 1.0); |
972 check_distance!(p1.dist_to(&p2), 1.0); |
942 check_distance!(p2.dist_to(&p3), 0.5_f64.sqrt()); |
973 check_distance!(p2.dist_to(&p3), 0.5_f64.sqrt()); |
943 check_distance!(p3.dist_to(&p1), 0.5_f64.sqrt()); |
974 check_distance!(p3.dist_to(&p1), 0.5_f64.sqrt()); |
944 } |
975 } |
945 |
976 |
|
977 /// Tests for correct distance calculation beween two points on the side. |
946 #[test] |
978 #[test] |
947 fn intra_side_log_dist() { |
979 fn intra_side_dist() { |
948 let π = f64::PI; |
980 let π = f64::PI; |
949 let p1 = CYL.on_side(0.0, 0.0); |
981 let p1 = CYL.on_side(0.0, 0.0); |
950 let p2 = CYL.on_side(0.0, 0.4); |
982 let p2 = CYL.on_side(0.0, 0.4); |
951 let p3 = CYL.on_side(π/2.0, 0.0); |
983 let p3 = CYL.on_side(π/2.0, 0.0); |
952 |
984 |
953 check_distance!(p1.dist_to(&p2), 0.4); |
985 check_distance!(p1.dist_to(&p2), 0.4); |
954 check_distance!(p1.dist_to(&p3), π/2.0*CYL.radius); |
986 check_distance!(p1.dist_to(&p3), π/2.0*CYL.radius); |
955 } |
987 } |
956 |
988 |
|
989 /// Tests for correct distance calculation beween two points on the side, when the corresponding |
|
990 /// minimal geodesic has to cross a cap. |
957 #[test] |
991 #[test] |
958 fn intra_side_over_cap_log_dist() { |
992 fn intra_side_over_cap_dist() { |
959 let π = f64::PI; |
993 let π = f64::PI; |
960 let off = 0.05; |
994 let off = 0.05; |
961 let z = CYL.top_z() - off; |
995 let z = CYL.top_z() - off; |
962 let p1 = CYL.on_side(0.0, z); |
996 let p1 = CYL.on_side(0.0, z); |
963 let p2 = CYL.on_side(π, z); |
997 let p2 = CYL.on_side(π, z); |
964 |
998 |
965 check_distance!(p1.dist_to(&p2), 2.0 * (CYL.radius + off)); |
999 check_distance!(p1.dist_to(&p2), 2.0 * (CYL.radius + off)); |
966 } |
1000 } |
967 |
1001 |
|
1002 /// Tests for correct distance calculation between points on top and bottom caps. |
968 #[test] |
1003 #[test] |
969 fn top_bottom_log_dist() { |
1004 fn top_bottom_dist() { |
970 let π = f64::PI; |
1005 let π = f64::PI; |
971 let p1 = CYL.on_top(0.0, 0.0); |
1006 let p1 = CYL.on_top(0.0, 0.0); |
972 let p2 = CYL.on_bottom(0.0, 0.0); |
1007 let p2 = CYL.on_bottom(0.0, 0.0); |
973 |
1008 |
974 check_distance!(p1.dist_to(&p2), 2.0 * CYL.radius + CYL.height); |
1009 check_distance!(p1.dist_to(&p2), 2.0 * CYL.radius + CYL.height); |
979 |
1014 |
980 check_distance!(p1.dist_to(&p2), CYL.radius + CYL.height); |
1015 check_distance!(p1.dist_to(&p2), CYL.radius + CYL.height); |
981 check_distance!(p1.dist_to(&p3), 2.0 * CYL.radius + CYL.height); |
1016 check_distance!(p1.dist_to(&p3), 2.0 * CYL.radius + CYL.height); |
982 } |
1017 } |
983 |
1018 |
|
1019 /// Test for correct distance calculation between points on the side and a cap. |
984 #[test] |
1020 #[test] |
985 fn top_side_log_dist() { |
1021 fn top_side_dist() { |
986 let p1 = CYL.on_top(0.0, 0.0); |
1022 let p1 = CYL.on_top(0.0, 0.0); |
987 let p2 = CYL.on_side(0.0, 0.0); |
1023 let p2 = CYL.on_side(0.0, 0.0); |
988 |
1024 |
989 check_distance!(p1.dist_to(&p2), CYL.radius + CYL.height / 2.0); |
1025 check_distance!(p1.dist_to(&p2), CYL.radius + CYL.height / 2.0); |
990 } |
1026 } |
991 |
1027 |
|
1028 /// Tests for correct tangent calculation from a cap to the side. |
992 #[test] |
1029 #[test] |
993 fn cap_side_partial_exp() { |
1030 fn cap_side_partial_exp() { |
994 let π = f64::PI; |
1031 let π = f64::PI; |
995 let angles = [0.0, π/2.0, π*5.0/6.0, π*7.0/5.0]; |
1032 let angles = [0.0, π/2.0, π*5.0/6.0, π*7.0/5.0]; |
996 |
1033 |