Skip to main content

threecrate_io/
ply.rs

1//! Robust PLY format support
2//!
3//! This module provides comprehensive PLY (Polygon File Format) reading and writing
4//! capabilities including:
5//! - ASCII and binary (little/big endian) format support
6//! - Vertex, face, normal, color, and generic property parsing
7//! - Metadata and comment preservation
8//! - Streaming support for large files
9//! - Structured error handling
10
11#[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/// PLY file format variants
22#[derive(Debug, Clone, Copy, PartialEq, Eq)]
23pub enum PlyFormat {
24    Ascii,
25    BinaryLittleEndian,
26    BinaryBigEndian,
27}
28
29/// PLY property data types
30#[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>), // count type, item type
41}
42
43/// PLY property definition
44#[derive(Debug, Clone)]
45pub struct PlyProperty {
46    pub name: String,
47    pub property_type: PlyPropertyType,
48}
49
50/// PLY element definition
51#[derive(Debug, Clone)]
52pub struct PlyElement {
53    pub name: String,
54    pub count: usize,
55    pub properties: Vec<PlyProperty>,
56}
57
58/// PLY property value
59#[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/// PLY header information
73#[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/// Complete PLY file data
83#[derive(Debug)]
84pub struct PlyData {
85    pub header: PlyHeader,
86    pub elements: HashMap<String, Vec<HashMap<String, PlyValue>>>,
87}
88
89/// Enhanced PLY reader with comprehensive format support
90pub struct RobustPlyReader;
91
92/// PLY writer configuration options
93#[derive(Debug, Clone)]
94pub struct PlyWriteOptions {
95    /// Output format (ASCII or binary)
96    pub format: PlyFormat,
97    /// Comments to include in the header
98    pub comments: Vec<String>,
99    /// Object info to include in the header
100    pub obj_info: Vec<String>,
101    /// Custom properties to include for vertices
102    pub custom_vertex_properties: Vec<(String, Vec<PlyValue>)>,
103    /// Custom properties to include for faces
104    pub custom_face_properties: Vec<(String, Vec<PlyValue>)>,
105    /// Whether to include normals if available
106    pub include_normals: bool,
107    /// Whether to include colors if available
108    pub include_colors: bool,
109    /// Custom property ordering for vertices
110    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    /// Create new options with ASCII format
130    pub fn ascii() -> Self {
131        Self {
132            format: PlyFormat::Ascii,
133            ..Default::default()
134        }
135    }
136
137    /// Create new options with binary little endian format
138    pub fn binary_little_endian() -> Self {
139        Self {
140            format: PlyFormat::BinaryLittleEndian,
141            ..Default::default()
142        }
143    }
144
145    /// Create new options with binary big endian format
146    pub fn binary_big_endian() -> Self {
147        Self {
148            format: PlyFormat::BinaryBigEndian,
149            ..Default::default()
150        }
151    }
152
153    /// Add a comment to the header
154    pub fn with_comment<S: Into<String>>(mut self, comment: S) -> Self {
155        self.comments.push(comment.into());
156        self
157    }
158
159    /// Add object info to the header
160    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    /// Include normals in output
166    pub fn with_normals(mut self, include: bool) -> Self {
167        self.include_normals = include;
168        self
169    }
170
171    /// Include colors in output
172    pub fn with_colors(mut self, include: bool) -> Self {
173        self.include_colors = include;
174        self
175    }
176
177    /// Set custom vertex property ordering
178    pub fn with_vertex_property_order(mut self, order: Vec<String>) -> Self {
179        self.vertex_property_order = Some(order);
180        self
181    }
182
183    /// Add custom vertex property
184    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
194/// Enhanced PLY writer with comprehensive format support
195pub struct RobustPlyWriter;
196
197impl RobustPlyWriter {
198    /// Write point cloud to PLY file with options
199    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    /// Write point cloud to writer with options
210    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        // Build PLY data structure
216        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        // Build vertex element definition
228        let mut vertex_properties = Vec::new();
229        let mut vertex_data = Vec::new();
230
231        // Always include x, y, z
232        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        // Add custom properties if specified
246        for (prop_name, _) in &options.custom_vertex_properties {
247            // Determine property type from first value
248            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        // Reorder properties if custom order is specified
265        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        // Build vertex data
275        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            // Add custom properties
282            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        // Add vertex element
292        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        // Write PLY data
300        Self::write_ply_data(writer, &ply_data)
301    }
302
303    /// Write triangle mesh to PLY file with options
304    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    /// Write triangle mesh to writer with options
315    pub fn write_mesh_to_writer<W: std::io::Write>(
316        mesh: &TriangleMesh,
317        writer: &mut W,
318        options: &PlyWriteOptions,
319    ) -> Result<()> {
320        // Build PLY data structure
321        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        // Build vertex element
333        let mut vertex_properties = Vec::new();
334        let mut vertex_data = Vec::new();
335
336        // Always include x, y, z
337        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        // Add normals if requested and available
351        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        // Add custom vertex properties
367        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        // Reorder properties if specified
385        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        // Build vertex data
395        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            // Add normals if available and requested
402            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            // Add custom properties
413            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        // Add vertex element
423        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        // Build face element
431        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        // Write PLY data
461        Self::write_ply_data(writer, &ply_data)
462    }
463
464    /// Write PLY data to writer
465    fn write_ply_data<W: std::io::Write>(writer: &mut W, ply_data: &PlyData) -> Result<()> {
466        // Write header
467        Self::write_header(writer, &ply_data.header)?;
468
469        // Write element data
470        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    /// Write PLY header
484    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        // Write comments
495        for comment in &header.comments {
496            writeln!(writer, "comment {}", comment)?;
497        }
498
499        // Write obj_info
500        for info in &header.obj_info {
501            writeln!(writer, "obj_info {}", info)?;
502        }
503
504        // Write elements and properties
505        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    /// Write property definition
517    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    /// Convert property type to string
540    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", // Should not be called directly for lists
551        }
552    }
553
554    /// Write ASCII format data
555    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    /// Format a value for ASCII output
575    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    /// Write binary format data
596    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    /// Write a binary value
615    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                // Write count as uchar (assuming list count type is uchar)
632                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    /// Determine property type from PLY value
642    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    /// Read a complete PLY file with all metadata and elements
666    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    /// Read PLY file from path
674    pub fn read_ply_file<P: AsRef<Path>>(path: P) -> Result<PlyData> {
675        let path = path.as_ref();
676
677        // Try memory-mapped reading for binary files if feature is enabled
678        #[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        // Fall back to standard buffered reading
686        let file = File::open(path)?;
687        let mut reader = BufReader::new(file);
688        Self::read_ply_data(&mut reader)
689    }
690
691    /// Try to read PLY file using memory mapping (binary files only)
692    #[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        // Check if we should use memory mapping
697        if !crate::mmap::should_use_mmap(path) {
698            return Ok(None);
699        }
700
701        // First, read the header using standard I/O to determine format
702        let file = File::open(path)?;
703        let mut reader = BufReader::new(file);
704        let header = Self::read_header(&mut reader)?;
705
706        // Only use mmap for binary formats
707        match header.format {
708            PlyFormat::BinaryLittleEndian | PlyFormat::BinaryBigEndian => {
709                // Calculate header size by reading until "end_header"
710                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                // Now use memory mapping for the data section
732                if let Some(mut mmap_reader) = MmapReader::new(path)? {
733                    // Skip to the data section
734                    mmap_reader.seek(header_size)?;
735
736                    // Read elements using memory mapping
737                    let elements = Self::read_elements_mmap(&mut mmap_reader, &header)?;
738
739                    return Ok(Some(PlyData { header, elements }));
740                }
741            }
742            PlyFormat::Ascii => {
743                // ASCII format - use standard buffered I/O
744                return Ok(None);
745            }
746        }
747
748        Ok(None)
749    }
750
751    /// Read elements using memory mapping
752    #[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    /// Read binary property value using memory mapping (little endian)
801    #[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    /// Read binary property value using memory mapping (big endian)
831    #[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    /// Read and parse PLY header
861    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        // Read magic number
871        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        // Parse header lines
879        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                    // Ignore unknown header lines
945                }
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    /// Parse a property definition
962    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    /// Parse a scalar type
994    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    /// Read all elements according to header specification
1012    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                let mut instance = HashMap::new();
1023
1024                match header.format {
1025                    PlyFormat::Ascii => {
1026                        let mut line = String::new();
1027                        reader.read_line(&mut line)?;
1028                        let values = line.trim().split_whitespace().collect::<Vec<_>>();
1029                        let mut value_idx = 0;
1030
1031                        for property in &element_def.properties {
1032                            let value = Self::read_ascii_property_value(
1033                                &values,
1034                                &mut value_idx,
1035                                &property.property_type,
1036                            )?;
1037                            instance.insert(property.name.clone(), value);
1038                        }
1039                    }
1040                    PlyFormat::BinaryLittleEndian => {
1041                        for property in &element_def.properties {
1042                            let value = Self::read_binary_property_value::<LittleEndian, _>(
1043                                reader,
1044                                &property.property_type,
1045                            )?;
1046                            instance.insert(property.name.clone(), value);
1047                        }
1048                    }
1049                    PlyFormat::BinaryBigEndian => {
1050                        for property in &element_def.properties {
1051                            let value = Self::read_binary_property_value::<BigEndian, _>(
1052                                reader,
1053                                &property.property_type,
1054                            )?;
1055                            instance.insert(property.name.clone(), value);
1056                        }
1057                    }
1058                }
1059
1060                element_data.push(instance);
1061            }
1062
1063            elements.insert(element_def.name.clone(), element_data);
1064        }
1065
1066        Ok(elements)
1067    }
1068
1069    /// Read ASCII property value
1070    fn read_ascii_property_value(
1071        values: &[&str],
1072        value_idx: &mut usize,
1073        property_type: &PlyPropertyType,
1074    ) -> Result<PlyValue> {
1075        if *value_idx >= values.len() {
1076            return Err(Error::InvalidData("Not enough values in line".to_string()));
1077        }
1078
1079        match property_type {
1080            PlyPropertyType::Char => {
1081                let val = values[*value_idx]
1082                    .parse::<i8>()
1083                    .map_err(|_| Error::InvalidData("Invalid char value".to_string()))?;
1084                *value_idx += 1;
1085                Ok(PlyValue::Char(val))
1086            }
1087            PlyPropertyType::UChar => {
1088                let val = values[*value_idx]
1089                    .parse::<u8>()
1090                    .map_err(|_| Error::InvalidData("Invalid uchar value".to_string()))?;
1091                *value_idx += 1;
1092                Ok(PlyValue::UChar(val))
1093            }
1094            PlyPropertyType::Short => {
1095                let val = values[*value_idx]
1096                    .parse::<i16>()
1097                    .map_err(|_| Error::InvalidData("Invalid short value".to_string()))?;
1098                *value_idx += 1;
1099                Ok(PlyValue::Short(val))
1100            }
1101            PlyPropertyType::UShort => {
1102                let val = values[*value_idx]
1103                    .parse::<u16>()
1104                    .map_err(|_| Error::InvalidData("Invalid ushort value".to_string()))?;
1105                *value_idx += 1;
1106                Ok(PlyValue::UShort(val))
1107            }
1108            PlyPropertyType::Int => {
1109                let val = values[*value_idx]
1110                    .parse::<i32>()
1111                    .map_err(|_| Error::InvalidData("Invalid int value".to_string()))?;
1112                *value_idx += 1;
1113                Ok(PlyValue::Int(val))
1114            }
1115            PlyPropertyType::UInt => {
1116                let val = values[*value_idx]
1117                    .parse::<u32>()
1118                    .map_err(|_| Error::InvalidData("Invalid uint value".to_string()))?;
1119                *value_idx += 1;
1120                Ok(PlyValue::UInt(val))
1121            }
1122            PlyPropertyType::Float => {
1123                let val = values[*value_idx]
1124                    .parse::<f32>()
1125                    .map_err(|_| Error::InvalidData("Invalid float value".to_string()))?;
1126                *value_idx += 1;
1127                Ok(PlyValue::Float(val))
1128            }
1129            PlyPropertyType::Double => {
1130                let val = values[*value_idx]
1131                    .parse::<f64>()
1132                    .map_err(|_| Error::InvalidData("Invalid double value".to_string()))?;
1133                *value_idx += 1;
1134                Ok(PlyValue::Double(val))
1135            }
1136            PlyPropertyType::List(count_type, item_type) => {
1137                let count_value = Self::read_ascii_property_value(values, value_idx, count_type)?;
1138                let count = count_value.as_usize()?;
1139
1140                let mut list = Vec::with_capacity(crate::capacity_hint(count));
1141                for _ in 0..count {
1142                    let item = Self::read_ascii_property_value(values, value_idx, item_type)?;
1143                    list.push(item);
1144                }
1145
1146                Ok(PlyValue::List(list))
1147            }
1148        }
1149    }
1150
1151    /// Read binary property value
1152    fn read_binary_property_value<E: byteorder::ByteOrder, R: Read>(
1153        reader: &mut R,
1154        property_type: &PlyPropertyType,
1155    ) -> Result<PlyValue> {
1156        match property_type {
1157            PlyPropertyType::Char => Ok(PlyValue::Char(reader.read_i8()?)),
1158            PlyPropertyType::UChar => Ok(PlyValue::UChar(reader.read_u8()?)),
1159            PlyPropertyType::Short => Ok(PlyValue::Short(reader.read_i16::<E>()?)),
1160            PlyPropertyType::UShort => Ok(PlyValue::UShort(reader.read_u16::<E>()?)),
1161            PlyPropertyType::Int => Ok(PlyValue::Int(reader.read_i32::<E>()?)),
1162            PlyPropertyType::UInt => Ok(PlyValue::UInt(reader.read_u32::<E>()?)),
1163            PlyPropertyType::Float => Ok(PlyValue::Float(reader.read_f32::<E>()?)),
1164            PlyPropertyType::Double => Ok(PlyValue::Double(reader.read_f64::<E>()?)),
1165            PlyPropertyType::List(count_type, item_type) => {
1166                let count_value = Self::read_binary_property_value::<E, _>(reader, count_type)?;
1167                let count = count_value.as_usize()?;
1168
1169                let mut list = Vec::with_capacity(crate::capacity_hint(count));
1170                for _ in 0..count {
1171                    let item = Self::read_binary_property_value::<E, _>(reader, item_type)?;
1172                    list.push(item);
1173                }
1174
1175                Ok(PlyValue::List(list))
1176            }
1177        }
1178    }
1179}
1180
1181impl PlyValue {
1182    /// Convert PLY value to f32
1183    pub fn as_f32(&self) -> Result<f32> {
1184        match self {
1185            PlyValue::Char(v) => Ok(*v as f32),
1186            PlyValue::UChar(v) => Ok(*v as f32),
1187            PlyValue::Short(v) => Ok(*v as f32),
1188            PlyValue::UShort(v) => Ok(*v as f32),
1189            PlyValue::Int(v) => Ok(*v as f32),
1190            PlyValue::UInt(v) => Ok(*v as f32),
1191            PlyValue::Float(v) => Ok(*v),
1192            PlyValue::Double(v) => Ok(*v as f32),
1193            _ => Err(Error::InvalidData("Cannot convert list to f32".to_string())),
1194        }
1195    }
1196
1197    /// Convert PLY value to usize
1198    pub fn as_usize(&self) -> Result<usize> {
1199        match self {
1200            PlyValue::UChar(v) => Ok(*v as usize),
1201            PlyValue::UShort(v) => Ok(*v as usize),
1202            PlyValue::UInt(v) => Ok(*v as usize),
1203            PlyValue::Int(v) if *v >= 0 => Ok(*v as usize),
1204            _ => Err(Error::InvalidData(
1205                "Cannot convert value to usize".to_string(),
1206            )),
1207        }
1208    }
1209
1210    /// Convert PLY value to Vec<usize> (for face indices)
1211    pub fn as_usize_list(&self) -> Result<Vec<usize>> {
1212        match self {
1213            PlyValue::List(values) => values.iter().map(|v| v.as_usize()).collect(),
1214            _ => Err(Error::InvalidData("Value is not a list".to_string())),
1215        }
1216    }
1217}
1218
1219// Legacy PLY reader/writer using ply-rs for backward compatibility
1220pub struct PlyReader;
1221pub struct PlyWriter;
1222
1223// Implement the new unified traits
1224impl crate::registry::PointCloudReader for PlyReader {
1225    fn read_point_cloud(&self, path: &Path) -> Result<PointCloud<Point3f>> {
1226        // Use the robust reader for better format support
1227        let ply_data = RobustPlyReader::read_ply_file(path)?;
1228
1229        let mut points = Vec::new();
1230
1231        if let Some(vertex_elements) = ply_data.elements.get("vertex") {
1232            for vertex in vertex_elements {
1233                let x = vertex
1234                    .get("x")
1235                    .ok_or_else(|| Error::InvalidData("Missing x coordinate".to_string()))?
1236                    .as_f32()?;
1237                let y = vertex
1238                    .get("y")
1239                    .ok_or_else(|| Error::InvalidData("Missing y coordinate".to_string()))?
1240                    .as_f32()?;
1241                let z = vertex
1242                    .get("z")
1243                    .ok_or_else(|| Error::InvalidData("Missing z coordinate".to_string()))?
1244                    .as_f32()?;
1245
1246                points.push(Point3f::new(x, y, z));
1247            }
1248        }
1249
1250        Ok(PointCloud::from_points(points))
1251    }
1252
1253    fn can_read(&self, path: &Path) -> bool {
1254        // Check if file starts with "ply"
1255        if let Ok(mut file) = File::open(path) {
1256            let mut header = [0u8; 4];
1257            if let Ok(_) = file.read(&mut header) {
1258                return header.starts_with(b"ply");
1259            }
1260        }
1261        false
1262    }
1263
1264    fn format_name(&self) -> &'static str {
1265        "ply"
1266    }
1267}
1268
1269impl crate::registry::MeshReader for PlyReader {
1270    fn read_mesh(&self, path: &Path) -> Result<TriangleMesh> {
1271        let ply_data = RobustPlyReader::read_ply_file(path)?;
1272
1273        // Extract vertices
1274        let mut vertices = Vec::new();
1275        if let Some(vertex_elements) = ply_data.elements.get("vertex") {
1276            for vertex in vertex_elements {
1277                let x = vertex
1278                    .get("x")
1279                    .ok_or_else(|| Error::InvalidData("Missing x coordinate".to_string()))?
1280                    .as_f32()?;
1281                let y = vertex
1282                    .get("y")
1283                    .ok_or_else(|| Error::InvalidData("Missing y coordinate".to_string()))?
1284                    .as_f32()?;
1285                let z = vertex
1286                    .get("z")
1287                    .ok_or_else(|| Error::InvalidData("Missing z coordinate".to_string()))?
1288                    .as_f32()?;
1289
1290                vertices.push(Point3f::new(x, y, z));
1291            }
1292        }
1293
1294        // Extract faces
1295        let mut faces = Vec::new();
1296        if let Some(face_elements) = ply_data.elements.get("face") {
1297            for face in face_elements {
1298                // Look for vertex_indices or vertex_index property
1299                let indices = if let Some(vertex_indices) = face.get("vertex_indices") {
1300                    vertex_indices.as_usize_list()?
1301                } else if let Some(vertex_index) = face.get("vertex_index") {
1302                    vertex_index.as_usize_list()?
1303                } else {
1304                    return Err(Error::InvalidData(
1305                        "Face missing vertex indices".to_string(),
1306                    ));
1307                };
1308
1309                // Convert to triangles (assuming triangular faces or taking first 3 vertices)
1310                if indices.len() >= 3 {
1311                    faces.push([indices[0], indices[1], indices[2]]);
1312                }
1313                // For quads and other polygons, we could triangulate here
1314                if indices.len() == 4 {
1315                    faces.push([indices[0], indices[2], indices[3]]);
1316                }
1317            }
1318        }
1319
1320        // Extract normals if available
1321        let normals = if let Some(vertex_elements) = ply_data.elements.get("vertex") {
1322            let mut normals = Vec::new();
1323            let mut has_normals = true;
1324
1325            for vertex in vertex_elements {
1326                if let (Some(nx), Some(ny), Some(nz)) =
1327                    (vertex.get("nx"), vertex.get("ny"), vertex.get("nz"))
1328                {
1329                    normals.push(Vector3f::new(nx.as_f32()?, ny.as_f32()?, nz.as_f32()?));
1330                } else {
1331                    has_normals = false;
1332                    break;
1333                }
1334            }
1335
1336            if has_normals {
1337                Some(normals)
1338            } else {
1339                None
1340            }
1341        } else {
1342            None
1343        };
1344
1345        let mut mesh = TriangleMesh::from_vertices_and_faces(vertices, faces);
1346        if let Some(normals) = normals {
1347            mesh.set_normals(normals);
1348        }
1349
1350        Ok(mesh)
1351    }
1352
1353    fn can_read(&self, path: &Path) -> bool {
1354        // Check if file starts with "ply"
1355        if let Ok(mut file) = File::open(path) {
1356            let mut header = [0u8; 4];
1357            if let Ok(_) = file.read(&mut header) {
1358                return header.starts_with(b"ply");
1359            }
1360        }
1361        false
1362    }
1363
1364    fn format_name(&self) -> &'static str {
1365        "ply"
1366    }
1367}
1368
1369impl crate::registry::PointCloudWriter for PlyWriter {
1370    fn write_point_cloud(&self, cloud: &PointCloud<Point3f>, path: &Path) -> Result<()> {
1371        // Use the legacy ply-rs for writing for now
1372        use ply_rs::{
1373            ply::{
1374                Addable, DefaultElement, ElementDef, Ply, Property, PropertyDef, PropertyType,
1375                ScalarType,
1376            },
1377            writer::Writer,
1378        };
1379
1380        let file = File::create(path)?;
1381        let mut writer = BufWriter::new(file);
1382
1383        // Create PLY structure
1384        let mut ply = Ply::<DefaultElement>::new();
1385
1386        // Define vertex element
1387        let mut vertex_element = ElementDef::new("vertex".to_string());
1388        vertex_element.count = cloud.len();
1389        vertex_element.properties.add(PropertyDef::new(
1390            "x".to_string(),
1391            PropertyType::Scalar(ScalarType::Float),
1392        ));
1393        vertex_element.properties.add(PropertyDef::new(
1394            "y".to_string(),
1395            PropertyType::Scalar(ScalarType::Float),
1396        ));
1397        vertex_element.properties.add(PropertyDef::new(
1398            "z".to_string(),
1399            PropertyType::Scalar(ScalarType::Float),
1400        ));
1401
1402        ply.header.elements.add(vertex_element);
1403
1404        // Add vertex data
1405        let mut vertices = Vec::new();
1406        for point in &cloud.points {
1407            let mut vertex = DefaultElement::new();
1408            vertex.insert("x".to_string(), Property::Float(point.x));
1409            vertex.insert("y".to_string(), Property::Float(point.y));
1410            vertex.insert("z".to_string(), Property::Float(point.z));
1411            vertices.push(vertex);
1412        }
1413        ply.payload.insert("vertex".to_string(), vertices);
1414
1415        // Write PLY file
1416        let writer_instance = Writer::new();
1417        writer_instance.write_ply(&mut writer, &mut ply)?;
1418
1419        Ok(())
1420    }
1421
1422    fn format_name(&self) -> &'static str {
1423        "ply"
1424    }
1425}
1426
1427impl crate::registry::MeshWriter for PlyWriter {
1428    fn write_mesh(&self, mesh: &TriangleMesh, path: &Path) -> Result<()> {
1429        // Use the legacy ply-rs for writing for now
1430        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        // Create PLY structure
1442        let mut ply = Ply::<DefaultElement>::new();
1443
1444        // Define vertex element
1445        let mut vertex_element = ElementDef::new("vertex".to_string());
1446        vertex_element.count = mesh.vertices.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        // Add normal properties if available
1461        if mesh.normals.is_some() {
1462            vertex_element.properties.add(PropertyDef::new(
1463                "nx".to_string(),
1464                PropertyType::Scalar(ScalarType::Float),
1465            ));
1466            vertex_element.properties.add(PropertyDef::new(
1467                "ny".to_string(),
1468                PropertyType::Scalar(ScalarType::Float),
1469            ));
1470            vertex_element.properties.add(PropertyDef::new(
1471                "nz".to_string(),
1472                PropertyType::Scalar(ScalarType::Float),
1473            ));
1474        }
1475
1476        ply.header.elements.add(vertex_element);
1477
1478        // Add vertex data
1479        let mut vertices = Vec::new();
1480        for (i, point) in mesh.vertices.iter().enumerate() {
1481            let mut vertex = DefaultElement::new();
1482            vertex.insert("x".to_string(), Property::Float(point.x));
1483            vertex.insert("y".to_string(), Property::Float(point.y));
1484            vertex.insert("z".to_string(), Property::Float(point.z));
1485
1486            // Add normals if available
1487            if let Some(ref normals) = mesh.normals {
1488                if i < normals.len() {
1489                    vertex.insert("nx".to_string(), Property::Float(normals[i].x));
1490                    vertex.insert("ny".to_string(), Property::Float(normals[i].y));
1491                    vertex.insert("nz".to_string(), Property::Float(normals[i].z));
1492                }
1493            }
1494
1495            vertices.push(vertex);
1496        }
1497        ply.payload.insert("vertex".to_string(), vertices);
1498
1499        // Define face element if we have faces
1500        if !mesh.faces.is_empty() {
1501            let mut face_element = ElementDef::new("face".to_string());
1502            face_element.count = mesh.faces.len();
1503            face_element.properties.add(PropertyDef::new(
1504                "vertex_indices".to_string(),
1505                PropertyType::List(ScalarType::UChar, ScalarType::Int),
1506            ));
1507
1508            ply.header.elements.add(face_element);
1509
1510            // Add face data
1511            let mut faces = Vec::new();
1512            for face in &mesh.faces {
1513                let mut face_data = DefaultElement::new();
1514                let indices = vec![face[0] as i32, face[1] as i32, face[2] as i32];
1515                face_data.insert("vertex_indices".to_string(), Property::ListInt(indices));
1516                faces.push(face_data);
1517            }
1518            ply.payload.insert("face".to_string(), faces);
1519        }
1520
1521        // Write PLY file
1522        let writer_instance = Writer::new();
1523        writer_instance.write_ply(&mut writer, &mut ply)?;
1524
1525        Ok(())
1526    }
1527
1528    fn format_name(&self) -> &'static str {
1529        "ply"
1530    }
1531}
1532
1533// Keep the legacy trait implementations for backward compatibility
1534impl PointCloudReader for PlyReader {
1535    fn read_point_cloud<P: AsRef<Path>>(path: P) -> Result<PointCloud<Point3f>> {
1536        let reader = PlyReader;
1537        crate::registry::PointCloudReader::read_point_cloud(&reader, path.as_ref())
1538    }
1539}
1540
1541impl MeshReader for PlyReader {
1542    fn read_mesh<P: AsRef<Path>>(path: P) -> Result<TriangleMesh> {
1543        let reader = PlyReader;
1544        crate::registry::MeshReader::read_mesh(&reader, path.as_ref())
1545    }
1546}
1547
1548impl PointCloudWriter for PlyWriter {
1549    fn write_point_cloud<P: AsRef<Path>>(cloud: &PointCloud<Point3f>, path: P) -> Result<()> {
1550        let writer = PlyWriter;
1551        crate::registry::PointCloudWriter::write_point_cloud(&writer, cloud, path.as_ref())
1552    }
1553}
1554
1555impl MeshWriter for PlyWriter {
1556    fn write_mesh<P: AsRef<Path>>(mesh: &TriangleMesh, path: P) -> Result<()> {
1557        let writer = PlyWriter;
1558        crate::registry::MeshWriter::write_mesh(&writer, mesh, path.as_ref())
1559    }
1560}
1561
1562/// Streaming PLY reader for point clouds
1563pub struct PlyStreamingReader {
1564    reader: BufReader<File>,
1565    current_count: usize,
1566    current_index: usize,
1567    chunk_size: usize,
1568    buffer: Vec<u8>,
1569    format: PlyFormat,
1570}
1571
1572impl PlyStreamingReader {
1573    /// Create a new streaming PLY reader
1574    pub fn new<P: AsRef<Path>>(path: P, chunk_size: usize) -> Result<Self> {
1575        let file = File::open(path)?;
1576        let mut reader = BufReader::new(file);
1577        let header = RobustPlyReader::read_header(&mut reader)?;
1578
1579        // Find vertex element
1580        let vertex_element = header
1581            .elements
1582            .iter()
1583            .find(|e| e.name == "vertex")
1584            .ok_or_else(|| Error::InvalidData("No vertex element found in PLY file".to_string()))?;
1585
1586        Ok(Self {
1587            reader,
1588            current_count: vertex_element.count,
1589            current_index: 0,
1590            chunk_size,
1591            buffer: Vec::with_capacity(chunk_size * 12), // Estimate 12 bytes per vertex
1592            format: header.format,
1593        })
1594    }
1595}
1596
1597impl Iterator for PlyStreamingReader {
1598    type Item = Result<Point3f>;
1599
1600    fn next(&mut self) -> Option<Self::Item> {
1601        if self.current_index >= self.current_count {
1602            return None;
1603        }
1604
1605        // Read a chunk if buffer is empty
1606        if self.buffer.is_empty() {
1607            let remaining = self.current_count - self.current_index;
1608            let to_read = std::cmp::min(remaining, self.chunk_size);
1609
1610            match self.format {
1611                PlyFormat::Ascii => {
1612                    // For ASCII, we need to read line by line
1613                    let mut line = String::new();
1614                    if let Err(e) = self.reader.read_line(&mut line) {
1615                        return Some(Err(Error::Io(e)));
1616                    }
1617
1618                    let values: Vec<&str> = line.trim().split_whitespace().collect();
1619                    if values.len() < 3 {
1620                        return Some(Err(Error::InvalidData(
1621                            "Not enough coordinates in vertex line".to_string(),
1622                        )));
1623                    }
1624
1625                    let x = match values[0].parse::<f32>() {
1626                        Ok(v) => v,
1627                        Err(_) => {
1628                            return Some(Err(Error::InvalidData(
1629                                "Invalid x coordinate".to_string(),
1630                            )))
1631                        }
1632                    };
1633                    let y = match values[1].parse::<f32>() {
1634                        Ok(v) => v,
1635                        Err(_) => {
1636                            return Some(Err(Error::InvalidData(
1637                                "Invalid y coordinate".to_string(),
1638                            )))
1639                        }
1640                    };
1641                    let z = match values[2].parse::<f32>() {
1642                        Ok(v) => v,
1643                        Err(_) => {
1644                            return Some(Err(Error::InvalidData(
1645                                "Invalid z coordinate".to_string(),
1646                            )))
1647                        }
1648                    };
1649
1650                    self.current_index += 1;
1651                    return Some(Ok(Point3f::new(x, y, z)));
1652                }
1653                PlyFormat::BinaryLittleEndian => {
1654                    // For binary, read the chunk
1655                    let bytes_per_vertex = 12; // 3 * f32
1656                    let chunk_bytes = to_read * bytes_per_vertex;
1657                    self.buffer.resize(chunk_bytes, 0);
1658
1659                    if let Err(e) = self.reader.read_exact(&mut self.buffer[..chunk_bytes]) {
1660                        return Some(Err(Error::Io(e)));
1661                    }
1662                }
1663                PlyFormat::BinaryBigEndian => {
1664                    // For binary, read the chunk
1665                    let bytes_per_vertex = 12; // 3 * f32
1666                    let chunk_bytes = to_read * bytes_per_vertex;
1667                    self.buffer.resize(chunk_bytes, 0);
1668
1669                    if let Err(e) = self.reader.read_exact(&mut self.buffer[..chunk_bytes]) {
1670                        return Some(Err(Error::Io(e)));
1671                    }
1672                }
1673            }
1674        }
1675
1676        // Extract point from buffer
1677        match self.format {
1678            PlyFormat::Ascii => {
1679                // Already handled above
1680                unreachable!()
1681            }
1682            PlyFormat::BinaryLittleEndian => {
1683                if self.buffer.len() < 12 {
1684                    return Some(Err(Error::InvalidData(
1685                        "Insufficient data in buffer".to_string(),
1686                    )));
1687                }
1688
1689                let x = f32::from_le_bytes([
1690                    self.buffer[0],
1691                    self.buffer[1],
1692                    self.buffer[2],
1693                    self.buffer[3],
1694                ]);
1695                let y = f32::from_le_bytes([
1696                    self.buffer[4],
1697                    self.buffer[5],
1698                    self.buffer[6],
1699                    self.buffer[7],
1700                ]);
1701                let z = f32::from_le_bytes([
1702                    self.buffer[8],
1703                    self.buffer[9],
1704                    self.buffer[10],
1705                    self.buffer[11],
1706                ]);
1707
1708                // Remove processed data from buffer
1709                self.buffer.drain(0..12);
1710                self.current_index += 1;
1711
1712                Some(Ok(Point3f::new(x, y, z)))
1713            }
1714            PlyFormat::BinaryBigEndian => {
1715                if self.buffer.len() < 12 {
1716                    return Some(Err(Error::InvalidData(
1717                        "Insufficient data in buffer".to_string(),
1718                    )));
1719                }
1720
1721                let x = f32::from_be_bytes([
1722                    self.buffer[0],
1723                    self.buffer[1],
1724                    self.buffer[2],
1725                    self.buffer[3],
1726                ]);
1727                let y = f32::from_be_bytes([
1728                    self.buffer[4],
1729                    self.buffer[5],
1730                    self.buffer[6],
1731                    self.buffer[7],
1732                ]);
1733                let z = f32::from_be_bytes([
1734                    self.buffer[8],
1735                    self.buffer[9],
1736                    self.buffer[10],
1737                    self.buffer[11],
1738                ]);
1739
1740                // Remove processed data from buffer
1741                self.buffer.drain(0..12);
1742                self.current_index += 1;
1743
1744                Some(Ok(Point3f::new(x, y, z)))
1745            }
1746        }
1747    }
1748}
1749
1750/// Streaming PLY reader for mesh faces
1751pub struct PlyMeshStreamingReader {
1752    reader: BufReader<File>,
1753    current_count: usize,
1754    current_index: usize,
1755    chunk_size: usize,
1756    buffer: Vec<u8>,
1757    format: PlyFormat,
1758    vertices: Vec<Point3f>, // We need to store vertices to convert face indices
1759}
1760
1761impl PlyMeshStreamingReader {
1762    /// Create a new streaming PLY mesh reader
1763    pub fn new<P: AsRef<Path>>(path: P, chunk_size: usize) -> Result<Self> {
1764        let file = File::open(path)?;
1765        let mut reader = BufReader::new(file);
1766        let header = RobustPlyReader::read_header(&mut reader)?;
1767
1768        // Find face element
1769        let face_element = header
1770            .elements
1771            .iter()
1772            .find(|e| e.name == "face")
1773            .ok_or_else(|| Error::InvalidData("No face element found in PLY file".to_string()))?;
1774
1775        // First, we need to read all vertices to convert face indices
1776        let vertex_element = header
1777            .elements
1778            .iter()
1779            .find(|e| e.name == "vertex")
1780            .ok_or_else(|| Error::InvalidData("No vertex element found in PLY file".to_string()))?;
1781
1782        let mut vertices = Vec::with_capacity(crate::capacity_hint(vertex_element.count));
1783
1784        // Read vertices first
1785        for _ in 0..vertex_element.count {
1786            let mut line = String::new();
1787            reader.read_line(&mut line)?;
1788            let values: Vec<&str> = line.trim().split_whitespace().collect();
1789            if values.len() >= 3 {
1790                let x = values[0]
1791                    .parse::<f32>()
1792                    .map_err(|_| Error::InvalidData("Invalid x coordinate".to_string()))?;
1793                let y = values[1]
1794                    .parse::<f32>()
1795                    .map_err(|_| Error::InvalidData("Invalid y coordinate".to_string()))?;
1796                let z = values[2]
1797                    .parse::<f32>()
1798                    .map_err(|_| Error::InvalidData("Invalid z coordinate".to_string()))?;
1799                vertices.push(Point3f::new(x, y, z));
1800            }
1801        }
1802
1803        Ok(Self {
1804            reader,
1805            current_count: face_element.count,
1806            current_index: 0,
1807            chunk_size,
1808            buffer: Vec::with_capacity(chunk_size * 16), // Estimate 16 bytes per face
1809            format: header.format,
1810            vertices,
1811        })
1812    }
1813}
1814
1815impl Iterator for PlyMeshStreamingReader {
1816    type Item = Result<[usize; 3]>;
1817
1818    fn next(&mut self) -> Option<Self::Item> {
1819        if self.current_index >= self.current_count {
1820            return None;
1821        }
1822
1823        // Read a chunk if buffer is empty
1824        if self.buffer.is_empty() {
1825            let remaining = self.current_count - self.current_index;
1826            let to_read = std::cmp::min(remaining, self.chunk_size);
1827
1828            match self.format {
1829                PlyFormat::Ascii => {
1830                    // For ASCII, we need to read line by line
1831                    let mut line = String::new();
1832                    if let Err(e) = self.reader.read_line(&mut line) {
1833                        return Some(Err(Error::Io(e)));
1834                    }
1835
1836                    let values: Vec<&str> = line.trim().split_whitespace().collect();
1837                    if values.len() < 4 {
1838                        // count + 3 indices
1839                        return Some(Err(Error::InvalidData(
1840                            "Not enough values in face line".to_string(),
1841                        )));
1842                    }
1843
1844                    let count = match values[0].parse::<usize>() {
1845                        Ok(v) => v,
1846                        Err(_) => {
1847                            return Some(Err(Error::InvalidData("Invalid face count".to_string())))
1848                        }
1849                    };
1850
1851                    if count != 3 {
1852                        return Some(Err(Error::InvalidData(
1853                            "Only triangular faces supported".to_string(),
1854                        )));
1855                    }
1856
1857                    let i1 = match values[1].parse::<usize>() {
1858                        Ok(v) => v,
1859                        Err(_) => {
1860                            return Some(Err(Error::InvalidData("Invalid face index".to_string())))
1861                        }
1862                    };
1863                    let i2 = match values[2].parse::<usize>() {
1864                        Ok(v) => v,
1865                        Err(_) => {
1866                            return Some(Err(Error::InvalidData("Invalid face index".to_string())))
1867                        }
1868                    };
1869                    let i3 = match values[3].parse::<usize>() {
1870                        Ok(v) => v,
1871                        Err(_) => {
1872                            return Some(Err(Error::InvalidData("Invalid face index".to_string())))
1873                        }
1874                    };
1875
1876                    // Validate indices
1877                    if i1 >= self.vertices.len()
1878                        || i2 >= self.vertices.len()
1879                        || i3 >= self.vertices.len()
1880                    {
1881                        return Some(Err(Error::InvalidData(
1882                            "Face index out of range".to_string(),
1883                        )));
1884                    }
1885
1886                    self.current_index += 1;
1887                    return Some(Ok([i1, i2, i3]));
1888                }
1889                PlyFormat::BinaryLittleEndian | PlyFormat::BinaryBigEndian => {
1890                    // For binary, read the chunk
1891                    let bytes_per_face = 16; // 1 uchar count + 3 * u32 indices
1892                    let chunk_bytes = to_read * bytes_per_face;
1893                    self.buffer.resize(chunk_bytes, 0);
1894
1895                    if let Err(e) = self.reader.read_exact(&mut self.buffer[..chunk_bytes]) {
1896                        return Some(Err(Error::Io(e)));
1897                    }
1898                }
1899            }
1900        }
1901
1902        // Extract face from buffer
1903        match self.format {
1904            PlyFormat::Ascii => {
1905                // Already handled above
1906                unreachable!()
1907            }
1908            PlyFormat::BinaryLittleEndian => {
1909                if self.buffer.len() < 16 {
1910                    return Some(Err(Error::InvalidData(
1911                        "Insufficient data in buffer".to_string(),
1912                    )));
1913                }
1914
1915                let count = self.buffer[0] as usize;
1916                if count != 3 {
1917                    return Some(Err(Error::InvalidData(
1918                        "Only triangular faces supported".to_string(),
1919                    )));
1920                }
1921
1922                let i1 = u32::from_le_bytes([
1923                    self.buffer[1],
1924                    self.buffer[2],
1925                    self.buffer[3],
1926                    self.buffer[4],
1927                ]) as usize;
1928                let i2 = u32::from_le_bytes([
1929                    self.buffer[5],
1930                    self.buffer[6],
1931                    self.buffer[7],
1932                    self.buffer[8],
1933                ]) as usize;
1934                let i3 = u32::from_le_bytes([
1935                    self.buffer[9],
1936                    self.buffer[10],
1937                    self.buffer[11],
1938                    self.buffer[12],
1939                ]) as usize;
1940
1941                // Validate indices
1942                if i1 >= self.vertices.len()
1943                    || i2 >= self.vertices.len()
1944                    || i3 >= self.vertices.len()
1945                {
1946                    return Some(Err(Error::InvalidData(
1947                        "Face index out of range".to_string(),
1948                    )));
1949                }
1950
1951                // Remove processed data from buffer
1952                self.buffer.drain(0..16);
1953                self.current_index += 1;
1954
1955                Some(Ok([i1, i2, i3]))
1956            }
1957            PlyFormat::BinaryBigEndian => {
1958                if self.buffer.len() < 16 {
1959                    return Some(Err(Error::InvalidData(
1960                        "Insufficient data in buffer".to_string(),
1961                    )));
1962                }
1963
1964                let count = self.buffer[0] as usize;
1965                if count != 3 {
1966                    return Some(Err(Error::InvalidData(
1967                        "Only triangular faces supported".to_string(),
1968                    )));
1969                }
1970
1971                let i1 = u32::from_be_bytes([
1972                    self.buffer[1],
1973                    self.buffer[2],
1974                    self.buffer[3],
1975                    self.buffer[4],
1976                ]) as usize;
1977                let i2 = u32::from_be_bytes([
1978                    self.buffer[5],
1979                    self.buffer[6],
1980                    self.buffer[7],
1981                    self.buffer[8],
1982                ]) as usize;
1983                let i3 = u32::from_be_bytes([
1984                    self.buffer[9],
1985                    self.buffer[10],
1986                    self.buffer[11],
1987                    self.buffer[12],
1988                ]) as usize;
1989
1990                // Validate indices
1991                if i1 >= self.vertices.len()
1992                    || i2 >= self.vertices.len()
1993                    || i3 >= self.vertices.len()
1994                {
1995                    return Some(Err(Error::InvalidData(
1996                        "Face index out of range".to_string(),
1997                    )));
1998                }
1999
2000                // Remove processed data from buffer
2001                self.buffer.drain(0..16);
2002                self.current_index += 1;
2003
2004                Some(Ok([i1, i2, i3]))
2005            }
2006        }
2007    }
2008}