Skip to content

Latest commit

 

History

History
91 lines (69 loc) · 3.71 KB

custom_distances.md

File metadata and controls

91 lines (69 loc) · 3.71 KB

Custom Distances

In the previous tutorial, we declared a struct, Circle, and made it be an RTreeObject. Then we can insert a Circle into an RTree. However, if we call nearest_neighbor on the RTree, we get an error:

error[E0599]: the method `nearest_neighbor` exists for struct `RTree<Circle>`, but its trait bounds were not satisfied

To solve this problem, we need to implement PointDistance for our Circle.

impl PointDistance for Circle {
    fn distance_2(&self, point: &(f64, f64)) -> f64 {
        /**/
    }
}

The point parameter in distance_2 is of type &(f64, f64) because the Envelope in our RTreeObject implementation is specified to AABB<(f64, f64)>, where (f64, f64) in the AABB<(f64, f64)> corresponds to the type of point.

We use the implementation of distance_2 provided by the document of PointDistance. Note that the returned value of distance_2 must be square of the distance.

The complete code is presented below:

use rstar::{PointDistance, RTree, RTreeObject, AABB};

#[derive(Debug)]
struct Circle {
    center: (f64, f64),
    radius: f64,
}

impl RTreeObject for Circle {
    type Envelope = AABB<(f64, f64)>;

    fn envelope(&self) -> Self::Envelope {
        let p1 = (self.center.0 - self.radius, self.center.1 - self.radius);
        let p2 = (self.center.0 + self.radius, self.center.1 + self.radius);

        AABB::from_corners(p1, p2)
    }
}

impl PointDistance for Circle {
    fn distance_2(&self, point: &(f64, f64)) -> f64 {
        let d_x = self.center.0 - point.0;
        let d_y = self.center.1 - point.1;
        
        let distance_to_center = (d_x * d_x + d_y * d_y).sqrt();
        let distance_to_ring = distance_to_center - self.radius;
        let distance_to_circle = f64::max(0., distance_to_ring);

        distance_to_circle * distance_to_circle
    }
}

fn main() {
    let c1 = Circle {
        center: (1., 1.),
        radius: 1.,
    };
    let c2 = Circle {
        center: (0., -1.),
        radius: 1.,
    };
    let tree = RTree::bulk_load(vec![c1, c2]);

    for c in tree.nearest_neighbor_iter_with_distance_2(&(0., 0.)) {
        println!("{:?}", c);
    }
}

Output:

(Circle { center: (0.0, -1.0), radius: 1.0 }, 0.0)
(Circle { center: (1.0, 1.0), radius: 1.0 }, 0.17157287525381)

There are other methods in trait PointDistance that we can implement for better efficiency. For example, locate_within_distance and drain_within_distance uses PointDistance::distance_2_if_less_or_equal. We can override the method to gain more runtime efficiency if possible.

📘 Back: Table of contents