use anyhow::bail;
use chrono::{DateTime, Utc};
use nalgebra::{Matrix4, Vector4};
use std::fs::File;
use std::io::{BufRead, BufReader, Write};
pub struct PointCloud {
points: Vec<[f32; 3]>,
no_of_points: usize,
global_timestamp: DateTime<Utc>,
filename: Option<String>,
}
impl PointCloud {
pub fn create_from_list(pnts: Vec<[f32; 3]>,) -> Self {
let no_of_points = pnts.len();
Self {
points: pnts,
no_of_points,
global_timestamp: Utc::now(),
filename: None,
}
}
pub fn create_from_iter(
vertices: Vec<[f32; 3]>
) -> Result<Self, anyhow::Error> {
let mut points: Vec<[f32; 3]> = vec![];
let mut no_of_points = 0;
for vertex in vertices.iter() {
if vertex[2] == 0.0 || vertex[2] > 2.0 {
continue;
}
points.push([vertex[0], vertex[1], vertex[2]]);
no_of_points += 1;
}
Ok(Self {
points,
no_of_points,
global_timestamp: Utc::now(),
filename: None,
})
}
pub fn create_from_file(filepath: String) -> Result<Self, anyhow::Error> {
let fpath;
if let Some(fp) = filepath.strip_suffix(".txt") {
fpath = fp.to_string();
} else {
bail!("Failed to create filepath")
}
let file = File::open(filepath.clone())?;
let mut line_reader = BufReader::new(file);
let timestamp_str: &mut String = &mut "".to_string();
line_reader.read_line(timestamp_str)?;
let global_timestamp: DateTime<Utc> = timestamp_str.parse()?;
let mut points = vec![];
for line in line_reader.lines() {
let mut pnt: [f32; 3] = [f32::NAN, f32::NAN, f32::NAN];
for (cnt, token) in line?.split(",").enumerate() {
pnt[cnt] = token.parse()?;
}
points.push(pnt);
}
let no_of_points = points.len();
Ok(Self {
points,
no_of_points,
global_timestamp,
filename: Some(fpath),
})
}
pub fn points(&self) -> Vec<[f32; 3]> {
self.points.clone()
}
pub fn consume_points(self) -> Vec<[f32; 3]> {
self.points
}
pub fn timestamp(&self) -> DateTime<Utc> {
self.global_timestamp
}
pub fn size(&self) -> usize {
self.no_of_points
}
pub fn filename(&self) -> Option<String> {
self.filename.clone()
}
pub fn print_points(&self) {
for pnt in self.points.iter() {
println!("{:?}", pnt);
}
}
pub fn get_bounds(&self) -> [f32; 4] {
let mut x_min: f32 = 9999.0;
let mut y_min: f32 = 9999.0;
let mut x_max: f32 = -9999.0;
let mut y_max: f32 = -9999.0;
for pnt in self.points.iter() {
if pnt[0] < x_min {
x_min = pnt[0];
} else if pnt[0] > x_max {
x_max = pnt[0];
}
if pnt[1] < y_min {
y_min = pnt[1];
} else if pnt[1] > y_max {
y_max = pnt[1];
}
}
[x_min, x_max, y_min, y_max]
}
pub fn get_xy_area(&self) -> f32 {
let bounds = self.get_bounds();
((bounds[1] - bounds[0]) * (bounds[3] - bounds[2])).abs()
}
pub fn rotate(&mut self, yaw: f32, pitch: f32, roll: f32) {
let yaw_c = yaw.cos();
let yaw_s = yaw.sin();
let pitch_c = pitch.cos();
let pitch_s = pitch.sin();
let roll_c = roll.cos();
let roll_s = roll.sin();
let x_rot = [
yaw_c * pitch_c,
((yaw_c * pitch_s * roll_s) - (yaw_s * roll_c)),
((yaw_c * pitch_s * roll_c) + (yaw_s * roll_s)),
];
let y_rot = [
yaw_s * pitch_c,
(yaw_s * pitch_s * roll_s) + (yaw_c * roll_c),
(yaw_s * pitch_s * roll_c) - (yaw_c * roll_s),
];
let z_rot = [-pitch_s, pitch_c * roll_s, pitch_c * roll_c];
for pnt in self.points.iter_mut() {
let og_x = pnt[0];
let og_y = pnt[1];
let og_z = pnt[2];
pnt[0] = (x_rot[0] * og_x) + (x_rot[1] * og_y) + (x_rot[2] * og_z);
pnt[1] = (y_rot[0] * og_x) + (y_rot[1] * og_y) + (y_rot[2] * og_z);
pnt[2] = (z_rot[0] * og_x) + (z_rot[1] * og_y) + (z_rot[2] * og_z);
}
}
pub fn translate(&mut self, x: f32, y: f32, z: f32) {
for pnt in self.points.iter_mut() {
pnt[0] += x;
pnt[1] += y;
pnt[2] += z;
}
}
pub fn transform_with(&mut self, tmat: &Matrix4<f32>) {
for pnt in self.points.iter_mut() {
let temp_pnt = Vector4::new(pnt[0], pnt[1], pnt[2], 1.0);
let temp_pnt = tmat * temp_pnt;
pnt[0] = temp_pnt[0];
pnt[1] = temp_pnt[1];
pnt[2] = temp_pnt[2];
}
}
pub fn scale_even(&mut self, scale_val: f32) {
for pnt in self.points.iter_mut() {
pnt[0] *= scale_val;
pnt[1] *= scale_val;
pnt[2] *= scale_val;
}
}
pub fn crop(&mut self, min_x: f32, max_x: f32, min_y: f32, max_y: f32, min_z: f32, max_z: f32) {
let pnts = self.points();
let mut new_pnts: Vec<[f32; 3]> = vec![];
for pnt in pnts {
if pnt[0] < min_x
|| pnt[0] > max_x
|| pnt[1] < min_y
|| pnt[1] > max_y
|| pnt[2] < min_z
|| pnt[2] > max_z
{
continue;
} else {
new_pnts.push(pnt);
}
}
self.points = new_pnts;
self.no_of_points = self.points.len();
}
pub fn save_to_file(&self, filepath: &str) -> Result<(), anyhow::Error> {
let mut file = File::create(filepath.to_owned() + ".txt")?;
let datetime_fmt = format!("{}\n", self.global_timestamp);
file.write_all(datetime_fmt.as_bytes())?;
for i in 0..self.no_of_points {
let pnt = format!(
"{:?},{:?},{:?}\n",
self.points[i][0], self.points[i][1], self.points[i][2]
);
file.write_all(pnt.as_bytes())?;
}
Ok(())
}
pub fn is_end(&self) -> bool {
if let Some(fp) = &self.filename {
return fp.contains("_END");
}
false
}
pub fn combine(&mut self, other: PointCloud) {
for pnt in other.points {
self.points.push(pnt);
self.no_of_points += 1;
}
}
pub fn decimation_filter(&mut self, n: i32) {
for i in 0..self.no_of_points {
if (i as i32) % n == 0 {
self.points.remove(i);
}
}
}
}