I want to iterate over array and find the best element with some corresponding values and then perform some calculation, now I perform the calculation on every element and return the best calculation which is unnecessary. The additional values here is closest_intersection_info. The compare method of IntersectionInfo :
I just want to get closest_intersection_info and the corresponding element of the array without using unsafe or clone, in c++ I would use pointers, what experienced rustinace would use in this case
fn reytrace(&mut self, ray : Ray, scene : &Scene) -> Color {
let mut closest_intersection_info = IntersectionInfo::new();
let mut result_color = Color::new(0.0,0.0,0.0);
for element in &scene.elements {
if let Some(info) = element.geometry().intersect(&ray) {
if closest_intersection_info.compare(info) {
result_color = element.material().color(&ray, &closest_intersection_info, &scene.lights);
}
}
}
return result_color;
}
Here's a straightforward way to store a reference to the element, and then perform a computation on it after the loop:
fn reytrace(&mut self, ray : Ray, scene : &Scene) -> Color {
let mut closest_intersection_info = IntersectionInfo::new();
let mut closest_element = None;
for element in &scene.elements {
if let Some(info) = element.geometry().intersect(&ray) {
if closest_intersection_info.compare(info) {
closest_element = Some(element);
}
}
}
match closest_element {
Some(element) => element.material().color(&ray, &closest_intersection_info, &scene.lights),
None => Color::new(0.0,0.0,0.0),
}
}
You could also use a more "rustic" approach with some iterator methods like filter_map and min_by_key to replace the compare method:
fn reytrace(&mut self, ray : Ray, scene : &Scene) -> Color {
let intersections = scene.elements.iter().filter_map(|element| {
let info = element.geometry().intersect(&ray)?;
Some((element, info))
});
let closest_intersection = intersections.min_by_key(|(_, info)| info.distance);
match closest_intersection {
Some((element, info)) => element.material().color(&ray, &info, &scene.lights),
None => Color::new(0.0,0.0,0.0),
}
}
Here's one possibility (as far as I can tell from the size of the snippet). It uses ordered-float, which may or may not be appropriate for your use case (you should consider what you want to happen should you hit a NaN value and judge for yourself).
// This could be a method on Scene or such
pub fn closest_intersection<'a>(
ray: &Ray,
scene: &'a Scene,
) -> Option<(&'a Element, IntersectionInfo)> {
scene
.elements
.iter()
.flat_map(|e| e.geometry().intersect(ray).map(|info| (e, info)))
.max_by_key(|tuple| OrderedFloat(tuple.1.distance))
}
impl Universe {
// Unclear why `&mut self` or even `&self` is needed here
// Maybe you could use `.map(|tuple| ...).unwrap_or_default()` or such here
pub fn reytrace(&mut self, ray: Ray, scene: &Scene) -> Color {
match closest_intersection(&ray, scene) {
None => Color::new(0.0, 0.0, 0.0),
Some((element, info)) => element.material().color(&ray, &info, &scene.lights),
}
}
}