use carla_sys::carla_rust::geom::FfiBoundingBox as NativeBoundingBox;
use nalgebra::{Isometry3, Point3, Translation3, UnitQuaternion, Vector2, Vector3};
use static_assertions::assert_impl_all;
pub use carla_sys::carla_rust::geom::{
FfiGeoLocation, FfiLocation, FfiRotation, FfiTransform, FfiVector2D, FfiVector3D,
};
#[repr(C)]
#[derive(Debug, Clone)]
pub struct Transform {
pub location: Location,
pub rotation: Rotation,
}
impl Transform {
#[inline(always)]
pub fn as_ffi(&self) -> &FfiTransform {
unsafe { std::mem::transmute(self) }
}
#[inline(always)]
pub fn from_ffi(ffi: FfiTransform) -> Self {
unsafe { std::mem::transmute(ffi) }
}
#[inline(always)]
pub fn into_ffi(self) -> FfiTransform {
unsafe { std::mem::transmute(self) }
}
pub fn from_na(pose: &Isometry3<f32>) -> Self {
let Isometry3 {
rotation,
translation,
} = pose;
let location = Location::from_na_translation(translation);
let rotation = Rotation::from_na(rotation);
Self { location, rotation }
}
pub fn to_na(&self) -> Isometry3<f32> {
Isometry3 {
rotation: self.rotation.to_na(),
translation: self.location.to_na_translation(),
}
}
#[inline]
pub fn get_forward_vector(&self) -> Vector3D {
self.rotation.forward_vector()
}
#[inline]
pub fn get_right_vector(&self) -> Vector3D {
self.rotation.right_vector()
}
#[inline]
pub fn get_up_vector(&self) -> Vector3D {
self.rotation.up_vector()
}
pub fn transform_point(&self, point: &Location) -> Location {
let rotated = self
.rotation
.rotate_vector(&Vector3D::new(point.x, point.y, point.z));
Location::new(
rotated.x + self.location.x,
rotated.y + self.location.y,
rotated.z + self.location.z,
)
}
pub fn transform_vector(&self, vector: &Vector3D) -> Vector3D {
self.rotation.rotate_vector(vector)
}
pub fn inverse_transform_point(&self, point: &Location) -> Location {
let translated = Vector3D::new(
point.x - self.location.x,
point.y - self.location.y,
point.z - self.location.z,
);
let result = self.rotation.inverse_rotate_vector(&translated);
Location::new(result.x, result.y, result.z)
}
pub fn get_matrix(&self) -> [[f32; 4]; 4] {
let fwd = self.rotation.forward_vector();
let right = self.rotation.right_vector();
let up = self.rotation.up_vector();
let loc = &self.location;
[
[fwd.x, right.x, up.x, loc.x],
[fwd.y, right.y, up.y, loc.y],
[fwd.z, right.z, up.z, loc.z],
[0.0, 0.0, 0.0, 1.0],
]
}
pub fn get_inverse_matrix(&self) -> [[f32; 4]; 4] {
let fwd = self.rotation.forward_vector();
let right = self.rotation.right_vector();
let up = self.rotation.up_vector();
let loc = &self.location;
let tx = -(fwd.x * loc.x + fwd.y * loc.y + fwd.z * loc.z);
let ty = -(right.x * loc.x + right.y * loc.y + right.z * loc.z);
let tz = -(up.x * loc.x + up.y * loc.y + up.z * loc.z);
[
[fwd.x, fwd.y, fwd.z, tx],
[right.x, right.y, right.z, ty],
[up.x, up.y, up.z, tz],
[0.0, 0.0, 0.0, 1.0],
]
}
fn compose(&self, other: &Self) -> Self {
Transform::from_na(&(self.to_na() * other.to_na()))
}
}
static_assertions::assert_eq_size!(Transform, FfiTransform);
static_assertions::assert_eq_align!(Transform, FfiTransform);
static_assertions::assert_eq_size!(Transform, [f32; 6]);
static_assertions::const_assert_eq!(memoffset::offset_of!(Transform, location), 0);
static_assertions::const_assert_eq!(memoffset::offset_of!(Transform, rotation), 12);
#[repr(C)]
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct Location {
pub x: f32,
pub y: f32,
pub z: f32,
}
impl Location {
#[inline]
pub fn new(x: f32, y: f32, z: f32) -> Self {
Self { x, y, z }
}
#[inline]
pub fn zero() -> Self {
Self {
x: 0.0,
y: 0.0,
z: 0.0,
}
}
pub fn distance(&self, other: &Location) -> f32 {
let dx = self.x - other.x;
let dy = self.y - other.y;
let dz = self.z - other.z;
(dx * dx + dy * dy + dz * dz).sqrt()
}
pub fn distance_squared(&self, other: &Location) -> f32 {
let dx = self.x - other.x;
let dy = self.y - other.y;
let dz = self.z - other.z;
dx * dx + dy * dy + dz * dz
}
pub fn distance_2d(&self, other: &Location) -> f32 {
let dx = self.x - other.x;
let dy = self.y - other.y;
(dx * dx + dy * dy).sqrt()
}
pub fn from_na_translation(from: &Translation3<f32>) -> Self {
Self {
x: from.x,
y: from.y,
z: from.z,
}
}
pub fn from_na_point(from: &Point3<f32>) -> Self {
Self {
x: from.x,
y: from.y,
z: from.z,
}
}
pub fn to_na_translation(&self) -> Translation3<f32> {
Translation3::new(self.x, self.y, self.z)
}
pub fn to_na_point(&self) -> Point3<f32> {
Point3::new(self.x, self.y, self.z)
}
#[inline(always)]
pub fn as_ffi(&self) -> &FfiLocation {
unsafe { std::mem::transmute(self) }
}
#[inline(always)]
pub fn from_ffi(ffi: FfiLocation) -> Self {
unsafe { std::mem::transmute(ffi) }
}
#[inline(always)]
pub fn into_ffi(self) -> FfiLocation {
unsafe { std::mem::transmute(self) }
}
}
static_assertions::assert_eq_size!(Location, FfiLocation);
static_assertions::assert_eq_align!(Location, FfiLocation);
static_assertions::assert_eq_size!(Location, [f32; 3]);
static_assertions::const_assert_eq!(memoffset::offset_of!(Location, x), 0);
static_assertions::const_assert_eq!(memoffset::offset_of!(Location, y), 4);
static_assertions::const_assert_eq!(memoffset::offset_of!(Location, z), 8);
use std::ops::{Add, AddAssign, Sub, SubAssign};
impl Add for Location {
type Output = Location;
fn add(self, rhs: Location) -> Location {
Location {
x: self.x + rhs.x,
y: self.y + rhs.y,
z: self.z + rhs.z,
}
}
}
impl AddAssign for Location {
fn add_assign(&mut self, rhs: Location) {
self.x += rhs.x;
self.y += rhs.y;
self.z += rhs.z;
}
}
impl Sub for Location {
type Output = Location;
fn sub(self, rhs: Location) -> Location {
Location {
x: self.x - rhs.x,
y: self.y - rhs.y,
z: self.z - rhs.z,
}
}
}
impl SubAssign for Location {
fn sub_assign(&mut self, rhs: Location) {
self.x -= rhs.x;
self.y -= rhs.y;
self.z -= rhs.z;
}
}
impl Add<&Location> for &Location {
type Output = Location;
fn add(self, rhs: &Location) -> Location {
Location {
x: self.x + rhs.x,
y: self.y + rhs.y,
z: self.z + rhs.z,
}
}
}
impl Sub<&Location> for &Location {
type Output = Location;
fn sub(self, rhs: &Location) -> Location {
Location {
x: self.x - rhs.x,
y: self.y - rhs.y,
z: self.z - rhs.z,
}
}
}
#[repr(C)]
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct Rotation {
pub pitch: f32,
pub yaw: f32,
pub roll: f32,
}
impl Rotation {
#[inline]
pub fn new(pitch: f32, yaw: f32, roll: f32) -> Self {
Self { pitch, yaw, roll }
}
#[inline]
pub fn identity() -> Self {
Self {
pitch: 0.0,
yaw: 0.0,
roll: 0.0,
}
}
pub fn forward_vector(&self) -> Vector3D {
unsafe {
let cpp_vec = self.as_ffi().GetForwardVector();
Vector3D::from_ffi((&cpp_vec as *const _ as *const FfiVector3D).read())
}
}
pub fn right_vector(&self) -> Vector3D {
unsafe {
let cpp_vec = self.as_ffi().GetRightVector();
Vector3D::from_ffi((&cpp_vec as *const _ as *const FfiVector3D).read())
}
}
pub fn up_vector(&self) -> Vector3D {
unsafe {
let cpp_vec = self.as_ffi().GetUpVector();
Vector3D::from_ffi((&cpp_vec as *const _ as *const FfiVector3D).read())
}
}
pub fn rotate_vector(&self, point: &Vector3D) -> Vector3D {
unsafe {
let cpp_input =
&*(point.as_ffi() as *const FfiVector3D as *const carla_sys::carla::geom::Vector3D);
let cpp_result = self.as_ffi().RotateVector(cpp_input);
Vector3D::from_ffi((&cpp_result as *const _ as *const FfiVector3D).read())
}
}
pub fn inverse_rotate_vector(&self, point: &Vector3D) -> Vector3D {
unsafe {
let cpp_input =
&*(point.as_ffi() as *const FfiVector3D as *const carla_sys::carla::geom::Vector3D);
let cpp_result = self.as_ffi().InverseRotateVector(cpp_input);
Vector3D::from_ffi((&cpp_result as *const _ as *const FfiVector3D).read())
}
}
pub fn get_normalized(&self) -> Rotation {
Rotation {
pitch: self.pitch.rem_euclid(360.0),
yaw: self.yaw.rem_euclid(360.0),
roll: self.roll.rem_euclid(360.0),
}
}
pub fn from_na(from: &UnitQuaternion<f32>) -> Self {
let (roll, pitch, yaw) = from.euler_angles();
Self {
pitch: pitch.to_degrees(),
yaw: yaw.to_degrees(),
roll: roll.to_degrees(),
}
}
pub fn to_na(&self) -> UnitQuaternion<f32> {
UnitQuaternion::from_euler_angles(
self.roll.to_radians(),
self.pitch.to_radians(),
self.yaw.to_radians(),
)
}
#[inline(always)]
pub fn as_ffi(&self) -> &FfiRotation {
unsafe { std::mem::transmute(self) }
}
#[inline(always)]
pub fn from_ffi(ffi: FfiRotation) -> Self {
unsafe { std::mem::transmute(ffi) }
}
#[inline(always)]
pub fn into_ffi(self) -> FfiRotation {
unsafe { std::mem::transmute(self) }
}
}
static_assertions::assert_eq_size!(Rotation, FfiRotation);
static_assertions::assert_eq_align!(Rotation, FfiRotation);
static_assertions::assert_eq_size!(Rotation, [f32; 3]);
static_assertions::const_assert_eq!(memoffset::offset_of!(Rotation, pitch), 0);
static_assertions::const_assert_eq!(memoffset::offset_of!(Rotation, yaw), 4);
static_assertions::const_assert_eq!(memoffset::offset_of!(Rotation, roll), 8);
#[repr(C)]
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct Vector3D {
pub x: f32,
pub y: f32,
pub z: f32,
}
impl Vector3D {
#[inline]
pub fn new(x: f32, y: f32, z: f32) -> Self {
Self { x, y, z }
}
#[inline]
pub fn zero() -> Self {
Self {
x: 0.0,
y: 0.0,
z: 0.0,
}
}
pub fn length(&self) -> f32 {
(self.x * self.x + self.y * self.y + self.z * self.z).sqrt()
}
#[inline]
pub fn norm(&self) -> f32 {
self.length()
}
pub fn length_squared(&self) -> f32 {
self.x * self.x + self.y * self.y + self.z * self.z
}
pub fn normalize(&self) -> Vector3D {
let len = self.length();
if len > 0.0 { *self / len } else { *self }
}
pub fn dot(&self, other: &Vector3D) -> f32 {
self.x * other.x + self.y * other.y + self.z * other.z
}
pub fn cross(&self, other: &Vector3D) -> Vector3D {
Vector3D {
x: self.y * other.z - self.z * other.y,
y: self.z * other.x - self.x * other.z,
z: self.x * other.y - self.y * other.x,
}
}
pub fn abs(&self) -> Vector3D {
Vector3D {
x: self.x.abs(),
y: self.y.abs(),
z: self.z.abs(),
}
}
#[inline]
pub fn make_unit_vector(&self) -> Vector3D {
self.normalize()
}
pub fn make_safe_unit_vector(&self) -> Vector3D {
let len = self.length();
if len > 1e-8 {
*self / len
} else {
Vector3D::zero()
}
}
pub fn squared_length_2d(&self) -> f32 {
self.x * self.x + self.y * self.y
}
pub fn length_2d(&self) -> f32 {
self.squared_length_2d().sqrt()
}
pub fn from_na(from: &Vector3<f32>) -> Self {
Self {
x: from.x,
y: from.y,
z: from.z,
}
}
pub fn to_na(&self) -> Vector3<f32> {
Vector3::new(self.x, self.y, self.z)
}
#[inline(always)]
pub fn as_ffi(&self) -> &FfiVector3D {
unsafe { std::mem::transmute(self) }
}
#[inline(always)]
pub fn from_ffi(ffi: FfiVector3D) -> Self {
unsafe { std::mem::transmute(ffi) }
}
#[inline(always)]
pub fn into_ffi(self) -> FfiVector3D {
unsafe { std::mem::transmute(self) }
}
}
static_assertions::assert_eq_size!(Vector3D, FfiVector3D);
static_assertions::assert_eq_align!(Vector3D, FfiVector3D);
static_assertions::assert_eq_size!(Vector3D, [f32; 3]);
static_assertions::const_assert_eq!(memoffset::offset_of!(Vector3D, x), 0);
static_assertions::const_assert_eq!(memoffset::offset_of!(Vector3D, y), 4);
static_assertions::const_assert_eq!(memoffset::offset_of!(Vector3D, z), 8);
use std::ops::{Div, DivAssign, Mul, MulAssign};
impl Add for Vector3D {
type Output = Vector3D;
fn add(self, rhs: Vector3D) -> Vector3D {
Vector3D {
x: self.x + rhs.x,
y: self.y + rhs.y,
z: self.z + rhs.z,
}
}
}
impl AddAssign for Vector3D {
fn add_assign(&mut self, rhs: Vector3D) {
self.x += rhs.x;
self.y += rhs.y;
self.z += rhs.z;
}
}
impl Sub for Vector3D {
type Output = Vector3D;
fn sub(self, rhs: Vector3D) -> Vector3D {
Vector3D {
x: self.x - rhs.x,
y: self.y - rhs.y,
z: self.z - rhs.z,
}
}
}
impl SubAssign for Vector3D {
fn sub_assign(&mut self, rhs: Vector3D) {
self.x -= rhs.x;
self.y -= rhs.y;
self.z -= rhs.z;
}
}
impl Sub<f32> for Vector3D {
type Output = Vector3D;
fn sub(self, scalar: f32) -> Vector3D {
Vector3D {
x: self.x - scalar,
y: self.y - scalar,
z: self.z - scalar,
}
}
}
impl SubAssign<f32> for Vector3D {
fn sub_assign(&mut self, scalar: f32) {
self.x -= scalar;
self.y -= scalar;
self.z -= scalar;
}
}
impl Mul<f32> for Vector3D {
type Output = Vector3D;
fn mul(self, scalar: f32) -> Vector3D {
Vector3D {
x: self.x * scalar,
y: self.y * scalar,
z: self.z * scalar,
}
}
}
impl Mul<Vector3D> for f32 {
type Output = Vector3D;
fn mul(self, vector: Vector3D) -> Vector3D {
vector * self
}
}
impl MulAssign<f32> for Vector3D {
fn mul_assign(&mut self, scalar: f32) {
self.x *= scalar;
self.y *= scalar;
self.z *= scalar;
}
}
impl Div<f32> for Vector3D {
type Output = Vector3D;
fn div(self, scalar: f32) -> Vector3D {
Vector3D {
x: self.x / scalar,
y: self.y / scalar,
z: self.z / scalar,
}
}
}
impl Div<Vector3D> for f32 {
type Output = Vector3D;
fn div(self, vector: Vector3D) -> Vector3D {
Vector3D {
x: self / vector.x,
y: self / vector.y,
z: self / vector.z,
}
}
}
impl DivAssign<f32> for Vector3D {
fn div_assign(&mut self, scalar: f32) {
self.x /= scalar;
self.y /= scalar;
self.z /= scalar;
}
}
impl Add<&Vector3D> for &Vector3D {
type Output = Vector3D;
fn add(self, rhs: &Vector3D) -> Vector3D {
Vector3D {
x: self.x + rhs.x,
y: self.y + rhs.y,
z: self.z + rhs.z,
}
}
}
impl Sub<&Vector3D> for &Vector3D {
type Output = Vector3D;
fn sub(self, rhs: &Vector3D) -> Vector3D {
Vector3D {
x: self.x - rhs.x,
y: self.y - rhs.y,
z: self.z - rhs.z,
}
}
}
impl Mul<f32> for &Vector3D {
type Output = Vector3D;
fn mul(self, scalar: f32) -> Vector3D {
Vector3D {
x: self.x * scalar,
y: self.y * scalar,
z: self.z * scalar,
}
}
}
impl Div<f32> for &Vector3D {
type Output = Vector3D;
fn div(self, scalar: f32) -> Vector3D {
Vector3D {
x: self.x / scalar,
y: self.y / scalar,
z: self.z / scalar,
}
}
}
#[repr(C)]
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct Vector2D {
pub x: f32,
pub y: f32,
}
impl Vector2D {
#[inline]
pub fn new(x: f32, y: f32) -> Self {
Self { x, y }
}
#[inline]
pub fn zero() -> Self {
Self { x: 0.0, y: 0.0 }
}
pub fn length(&self) -> f32 {
(self.x * self.x + self.y * self.y).sqrt()
}
pub fn length_squared(&self) -> f32 {
self.x * self.x + self.y * self.y
}
pub fn normalize(&self) -> Vector2D {
let len = self.length();
if len > 0.0 { *self / len } else { *self }
}
pub fn dot(&self, other: &Vector2D) -> f32 {
self.x * other.x + self.y * other.y
}
pub fn from_na(from: &Vector2<f32>) -> Self {
Self {
x: from.x,
y: from.y,
}
}
pub fn to_na(&self) -> Vector2<f32> {
Vector2::new(self.x, self.y)
}
#[inline(always)]
pub fn as_ffi(&self) -> &FfiVector2D {
unsafe { std::mem::transmute(self) }
}
#[inline(always)]
pub fn from_ffi(ffi: FfiVector2D) -> Self {
unsafe { std::mem::transmute(ffi) }
}
#[inline(always)]
pub fn into_ffi(self) -> FfiVector2D {
unsafe { std::mem::transmute(self) }
}
}
static_assertions::assert_eq_size!(Vector2D, FfiVector2D);
static_assertions::assert_eq_align!(Vector2D, FfiVector2D);
static_assertions::assert_eq_size!(Vector2D, [f32; 2]);
static_assertions::const_assert_eq!(memoffset::offset_of!(Vector2D, x), 0);
static_assertions::const_assert_eq!(memoffset::offset_of!(Vector2D, y), 4);
impl Add for Vector2D {
type Output = Vector2D;
fn add(self, rhs: Vector2D) -> Vector2D {
Vector2D {
x: self.x + rhs.x,
y: self.y + rhs.y,
}
}
}
impl AddAssign for Vector2D {
fn add_assign(&mut self, rhs: Vector2D) {
self.x += rhs.x;
self.y += rhs.y;
}
}
impl Sub for Vector2D {
type Output = Vector2D;
fn sub(self, rhs: Vector2D) -> Vector2D {
Vector2D {
x: self.x - rhs.x,
y: self.y - rhs.y,
}
}
}
impl SubAssign for Vector2D {
fn sub_assign(&mut self, rhs: Vector2D) {
self.x -= rhs.x;
self.y -= rhs.y;
}
}
impl Sub<f32> for Vector2D {
type Output = Vector2D;
fn sub(self, scalar: f32) -> Vector2D {
Vector2D {
x: self.x - scalar,
y: self.y - scalar,
}
}
}
impl SubAssign<f32> for Vector2D {
fn sub_assign(&mut self, scalar: f32) {
self.x -= scalar;
self.y -= scalar;
}
}
impl Mul<f32> for Vector2D {
type Output = Vector2D;
fn mul(self, scalar: f32) -> Vector2D {
Vector2D {
x: self.x * scalar,
y: self.y * scalar,
}
}
}
impl Mul<Vector2D> for f32 {
type Output = Vector2D;
fn mul(self, vector: Vector2D) -> Vector2D {
vector * self
}
}
impl MulAssign<f32> for Vector2D {
fn mul_assign(&mut self, scalar: f32) {
self.x *= scalar;
self.y *= scalar;
}
}
impl Div<f32> for Vector2D {
type Output = Vector2D;
fn div(self, scalar: f32) -> Vector2D {
Vector2D {
x: self.x / scalar,
y: self.y / scalar,
}
}
}
impl Div<Vector2D> for f32 {
type Output = Vector2D;
fn div(self, vector: Vector2D) -> Vector2D {
Vector2D {
x: self / vector.x,
y: self / vector.y,
}
}
}
impl DivAssign<f32> for Vector2D {
fn div_assign(&mut self, scalar: f32) {
self.x /= scalar;
self.y /= scalar;
}
}
impl Add<&Vector2D> for &Vector2D {
type Output = Vector2D;
fn add(self, rhs: &Vector2D) -> Vector2D {
Vector2D {
x: self.x + rhs.x,
y: self.y + rhs.y,
}
}
}
impl Sub<&Vector2D> for &Vector2D {
type Output = Vector2D;
fn sub(self, rhs: &Vector2D) -> Vector2D {
Vector2D {
x: self.x - rhs.x,
y: self.y - rhs.y,
}
}
}
impl Mul<f32> for &Vector2D {
type Output = Vector2D;
fn mul(self, scalar: f32) -> Vector2D {
Vector2D {
x: self.x * scalar,
y: self.y * scalar,
}
}
}
impl Div<f32> for &Vector2D {
type Output = Vector2D;
fn div(self, scalar: f32) -> Vector2D {
Vector2D {
x: self.x / scalar,
y: self.y / scalar,
}
}
}
#[repr(C)]
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct GeoLocation {
pub latitude: f64,
pub longitude: f64,
pub altitude: f64,
}
impl GeoLocation {
#[inline]
pub fn new(latitude: f64, longitude: f64, altitude: f64) -> Self {
Self {
latitude,
longitude,
altitude,
}
}
#[inline(always)]
pub fn as_ffi(&self) -> &FfiGeoLocation {
unsafe { std::mem::transmute(self) }
}
#[inline(always)]
pub fn from_ffi(ffi: FfiGeoLocation) -> Self {
unsafe { std::mem::transmute(ffi) }
}
#[inline(always)]
pub fn into_ffi(self) -> FfiGeoLocation {
unsafe { std::mem::transmute(self) }
}
}
static_assertions::assert_eq_size!(GeoLocation, FfiGeoLocation);
static_assertions::assert_eq_align!(GeoLocation, FfiGeoLocation);
static_assertions::assert_eq_size!(GeoLocation, [f64; 3]);
static_assertions::const_assert_eq!(memoffset::offset_of!(GeoLocation, latitude), 0);
static_assertions::const_assert_eq!(memoffset::offset_of!(GeoLocation, longitude), 8);
static_assertions::const_assert_eq!(memoffset::offset_of!(GeoLocation, altitude), 16);
impl std::ops::Mul for Transform {
type Output = Transform;
fn mul(self, rhs: Transform) -> Transform {
self.compose(&rhs)
}
}
impl std::ops::Mul<&Transform> for Transform {
type Output = Transform;
fn mul(self, rhs: &Transform) -> Transform {
self.compose(rhs)
}
}
impl std::ops::Mul<Transform> for &Transform {
type Output = Transform;
fn mul(self, rhs: Transform) -> Transform {
self.compose(&rhs)
}
}
impl std::ops::Mul<&Transform> for &Transform {
type Output = Transform;
fn mul(self, rhs: &Transform) -> Transform {
self.compose(rhs)
}
}
#[derive(Debug, Clone)]
pub struct BoundingBox {
pub transform: Transform,
pub extent: Vector3D,
}
impl BoundingBox {
pub fn new(location: Location, extent: Vector3D) -> Self {
Self {
transform: Transform {
location,
rotation: Rotation::identity(),
},
extent,
}
}
pub fn to_native(&self) -> NativeBoundingBox {
let Self { transform, extent } = self;
NativeBoundingBox {
location: transform.location.into_ffi(),
rotation: transform.rotation.into_ffi(),
extent: extent.into_ffi(),
#[cfg(carla_version_0916)]
actor_id: 0,
}
}
pub fn from_native(bbox: &NativeBoundingBox) -> Self {
#[cfg(carla_version_0916)]
let NativeBoundingBox {
location,
extent,
rotation,
actor_id: _,
} = bbox;
#[cfg(not(carla_version_0916))]
let NativeBoundingBox {
location,
extent,
rotation,
} = bbox;
Self {
transform: Transform {
location: Location::from_ffi(location.clone()),
rotation: Rotation::from_ffi(rotation.clone()),
},
extent: Vector3D::from_ffi(extent.clone()),
}
}
pub fn contains(
&self,
in_world_point: &Location,
in_bbox_to_world_transform: &Transform,
) -> bool {
self.to_native()
.Contains(in_world_point.as_ffi(), in_bbox_to_world_transform.as_ffi())
}
pub fn local_vertices(&self) -> Vec<Location> {
self.to_native()
.GetLocalVertices()
.iter()
.map(|loc| Location::from_ffi(loc.clone()))
.collect()
}
pub fn world_vertices(&self, transform: &Transform) -> Vec<Location> {
self.to_native()
.GetWorldVertices(transform.as_ffi())
.iter()
.map(|loc| Location::from_ffi(loc.clone()))
.collect()
}
}
#[cfg(test)]
mod tests {
use super::*;
fn assert_approx_eq(a: f32, b: f32, epsilon: f32) {
assert!(
(a - b).abs() < epsilon,
"assertion failed: `{} ≈ {}` (diff: {})",
a,
b,
(a - b).abs()
);
}
#[test]
fn test_transform_size() {
assert_eq!(
std::mem::size_of::<Transform>(),
std::mem::size_of::<FfiTransform>()
);
assert_eq!(std::mem::size_of::<Transform>(), 24); }
#[test]
fn test_transform_alignment() {
assert_eq!(
std::mem::align_of::<Transform>(),
std::mem::align_of::<FfiTransform>()
);
assert_eq!(std::mem::align_of::<Transform>(), 4); }
#[test]
fn test_transform_round_trip_conversion() {
let ffi = FfiTransform {
location: FfiLocation {
x: 10.5,
y: 20.3,
z: 5.7,
},
rotation: FfiRotation {
pitch: 15.0,
yaw: 45.0,
roll: -10.0,
},
};
let ffi_copy = ffi.clone();
let transform = Transform::from_ffi(ffi);
let ffi_back = transform.into_ffi();
assert_eq!(ffi_back.location.x, ffi_copy.location.x);
assert_eq!(ffi_back.location.y, ffi_copy.location.y);
assert_eq!(ffi_back.location.z, ffi_copy.location.z);
assert_eq!(ffi_back.rotation.pitch, ffi_copy.rotation.pitch);
assert_eq!(ffi_back.rotation.yaw, ffi_copy.rotation.yaw);
assert_eq!(ffi_back.rotation.roll, ffi_copy.rotation.roll);
}
#[test]
fn test_transform_accessors() {
let transform = Transform {
location: Location {
x: 1.0,
y: 2.0,
z: 3.0,
},
rotation: Rotation {
pitch: 10.0,
yaw: 20.0,
roll: 30.0,
},
};
assert_eq!(transform.location.x, 1.0);
assert_eq!(transform.location.y, 2.0);
assert_eq!(transform.location.z, 3.0);
assert_eq!(transform.rotation.pitch, 10.0);
assert_eq!(transform.rotation.yaw, 20.0);
assert_eq!(transform.rotation.roll, 30.0);
let mut transform_mut = transform;
transform_mut.location.x = 5.0;
transform_mut.rotation.yaw = 45.0;
assert_eq!(transform_mut.location.x, 5.0);
assert_eq!(transform_mut.rotation.yaw, 45.0);
}
#[test]
fn test_transform_as_ffi_reference() {
let transform = Transform {
location: Location {
x: 7.0,
y: 8.0,
z: 9.0,
},
rotation: Rotation {
pitch: 5.0,
yaw: 15.0,
roll: 25.0,
},
};
let ffi_ref: &FfiTransform = transform.as_ffi();
assert_eq!(ffi_ref.location.x, 7.0);
assert_eq!(ffi_ref.location.y, 8.0);
assert_eq!(ffi_ref.location.z, 9.0);
assert_eq!(ffi_ref.rotation.pitch, 5.0);
assert_eq!(ffi_ref.rotation.yaw, 15.0);
assert_eq!(ffi_ref.rotation.roll, 25.0);
}
#[test]
fn test_transform_mul_identity() {
let transform = Transform {
location: Location {
x: 10.0,
y: 5.0,
z: 2.0,
},
rotation: Rotation {
pitch: 10.0,
yaw: 20.0,
roll: 5.0,
},
};
let identity = Transform {
location: Location {
x: 0.0,
y: 0.0,
z: 0.0,
},
rotation: Rotation {
pitch: 0.0,
yaw: 0.0,
roll: 0.0,
},
};
let result = &transform * &identity;
assert_approx_eq(result.location.x, transform.location.x, 0.001);
assert_approx_eq(result.location.y, transform.location.y, 0.001);
assert_approx_eq(result.location.z, transform.location.z, 0.001);
assert_approx_eq(result.rotation.pitch, transform.rotation.pitch, 0.01);
assert_approx_eq(result.rotation.yaw, transform.rotation.yaw, 0.01);
assert_approx_eq(result.rotation.roll, transform.rotation.roll, 0.01);
}
#[test]
fn test_transform_mul_translation_only() {
let t1 = Transform {
location: Location {
x: 10.0,
y: 5.0,
z: 2.0,
},
rotation: Rotation {
pitch: 0.0,
yaw: 0.0,
roll: 0.0,
},
};
let t2 = Transform {
location: Location {
x: 3.0,
y: 2.0,
z: 1.0,
},
rotation: Rotation {
pitch: 0.0,
yaw: 0.0,
roll: 0.0,
},
};
let result = t1 * t2;
assert_approx_eq(result.location.x, 13.0, 0.001);
assert_approx_eq(result.location.y, 7.0, 0.001);
assert_approx_eq(result.location.z, 3.0, 0.001);
assert_approx_eq(result.rotation.pitch, 0.0, 0.01);
assert_approx_eq(result.rotation.yaw, 0.0, 0.01);
assert_approx_eq(result.rotation.roll, 0.0, 0.01);
}
#[test]
fn test_transform_mul_rotation_90deg_yaw() {
let parent = Transform {
location: Location {
x: 0.0,
y: 0.0,
z: 0.0,
},
rotation: Rotation {
pitch: 0.0,
yaw: 90.0,
roll: 0.0,
},
};
let child = Transform {
location: Location {
x: 5.0,
y: 0.0,
z: 0.0,
},
rotation: Rotation {
pitch: 0.0,
yaw: 0.0,
roll: 0.0,
},
};
let result = parent * child;
assert_approx_eq(result.location.x, 0.0, 0.01);
assert_approx_eq(result.location.y, 5.0, 0.01);
assert_approx_eq(result.location.z, 0.0, 0.01);
assert_approx_eq(result.rotation.yaw, 90.0, 0.1);
}
#[test]
fn test_transform_mul_rotation_180deg_yaw() {
let parent = Transform {
location: Location {
x: 0.0,
y: 0.0,
z: 0.0,
},
rotation: Rotation {
pitch: 0.0,
yaw: 180.0,
roll: 0.0,
},
};
let child = Transform {
location: Location {
x: 5.0,
y: 0.0,
z: 0.0,
},
rotation: Rotation {
pitch: 0.0,
yaw: 0.0,
roll: 0.0,
},
};
let result = parent * child;
assert_approx_eq(result.location.x, -5.0, 0.01);
assert_approx_eq(result.location.y, 0.0, 0.01);
assert_approx_eq(result.location.z, 0.0, 0.01);
assert_approx_eq(result.rotation.yaw.abs(), 180.0, 0.1);
}
#[test]
fn test_transform_mul_combined_translation_rotation() {
let vehicle = Transform {
location: Location {
x: 10.0,
y: 0.0,
z: 0.0,
},
rotation: Rotation {
pitch: 0.0,
yaw: 0.0,
roll: 0.0,
},
};
let sensor_offset = Transform {
location: Location {
x: 2.0,
y: 1.0,
z: 0.5,
},
rotation: Rotation {
pitch: 0.0,
yaw: 0.0,
roll: 0.0,
},
};
let sensor_world = vehicle * sensor_offset;
assert_approx_eq(sensor_world.location.x, 12.0, 0.001);
assert_approx_eq(sensor_world.location.y, 1.0, 0.001);
assert_approx_eq(sensor_world.location.z, 0.5, 0.001);
}
#[test]
fn test_transform_mul_sensor_mounting_rotated() {
let vehicle = Transform {
location: Location {
x: 10.0,
y: 0.0,
z: 0.0,
},
rotation: Rotation {
pitch: 0.0,
yaw: 90.0,
roll: 0.0,
},
};
let sensor_offset = Transform {
location: Location {
x: 2.0,
y: 0.0,
z: 0.0,
},
rotation: Rotation {
pitch: 0.0,
yaw: 0.0,
roll: 0.0,
},
};
let sensor_world = vehicle * sensor_offset;
assert_approx_eq(sensor_world.location.x, 10.0, 0.01);
assert_approx_eq(sensor_world.location.y, 2.0, 0.01);
assert_approx_eq(sensor_world.location.z, 0.0, 0.01);
assert_approx_eq(sensor_world.rotation.yaw, 90.0, 0.1);
}
#[test]
fn test_transform_mul_associativity() {
let t1 = Transform {
location: Location {
x: 1.0,
y: 2.0,
z: 3.0,
},
rotation: Rotation {
pitch: 10.0,
yaw: 20.0,
roll: 5.0,
},
};
let t2 = Transform {
location: Location {
x: 4.0,
y: 5.0,
z: 6.0,
},
rotation: Rotation {
pitch: 15.0,
yaw: 30.0,
roll: 10.0,
},
};
let t3 = Transform {
location: Location {
x: 7.0,
y: 8.0,
z: 9.0,
},
rotation: Rotation {
pitch: 5.0,
yaw: 10.0,
roll: 15.0,
},
};
let left = (&t1 * &t2) * &t3;
let right = &t1 * (&t2 * &t3);
assert_approx_eq(left.location.x, right.location.x, 0.001);
assert_approx_eq(left.location.y, right.location.y, 0.001);
assert_approx_eq(left.location.z, right.location.z, 0.001);
assert_approx_eq(left.rotation.pitch, right.rotation.pitch, 0.01);
assert_approx_eq(left.rotation.yaw, right.rotation.yaw, 0.01);
assert_approx_eq(left.rotation.roll, right.rotation.roll, 0.01);
}
#[test]
fn test_transform_mul_reference_variants() {
let t1 = Transform {
location: Location {
x: 10.0,
y: 5.0,
z: 2.0,
},
rotation: Rotation {
pitch: 0.0,
yaw: 0.0,
roll: 0.0,
},
};
let t2 = Transform {
location: Location {
x: 3.0,
y: 2.0,
z: 1.0,
},
rotation: Rotation {
pitch: 0.0,
yaw: 0.0,
roll: 0.0,
},
};
let result = &t1 * &t2;
assert_eq!(t1.location.x, 10.0);
assert_eq!(t2.location.x, 3.0);
assert_approx_eq(result.location.x, 13.0, 0.001);
assert_approx_eq(result.location.y, 7.0, 0.001);
assert_approx_eq(result.location.z, 3.0, 0.001);
}
#[test]
fn test_transform_mul_rotation_composition() {
let rot1 = Transform {
location: Location {
x: 0.0,
y: 0.0,
z: 0.0,
},
rotation: Rotation {
pitch: 0.0,
yaw: 45.0,
roll: 0.0,
},
};
let rot2 = Transform {
location: Location {
x: 0.0,
y: 0.0,
z: 0.0,
},
rotation: Rotation {
pitch: 0.0,
yaw: 45.0,
roll: 0.0,
},
};
let result = rot1 * rot2;
assert_approx_eq(result.rotation.yaw, 90.0, 0.1);
assert_approx_eq(result.rotation.pitch, 0.0, 0.1);
assert_approx_eq(result.rotation.roll, 0.0, 0.1);
}
#[test]
fn test_transform_point_identity() {
let t = Transform {
location: Location::new(10.0, 20.0, 30.0),
rotation: Rotation::identity(),
};
let p = Location::new(1.0, 2.0, 3.0);
let result = t.transform_point(&p);
assert_approx_eq(result.x, 11.0, 0.01);
assert_approx_eq(result.y, 22.0, 0.01);
assert_approx_eq(result.z, 33.0, 0.01);
}
#[test]
fn test_inverse_transform_point_roundtrip() {
let t = Transform {
location: Location::new(5.0, 10.0, 15.0),
rotation: Rotation::new(0.0, 90.0, 0.0),
};
let p = Location::new(1.0, 2.0, 3.0);
let world = t.transform_point(&p);
let back = t.inverse_transform_point(&world);
assert_approx_eq(back.x, p.x, 0.01);
assert_approx_eq(back.y, p.y, 0.01);
assert_approx_eq(back.z, p.z, 0.01);
}
#[test]
fn test_get_matrix_identity() {
let t = Transform {
location: Location::zero(),
rotation: Rotation::identity(),
};
let m = t.get_matrix();
assert_approx_eq(m[0][0], 1.0, 0.01);
assert_approx_eq(m[1][1], 1.0, 0.01);
assert_approx_eq(m[2][2], 1.0, 0.01);
assert_approx_eq(m[3][3], 1.0, 0.01);
}
#[test]
fn test_vector3d_make_safe_unit_vector() {
let v = Vector3D::new(3.0, 4.0, 0.0);
let unit = v.make_safe_unit_vector();
assert_approx_eq(unit.length(), 1.0, 0.001);
let zero = Vector3D::zero();
let safe = zero.make_safe_unit_vector();
assert_approx_eq(safe.length(), 0.0, 0.001);
}
#[test]
fn test_vector3d_length_2d() {
let v = Vector3D::new(3.0, 4.0, 100.0);
assert_approx_eq(v.length_2d(), 5.0, 0.001);
assert_approx_eq(v.squared_length_2d(), 25.0, 0.001);
}
#[test]
fn test_rotation_get_normalized() {
let r = Rotation::new(-10.0, 370.0, -720.0);
let n = r.get_normalized();
assert_approx_eq(n.pitch, 350.0, 0.001);
assert_approx_eq(n.yaw, 10.0, 0.001);
assert_approx_eq(n.roll, 0.0, 0.001);
}
}
assert_impl_all!(Location: Send, Sync);
assert_impl_all!(Rotation: Send, Sync);
assert_impl_all!(Vector2D: Send, Sync);
assert_impl_all!(Vector3D: Send, Sync);
assert_impl_all!(GeoLocation: Send, Sync);
assert_impl_all!(Transform: Send, Sync);
assert_impl_all!(NativeBoundingBox: Send, Sync);