use std::collections::BTreeSet;
use super::Points3D;
impl Points3D {
#[cfg(not(target_arch = "wasm32"))]
pub fn from_file_path(filepath: &std::path::Path) -> anyhow::Result<Self> {
re_tracing::profile_function!(filepath.to_string_lossy());
use anyhow::Context as _;
let file = std::fs::File::open(filepath)
.with_context(|| format!("Failed to open file {filepath:?}"))?;
let mut file = std::io::BufReader::new(file);
let parser = ply_rs::parser::Parser::<ply_rs::ply::DefaultElement>::new();
let ply = {
re_tracing::profile_scope!("read_ply");
parser.read_ply(&mut file)?
};
Ok(from_ply(ply))
}
pub fn from_file_contents(contents: &[u8]) -> anyhow::Result<Self> {
re_tracing::profile_function!();
let parser = ply_rs::parser::Parser::<ply_rs::ply::DefaultElement>::new();
let mut contents = std::io::Cursor::new(contents);
let ply = {
re_tracing::profile_scope!("read_ply");
parser.read_ply(&mut contents)?
};
Ok(from_ply(ply))
}
}
fn from_ply(ply: ply_rs::ply::Ply<ply_rs::ply::DefaultElement>) -> Points3D {
re_tracing::profile_function!();
use std::borrow::Cow;
use linked_hash_map::LinkedHashMap;
use ply_rs::ply::Property;
use crate::components::{Color, Position3D, Radius, Text};
fn f32(prop: &Property) -> Option<f32> {
match *prop {
Property::Short(v) => Some(v as f32),
Property::UShort(v) => Some(v as f32),
Property::Int(v) => Some(v as f32),
Property::UInt(v) => Some(v as f32),
Property::Float(v) => Some(v),
Property::Double(v) => Some(v as f32),
Property::Char(_)
| Property::UChar(_)
| Property::ListChar(_)
| Property::ListUChar(_)
| Property::ListShort(_)
| Property::ListUShort(_)
| Property::ListInt(_)
| Property::ListUInt(_)
| Property::ListFloat(_)
| Property::ListDouble(_) => None,
}
}
fn u8(prop: &Property) -> Option<u8> {
match *prop {
Property::Short(v) => Some(v as u8),
Property::UShort(v) => Some(v as u8),
Property::Int(v) => Some(v as u8),
Property::UInt(v) => Some(v as u8),
Property::Float(v) => Some((v * 255.0) as u8),
Property::Double(v) => Some((v * 255.0) as u8),
Property::Char(v) => Some(v as u8),
Property::UChar(v) => Some(v),
Property::ListChar(_)
| Property::ListUChar(_)
| Property::ListShort(_)
| Property::ListUShort(_)
| Property::ListInt(_)
| Property::ListUInt(_)
| Property::ListFloat(_)
| Property::ListDouble(_) => None,
}
}
fn string(prop: &Property) -> Option<Cow<'_, str>> {
match prop {
Property::ListUChar(chars) => Some(String::from_utf8_lossy(chars)),
Property::ListChar(_)
| Property::ListShort(_)
| Property::ListUShort(_)
| Property::ListInt(_)
| Property::ListUInt(_)
| Property::ListFloat(_)
| Property::ListDouble(_)
| Property::Char(_)
| Property::UChar(_)
| Property::Short(_)
| Property::UShort(_)
| Property::Int(_)
| Property::UInt(_)
| Property::Float(_)
| Property::Double(_) => None,
}
}
struct Vertex {
position: Position3D,
color: Option<Color>,
radius: Option<Radius>,
label: Option<Text>,
}
impl Vertex {
fn from_props(
mut props: LinkedHashMap<String, Property>,
ignored_props: &mut BTreeSet<String>,
) -> Option<Vertex> {
const PROP_X: &str = "x";
const PROP_Y: &str = "y";
const PROP_Z: &str = "z";
const PROP_RED: &str = "red";
const PROP_GREEN: &str = "green";
const PROP_BLUE: &str = "blue";
const PROP_ALPHA: &str = "alpha";
const PROP_RADIUS: &str = "radius";
const PROP_LABEL: &str = "label";
let (Some(x), Some(y), Some(z)) = (
props.get(PROP_X).and_then(f32),
props.get(PROP_Y).and_then(f32),
props.get(PROP_Z).and_then(f32),
) else {
for (key, _value) in props {
ignored_props.insert(key);
}
return None;
};
props.remove(PROP_X);
props.remove(PROP_Y);
props.remove(PROP_Z);
let mut this = Self {
position: Position3D::new(x, y, z),
color: None,
radius: None,
label: None,
};
if let (Some(r), Some(g), Some(b)) = (
props.get(PROP_RED).and_then(u8),
props.get(PROP_GREEN).and_then(u8),
props.get(PROP_BLUE).and_then(u8),
) {
let a = props.get(PROP_ALPHA).and_then(u8).unwrap_or(255);
props.remove(PROP_RED);
props.remove(PROP_GREEN);
props.remove(PROP_BLUE);
props.remove(PROP_ALPHA);
this.color = Some(Color::new((r, g, b, a)));
};
if let Some(radius) = props.get(PROP_RADIUS).and_then(f32) {
props.remove(PROP_RADIUS);
this.radius = Some(Radius(radius));
}
if let Some(label) = props.get(PROP_LABEL).and_then(string) {
this.label = Some(Text(label.to_string().into()));
props.remove(PROP_LABEL);
}
for (key, _value) in props {
ignored_props.insert(key);
}
Some(this)
}
}
let mut positions = Vec::new();
let mut colors = Vec::new();
let mut radii = Vec::new();
let mut labels = Vec::new();
let mut ignored_props = BTreeSet::new();
for (key, all_props) in ply.payload {
if key == "vertex" {
for props in all_props {
if let Some(vertex) = Vertex::from_props(props, &mut ignored_props) {
let Vertex {
position,
color,
radius,
label,
} = vertex;
positions.push(position);
colors.push(color); radii.push(radius); labels.push(label); }
}
} else {
re_log::warn!("Ignoring {key:?} in .ply file");
}
}
if !ignored_props.is_empty() {
re_log::warn!("Ignored properties of .ply file: {ignored_props:?}");
}
re_tracing::profile_scope!("fill-in");
colors.truncate(positions.len());
radii.truncate(positions.len());
labels.truncate(positions.len());
let mut arch = crate::archetypes::Points3D::new(positions);
if colors.iter().any(|opt| opt.is_some()) {
let colors = colors
.into_iter()
.map(|opt| opt.unwrap_or(Color::from_rgb(255, 255, 255)));
arch = arch.with_colors(colors);
}
if radii.iter().any(|opt| opt.is_some()) {
let radii = radii.into_iter().map(|opt| opt.unwrap_or(Radius(1.0)));
arch = arch.with_radii(radii);
}
if labels.iter().any(|opt| opt.is_some()) {
let labels = labels
.into_iter()
.map(|opt| opt.unwrap_or(Text("undef".into())));
arch = arch.with_labels(labels);
}
arch
}