diff --git a/crates/ransac/src/lib.rs b/crates/ransac/src/lib.rs
index 9ab03fc29b..471916f473 100644
--- a/crates/ransac/src/lib.rs
+++ b/crates/ransac/src/lib.rs
@@ -11,10 +11,8 @@ use ordered_float::NotNan;
use rand::{seq::SliceRandom, Rng};
use serde::{Deserialize, Serialize};
-#[derive(Clone, Debug, Default, Deserialize, Serialize, PartialEq)]
+#[derive(Clone, Debug, Deserialize, Serialize, PartialEq)]
pub enum RansacFeature {
- #[default]
- None,
Line(Line2),
TwoLines(TwoLines),
}
@@ -51,7 +49,6 @@ impl RansacFeature {
impl Distance> for RansacFeature {
fn squared_distance_to(&self, point: Point2) -> f32 {
match self {
- RansacFeature::None => f32::INFINITY,
RansacFeature::Line(line) => line.squared_distance_to(point),
RansacFeature::TwoLines(two_lines) => two_lines.squared_distance_to(point),
}
@@ -65,7 +62,6 @@ impl IntoIterator for RansacResult {
fn into_iter(self) -> Self::IntoIter {
match self.feature {
- RansacFeature::None => Vec::new(),
RansacFeature::Line(line) => {
RansacLineSegment::try_from_used_points(line, self.used_points)
.map(|line_segment| vec![line_segment])
@@ -140,7 +136,7 @@ impl RansacLineSegment {
}
}
-#[derive(Default, Debug, PartialEq)]
+#[derive(Debug, PartialEq)]
pub struct RansacResult {
pub feature: RansacFeature,
pub used_points: Vec>,
@@ -164,21 +160,18 @@ impl Ransac {
fit_two_lines: bool,
maximum_score_distance: f32,
maximum_inclusion_distance: f32,
- ) -> RansacResult {
+ ) -> Option> {
if self.unused_points.len() < 2 {
- return RansacResult {
- feature: RansacFeature::None,
- used_points: vec![],
- };
+ return None;
}
if self.unused_points.len() == 2 {
- return RansacResult {
+ return Some(RansacResult {
feature: RansacFeature::Line(Line::from_points(
self.unused_points[0],
self.unused_points[1],
)),
used_points: self.unused_points.clone(),
- };
+ });
}
let maximum_score_distance_squared = maximum_score_distance * maximum_score_distance;
@@ -224,10 +217,10 @@ impl Ransac {
});
self.unused_points = unused_points;
- RansacResult {
+ Some(RansacResult {
feature: best_feature,
used_points,
- }
+ })
}
}
@@ -247,20 +240,14 @@ mod test {
fn ransac_empty_input() {
let mut ransac = Ransac::::new(vec![]);
let mut rng = ChaChaRng::from_entropy();
- assert_eq!(
- ransac.next_feature(&mut rng, 10, false, 5.0, 5.0),
- RansacResult::default()
- );
+ assert_eq!(ransac.next_feature(&mut rng, 10, false, 5.0, 5.0), None);
}
#[test]
fn ransac_single_point() {
let mut ransac = Ransac::::new(vec![]);
let mut rng = ChaChaRng::from_entropy();
- assert_eq!(
- ransac.next_feature(&mut rng, 10, false, 5.0, 5.0),
- RansacResult::default()
- );
+ assert_eq!(ransac.next_feature(&mut rng, 10, false, 5.0, 5.0), None);
}
#[test]
@@ -272,7 +259,9 @@ mod test {
let RansacResult {
feature,
used_points,
- } = ransac.next_feature(&mut rng, 10, false, 5.0, 5.0);
+ } = ransac
+ .next_feature(&mut rng, 10, false, 5.0, 5.0)
+ .expect("expected a feature");
println!("{feature:#?}");
println!("{used_points:#?}");
@@ -298,7 +287,9 @@ mod test {
let mut ransac = Ransac::::new(points.clone());
let mut rng = ChaChaRng::from_entropy();
- let result = ransac.next_feature(&mut rng, 15, false, 1.0, 1.0);
+ let result = ransac
+ .next_feature(&mut rng, 15, false, 1.0, 1.0)
+ .expect("expected feature");
if let RansacFeature::Line(line) = result.feature {
assert_relative_eq!(line.slope(), slope, epsilon = 0.0001);
diff --git a/crates/vision/src/field_border_detection.rs b/crates/vision/src/field_border_detection.rs
index fa0fb5278e..35a3dd6c20 100644
--- a/crates/vision/src/field_border_detection.rs
+++ b/crates/vision/src/field_border_detection.rs
@@ -110,13 +110,15 @@ fn find_border_lines(
second_line_association_distance: f32,
) -> Vec> {
// first line
- let result = ransac.next_feature(
+ let Some(result) = ransac.next_feature(
random_state,
20,
false,
first_line_association_distance,
first_line_association_distance,
- );
+ ) else {
+ return Vec::new();
+ };
if !matches!(result.feature, RansacFeature::Line(_))
|| result.used_points.len() < min_points_per_line
{
@@ -124,13 +126,15 @@ fn find_border_lines(
}
let first_line = best_fit_line(&result.used_points);
// second line
- let result = ransac.next_feature(
+ let Some(result) = ransac.next_feature(
random_state,
20,
false,
second_line_association_distance,
second_line_association_distance,
- );
+ ) else {
+ return Vec::new();
+ };
if !matches!(result.feature, RansacFeature::Line(_))
|| result.used_points.len() < min_points_per_line
{
diff --git a/crates/vision/src/line_detection.rs b/crates/vision/src/line_detection.rs
index 07f4939bd3..4fe52930c8 100644
--- a/crates/vision/src/line_detection.rs
+++ b/crates/vision/src/line_detection.rs
@@ -171,13 +171,15 @@ impl LineDetection {
if ransac.unused_points.len() < *context.minimum_number_of_points_on_line {
break;
}
- let ransac_result = ransac.next_feature(
+ let Some(ransac_result) = ransac.next_feature(
&mut self.random_state,
*context.ransac_iterations,
*context.ransac_fit_two_lines,
*context.maximum_fit_distance_in_ground,
*context.maximum_fit_distance_in_ground + *context.margin_for_point_inclusion,
- );
+ ) else {
+ break;
+ };
detected_features.push(ransac_result.feature.clone());
for line_segment in ransac_result {
@@ -241,7 +243,6 @@ impl LineDetection {
detected_features
.into_iter()
.map(|feature| match feature {
- RansacFeature::None => RansacFeature::None,
RansacFeature::Line(line) => RansacFeature::Line(Line::from_points(
context.camera_matrix.ground_to_pixel(line.point).unwrap(),
context
diff --git a/tools/twix/src/panels/image/overlays/line_detection.rs b/tools/twix/src/panels/image/overlays/line_detection.rs
index 2a420fff29..d6ebc4e0f5 100644
--- a/tools/twix/src/panels/image/overlays/line_detection.rs
+++ b/tools/twix/src/panels/image/overlays/line_detection.rs
@@ -58,7 +58,6 @@ impl Overlay for LineDetection {
};
for feature in detected_features {
match feature {
- RansacFeature::None => {}
RansacFeature::Line(line) => {
painter.line(line.point, line.direction, Stroke::new(2.0, Color32::RED))
}