#[derive(Debug, Clone, PartialEq)]
pub struct Point3D {
pub x: f64,
pub y: f64,
pub z: f64,
pub intensity: u16,
pub return_number: u8,
pub number_of_returns: u8,
pub classification: u8,
pub scan_angle_rank: i8,
pub user_data: u8,
pub point_source_id: u16,
pub gps_time: Option<f64>,
pub red: Option<u16>,
pub green: Option<u16>,
pub blue: Option<u16>,
}
impl Point3D {
#[inline]
pub fn new(x: f64, y: f64, z: f64) -> Self {
Self {
x,
y,
z,
intensity: 0,
return_number: 1,
number_of_returns: 1,
classification: 0,
scan_angle_rank: 0,
user_data: 0,
point_source_id: 0,
gps_time: None,
red: None,
green: None,
blue: None,
}
}
#[inline]
pub fn with_intensity(mut self, intensity: u16) -> Self {
self.intensity = intensity;
self
}
#[inline]
pub fn with_classification(mut self, class: u8) -> Self {
self.classification = class;
self
}
#[inline]
pub fn with_color(mut self, r: u16, g: u16, b: u16) -> Self {
self.red = Some(r);
self.green = Some(g);
self.blue = Some(b);
self
}
#[inline]
pub fn with_gps_time(mut self, t: f64) -> Self {
self.gps_time = Some(t);
self
}
#[inline]
pub fn distance_to(&self, other: &Point3D) -> f64 {
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()
}
#[inline]
pub fn distance_2d(&self, other: &Point3D) -> f64 {
let dx = self.x - other.x;
let dy = self.y - other.y;
(dx * dx + dy * dy).sqrt()
}
pub fn classification_name(&self) -> &'static str {
match self.classification {
0 => "Created, Never Classified",
1 => "Unclassified",
2 => "Ground",
3 => "Low Vegetation",
4 => "Medium Vegetation",
5 => "High Vegetation",
6 => "Building",
7 => "Low Point (Noise)",
8 => "Reserved",
9 => "Water",
10 => "Rail",
11 => "Road Surface",
12 => "Reserved",
13 => "Wire - Guard (Shield)",
14 => "Wire - Conductor (Phase)",
15 => "Transmission Tower",
16 => "Wire-Structure Connector (Insulator)",
17 => "Bridge Deck",
18 => "High Noise",
_ => "Reserved/Unknown",
}
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct BoundingBox3D {
pub min_x: f64,
pub min_y: f64,
pub min_z: f64,
pub max_x: f64,
pub max_y: f64,
pub max_z: f64,
}
impl BoundingBox3D {
pub fn new(
min_x: f64,
min_y: f64,
min_z: f64,
max_x: f64,
max_y: f64,
max_z: f64,
) -> Option<Self> {
if min_x > max_x || min_y > max_y || min_z > max_z {
return None;
}
Some(Self {
min_x,
min_y,
min_z,
max_x,
max_y,
max_z,
})
}
pub fn from_points(points: &[Point3D]) -> Option<Self> {
let first = points.first()?;
let mut min_x = first.x;
let mut min_y = first.y;
let mut min_z = first.z;
let mut max_x = first.x;
let mut max_y = first.y;
let mut max_z = first.z;
for p in points.iter().skip(1) {
if p.x < min_x {
min_x = p.x;
}
if p.y < min_y {
min_y = p.y;
}
if p.z < min_z {
min_z = p.z;
}
if p.x > max_x {
max_x = p.x;
}
if p.y > max_y {
max_y = p.y;
}
if p.z > max_z {
max_z = p.z;
}
}
Some(Self {
min_x,
min_y,
min_z,
max_x,
max_y,
max_z,
})
}
#[inline]
pub fn contains(&self, p: &Point3D) -> bool {
p.x >= self.min_x
&& p.x <= self.max_x
&& p.y >= self.min_y
&& p.y <= self.max_y
&& p.z >= self.min_z
&& p.z <= self.max_z
}
#[inline]
pub fn intersects_2d(&self, other: &BoundingBox3D) -> bool {
self.min_x <= other.max_x
&& self.max_x >= other.min_x
&& self.min_y <= other.max_y
&& self.max_y >= other.min_y
}
#[inline]
pub fn intersects_3d(&self, other: &BoundingBox3D) -> bool {
self.intersects_2d(other) && self.min_z <= other.max_z && self.max_z >= other.min_z
}
#[inline]
pub fn center(&self) -> (f64, f64, f64) {
(
(self.min_x + self.max_x) * 0.5,
(self.min_y + self.max_y) * 0.5,
(self.min_z + self.max_z) * 0.5,
)
}
#[inline]
pub fn diagonal(&self) -> f64 {
let dx = self.max_x - self.min_x;
let dy = self.max_y - self.min_y;
let dz = self.max_z - self.min_z;
(dx * dx + dy * dy + dz * dz).sqrt()
}
#[inline]
pub fn expand_by(&self, delta: f64) -> Self {
Self {
min_x: self.min_x - delta,
min_y: self.min_y - delta,
min_z: self.min_z - delta,
max_x: self.max_x + delta,
max_y: self.max_y + delta,
max_z: self.max_z + delta,
}
}
#[inline]
pub fn volume(&self) -> f64 {
(self.max_x - self.min_x) * (self.max_y - self.min_y) * (self.max_z - self.min_z)
}
pub fn split_octants(&self) -> [BoundingBox3D; 8] {
let (cx, cy, cz) = self.center();
[
BoundingBox3D {
min_x: self.min_x,
min_y: self.min_y,
min_z: self.min_z,
max_x: cx,
max_y: cy,
max_z: cz,
},
BoundingBox3D {
min_x: self.min_x,
min_y: self.min_y,
min_z: cz,
max_x: cx,
max_y: cy,
max_z: self.max_z,
},
BoundingBox3D {
min_x: self.min_x,
min_y: cy,
min_z: self.min_z,
max_x: cx,
max_y: self.max_y,
max_z: cz,
},
BoundingBox3D {
min_x: self.min_x,
min_y: cy,
min_z: cz,
max_x: cx,
max_y: self.max_y,
max_z: self.max_z,
},
BoundingBox3D {
min_x: cx,
min_y: self.min_y,
min_z: self.min_z,
max_x: self.max_x,
max_y: cy,
max_z: cz,
},
BoundingBox3D {
min_x: cx,
min_y: self.min_y,
min_z: cz,
max_x: self.max_x,
max_y: cy,
max_z: self.max_z,
},
BoundingBox3D {
min_x: cx,
min_y: cy,
min_z: self.min_z,
max_x: self.max_x,
max_y: self.max_y,
max_z: cz,
},
BoundingBox3D {
min_x: cx,
min_y: cy,
min_z: cz,
max_x: self.max_x,
max_y: self.max_y,
max_z: self.max_z,
},
]
}
}