1#[cfg(feature = "io-mmap")]
12use crate::mmap::MmapReader;
13use crate::{MeshReader, MeshWriter, PointCloudReader, PointCloudWriter};
14use byteorder::{BigEndian, LittleEndian, ReadBytesExt};
15use std::collections::HashMap;
16use std::fs::File;
17use std::io::{BufRead, BufReader, BufWriter, Read};
18use std::path::Path;
19use threecrate_core::{Error, Point3f, PointCloud, Result, TriangleMesh, Vector3f};
20
21#[derive(Debug, Clone, Copy, PartialEq, Eq)]
23pub enum PlyFormat {
24 Ascii,
25 BinaryLittleEndian,
26 BinaryBigEndian,
27}
28
29#[derive(Debug, Clone, PartialEq)]
31pub enum PlyPropertyType {
32 Char,
33 UChar,
34 Short,
35 UShort,
36 Int,
37 UInt,
38 Float,
39 Double,
40 List(Box<PlyPropertyType>, Box<PlyPropertyType>), }
42
43#[derive(Debug, Clone)]
45pub struct PlyProperty {
46 pub name: String,
47 pub property_type: PlyPropertyType,
48}
49
50#[derive(Debug, Clone)]
52pub struct PlyElement {
53 pub name: String,
54 pub count: usize,
55 pub properties: Vec<PlyProperty>,
56}
57
58#[derive(Debug, Clone)]
60pub enum PlyValue {
61 Char(i8),
62 UChar(u8),
63 Short(i16),
64 UShort(u16),
65 Int(i32),
66 UInt(u32),
67 Float(f32),
68 Double(f64),
69 List(Vec<PlyValue>),
70}
71
72#[derive(Debug, Clone)]
74pub struct PlyHeader {
75 pub format: PlyFormat,
76 pub version: String,
77 pub elements: Vec<PlyElement>,
78 pub comments: Vec<String>,
79 pub obj_info: Vec<String>,
80}
81
82#[derive(Debug)]
84pub struct PlyData {
85 pub header: PlyHeader,
86 pub elements: HashMap<String, Vec<HashMap<String, PlyValue>>>,
87}
88
89pub struct RobustPlyReader;
91
92#[derive(Debug, Clone)]
94pub struct PlyWriteOptions {
95 pub format: PlyFormat,
97 pub comments: Vec<String>,
99 pub obj_info: Vec<String>,
101 pub custom_vertex_properties: Vec<(String, Vec<PlyValue>)>,
103 pub custom_face_properties: Vec<(String, Vec<PlyValue>)>,
105 pub include_normals: bool,
107 pub include_colors: bool,
109 pub vertex_property_order: Option<Vec<String>>,
111}
112
113impl Default for PlyWriteOptions {
114 fn default() -> Self {
115 Self {
116 format: PlyFormat::Ascii,
117 comments: Vec::new(),
118 obj_info: Vec::new(),
119 custom_vertex_properties: Vec::new(),
120 custom_face_properties: Vec::new(),
121 include_normals: true,
122 include_colors: false,
123 vertex_property_order: None,
124 }
125 }
126}
127
128impl PlyWriteOptions {
129 pub fn ascii() -> Self {
131 Self {
132 format: PlyFormat::Ascii,
133 ..Default::default()
134 }
135 }
136
137 pub fn binary_little_endian() -> Self {
139 Self {
140 format: PlyFormat::BinaryLittleEndian,
141 ..Default::default()
142 }
143 }
144
145 pub fn binary_big_endian() -> Self {
147 Self {
148 format: PlyFormat::BinaryBigEndian,
149 ..Default::default()
150 }
151 }
152
153 pub fn with_comment<S: Into<String>>(mut self, comment: S) -> Self {
155 self.comments.push(comment.into());
156 self
157 }
158
159 pub fn with_obj_info<S: Into<String>>(mut self, info: S) -> Self {
161 self.obj_info.push(info.into());
162 self
163 }
164
165 pub fn with_normals(mut self, include: bool) -> Self {
167 self.include_normals = include;
168 self
169 }
170
171 pub fn with_colors(mut self, include: bool) -> Self {
173 self.include_colors = include;
174 self
175 }
176
177 pub fn with_vertex_property_order(mut self, order: Vec<String>) -> Self {
179 self.vertex_property_order = Some(order);
180 self
181 }
182
183 pub fn with_custom_vertex_property<S: Into<String>>(
185 mut self,
186 name: S,
187 values: Vec<PlyValue>,
188 ) -> Self {
189 self.custom_vertex_properties.push((name.into(), values));
190 self
191 }
192}
193
194pub struct RobustPlyWriter;
196
197impl RobustPlyWriter {
198 pub fn write_point_cloud<P: AsRef<Path>>(
200 cloud: &PointCloud<Point3f>,
201 path: P,
202 options: &PlyWriteOptions,
203 ) -> Result<()> {
204 let file = File::create(path)?;
205 let mut writer = BufWriter::new(file);
206 Self::write_point_cloud_to_writer(cloud, &mut writer, options)
207 }
208
209 pub fn write_point_cloud_to_writer<W: std::io::Write>(
211 cloud: &PointCloud<Point3f>,
212 writer: &mut W,
213 options: &PlyWriteOptions,
214 ) -> Result<()> {
215 let mut ply_data = PlyData {
217 header: PlyHeader {
218 format: options.format,
219 version: "1.0".to_string(),
220 elements: Vec::new(),
221 comments: options.comments.clone(),
222 obj_info: options.obj_info.clone(),
223 },
224 elements: HashMap::new(),
225 };
226
227 let mut vertex_properties = Vec::new();
229 let mut vertex_data = Vec::new();
230
231 vertex_properties.push(PlyProperty {
233 name: "x".to_string(),
234 property_type: PlyPropertyType::Float,
235 });
236 vertex_properties.push(PlyProperty {
237 name: "y".to_string(),
238 property_type: PlyPropertyType::Float,
239 });
240 vertex_properties.push(PlyProperty {
241 name: "z".to_string(),
242 property_type: PlyPropertyType::Float,
243 });
244
245 for (prop_name, _) in &options.custom_vertex_properties {
247 if let Some(first_values) = options
249 .custom_vertex_properties
250 .iter()
251 .find(|(name, _)| name == prop_name)
252 .map(|(_, values)| values)
253 {
254 if let Some(first_value) = first_values.first() {
255 let prop_type = Self::value_to_property_type(first_value);
256 vertex_properties.push(PlyProperty {
257 name: prop_name.clone(),
258 property_type: prop_type,
259 });
260 }
261 }
262 }
263
264 if let Some(order) = &options.vertex_property_order {
266 vertex_properties.sort_by_key(|prop| {
267 order
268 .iter()
269 .position(|name| name == &prop.name)
270 .unwrap_or(order.len())
271 });
272 }
273
274 for (i, point) in cloud.iter().enumerate() {
276 let mut vertex_instance = HashMap::new();
277 vertex_instance.insert("x".to_string(), PlyValue::Float(point.x));
278 vertex_instance.insert("y".to_string(), PlyValue::Float(point.y));
279 vertex_instance.insert("z".to_string(), PlyValue::Float(point.z));
280
281 for (prop_name, values) in &options.custom_vertex_properties {
283 if i < values.len() {
284 vertex_instance.insert(prop_name.clone(), values[i].clone());
285 }
286 }
287
288 vertex_data.push(vertex_instance);
289 }
290
291 ply_data.header.elements.push(PlyElement {
293 name: "vertex".to_string(),
294 count: cloud.len(),
295 properties: vertex_properties,
296 });
297 ply_data.elements.insert("vertex".to_string(), vertex_data);
298
299 Self::write_ply_data(writer, &ply_data)
301 }
302
303 pub fn write_mesh<P: AsRef<Path>>(
305 mesh: &TriangleMesh,
306 path: P,
307 options: &PlyWriteOptions,
308 ) -> Result<()> {
309 let file = File::create(path)?;
310 let mut writer = BufWriter::new(file);
311 Self::write_mesh_to_writer(mesh, &mut writer, options)
312 }
313
314 pub fn write_mesh_to_writer<W: std::io::Write>(
316 mesh: &TriangleMesh,
317 writer: &mut W,
318 options: &PlyWriteOptions,
319 ) -> Result<()> {
320 let mut ply_data = PlyData {
322 header: PlyHeader {
323 format: options.format,
324 version: "1.0".to_string(),
325 elements: Vec::new(),
326 comments: options.comments.clone(),
327 obj_info: options.obj_info.clone(),
328 },
329 elements: HashMap::new(),
330 };
331
332 let mut vertex_properties = Vec::new();
334 let mut vertex_data = Vec::new();
335
336 vertex_properties.push(PlyProperty {
338 name: "x".to_string(),
339 property_type: PlyPropertyType::Float,
340 });
341 vertex_properties.push(PlyProperty {
342 name: "y".to_string(),
343 property_type: PlyPropertyType::Float,
344 });
345 vertex_properties.push(PlyProperty {
346 name: "z".to_string(),
347 property_type: PlyPropertyType::Float,
348 });
349
350 if options.include_normals && mesh.normals.is_some() {
352 vertex_properties.push(PlyProperty {
353 name: "nx".to_string(),
354 property_type: PlyPropertyType::Float,
355 });
356 vertex_properties.push(PlyProperty {
357 name: "ny".to_string(),
358 property_type: PlyPropertyType::Float,
359 });
360 vertex_properties.push(PlyProperty {
361 name: "nz".to_string(),
362 property_type: PlyPropertyType::Float,
363 });
364 }
365
366 for (prop_name, _) in &options.custom_vertex_properties {
368 if let Some(first_values) = options
369 .custom_vertex_properties
370 .iter()
371 .find(|(name, _)| name == prop_name)
372 .map(|(_, values)| values)
373 {
374 if let Some(first_value) = first_values.first() {
375 let prop_type = Self::value_to_property_type(first_value);
376 vertex_properties.push(PlyProperty {
377 name: prop_name.clone(),
378 property_type: prop_type,
379 });
380 }
381 }
382 }
383
384 if let Some(order) = &options.vertex_property_order {
386 vertex_properties.sort_by_key(|prop| {
387 order
388 .iter()
389 .position(|name| name == &prop.name)
390 .unwrap_or(order.len())
391 });
392 }
393
394 for (i, vertex) in mesh.vertices.iter().enumerate() {
396 let mut vertex_instance = HashMap::new();
397 vertex_instance.insert("x".to_string(), PlyValue::Float(vertex.x));
398 vertex_instance.insert("y".to_string(), PlyValue::Float(vertex.y));
399 vertex_instance.insert("z".to_string(), PlyValue::Float(vertex.z));
400
401 if options.include_normals {
403 if let Some(normals) = &mesh.normals {
404 if i < normals.len() {
405 vertex_instance.insert("nx".to_string(), PlyValue::Float(normals[i].x));
406 vertex_instance.insert("ny".to_string(), PlyValue::Float(normals[i].y));
407 vertex_instance.insert("nz".to_string(), PlyValue::Float(normals[i].z));
408 }
409 }
410 }
411
412 for (prop_name, values) in &options.custom_vertex_properties {
414 if i < values.len() {
415 vertex_instance.insert(prop_name.clone(), values[i].clone());
416 }
417 }
418
419 vertex_data.push(vertex_instance);
420 }
421
422 ply_data.header.elements.push(PlyElement {
424 name: "vertex".to_string(),
425 count: mesh.vertices.len(),
426 properties: vertex_properties,
427 });
428 ply_data.elements.insert("vertex".to_string(), vertex_data);
429
430 if !mesh.faces.is_empty() {
432 let face_properties = vec![PlyProperty {
433 name: "vertex_indices".to_string(),
434 property_type: PlyPropertyType::List(
435 Box::new(PlyPropertyType::UChar),
436 Box::new(PlyPropertyType::Int),
437 ),
438 }];
439
440 let mut face_data = Vec::new();
441 for face in &mesh.faces {
442 let mut face_instance = HashMap::new();
443 let indices = vec![
444 PlyValue::Int(face[0] as i32),
445 PlyValue::Int(face[1] as i32),
446 PlyValue::Int(face[2] as i32),
447 ];
448 face_instance.insert("vertex_indices".to_string(), PlyValue::List(indices));
449 face_data.push(face_instance);
450 }
451
452 ply_data.header.elements.push(PlyElement {
453 name: "face".to_string(),
454 count: mesh.faces.len(),
455 properties: face_properties,
456 });
457 ply_data.elements.insert("face".to_string(), face_data);
458 }
459
460 Self::write_ply_data(writer, &ply_data)
462 }
463
464 fn write_ply_data<W: std::io::Write>(writer: &mut W, ply_data: &PlyData) -> Result<()> {
466 Self::write_header(writer, &ply_data.header)?;
468
469 match ply_data.header.format {
471 PlyFormat::Ascii => Self::write_ascii_data(writer, ply_data)?,
472 PlyFormat::BinaryLittleEndian => {
473 Self::write_binary_data::<LittleEndian, _>(writer, ply_data)?
474 }
475 PlyFormat::BinaryBigEndian => {
476 Self::write_binary_data::<BigEndian, _>(writer, ply_data)?
477 }
478 }
479
480 Ok(())
481 }
482
483 fn write_header<W: std::io::Write>(writer: &mut W, header: &PlyHeader) -> Result<()> {
485 writeln!(writer, "ply")?;
486
487 let format_str = match header.format {
488 PlyFormat::Ascii => "ascii",
489 PlyFormat::BinaryLittleEndian => "binary_little_endian",
490 PlyFormat::BinaryBigEndian => "binary_big_endian",
491 };
492 writeln!(writer, "format {} {}", format_str, header.version)?;
493
494 for comment in &header.comments {
496 writeln!(writer, "comment {}", comment)?;
497 }
498
499 for info in &header.obj_info {
501 writeln!(writer, "obj_info {}", info)?;
502 }
503
504 for element in &header.elements {
506 writeln!(writer, "element {} {}", element.name, element.count)?;
507 for property in &element.properties {
508 Self::write_property_definition(writer, property)?;
509 }
510 }
511
512 writeln!(writer, "end_header")?;
513 Ok(())
514 }
515
516 fn write_property_definition<W: std::io::Write>(
518 writer: &mut W,
519 property: &PlyProperty,
520 ) -> Result<()> {
521 match &property.property_type {
522 PlyPropertyType::List(count_type, item_type) => {
523 let count_str = Self::property_type_to_string(count_type);
524 let item_str = Self::property_type_to_string(item_type);
525 writeln!(
526 writer,
527 "property list {} {} {}",
528 count_str, item_str, property.name
529 )?;
530 }
531 _ => {
532 let type_str = Self::property_type_to_string(&property.property_type);
533 writeln!(writer, "property {} {}", type_str, property.name)?;
534 }
535 }
536 Ok(())
537 }
538
539 fn property_type_to_string(prop_type: &PlyPropertyType) -> &'static str {
541 match prop_type {
542 PlyPropertyType::Char => "char",
543 PlyPropertyType::UChar => "uchar",
544 PlyPropertyType::Short => "short",
545 PlyPropertyType::UShort => "ushort",
546 PlyPropertyType::Int => "int",
547 PlyPropertyType::UInt => "uint",
548 PlyPropertyType::Float => "float",
549 PlyPropertyType::Double => "double",
550 PlyPropertyType::List(_, _) => "list", }
552 }
553
554 fn write_ascii_data<W: std::io::Write>(writer: &mut W, ply_data: &PlyData) -> Result<()> {
556 for element_def in &ply_data.header.elements {
557 if let Some(element_data) = ply_data.elements.get(&element_def.name) {
558 for instance in element_data {
559 let mut values = Vec::new();
560
561 for property in &element_def.properties {
562 if let Some(value) = instance.get(&property.name) {
563 Self::format_ascii_value(value, &mut values)?;
564 }
565 }
566
567 writeln!(writer, "{}", values.join(" "))?;
568 }
569 }
570 }
571 Ok(())
572 }
573
574 fn format_ascii_value(value: &PlyValue, output: &mut Vec<String>) -> Result<()> {
576 match value {
577 PlyValue::Char(v) => output.push(v.to_string()),
578 PlyValue::UChar(v) => output.push(v.to_string()),
579 PlyValue::Short(v) => output.push(v.to_string()),
580 PlyValue::UShort(v) => output.push(v.to_string()),
581 PlyValue::Int(v) => output.push(v.to_string()),
582 PlyValue::UInt(v) => output.push(v.to_string()),
583 PlyValue::Float(v) => output.push(v.to_string()),
584 PlyValue::Double(v) => output.push(v.to_string()),
585 PlyValue::List(values) => {
586 output.push(values.len().to_string());
587 for item in values {
588 Self::format_ascii_value(item, output)?;
589 }
590 }
591 }
592 Ok(())
593 }
594
595 fn write_binary_data<E: byteorder::ByteOrder, W: std::io::Write>(
597 writer: &mut W,
598 ply_data: &PlyData,
599 ) -> Result<()> {
600 for element_def in &ply_data.header.elements {
601 if let Some(element_data) = ply_data.elements.get(&element_def.name) {
602 for instance in element_data {
603 for property in &element_def.properties {
604 if let Some(value) = instance.get(&property.name) {
605 Self::write_binary_value::<E, _>(writer, value)?;
606 }
607 }
608 }
609 }
610 }
611 Ok(())
612 }
613
614 fn write_binary_value<E: byteorder::ByteOrder, W: std::io::Write>(
616 writer: &mut W,
617 value: &PlyValue,
618 ) -> Result<()> {
619 use byteorder::WriteBytesExt;
620
621 match value {
622 PlyValue::Char(v) => writer.write_i8(*v)?,
623 PlyValue::UChar(v) => writer.write_u8(*v)?,
624 PlyValue::Short(v) => writer.write_i16::<E>(*v)?,
625 PlyValue::UShort(v) => writer.write_u16::<E>(*v)?,
626 PlyValue::Int(v) => writer.write_i32::<E>(*v)?,
627 PlyValue::UInt(v) => writer.write_u32::<E>(*v)?,
628 PlyValue::Float(v) => writer.write_f32::<E>(*v)?,
629 PlyValue::Double(v) => writer.write_f64::<E>(*v)?,
630 PlyValue::List(values) => {
631 writer.write_u8(values.len() as u8)?;
633 for item in values {
634 Self::write_binary_value::<E, _>(writer, item)?;
635 }
636 }
637 }
638 Ok(())
639 }
640
641 fn value_to_property_type(value: &PlyValue) -> PlyPropertyType {
643 match value {
644 PlyValue::Char(_) => PlyPropertyType::Char,
645 PlyValue::UChar(_) => PlyPropertyType::UChar,
646 PlyValue::Short(_) => PlyPropertyType::Short,
647 PlyValue::UShort(_) => PlyPropertyType::UShort,
648 PlyValue::Int(_) => PlyPropertyType::Int,
649 PlyValue::UInt(_) => PlyPropertyType::UInt,
650 PlyValue::Float(_) => PlyPropertyType::Float,
651 PlyValue::Double(_) => PlyPropertyType::Double,
652 PlyValue::List(values) => {
653 let item_type = if let Some(first_item) = values.first() {
654 Self::value_to_property_type(first_item)
655 } else {
656 PlyPropertyType::Int
657 };
658 PlyPropertyType::List(Box::new(PlyPropertyType::UChar), Box::new(item_type))
659 }
660 }
661 }
662}
663
664impl RobustPlyReader {
665 pub fn read_ply_data<R: BufRead>(reader: &mut R) -> Result<PlyData> {
667 let header = Self::read_header(reader)?;
668 let elements = Self::read_elements(reader, &header)?;
669
670 Ok(PlyData { header, elements })
671 }
672
673 pub fn read_ply_file<P: AsRef<Path>>(path: P) -> Result<PlyData> {
675 let path = path.as_ref();
676
677 #[cfg(feature = "io-mmap")]
679 {
680 if let Some(ply_data) = Self::try_read_ply_mmap(path)? {
681 return Ok(ply_data);
682 }
683 }
684
685 let file = File::open(path)?;
687 let mut reader = BufReader::new(file);
688 Self::read_ply_data(&mut reader)
689 }
690
691 #[cfg(feature = "io-mmap")]
693 fn try_read_ply_mmap<P: AsRef<Path>>(path: P) -> Result<Option<PlyData>> {
694 let path = path.as_ref();
695
696 if !crate::mmap::should_use_mmap(path) {
698 return Ok(None);
699 }
700
701 let file = File::open(path)?;
703 let mut reader = BufReader::new(file);
704 let header = Self::read_header(&mut reader)?;
705
706 match header.format {
708 PlyFormat::BinaryLittleEndian | PlyFormat::BinaryBigEndian => {
709 let file = File::open(path)?;
711 let mut reader = BufReader::new(file);
712 let mut header_size = 0;
713 let mut line = String::new();
714
715 loop {
716 let line_start = header_size;
717 line.clear();
718 let bytes_read = reader.read_line(&mut line)?;
719 if bytes_read == 0 {
720 return Err(Error::InvalidData(
721 "Unexpected end of file in header".to_string(),
722 ));
723 }
724 header_size += bytes_read;
725
726 if line.trim() == "end_header" {
727 break;
728 }
729 }
730
731 if let Some(mut mmap_reader) = MmapReader::new(path)? {
733 mmap_reader.seek(header_size)?;
735
736 let elements = Self::read_elements_mmap(&mut mmap_reader, &header)?;
738
739 return Ok(Some(PlyData { header, elements }));
740 }
741 }
742 PlyFormat::Ascii => {
743 return Ok(None);
745 }
746 }
747
748 Ok(None)
749 }
750
751 #[cfg(feature = "io-mmap")]
753 fn read_elements_mmap(
754 reader: &mut MmapReader,
755 header: &PlyHeader,
756 ) -> Result<HashMap<String, Vec<HashMap<String, PlyValue>>>> {
757 let mut elements = HashMap::new();
758
759 for element_def in &header.elements {
760 let mut element_data = Vec::with_capacity(crate::capacity_hint(element_def.count));
761
762 for _ in 0..element_def.count {
763 let mut instance = HashMap::new();
764
765 match header.format {
766 PlyFormat::BinaryLittleEndian => {
767 for property in &element_def.properties {
768 let value = Self::read_binary_property_value_mmap_le(
769 reader,
770 &property.property_type,
771 )?;
772 instance.insert(property.name.clone(), value);
773 }
774 }
775 PlyFormat::BinaryBigEndian => {
776 for property in &element_def.properties {
777 let value = Self::read_binary_property_value_mmap_be(
778 reader,
779 &property.property_type,
780 )?;
781 instance.insert(property.name.clone(), value);
782 }
783 }
784 PlyFormat::Ascii => {
785 return Err(Error::InvalidData(
786 "ASCII format should not use mmap reader".to_string(),
787 ));
788 }
789 }
790
791 element_data.push(instance);
792 }
793
794 elements.insert(element_def.name.clone(), element_data);
795 }
796
797 Ok(elements)
798 }
799
800 #[cfg(feature = "io-mmap")]
802 fn read_binary_property_value_mmap_le(
803 reader: &mut MmapReader,
804 property_type: &PlyPropertyType,
805 ) -> Result<PlyValue> {
806 match property_type {
807 PlyPropertyType::Char => Ok(PlyValue::Char(reader.read_u8()? as i8)),
808 PlyPropertyType::UChar => Ok(PlyValue::UChar(reader.read_u8()?)),
809 PlyPropertyType::Short => Ok(PlyValue::Short(reader.read_u16_le()? as i16)),
810 PlyPropertyType::UShort => Ok(PlyValue::UShort(reader.read_u16_le()?)),
811 PlyPropertyType::Int => Ok(PlyValue::Int(reader.read_u32_le()? as i32)),
812 PlyPropertyType::UInt => Ok(PlyValue::UInt(reader.read_u32_le()?)),
813 PlyPropertyType::Float => Ok(PlyValue::Float(reader.read_f32_le()?)),
814 PlyPropertyType::Double => Ok(PlyValue::Double(reader.read_f64_le()?)),
815 PlyPropertyType::List(count_type, item_type) => {
816 let count_value = Self::read_binary_property_value_mmap_le(reader, count_type)?;
817 let count = count_value.as_usize()?;
818
819 let mut list = Vec::with_capacity(crate::capacity_hint(count));
820 for _ in 0..count {
821 let item = Self::read_binary_property_value_mmap_le(reader, item_type)?;
822 list.push(item);
823 }
824
825 Ok(PlyValue::List(list))
826 }
827 }
828 }
829
830 #[cfg(feature = "io-mmap")]
832 fn read_binary_property_value_mmap_be(
833 reader: &mut MmapReader,
834 property_type: &PlyPropertyType,
835 ) -> Result<PlyValue> {
836 match property_type {
837 PlyPropertyType::Char => Ok(PlyValue::Char(reader.read_u8()? as i8)),
838 PlyPropertyType::UChar => Ok(PlyValue::UChar(reader.read_u8()?)),
839 PlyPropertyType::Short => Ok(PlyValue::Short(reader.read_u16_be()? as i16)),
840 PlyPropertyType::UShort => Ok(PlyValue::UShort(reader.read_u16_be()?)),
841 PlyPropertyType::Int => Ok(PlyValue::Int(reader.read_u32_be()? as i32)),
842 PlyPropertyType::UInt => Ok(PlyValue::UInt(reader.read_u32_be()?)),
843 PlyPropertyType::Float => Ok(PlyValue::Float(reader.read_f32_be()?)),
844 PlyPropertyType::Double => Ok(PlyValue::Double(reader.read_f64_be()?)),
845 PlyPropertyType::List(count_type, item_type) => {
846 let count_value = Self::read_binary_property_value_mmap_be(reader, count_type)?;
847 let count = count_value.as_usize()?;
848
849 let mut list = Vec::with_capacity(crate::capacity_hint(count));
850 for _ in 0..count {
851 let item = Self::read_binary_property_value_mmap_be(reader, item_type)?;
852 list.push(item);
853 }
854
855 Ok(PlyValue::List(list))
856 }
857 }
858 }
859
860 fn read_header<R: BufRead>(reader: &mut R) -> Result<PlyHeader> {
862 let mut format = None;
863 let mut version = "1.0".to_string();
864 let mut elements = Vec::new();
865 let mut comments = Vec::new();
866 let mut obj_info = Vec::new();
867
868 let mut line = String::new();
869
870 reader.read_line(&mut line)?;
872 if line.trim() != "ply" {
873 return Err(Error::InvalidData(
874 "Not a PLY file - missing magic number".to_string(),
875 ));
876 }
877
878 loop {
880 line.clear();
881 if reader.read_line(&mut line)? == 0 {
882 return Err(Error::InvalidData(
883 "Unexpected end of file in header".to_string(),
884 ));
885 }
886
887 let line = line.trim();
888 if line == "end_header" {
889 break;
890 }
891
892 let parts: Vec<&str> = line.split_whitespace().collect();
893 if parts.is_empty() {
894 continue;
895 }
896
897 match parts[0] {
898 "format" => {
899 if parts.len() < 3 {
900 return Err(Error::InvalidData("Invalid format line".to_string()));
901 }
902 format = Some(match parts[1] {
903 "ascii" => PlyFormat::Ascii,
904 "binary_little_endian" => PlyFormat::BinaryLittleEndian,
905 "binary_big_endian" => PlyFormat::BinaryBigEndian,
906 _ => {
907 return Err(Error::InvalidData(format!("Unknown format: {}", parts[1])))
908 }
909 });
910 version = parts[2].to_string();
911 }
912 "comment" => {
913 if parts.len() > 1 {
914 comments.push(parts[1..].join(" "));
915 }
916 }
917 "obj_info" => {
918 if parts.len() > 1 {
919 obj_info.push(parts[1..].join(" "));
920 }
921 }
922 "element" => {
923 if parts.len() < 3 {
924 return Err(Error::InvalidData("Invalid element line".to_string()));
925 }
926 let name = parts[1].to_string();
927 let count: usize = parts[2]
928 .parse()
929 .map_err(|_| Error::InvalidData("Invalid element count".to_string()))?;
930 elements.push(PlyElement {
931 name,
932 count,
933 properties: Vec::new(),
934 });
935 }
936 "property" => {
937 if elements.is_empty() {
938 return Err(Error::InvalidData("Property without element".to_string()));
939 }
940 let property = Self::parse_property(&parts[1..])?;
941 elements.last_mut().unwrap().properties.push(property);
942 }
943 _ => {
944 }
946 }
947 }
948
949 let format =
950 format.ok_or_else(|| Error::InvalidData("Missing format specification".to_string()))?;
951
952 Ok(PlyHeader {
953 format,
954 version,
955 elements,
956 comments,
957 obj_info,
958 })
959 }
960
961 fn parse_property(parts: &[&str]) -> Result<PlyProperty> {
963 if parts.is_empty() {
964 return Err(Error::InvalidData("Empty property definition".to_string()));
965 }
966
967 let property_type = if parts[0] == "list" {
968 if parts.len() < 4 {
969 return Err(Error::InvalidData(
970 "Invalid list property definition".to_string(),
971 ));
972 }
973 let count_type = Self::parse_scalar_type(parts[1])?;
974 let item_type = Self::parse_scalar_type(parts[2])?;
975 PlyPropertyType::List(Box::new(count_type), Box::new(item_type))
976 } else {
977 if parts.len() < 2 {
978 return Err(Error::InvalidData(
979 "Invalid property definition".to_string(),
980 ));
981 }
982 Self::parse_scalar_type(parts[0])?
983 };
984
985 let name = parts.last().unwrap().to_string();
986
987 Ok(PlyProperty {
988 name,
989 property_type,
990 })
991 }
992
993 fn parse_scalar_type(type_str: &str) -> Result<PlyPropertyType> {
995 match type_str {
996 "char" | "int8" => Ok(PlyPropertyType::Char),
997 "uchar" | "uint8" => Ok(PlyPropertyType::UChar),
998 "short" | "int16" => Ok(PlyPropertyType::Short),
999 "ushort" | "uint16" => Ok(PlyPropertyType::UShort),
1000 "int" | "int32" => Ok(PlyPropertyType::Int),
1001 "uint" | "uint32" => Ok(PlyPropertyType::UInt),
1002 "float" | "float32" => Ok(PlyPropertyType::Float),
1003 "double" | "float64" => Ok(PlyPropertyType::Double),
1004 _ => Err(Error::InvalidData(format!(
1005 "Unknown property type: {}",
1006 type_str
1007 ))),
1008 }
1009 }
1010
1011 fn read_elements<R: BufRead>(
1013 reader: &mut R,
1014 header: &PlyHeader,
1015 ) -> Result<HashMap<String, Vec<HashMap<String, PlyValue>>>> {
1016 let mut elements = HashMap::new();
1017
1018 for element_def in &header.elements {
1019 let mut element_data = Vec::with_capacity(crate::capacity_hint(element_def.count));
1020
1021 for _ in 0..element_def.count {
1022 element_data.push(Self::read_record(reader, header.format, element_def)?);
1023 }
1024
1025 elements.insert(element_def.name.clone(), element_data);
1026 }
1027
1028 Ok(elements)
1029 }
1030
1031 fn read_record<R: BufRead>(
1033 reader: &mut R,
1034 format: PlyFormat,
1035 element: &PlyElement,
1036 ) -> Result<HashMap<String, PlyValue>> {
1037 let mut instance = HashMap::new();
1038 match format {
1039 PlyFormat::Ascii => {
1040 let mut line = String::new();
1041 reader.read_line(&mut line)?;
1042 let values = line.trim().split_whitespace().collect::<Vec<_>>();
1043 let mut value_idx = 0;
1044
1045 for property in &element.properties {
1046 let value = Self::read_ascii_property_value(
1047 &values,
1048 &mut value_idx,
1049 &property.property_type,
1050 )?;
1051 instance.insert(property.name.clone(), value);
1052 }
1053 }
1054 PlyFormat::BinaryLittleEndian => {
1055 for property in &element.properties {
1056 let value = Self::read_binary_property_value::<LittleEndian, _>(
1057 reader,
1058 &property.property_type,
1059 )?;
1060 instance.insert(property.name.clone(), value);
1061 }
1062 }
1063 PlyFormat::BinaryBigEndian => {
1064 for property in &element.properties {
1065 let value = Self::read_binary_property_value::<BigEndian, _>(
1066 reader,
1067 &property.property_type,
1068 )?;
1069 instance.insert(property.name.clone(), value);
1070 }
1071 }
1072 }
1073 Ok(instance)
1074 }
1075
1076 fn read_ascii_property_value(
1078 values: &[&str],
1079 value_idx: &mut usize,
1080 property_type: &PlyPropertyType,
1081 ) -> Result<PlyValue> {
1082 if *value_idx >= values.len() {
1083 return Err(Error::InvalidData("Not enough values in line".to_string()));
1084 }
1085
1086 match property_type {
1087 PlyPropertyType::Char => {
1088 let val = values[*value_idx]
1089 .parse::<i8>()
1090 .map_err(|_| Error::InvalidData("Invalid char value".to_string()))?;
1091 *value_idx += 1;
1092 Ok(PlyValue::Char(val))
1093 }
1094 PlyPropertyType::UChar => {
1095 let val = values[*value_idx]
1096 .parse::<u8>()
1097 .map_err(|_| Error::InvalidData("Invalid uchar value".to_string()))?;
1098 *value_idx += 1;
1099 Ok(PlyValue::UChar(val))
1100 }
1101 PlyPropertyType::Short => {
1102 let val = values[*value_idx]
1103 .parse::<i16>()
1104 .map_err(|_| Error::InvalidData("Invalid short value".to_string()))?;
1105 *value_idx += 1;
1106 Ok(PlyValue::Short(val))
1107 }
1108 PlyPropertyType::UShort => {
1109 let val = values[*value_idx]
1110 .parse::<u16>()
1111 .map_err(|_| Error::InvalidData("Invalid ushort value".to_string()))?;
1112 *value_idx += 1;
1113 Ok(PlyValue::UShort(val))
1114 }
1115 PlyPropertyType::Int => {
1116 let val = values[*value_idx]
1117 .parse::<i32>()
1118 .map_err(|_| Error::InvalidData("Invalid int value".to_string()))?;
1119 *value_idx += 1;
1120 Ok(PlyValue::Int(val))
1121 }
1122 PlyPropertyType::UInt => {
1123 let val = values[*value_idx]
1124 .parse::<u32>()
1125 .map_err(|_| Error::InvalidData("Invalid uint value".to_string()))?;
1126 *value_idx += 1;
1127 Ok(PlyValue::UInt(val))
1128 }
1129 PlyPropertyType::Float => {
1130 let val = values[*value_idx]
1131 .parse::<f32>()
1132 .map_err(|_| Error::InvalidData("Invalid float value".to_string()))?;
1133 *value_idx += 1;
1134 Ok(PlyValue::Float(val))
1135 }
1136 PlyPropertyType::Double => {
1137 let val = values[*value_idx]
1138 .parse::<f64>()
1139 .map_err(|_| Error::InvalidData("Invalid double value".to_string()))?;
1140 *value_idx += 1;
1141 Ok(PlyValue::Double(val))
1142 }
1143 PlyPropertyType::List(count_type, item_type) => {
1144 let count_value = Self::read_ascii_property_value(values, value_idx, count_type)?;
1145 let count = count_value.as_usize()?;
1146
1147 let mut list = Vec::with_capacity(crate::capacity_hint(count));
1148 for _ in 0..count {
1149 let item = Self::read_ascii_property_value(values, value_idx, item_type)?;
1150 list.push(item);
1151 }
1152
1153 Ok(PlyValue::List(list))
1154 }
1155 }
1156 }
1157
1158 fn read_binary_property_value<E: byteorder::ByteOrder, R: Read>(
1160 reader: &mut R,
1161 property_type: &PlyPropertyType,
1162 ) -> Result<PlyValue> {
1163 match property_type {
1164 PlyPropertyType::Char => Ok(PlyValue::Char(reader.read_i8()?)),
1165 PlyPropertyType::UChar => Ok(PlyValue::UChar(reader.read_u8()?)),
1166 PlyPropertyType::Short => Ok(PlyValue::Short(reader.read_i16::<E>()?)),
1167 PlyPropertyType::UShort => Ok(PlyValue::UShort(reader.read_u16::<E>()?)),
1168 PlyPropertyType::Int => Ok(PlyValue::Int(reader.read_i32::<E>()?)),
1169 PlyPropertyType::UInt => Ok(PlyValue::UInt(reader.read_u32::<E>()?)),
1170 PlyPropertyType::Float => Ok(PlyValue::Float(reader.read_f32::<E>()?)),
1171 PlyPropertyType::Double => Ok(PlyValue::Double(reader.read_f64::<E>()?)),
1172 PlyPropertyType::List(count_type, item_type) => {
1173 let count_value = Self::read_binary_property_value::<E, _>(reader, count_type)?;
1174 let count = count_value.as_usize()?;
1175
1176 let mut list = Vec::with_capacity(crate::capacity_hint(count));
1177 for _ in 0..count {
1178 let item = Self::read_binary_property_value::<E, _>(reader, item_type)?;
1179 list.push(item);
1180 }
1181
1182 Ok(PlyValue::List(list))
1183 }
1184 }
1185 }
1186}
1187
1188impl PlyValue {
1189 pub fn as_f32(&self) -> Result<f32> {
1191 match self {
1192 PlyValue::Char(v) => Ok(*v as f32),
1193 PlyValue::UChar(v) => Ok(*v as f32),
1194 PlyValue::Short(v) => Ok(*v as f32),
1195 PlyValue::UShort(v) => Ok(*v as f32),
1196 PlyValue::Int(v) => Ok(*v as f32),
1197 PlyValue::UInt(v) => Ok(*v as f32),
1198 PlyValue::Float(v) => Ok(*v),
1199 PlyValue::Double(v) => Ok(*v as f32),
1200 _ => Err(Error::InvalidData("Cannot convert list to f32".to_string())),
1201 }
1202 }
1203
1204 pub fn as_usize(&self) -> Result<usize> {
1206 match self {
1207 PlyValue::UChar(v) => Ok(*v as usize),
1208 PlyValue::UShort(v) => Ok(*v as usize),
1209 PlyValue::UInt(v) => Ok(*v as usize),
1210 PlyValue::Int(v) if *v >= 0 => Ok(*v as usize),
1211 _ => Err(Error::InvalidData(
1212 "Cannot convert value to usize".to_string(),
1213 )),
1214 }
1215 }
1216
1217 pub fn as_usize_list(&self) -> Result<Vec<usize>> {
1219 match self {
1220 PlyValue::List(values) => values.iter().map(|v| v.as_usize()).collect(),
1221 _ => Err(Error::InvalidData("Value is not a list".to_string())),
1222 }
1223 }
1224}
1225
1226pub struct PlyReader;
1228pub struct PlyWriter;
1229
1230fn read_positions_fast(path: &Path) -> Result<Option<Vec<Point3f>>> {
1235 use crate::records::{read_ascii_xyz, read_binary_xyz, BinaryXyz, Scalar};
1236 let mut reader = BufReader::new(File::open(path)?);
1237 let header = RobustPlyReader::read_header(&mut reader)?;
1238 let Some(vertex) = header.elements.first().filter(|e| e.name == "vertex") else {
1239 return Ok(None);
1240 };
1241 let mut offset = 0;
1242 let mut found = [None; 3];
1243 for (column, property) in vertex.properties.iter().enumerate() {
1244 let scalar = match property.property_type {
1245 PlyPropertyType::Char => Scalar::I8,
1246 PlyPropertyType::UChar => Scalar::U8,
1247 PlyPropertyType::Short => Scalar::I16,
1248 PlyPropertyType::UShort => Scalar::U16,
1249 PlyPropertyType::Int => Scalar::I32,
1250 PlyPropertyType::UInt => Scalar::U32,
1251 PlyPropertyType::Float => Scalar::F32,
1252 PlyPropertyType::Double => Scalar::F64,
1253 PlyPropertyType::List(..) => return Ok(None),
1254 };
1255 if let Some(axis) = ["x", "y", "z"].iter().position(|n| *n == property.name) {
1256 found[axis] = Some((column, offset, scalar));
1257 }
1258 offset += scalar.size();
1259 }
1260 let [Some(x), Some(y), Some(z)] = found else {
1261 return Err(Error::InvalidData(
1262 "PLY vertex element is missing x, y or z".to_string(),
1263 ));
1264 };
1265 let points = match header.format {
1266 PlyFormat::Ascii => read_ascii_xyz(&mut reader, [x.0, y.0, z.0], vertex.count)?,
1267 PlyFormat::BinaryLittleEndian | PlyFormat::BinaryBigEndian => {
1268 let layout = BinaryXyz {
1269 record_size: offset,
1270 xyz: [(x.1, x.2), (y.1, y.2), (z.1, z.2)],
1271 big_endian: header.format == PlyFormat::BinaryBigEndian,
1272 };
1273 read_binary_xyz(&mut reader, &layout, vertex.count)?
1274 }
1275 };
1276 Ok(Some(points))
1277}
1278
1279impl crate::registry::PointCloudReader for PlyReader {
1280 fn read_point_cloud(&self, path: &Path) -> Result<PointCloud<Point3f>> {
1281 if let Some(points) = read_positions_fast(path)? {
1282 return Ok(PointCloud::from_points(points));
1283 }
1284 let ply_data = RobustPlyReader::read_ply_file(path)?;
1286
1287 let mut points = Vec::new();
1288
1289 if let Some(vertex_elements) = ply_data.elements.get("vertex") {
1290 for vertex in vertex_elements {
1291 let x = vertex
1292 .get("x")
1293 .ok_or_else(|| Error::InvalidData("Missing x coordinate".to_string()))?
1294 .as_f32()?;
1295 let y = vertex
1296 .get("y")
1297 .ok_or_else(|| Error::InvalidData("Missing y coordinate".to_string()))?
1298 .as_f32()?;
1299 let z = vertex
1300 .get("z")
1301 .ok_or_else(|| Error::InvalidData("Missing z coordinate".to_string()))?
1302 .as_f32()?;
1303
1304 points.push(Point3f::new(x, y, z));
1305 }
1306 }
1307
1308 Ok(PointCloud::from_points(points))
1309 }
1310
1311 fn can_read(&self, path: &Path) -> bool {
1312 if let Ok(mut file) = File::open(path) {
1314 let mut header = [0u8; 4];
1315 if let Ok(_) = file.read(&mut header) {
1316 return header.starts_with(b"ply");
1317 }
1318 }
1319 false
1320 }
1321
1322 fn format_name(&self) -> &'static str {
1323 "ply"
1324 }
1325}
1326
1327impl crate::registry::MeshReader for PlyReader {
1328 fn read_mesh(&self, path: &Path) -> Result<TriangleMesh> {
1329 let ply_data = RobustPlyReader::read_ply_file(path)?;
1330
1331 let mut vertices = Vec::new();
1333 if let Some(vertex_elements) = ply_data.elements.get("vertex") {
1334 for vertex in vertex_elements {
1335 let x = vertex
1336 .get("x")
1337 .ok_or_else(|| Error::InvalidData("Missing x coordinate".to_string()))?
1338 .as_f32()?;
1339 let y = vertex
1340 .get("y")
1341 .ok_or_else(|| Error::InvalidData("Missing y coordinate".to_string()))?
1342 .as_f32()?;
1343 let z = vertex
1344 .get("z")
1345 .ok_or_else(|| Error::InvalidData("Missing z coordinate".to_string()))?
1346 .as_f32()?;
1347
1348 vertices.push(Point3f::new(x, y, z));
1349 }
1350 }
1351
1352 let mut faces = Vec::new();
1354 if let Some(face_elements) = ply_data.elements.get("face") {
1355 for face in face_elements {
1356 let indices = if let Some(vertex_indices) = face.get("vertex_indices") {
1358 vertex_indices.as_usize_list()?
1359 } else if let Some(vertex_index) = face.get("vertex_index") {
1360 vertex_index.as_usize_list()?
1361 } else {
1362 return Err(Error::InvalidData(
1363 "Face missing vertex indices".to_string(),
1364 ));
1365 };
1366
1367 if indices.len() >= 3 {
1369 faces.push([indices[0], indices[1], indices[2]]);
1370 }
1371 if indices.len() == 4 {
1373 faces.push([indices[0], indices[2], indices[3]]);
1374 }
1375 }
1376 }
1377
1378 let normals = if let Some(vertex_elements) = ply_data.elements.get("vertex") {
1380 let mut normals = Vec::new();
1381 let mut has_normals = true;
1382
1383 for vertex in vertex_elements {
1384 if let (Some(nx), Some(ny), Some(nz)) =
1385 (vertex.get("nx"), vertex.get("ny"), vertex.get("nz"))
1386 {
1387 normals.push(Vector3f::new(nx.as_f32()?, ny.as_f32()?, nz.as_f32()?));
1388 } else {
1389 has_normals = false;
1390 break;
1391 }
1392 }
1393
1394 if has_normals {
1395 Some(normals)
1396 } else {
1397 None
1398 }
1399 } else {
1400 None
1401 };
1402
1403 let mut mesh = TriangleMesh::from_vertices_and_faces(vertices, faces);
1404 if let Some(normals) = normals {
1405 mesh.set_normals(normals);
1406 }
1407
1408 Ok(mesh)
1409 }
1410
1411 fn can_read(&self, path: &Path) -> bool {
1412 if let Ok(mut file) = File::open(path) {
1414 let mut header = [0u8; 4];
1415 if let Ok(_) = file.read(&mut header) {
1416 return header.starts_with(b"ply");
1417 }
1418 }
1419 false
1420 }
1421
1422 fn format_name(&self) -> &'static str {
1423 "ply"
1424 }
1425}
1426
1427impl crate::registry::PointCloudWriter for PlyWriter {
1428 fn write_point_cloud(&self, cloud: &PointCloud<Point3f>, path: &Path) -> Result<()> {
1429 use ply_rs::{
1431 ply::{
1432 Addable, DefaultElement, ElementDef, Ply, Property, PropertyDef, PropertyType,
1433 ScalarType,
1434 },
1435 writer::Writer,
1436 };
1437
1438 let file = File::create(path)?;
1439 let mut writer = BufWriter::new(file);
1440
1441 let mut ply = Ply::<DefaultElement>::new();
1443
1444 let mut vertex_element = ElementDef::new("vertex".to_string());
1446 vertex_element.count = cloud.len();
1447 vertex_element.properties.add(PropertyDef::new(
1448 "x".to_string(),
1449 PropertyType::Scalar(ScalarType::Float),
1450 ));
1451 vertex_element.properties.add(PropertyDef::new(
1452 "y".to_string(),
1453 PropertyType::Scalar(ScalarType::Float),
1454 ));
1455 vertex_element.properties.add(PropertyDef::new(
1456 "z".to_string(),
1457 PropertyType::Scalar(ScalarType::Float),
1458 ));
1459
1460 ply.header.elements.add(vertex_element);
1461
1462 let mut vertices = Vec::new();
1464 for point in &cloud.points {
1465 let mut vertex = DefaultElement::new();
1466 vertex.insert("x".to_string(), Property::Float(point.x));
1467 vertex.insert("y".to_string(), Property::Float(point.y));
1468 vertex.insert("z".to_string(), Property::Float(point.z));
1469 vertices.push(vertex);
1470 }
1471 ply.payload.insert("vertex".to_string(), vertices);
1472
1473 let writer_instance = Writer::new();
1475 writer_instance.write_ply(&mut writer, &mut ply)?;
1476
1477 Ok(())
1478 }
1479
1480 fn format_name(&self) -> &'static str {
1481 "ply"
1482 }
1483}
1484
1485impl crate::registry::MeshWriter for PlyWriter {
1486 fn write_mesh(&self, mesh: &TriangleMesh, path: &Path) -> Result<()> {
1487 use ply_rs::{
1489 ply::{
1490 Addable, DefaultElement, ElementDef, Ply, Property, PropertyDef, PropertyType,
1491 ScalarType,
1492 },
1493 writer::Writer,
1494 };
1495
1496 let file = File::create(path)?;
1497 let mut writer = BufWriter::new(file);
1498
1499 let mut ply = Ply::<DefaultElement>::new();
1501
1502 let mut vertex_element = ElementDef::new("vertex".to_string());
1504 vertex_element.count = mesh.vertices.len();
1505 vertex_element.properties.add(PropertyDef::new(
1506 "x".to_string(),
1507 PropertyType::Scalar(ScalarType::Float),
1508 ));
1509 vertex_element.properties.add(PropertyDef::new(
1510 "y".to_string(),
1511 PropertyType::Scalar(ScalarType::Float),
1512 ));
1513 vertex_element.properties.add(PropertyDef::new(
1514 "z".to_string(),
1515 PropertyType::Scalar(ScalarType::Float),
1516 ));
1517
1518 if mesh.normals.is_some() {
1520 vertex_element.properties.add(PropertyDef::new(
1521 "nx".to_string(),
1522 PropertyType::Scalar(ScalarType::Float),
1523 ));
1524 vertex_element.properties.add(PropertyDef::new(
1525 "ny".to_string(),
1526 PropertyType::Scalar(ScalarType::Float),
1527 ));
1528 vertex_element.properties.add(PropertyDef::new(
1529 "nz".to_string(),
1530 PropertyType::Scalar(ScalarType::Float),
1531 ));
1532 }
1533
1534 ply.header.elements.add(vertex_element);
1535
1536 let mut vertices = Vec::new();
1538 for (i, point) in mesh.vertices.iter().enumerate() {
1539 let mut vertex = DefaultElement::new();
1540 vertex.insert("x".to_string(), Property::Float(point.x));
1541 vertex.insert("y".to_string(), Property::Float(point.y));
1542 vertex.insert("z".to_string(), Property::Float(point.z));
1543
1544 if let Some(ref normals) = mesh.normals {
1546 if i < normals.len() {
1547 vertex.insert("nx".to_string(), Property::Float(normals[i].x));
1548 vertex.insert("ny".to_string(), Property::Float(normals[i].y));
1549 vertex.insert("nz".to_string(), Property::Float(normals[i].z));
1550 }
1551 }
1552
1553 vertices.push(vertex);
1554 }
1555 ply.payload.insert("vertex".to_string(), vertices);
1556
1557 if !mesh.faces.is_empty() {
1559 let mut face_element = ElementDef::new("face".to_string());
1560 face_element.count = mesh.faces.len();
1561 face_element.properties.add(PropertyDef::new(
1562 "vertex_indices".to_string(),
1563 PropertyType::List(ScalarType::UChar, ScalarType::Int),
1564 ));
1565
1566 ply.header.elements.add(face_element);
1567
1568 let mut faces = Vec::new();
1570 for face in &mesh.faces {
1571 let mut face_data = DefaultElement::new();
1572 let indices = vec![face[0] as i32, face[1] as i32, face[2] as i32];
1573 face_data.insert("vertex_indices".to_string(), Property::ListInt(indices));
1574 faces.push(face_data);
1575 }
1576 ply.payload.insert("face".to_string(), faces);
1577 }
1578
1579 let writer_instance = Writer::new();
1581 writer_instance.write_ply(&mut writer, &mut ply)?;
1582
1583 Ok(())
1584 }
1585
1586 fn format_name(&self) -> &'static str {
1587 "ply"
1588 }
1589}
1590
1591impl PointCloudReader for PlyReader {
1593 fn read_point_cloud<P: AsRef<Path>>(path: P) -> Result<PointCloud<Point3f>> {
1594 let reader = PlyReader;
1595 crate::registry::PointCloudReader::read_point_cloud(&reader, path.as_ref())
1596 }
1597}
1598
1599impl MeshReader for PlyReader {
1600 fn read_mesh<P: AsRef<Path>>(path: P) -> Result<TriangleMesh> {
1601 let reader = PlyReader;
1602 crate::registry::MeshReader::read_mesh(&reader, path.as_ref())
1603 }
1604}
1605
1606impl PointCloudWriter for PlyWriter {
1607 fn write_point_cloud<P: AsRef<Path>>(cloud: &PointCloud<Point3f>, path: P) -> Result<()> {
1608 let writer = PlyWriter;
1609 crate::registry::PointCloudWriter::write_point_cloud(&writer, cloud, path.as_ref())
1610 }
1611}
1612
1613impl MeshWriter for PlyWriter {
1614 fn write_mesh<P: AsRef<Path>>(mesh: &TriangleMesh, path: P) -> Result<()> {
1615 let writer = PlyWriter;
1616 crate::registry::MeshWriter::write_mesh(&writer, mesh, path.as_ref())
1617 }
1618}
1619
1620fn open_at_element<P: AsRef<Path>>(
1623 path: P,
1624 element: &str,
1625) -> Result<(BufReader<File>, PlyHeader, usize)> {
1626 let mut reader = BufReader::new(File::open(path)?);
1627 let header = RobustPlyReader::read_header(&mut reader)?;
1628 let index = header
1629 .elements
1630 .iter()
1631 .position(|e| e.name == element)
1632 .ok_or_else(|| Error::InvalidData(format!("No {element} element found in PLY file")))?;
1633 for skipped in &header.elements[..index] {
1634 for _ in 0..skipped.count {
1635 RobustPlyReader::read_record(&mut reader, header.format, skipped)?;
1636 }
1637 }
1638 Ok((reader, header, index))
1639}
1640
1641pub struct PlyStreamingReader {
1643 reader: BufReader<File>,
1644 format: PlyFormat,
1645 vertex: PlyElement,
1646 current_index: usize,
1647}
1648
1649impl PlyStreamingReader {
1650 pub fn new<P: AsRef<Path>>(path: P, _chunk_size: usize) -> Result<Self> {
1653 let (reader, header, index) = open_at_element(path, "vertex")?;
1654 Ok(Self {
1655 reader,
1656 format: header.format,
1657 vertex: header.elements[index].clone(),
1658 current_index: 0,
1659 })
1660 }
1661}
1662
1663impl Iterator for PlyStreamingReader {
1664 type Item = Result<Point3f>;
1665
1666 fn next(&mut self) -> Option<Self::Item> {
1667 if self.current_index >= self.vertex.count {
1668 return None;
1669 }
1670 self.current_index += 1;
1671 let point = RobustPlyReader::read_record(&mut self.reader, self.format, &self.vertex)
1672 .and_then(|record| {
1673 let coord = |name: &str| {
1674 record
1675 .get(name)
1676 .ok_or_else(|| Error::InvalidData(format!("Missing {name} coordinate")))?
1677 .as_f32()
1678 };
1679 Ok(Point3f::new(coord("x")?, coord("y")?, coord("z")?))
1680 });
1681 if point.is_err() {
1682 self.current_index = self.vertex.count; }
1684 Some(point)
1685 }
1686}
1687
1688pub struct PlyMeshStreamingReader {
1690 reader: BufReader<File>,
1691 format: PlyFormat,
1692 face: PlyElement,
1693 vertex_count: usize,
1694 current_index: usize,
1695}
1696
1697impl PlyMeshStreamingReader {
1698 pub fn new<P: AsRef<Path>>(path: P, _chunk_size: usize) -> Result<Self> {
1701 let (reader, header, index) = open_at_element(path, "face")?;
1702 let vertex_count = header
1703 .elements
1704 .iter()
1705 .find(|e| e.name == "vertex")
1706 .ok_or_else(|| Error::InvalidData("No vertex element found in PLY file".to_string()))?
1707 .count;
1708 Ok(Self {
1709 reader,
1710 format: header.format,
1711 face: header.elements[index].clone(),
1712 vertex_count,
1713 current_index: 0,
1714 })
1715 }
1716}
1717
1718impl Iterator for PlyMeshStreamingReader {
1719 type Item = Result<[usize; 3]>;
1720
1721 fn next(&mut self) -> Option<Self::Item> {
1722 if self.current_index >= self.face.count {
1723 return None;
1724 }
1725 self.current_index += 1;
1726 let face = RobustPlyReader::read_record(&mut self.reader, self.format, &self.face)
1727 .and_then(|record| {
1728 let indices = record
1729 .get("vertex_indices")
1730 .or_else(|| record.get("vertex_index"))
1731 .ok_or_else(|| Error::InvalidData("Face has no vertex indices".to_string()))?
1732 .as_usize_list()?;
1733 let [a, b, c] = indices[..] else {
1734 return Err(Error::InvalidData(
1735 "Only triangular faces supported".to_string(),
1736 ));
1737 };
1738 if a.max(b).max(c) >= self.vertex_count {
1739 return Err(Error::InvalidData("Face index out of range".to_string()));
1740 }
1741 Ok([a, b, c])
1742 });
1743 if face.is_err() {
1744 self.current_index = self.face.count; }
1746 Some(face)
1747 }
1748}
1749
1750#[cfg(test)]
1751mod streaming_tests {
1752 use super::*;
1753
1754 fn write_temp(name: &str, bytes: &[u8]) -> std::path::PathBuf {
1755 let path =
1756 std::env::temp_dir().join(format!("threecrate_{name}_{}.ply", std::process::id()));
1757 std::fs::write(&path, bytes).unwrap();
1758 path
1759 }
1760
1761 #[test]
1762 fn streams_binary_ply_with_extra_properties() {
1763 let mut bytes = b"ply\nformat binary_little_endian 1.0\nelement vertex 2\n\
1765property double x\nproperty double y\nproperty double z\n\
1766property float nx\nproperty float ny\nproperty float nz\n\
1767property uchar red\nproperty uchar green\nproperty uchar blue\n\
1768element face 1\nproperty list uchar int vertex_indices\nend_header\n"
1769 .to_vec();
1770 for (p, n) in [([1.0f64, 2.0, 3.0], 0.0f32), ([4.0, 5.0, 6.0], 1.0)] {
1771 p.iter().for_each(|v| bytes.extend(v.to_le_bytes()));
1772 [n, n, n].iter().for_each(|v| bytes.extend(v.to_le_bytes()));
1773 bytes.extend([10u8, 20, 30]);
1774 }
1775 bytes.push(3);
1776 [0i32, 1, 1]
1777 .iter()
1778 .for_each(|v| bytes.extend(v.to_le_bytes()));
1779 let path = write_temp("stream_bin", &bytes);
1780
1781 let points: Vec<Point3f> = PlyStreamingReader::new(&path, 1)
1782 .unwrap()
1783 .collect::<Result<_>>()
1784 .unwrap();
1785 assert_eq!(
1786 points,
1787 vec![Point3f::new(1.0, 2.0, 3.0), Point3f::new(4.0, 5.0, 6.0)]
1788 );
1789
1790 let faces: Vec<[usize; 3]> = PlyMeshStreamingReader::new(&path, 1)
1791 .unwrap()
1792 .collect::<Result<_>>()
1793 .unwrap();
1794 assert_eq!(faces, vec![[0, 1, 1]]);
1795 std::fs::remove_file(path).ok();
1796 }
1797
1798 #[test]
1799 fn streams_ascii_with_reordered_properties_and_rejects_bad_faces() {
1800 let text = "ply\nformat ascii 1.0\nelement vertex 2\nproperty float z\nproperty float x\n\
1801property float y\nelement face 1\nproperty list uchar int vertex_indices\nend_header\n\
18023 1 2\n6 4 5\n3 0 1 7\n";
1803 let path = write_temp("stream_ascii", text.as_bytes());
1804
1805 let points: Vec<Point3f> = PlyStreamingReader::new(&path, 8)
1806 .unwrap()
1807 .collect::<Result<_>>()
1808 .unwrap();
1809 assert_eq!(
1810 points,
1811 vec![Point3f::new(1.0, 2.0, 3.0), Point3f::new(4.0, 5.0, 6.0)]
1812 );
1813
1814 let faces: Vec<Result<[usize; 3]>> =
1815 PlyMeshStreamingReader::new(&path, 8).unwrap().collect();
1816 assert_eq!(faces.len(), 1);
1817 assert!(faces[0].is_err(), "index 7 is out of range");
1818 std::fs::remove_file(path).ok();
1819 }
1820}