modern_icp/
lib.rs

1//! A modern modular pure Rust implementation of the Iterative Closest Point algorithm.
2//!
3//! ## Example
4//!
5//! ```
6//! # use modern_icp::PointCloudPoint;
7//! # use modern_icp::icp::estimate_transform;
8//! # use modern_icp::correspondence::BidirectionalDistance;
9//! # use modern_icp::transform_estimation::point_to_plane_lls;
10//! # use modern_icp::convergence::same_squared_distance_error;
11//! # use modern_icp::reject_outliers::reject_n_sigma_dist;
12//! # use modern_icp::filter_points::accept_all;
13//! # use crate::modern_icp::correspondence::CorrespondenceEstimator;
14//! # use nalgebra::{Point3, Vector3};
15//! #
16//! # // Generate random point clouds
17//! # let mut alignee_cloud = Vec::with_capacity(100);
18//! # let mut target_cloud = Vec::with_capacity(100);
19//! #
20//! # // Generate 100 random points for alignee cloud
21//! # for _ in 0..100 {
22//! #
23//! #     let pos = Point3::new(
24//! #         rand::random::<f32>() * 10.0,
25//! #         rand::random::<f32>() * 10.0,
26//! #         rand::random::<f32>() * 10.0,
27//! #     );
28//! #     let norm = Vector3::new(
29//! #         rand::random::<f32>() * 2.0 - 1.0,
30//! #         rand::random::<f32>() * 2.0 - 1.0,
31//! #         rand::random::<f32>() * 2.0 - 1.0,
32//! #     ).normalize();
33//! #     alignee_cloud.push(PointCloudPoint::from_pos_norm(pos, norm));
34//! # }
35//! #
36//! # // Generate 100 random points for target cloud
37//! # for _ in 0..100 {
38//! #     let pos = Point3::new(
39//! #         rand::random::<f32>() * 10.0,
40//! #         rand::random::<f32>() * 10.0,
41//! #         rand::random::<f32>() * 10.0,
42//! #     );
43//! #     let norm = Vector3::new(
44//! #         rand::random::<f32>() * 2.0 - 1.0,
45//! #         rand::random::<f32>() * 2.0 - 1.0,
46//! #         rand::random::<f32>() * 2.0 - 1.0,
47//! #     ).normalize();
48//! #     target_cloud.push(PointCloudPoint::from_pos_norm(pos, norm));
49//! # }
50//! #
51//! let (alignee_transform, error_sum) = estimate_transform(
52//!     alignee_cloud,
53//!     &target_cloud,
54//!     20, // max iterations
55//!     BidirectionalDistance::new(&target_cloud),
56//!     accept_all,
57//!     reject_n_sigma_dist(3.0),
58//!     point_to_plane_lls::estimate_isometry,
59//!     same_squared_distance_error(1.0),
60//! );
61//! ```
62//!
63//! ## Integrations
64//!
65//! An integrations with the modelz crate is provided so you can use `Model3D` with the `estimate_transform` function.
66//!
67//! ```
68//! # use modern_icp::PointCloudPoint;
69//! # use modern_icp::icp::estimate_transform;
70//! # use modern_icp::correspondence::BidirectionalDistance;
71//! # use modern_icp::transform_estimation::point_to_plane_lls;
72//! # use modern_icp::convergence::same_squared_distance_error;
73//! # use modern_icp::reject_outliers::reject_n_sigma_dist;
74//! # use modern_icp::filter_points::accept_all;
75//! # use crate::modern_icp::correspondence::CorrespondenceEstimator;
76//! use modelz::Model3D;
77//!
78//! if let (Ok(alignee), Ok(target)) = (Model3D::load("alignee.gltf"), Model3D::load("target.stl")) {
79//!     let (transform, error_sum) = estimate_transform(
80//!         alignee,
81//!         &target,
82//!         20, // max iterations
83//!         BidirectionalDistance::new(&target),
84//!         accept_all,
85//!         reject_n_sigma_dist(3.0),
86//!         point_to_plane_lls::estimate_isometry,
87//!         same_squared_distance_error(1.0),
88//!     );
89//! }
90//! ```
91mod align;
92mod common;
93mod integrations;
94mod plane;
95mod point_cloud;
96
97pub use align::*;
98pub use common::*;
99#[allow(unused_imports)]
100pub use integrations::*;
101pub use plane::*;
102pub use point_cloud::*;
103
104#[cfg(feature = "rerun")]
105lazy_static::lazy_static! {
106    pub static ref RR: rerun::RecordingStream = rerun::RecordingStreamBuilder::new("shrink_to_fit_mesh").spawn().unwrap();
107}
108
109#[cfg(feature = "rerun")]
110fn pt3_array<T, const D: usize>(pt: nalgebra::Point<T, D>) -> [f32; 3]
111where
112    T: Clone + PartialEq + nalgebra::Scalar,
113    f32: From<T>,
114{
115    [
116        pt[0].clone().into(),
117        pt[1].clone().into(),
118        pt[2].clone().into(),
119    ]
120}
121
122#[cfg(feature = "rerun")]
123fn vec3_array<T, const D: usize>(vec: nalgebra::OVector<T, nalgebra::Const<D>>) -> [f32; 3]
124where
125    T: Clone + PartialEq + nalgebra::Scalar,
126    f32: From<T>,
127{
128    [
129        vec[0].clone().into(),
130        vec[1].clone().into(),
131        vec[2].clone().into(),
132    ]
133}
134
135#[cfg(feature = "rerun")]
136pub fn unit_quat_array<T>(quat: nalgebra::UnitQuaternion<T>) -> [f32; 4]
137where
138    T: Copy + PartialEq + nalgebra::SimdValue + nalgebra::Scalar + nalgebra::RealField,
139    f32: From<T>,
140{
141    let quat = quat.quaternion();
142    [quat.i.into(), quat.j.into(), quat.k.into(), quat.w.into()]
143}
144
145#[cfg(feature = "rerun")]
146pub fn rr_log_cloud<T>(name: &str, pt_cloud: &PointCloud<T, 3>)
147where
148    T: Copy + Clone + PartialEq + nalgebra::Scalar,
149    f32: From<T>,
150{
151    crate::RR
152        .log(
153            name,
154            &rerun::Points3D::new(
155                pt_cloud
156                    .iter()
157                    .map(|pt| pt3_array(pt.pos))
158                    .collect::<Vec<_>>(),
159            ),
160        )
161        .unwrap();
162}