use crate::{
client::{ActorBase, DebugHelper, Waypoint},
geom::{Location, Transform, Vector3D},
rpc::Color,
};
#[cfg(test)]
use crate::geom::Rotation;
use std::{borrow::Borrow, f32::consts::PI};
pub fn get_speed<T: ActorBase>(vehicle: &T) -> f32 {
let velocity = vehicle.velocity().unwrap_or(Vector3D {
x: 0.0,
y: 0.0,
z: 0.0,
});
let speed_ms = velocity.norm(); speed_ms * 3.6 }
pub fn compute_distance(location_1: &Location, location_2: &Location) -> f32 {
let dx = location_1.x - location_2.x;
let dy = location_1.y - location_2.y;
let dz = location_1.z - location_2.z;
(dx * dx + dy * dy + dz * dz).sqrt()
}
pub fn compute_distance_2d(location_1: &Location, location_2: &Location) -> f32 {
let dx = location_1.x - location_2.x;
let dy = location_1.y - location_2.y;
(dx * dx + dy * dy).sqrt()
}
pub fn compute_magnitude_angle(
target_location: &Location,
current_location: &Location,
orientation: f32,
) -> (f32, f32) {
let distance = compute_distance(target_location, current_location);
let dx = target_location.x - current_location.x;
let dy = target_location.y - current_location.y;
let target_angle = dy.atan2(dx); let orientation_rad = orientation * PI / 180.0;
let mut angle = target_angle - orientation_rad;
while angle > PI {
angle -= 2.0 * PI;
}
while angle < -PI {
angle += 2.0 * PI;
}
(distance, angle)
}
pub fn vector(location_1: &Location, location_2: &Location) -> Vector3D {
let dx = location_2.x - location_1.x;
let dy = location_2.y - location_1.y;
let dz = location_2.z - location_1.z;
let magnitude = (dx * dx + dy * dy + dz * dz).sqrt();
if magnitude > 0.0001 {
Vector3D {
x: dx / magnitude,
y: dy / magnitude,
z: dz / magnitude,
}
} else {
Vector3D {
x: 0.0,
y: 0.0,
z: 0.0,
}
}
}
pub fn is_within_distance(
target_transform: &Transform,
reference_transform: &Transform,
max_distance: f32,
angle_interval: Option<f32>,
) -> bool {
let target_loc = &target_transform.location;
let reference_loc = &reference_transform.location;
let distance = compute_distance(target_loc, reference_loc);
if distance > max_distance {
return false;
}
let angle_interval = match angle_interval {
Some(a) => a,
None => return true,
};
let (_, angle) =
compute_magnitude_angle(target_loc, reference_loc, reference_transform.rotation.yaw);
let angle_deg = angle.abs() * 180.0 / PI;
angle_deg <= angle_interval
}
pub fn distance_vehicle(waypoint_location: &Location, vehicle_transform: &Transform) -> f32 {
compute_distance_2d(waypoint_location, &vehicle_transform.location)
}
pub fn draw_waypoints<I>(debug: &DebugHelper, waypoints: I, z_offset: f32, life_time: f32)
where
I: IntoIterator,
I::Item: Borrow<Waypoint>,
{
for waypoint in waypoints {
let waypoint = waypoint.borrow();
let transform = waypoint.transform();
let mut begin = transform.location;
begin.z += z_offset;
let angle_rad = transform.rotation.yaw.to_radians();
let end = Location {
x: begin.x + angle_rad.cos(),
y: begin.y + angle_rad.sin(),
z: begin.z,
};
let _ = debug.draw_arrow(begin, end, 10.0, 30.0, Color::GREEN, life_time, false);
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_compute_distance() {
let loc1 = Location {
x: 0.0,
y: 0.0,
z: 0.0,
};
let loc2 = Location {
x: 3.0,
y: 4.0,
z: 0.0,
};
let distance = compute_distance(&loc1, &loc2);
assert!((distance - 5.0).abs() < 0.001);
}
#[test]
fn test_compute_distance_2d() {
let loc1 = Location {
x: 0.0,
y: 0.0,
z: 10.0,
};
let loc2 = Location {
x: 3.0,
y: 4.0,
z: 20.0,
};
let distance = compute_distance_2d(&loc1, &loc2);
assert!((distance - 5.0).abs() < 0.001); }
#[test]
fn test_vector() {
let loc1 = Location {
x: 0.0,
y: 0.0,
z: 0.0,
};
let loc2 = Location {
x: 3.0,
y: 4.0,
z: 0.0,
};
let vec = vector(&loc1, &loc2);
assert!((vec.x - 0.6).abs() < 0.001);
assert!((vec.y - 0.8).abs() < 0.001);
assert!((vec.z - 0.0).abs() < 0.001);
}
#[test]
fn test_compute_magnitude_angle() {
let current = Location {
x: 0.0,
y: 0.0,
z: 0.0,
};
let target = Location {
x: 1.0,
y: 0.0,
z: 0.0,
};
let (dist, angle) = compute_magnitude_angle(&target, ¤t, 0.0);
assert!((dist - 1.0).abs() < 0.001);
assert!(angle.abs() < 0.001);
let (dist, angle) = compute_magnitude_angle(&target, ¤t, 180.0);
assert!((dist - 1.0).abs() < 0.001);
assert!((angle.abs() - PI).abs() < 0.001); }
#[test]
fn test_is_within_distance_only() {
let target = Transform {
location: Location {
x: 5.0,
y: 0.0,
z: 0.0,
},
rotation: Rotation {
pitch: 0.0,
yaw: 0.0,
roll: 0.0,
},
};
let reference = Transform {
location: Location {
x: 0.0,
y: 0.0,
z: 0.0,
},
rotation: Rotation {
pitch: 0.0,
yaw: 0.0,
roll: 0.0,
},
};
assert!(is_within_distance(&target, &reference, 10.0, None));
assert!(!is_within_distance(&target, &reference, 4.0, None));
}
#[test]
fn test_is_within_distance_with_angle() {
let target = Transform {
location: Location {
x: 5.0,
y: 0.0,
z: 0.0,
},
rotation: Rotation {
pitch: 0.0,
yaw: 0.0,
roll: 0.0,
},
};
let reference = Transform {
location: Location {
x: 0.0,
y: 0.0,
z: 0.0,
},
rotation: Rotation {
pitch: 0.0,
yaw: 0.0,
roll: 0.0,
},
};
assert!(is_within_distance(&target, &reference, 10.0, Some(30.0)));
let reference_back = Transform {
location: Location {
x: 0.0,
y: 0.0,
z: 0.0,
},
rotation: Rotation {
pitch: 0.0,
yaw: 180.0,
roll: 0.0,
},
};
assert!(!is_within_distance(
&target,
&reference_back,
10.0,
Some(30.0)
));
}
#[test]
fn test_distance_vehicle() {
let waypoint_loc = Location {
x: 10.0,
y: 10.0,
z: 5.0,
};
let vehicle_transform = Transform {
location: Location {
x: 13.0,
y: 14.0,
z: 100.0, },
rotation: Rotation {
pitch: 0.0,
yaw: 0.0,
roll: 0.0,
},
};
let distance = distance_vehicle(&waypoint_loc, &vehicle_transform);
assert!((distance - 5.0).abs() < 0.001); }
}