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
11use crate::{PointCloudReader, PointCloudWriter, MeshReader, MeshWriter};
12use threecrate_core::{PointCloud, TriangleMesh, Result, Point3f, Vector3f, Error};
13use std::path::Path;
14use std::fs::File;
15use std::io::{BufRead, BufReader, BufWriter, Read};
16use std::collections::HashMap;
17use byteorder::{LittleEndian, BigEndian, ReadBytesExt};
18#[cfg(feature = "io-mmap")]
19use crate::mmap::MmapReader;
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>>(mut self, name: S, values: Vec<PlyValue>) -> Self {
185        self.custom_vertex_properties.push((name.into(), values));
186        self
187    }
188}
189
190/// Enhanced PLY writer with comprehensive format support
191pub struct RobustPlyWriter;
192
193impl RobustPlyWriter {
194    /// Write point cloud to PLY file with options
195    pub fn write_point_cloud<P: AsRef<Path>>(
196        cloud: &PointCloud<Point3f>, 
197        path: P, 
198        options: &PlyWriteOptions
199    ) -> Result<()> {
200        let file = File::create(path)?;
201        let mut writer = BufWriter::new(file);
202        Self::write_point_cloud_to_writer(cloud, &mut writer, options)
203    }
204    
205    /// Write point cloud to writer with options
206    pub fn write_point_cloud_to_writer<W: std::io::Write>(
207        cloud: &PointCloud<Point3f>,
208        writer: &mut W,
209        options: &PlyWriteOptions,
210    ) -> Result<()> {
211        // Build PLY data structure
212        let mut ply_data = PlyData {
213            header: PlyHeader {
214                format: options.format,
215                version: "1.0".to_string(),
216                elements: Vec::new(),
217                comments: options.comments.clone(),
218                obj_info: options.obj_info.clone(),
219            },
220            elements: HashMap::new(),
221        };
222        
223        // Build vertex element definition
224        let mut vertex_properties = Vec::new();
225        let mut vertex_data = Vec::new();
226        
227        // Always include x, y, z
228        vertex_properties.push(PlyProperty {
229            name: "x".to_string(),
230            property_type: PlyPropertyType::Float,
231        });
232        vertex_properties.push(PlyProperty {
233            name: "y".to_string(),
234            property_type: PlyPropertyType::Float,
235        });
236        vertex_properties.push(PlyProperty {
237            name: "z".to_string(),
238            property_type: PlyPropertyType::Float,
239        });
240        
241        // Add custom properties if specified
242        for (prop_name, _) in &options.custom_vertex_properties {
243            // Determine property type from first value
244            if let Some(first_values) = options.custom_vertex_properties.iter()
245                .find(|(name, _)| name == prop_name)
246                .map(|(_, values)| values)
247            {
248                if let Some(first_value) = first_values.first() {
249                    let prop_type = Self::value_to_property_type(first_value);
250                    vertex_properties.push(PlyProperty {
251                        name: prop_name.clone(),
252                        property_type: prop_type,
253                    });
254                }
255            }
256        }
257        
258        // Reorder properties if custom order is specified
259        if let Some(order) = &options.vertex_property_order {
260            vertex_properties.sort_by_key(|prop| {
261                order.iter().position(|name| name == &prop.name)
262                    .unwrap_or(order.len())
263            });
264        }
265        
266        // Build vertex data
267        for (i, point) in cloud.iter().enumerate() {
268            let mut vertex_instance = HashMap::new();
269            vertex_instance.insert("x".to_string(), PlyValue::Float(point.x));
270            vertex_instance.insert("y".to_string(), PlyValue::Float(point.y));
271            vertex_instance.insert("z".to_string(), PlyValue::Float(point.z));
272            
273            // Add custom properties
274            for (prop_name, values) in &options.custom_vertex_properties {
275                if i < values.len() {
276                    vertex_instance.insert(prop_name.clone(), values[i].clone());
277                }
278            }
279            
280            vertex_data.push(vertex_instance);
281        }
282        
283        // Add vertex element
284        ply_data.header.elements.push(PlyElement {
285            name: "vertex".to_string(),
286            count: cloud.len(),
287            properties: vertex_properties,
288        });
289        ply_data.elements.insert("vertex".to_string(), vertex_data);
290        
291        // Write PLY data
292        Self::write_ply_data(writer, &ply_data)
293    }
294    
295    /// Write triangle mesh to PLY file with options
296    pub fn write_mesh<P: AsRef<Path>>(
297        mesh: &TriangleMesh,
298        path: P,
299        options: &PlyWriteOptions,
300    ) -> Result<()> {
301        let file = File::create(path)?;
302        let mut writer = BufWriter::new(file);
303        Self::write_mesh_to_writer(mesh, &mut writer, options)
304    }
305    
306    /// Write triangle mesh to writer with options
307    pub fn write_mesh_to_writer<W: std::io::Write>(
308        mesh: &TriangleMesh,
309        writer: &mut W,
310        options: &PlyWriteOptions,
311    ) -> Result<()> {
312        // Build PLY data structure
313        let mut ply_data = PlyData {
314            header: PlyHeader {
315                format: options.format,
316                version: "1.0".to_string(),
317                elements: Vec::new(),
318                comments: options.comments.clone(),
319                obj_info: options.obj_info.clone(),
320            },
321            elements: HashMap::new(),
322        };
323        
324        // Build vertex element
325        let mut vertex_properties = Vec::new();
326        let mut vertex_data = Vec::new();
327        
328        // Always include x, y, z
329        vertex_properties.push(PlyProperty {
330            name: "x".to_string(),
331            property_type: PlyPropertyType::Float,
332        });
333        vertex_properties.push(PlyProperty {
334            name: "y".to_string(),
335            property_type: PlyPropertyType::Float,
336        });
337        vertex_properties.push(PlyProperty {
338            name: "z".to_string(),
339            property_type: PlyPropertyType::Float,
340        });
341        
342        // Add normals if requested and available
343        if options.include_normals && mesh.normals.is_some() {
344            vertex_properties.push(PlyProperty {
345                name: "nx".to_string(),
346                property_type: PlyPropertyType::Float,
347            });
348            vertex_properties.push(PlyProperty {
349                name: "ny".to_string(),
350                property_type: PlyPropertyType::Float,
351            });
352            vertex_properties.push(PlyProperty {
353                name: "nz".to_string(),
354                property_type: PlyPropertyType::Float,
355            });
356        }
357        
358        // Add custom vertex properties
359        for (prop_name, _) in &options.custom_vertex_properties {
360            if let Some(first_values) = options.custom_vertex_properties.iter()
361                .find(|(name, _)| name == prop_name)
362                .map(|(_, values)| values)
363            {
364                if let Some(first_value) = first_values.first() {
365                    let prop_type = Self::value_to_property_type(first_value);
366                    vertex_properties.push(PlyProperty {
367                        name: prop_name.clone(),
368                        property_type: prop_type,
369                    });
370                }
371            }
372        }
373        
374        // Reorder properties if specified
375        if let Some(order) = &options.vertex_property_order {
376            vertex_properties.sort_by_key(|prop| {
377                order.iter().position(|name| name == &prop.name)
378                    .unwrap_or(order.len())
379            });
380        }
381        
382        // Build vertex data
383        for (i, vertex) in mesh.vertices.iter().enumerate() {
384            let mut vertex_instance = HashMap::new();
385            vertex_instance.insert("x".to_string(), PlyValue::Float(vertex.x));
386            vertex_instance.insert("y".to_string(), PlyValue::Float(vertex.y));
387            vertex_instance.insert("z".to_string(), PlyValue::Float(vertex.z));
388            
389            // Add normals if available and requested
390            if options.include_normals {
391                if let Some(normals) = &mesh.normals {
392                    if i < normals.len() {
393                        vertex_instance.insert("nx".to_string(), PlyValue::Float(normals[i].x));
394                        vertex_instance.insert("ny".to_string(), PlyValue::Float(normals[i].y));
395                        vertex_instance.insert("nz".to_string(), PlyValue::Float(normals[i].z));
396                    }
397                }
398            }
399            
400            // Add custom properties
401            for (prop_name, values) in &options.custom_vertex_properties {
402                if i < values.len() {
403                    vertex_instance.insert(prop_name.clone(), values[i].clone());
404                }
405            }
406            
407            vertex_data.push(vertex_instance);
408        }
409        
410        // Add vertex element
411        ply_data.header.elements.push(PlyElement {
412            name: "vertex".to_string(),
413            count: mesh.vertices.len(),
414            properties: vertex_properties,
415        });
416        ply_data.elements.insert("vertex".to_string(), vertex_data);
417        
418        // Build face element
419        if !mesh.faces.is_empty() {
420            let face_properties = vec![
421                PlyProperty {
422                    name: "vertex_indices".to_string(),
423                    property_type: PlyPropertyType::List(
424                        Box::new(PlyPropertyType::UChar),
425                        Box::new(PlyPropertyType::Int),
426                    ),
427                },
428            ];
429            
430            let mut face_data = Vec::new();
431            for face in &mesh.faces {
432                let mut face_instance = HashMap::new();
433                let indices = vec![
434                    PlyValue::Int(face[0] as i32),
435                    PlyValue::Int(face[1] as i32),
436                    PlyValue::Int(face[2] as i32),
437                ];
438                face_instance.insert("vertex_indices".to_string(), PlyValue::List(indices));
439                face_data.push(face_instance);
440            }
441            
442            ply_data.header.elements.push(PlyElement {
443                name: "face".to_string(),
444                count: mesh.faces.len(),
445                properties: face_properties,
446            });
447            ply_data.elements.insert("face".to_string(), face_data);
448        }
449        
450        // Write PLY data
451        Self::write_ply_data(writer, &ply_data)
452    }
453    
454    /// Write PLY data to writer
455    fn write_ply_data<W: std::io::Write>(writer: &mut W, ply_data: &PlyData) -> Result<()> {
456        // Write header
457        Self::write_header(writer, &ply_data.header)?;
458        
459        // Write element data
460        match ply_data.header.format {
461            PlyFormat::Ascii => Self::write_ascii_data(writer, ply_data)?,
462            PlyFormat::BinaryLittleEndian => Self::write_binary_data::<LittleEndian, _>(writer, ply_data)?,
463            PlyFormat::BinaryBigEndian => Self::write_binary_data::<BigEndian, _>(writer, ply_data)?,
464        }
465        
466        Ok(())
467    }
468    
469    /// Write PLY header
470    fn write_header<W: std::io::Write>(writer: &mut W, header: &PlyHeader) -> Result<()> {
471        writeln!(writer, "ply")?;
472        
473        let format_str = match header.format {
474            PlyFormat::Ascii => "ascii",
475            PlyFormat::BinaryLittleEndian => "binary_little_endian",
476            PlyFormat::BinaryBigEndian => "binary_big_endian",
477        };
478        writeln!(writer, "format {} {}", format_str, header.version)?;
479        
480        // Write comments
481        for comment in &header.comments {
482            writeln!(writer, "comment {}", comment)?;
483        }
484        
485        // Write obj_info
486        for info in &header.obj_info {
487            writeln!(writer, "obj_info {}", info)?;
488        }
489        
490        // Write elements and properties
491        for element in &header.elements {
492            writeln!(writer, "element {} {}", element.name, element.count)?;
493            for property in &element.properties {
494                Self::write_property_definition(writer, property)?;
495            }
496        }
497        
498        writeln!(writer, "end_header")?;
499        Ok(())
500    }
501    
502    /// Write property definition
503    fn write_property_definition<W: std::io::Write>(writer: &mut W, property: &PlyProperty) -> Result<()> {
504        match &property.property_type {
505            PlyPropertyType::List(count_type, item_type) => {
506                let count_str = Self::property_type_to_string(count_type);
507                let item_str = Self::property_type_to_string(item_type);
508                writeln!(writer, "property list {} {} {}", count_str, item_str, property.name)?;
509            }
510            _ => {
511                let type_str = Self::property_type_to_string(&property.property_type);
512                writeln!(writer, "property {} {}", type_str, property.name)?;
513            }
514        }
515        Ok(())
516    }
517    
518    /// Convert property type to string
519    fn property_type_to_string(prop_type: &PlyPropertyType) -> &'static str {
520        match prop_type {
521            PlyPropertyType::Char => "char",
522            PlyPropertyType::UChar => "uchar",
523            PlyPropertyType::Short => "short",
524            PlyPropertyType::UShort => "ushort",
525            PlyPropertyType::Int => "int",
526            PlyPropertyType::UInt => "uint",
527            PlyPropertyType::Float => "float",
528            PlyPropertyType::Double => "double",
529            PlyPropertyType::List(_, _) => "list", // Should not be called directly for lists
530        }
531    }
532    
533    /// Write ASCII format data
534    fn write_ascii_data<W: std::io::Write>(writer: &mut W, ply_data: &PlyData) -> Result<()> {
535        for element_def in &ply_data.header.elements {
536            if let Some(element_data) = ply_data.elements.get(&element_def.name) {
537                for instance in element_data {
538                    let mut values = Vec::new();
539                    
540                    for property in &element_def.properties {
541                        if let Some(value) = instance.get(&property.name) {
542                            Self::format_ascii_value(value, &mut values)?;
543                        }
544                    }
545                    
546                    writeln!(writer, "{}", values.join(" "))?;
547                }
548            }
549        }
550        Ok(())
551    }
552    
553    /// Format a value for ASCII output
554    fn format_ascii_value(value: &PlyValue, output: &mut Vec<String>) -> Result<()> {
555        match value {
556            PlyValue::Char(v) => output.push(v.to_string()),
557            PlyValue::UChar(v) => output.push(v.to_string()),
558            PlyValue::Short(v) => output.push(v.to_string()),
559            PlyValue::UShort(v) => output.push(v.to_string()),
560            PlyValue::Int(v) => output.push(v.to_string()),
561            PlyValue::UInt(v) => output.push(v.to_string()),
562            PlyValue::Float(v) => output.push(v.to_string()),
563            PlyValue::Double(v) => output.push(v.to_string()),
564            PlyValue::List(values) => {
565                output.push(values.len().to_string());
566                for item in values {
567                    Self::format_ascii_value(item, output)?;
568                }
569            }
570        }
571        Ok(())
572    }
573    
574    /// Write binary format data
575    fn write_binary_data<E: byteorder::ByteOrder, W: std::io::Write>(
576        writer: &mut W,
577        ply_data: &PlyData,
578    ) -> Result<()> {
579        
580        for element_def in &ply_data.header.elements {
581            if let Some(element_data) = ply_data.elements.get(&element_def.name) {
582                for instance in element_data {
583                    for property in &element_def.properties {
584                        if let Some(value) = instance.get(&property.name) {
585                            Self::write_binary_value::<E, _>(writer, value)?;
586                        }
587                    }
588                }
589            }
590        }
591        Ok(())
592    }
593    
594    /// Write a binary value
595    fn write_binary_value<E: byteorder::ByteOrder, W: std::io::Write>(
596        writer: &mut W,
597        value: &PlyValue,
598    ) -> Result<()> {
599        use byteorder::WriteBytesExt;
600        
601        match value {
602            PlyValue::Char(v) => writer.write_i8(*v)?,
603            PlyValue::UChar(v) => writer.write_u8(*v)?,
604            PlyValue::Short(v) => writer.write_i16::<E>(*v)?,
605            PlyValue::UShort(v) => writer.write_u16::<E>(*v)?,
606            PlyValue::Int(v) => writer.write_i32::<E>(*v)?,
607            PlyValue::UInt(v) => writer.write_u32::<E>(*v)?,
608            PlyValue::Float(v) => writer.write_f32::<E>(*v)?,
609            PlyValue::Double(v) => writer.write_f64::<E>(*v)?,
610            PlyValue::List(values) => {
611                // Write count as uchar (assuming list count type is uchar)
612                writer.write_u8(values.len() as u8)?;
613                for item in values {
614                    Self::write_binary_value::<E, _>(writer, item)?;
615                }
616            }
617        }
618        Ok(())
619    }
620    
621    /// Determine property type from PLY value
622    fn value_to_property_type(value: &PlyValue) -> PlyPropertyType {
623        match value {
624            PlyValue::Char(_) => PlyPropertyType::Char,
625            PlyValue::UChar(_) => PlyPropertyType::UChar,
626            PlyValue::Short(_) => PlyPropertyType::Short,
627            PlyValue::UShort(_) => PlyPropertyType::UShort,
628            PlyValue::Int(_) => PlyPropertyType::Int,
629            PlyValue::UInt(_) => PlyPropertyType::UInt,
630            PlyValue::Float(_) => PlyPropertyType::Float,
631            PlyValue::Double(_) => PlyPropertyType::Double,
632            PlyValue::List(values) => {
633                let item_type = if let Some(first_item) = values.first() {
634                    Self::value_to_property_type(first_item)
635                } else {
636                    PlyPropertyType::Int
637                };
638                PlyPropertyType::List(Box::new(PlyPropertyType::UChar), Box::new(item_type))
639            }
640        }
641    }
642}
643
644impl RobustPlyReader {
645    /// Read a complete PLY file with all metadata and elements
646    pub fn read_ply_data<R: BufRead>(reader: &mut R) -> Result<PlyData> {
647        let header = Self::read_header(reader)?;
648        let elements = Self::read_elements(reader, &header)?;
649        
650        Ok(PlyData { header, elements })
651    }
652    
653    /// Read PLY file from path
654    pub fn read_ply_file<P: AsRef<Path>>(path: P) -> Result<PlyData> {
655        let path = path.as_ref();
656        
657        // Try memory-mapped reading for binary files if feature is enabled
658        #[cfg(feature = "io-mmap")]
659        {
660            if let Some(ply_data) = Self::try_read_ply_mmap(path)? {
661                return Ok(ply_data);
662            }
663        }
664        
665        // Fall back to standard buffered reading
666        let file = File::open(path)?;
667        let mut reader = BufReader::new(file);
668        Self::read_ply_data(&mut reader)
669    }
670
671    /// Try to read PLY file using memory mapping (binary files only)
672    #[cfg(feature = "io-mmap")]
673    fn try_read_ply_mmap<P: AsRef<Path>>(path: P) -> Result<Option<PlyData>> {
674        let path = path.as_ref();
675        
676        // Check if we should use memory mapping
677        if !crate::mmap::should_use_mmap(path) {
678            return Ok(None);
679        }
680        
681        // First, read the header using standard I/O to determine format
682        let file = File::open(path)?;
683        let mut reader = BufReader::new(file);
684        let header = Self::read_header(&mut reader)?;
685        
686        // Only use mmap for binary formats
687        match header.format {
688            PlyFormat::BinaryLittleEndian | PlyFormat::BinaryBigEndian => {
689                // Calculate header size by reading until "end_header"
690                let file = File::open(path)?;
691                let mut reader = BufReader::new(file);
692                let mut header_size = 0;
693                let mut line = String::new();
694                
695                loop {
696                    let line_start = header_size;
697                    line.clear();
698                    let bytes_read = reader.read_line(&mut line)?;
699                    if bytes_read == 0 {
700                        return Err(Error::InvalidData("Unexpected end of file in header".to_string()));
701                    }
702                    header_size += bytes_read;
703                    
704                    if line.trim() == "end_header" {
705                        break;
706                    }
707                }
708                
709                // Now use memory mapping for the data section
710                if let Some(mut mmap_reader) = MmapReader::new(path)? {
711                    // Skip to the data section
712                    mmap_reader.seek(header_size)?;
713                    
714                    // Read elements using memory mapping
715                    let elements = Self::read_elements_mmap(&mut mmap_reader, &header)?;
716                    
717                    return Ok(Some(PlyData { header, elements }));
718                }
719            }
720            PlyFormat::Ascii => {
721                // ASCII format - use standard buffered I/O
722                return Ok(None);
723            }
724        }
725        
726        Ok(None)
727    }
728
729    /// Read elements using memory mapping
730    #[cfg(feature = "io-mmap")]
731    fn read_elements_mmap(
732        reader: &mut MmapReader, 
733        header: &PlyHeader
734    ) -> Result<HashMap<String, Vec<HashMap<String, PlyValue>>>> {
735        let mut elements = HashMap::new();
736        
737        for element_def in &header.elements {
738            let mut element_data = Vec::with_capacity(element_def.count);
739            
740            for _ in 0..element_def.count {
741                let mut instance = HashMap::new();
742                
743                match header.format {
744                    PlyFormat::BinaryLittleEndian => {
745                        for property in &element_def.properties {
746                            let value = Self::read_binary_property_value_mmap_le(reader, &property.property_type)?;
747                            instance.insert(property.name.clone(), value);
748                        }
749                    }
750                    PlyFormat::BinaryBigEndian => {
751                        for property in &element_def.properties {
752                            let value = Self::read_binary_property_value_mmap_be(reader, &property.property_type)?;
753                            instance.insert(property.name.clone(), value);
754                        }
755                    }
756                    PlyFormat::Ascii => {
757                        return Err(Error::InvalidData("ASCII format should not use mmap reader".to_string()));
758                    }
759                }
760                
761                element_data.push(instance);
762            }
763            
764            elements.insert(element_def.name.clone(), element_data);
765        }
766        
767        Ok(elements)
768    }
769
770    /// Read binary property value using memory mapping (little endian)
771    #[cfg(feature = "io-mmap")]
772    fn read_binary_property_value_mmap_le(reader: &mut MmapReader, property_type: &PlyPropertyType) -> Result<PlyValue> {
773        match property_type {
774            PlyPropertyType::Char => Ok(PlyValue::Char(reader.read_u8()? as i8)),
775            PlyPropertyType::UChar => Ok(PlyValue::UChar(reader.read_u8()?)),
776            PlyPropertyType::Short => Ok(PlyValue::Short(reader.read_u16_le()? as i16)),
777            PlyPropertyType::UShort => Ok(PlyValue::UShort(reader.read_u16_le()?)),
778            PlyPropertyType::Int => Ok(PlyValue::Int(reader.read_u32_le()? as i32)),
779            PlyPropertyType::UInt => Ok(PlyValue::UInt(reader.read_u32_le()?)),
780            PlyPropertyType::Float => Ok(PlyValue::Float(reader.read_f32_le()?)),
781            PlyPropertyType::Double => Ok(PlyValue::Double(reader.read_f64_le()?)),
782            PlyPropertyType::List(count_type, item_type) => {
783                let count_value = Self::read_binary_property_value_mmap_le(reader, count_type)?;
784                let count = count_value.as_usize()?;
785                
786                let mut list = Vec::with_capacity(count);
787                for _ in 0..count {
788                    let item = Self::read_binary_property_value_mmap_le(reader, item_type)?;
789                    list.push(item);
790                }
791                
792                Ok(PlyValue::List(list))
793            }
794        }
795    }
796
797    /// Read binary property value using memory mapping (big endian)
798    #[cfg(feature = "io-mmap")]
799    fn read_binary_property_value_mmap_be(reader: &mut MmapReader, property_type: &PlyPropertyType) -> Result<PlyValue> {
800        match property_type {
801            PlyPropertyType::Char => Ok(PlyValue::Char(reader.read_u8()? as i8)),
802            PlyPropertyType::UChar => Ok(PlyValue::UChar(reader.read_u8()?)),
803            PlyPropertyType::Short => Ok(PlyValue::Short(reader.read_u16_be()? as i16)),
804            PlyPropertyType::UShort => Ok(PlyValue::UShort(reader.read_u16_be()?)),
805            PlyPropertyType::Int => Ok(PlyValue::Int(reader.read_u32_be()? as i32)),
806            PlyPropertyType::UInt => Ok(PlyValue::UInt(reader.read_u32_be()?)),
807            PlyPropertyType::Float => Ok(PlyValue::Float(reader.read_f32_be()?)),
808            PlyPropertyType::Double => Ok(PlyValue::Double(reader.read_f64_be()?)),
809            PlyPropertyType::List(count_type, item_type) => {
810                let count_value = Self::read_binary_property_value_mmap_be(reader, count_type)?;
811                let count = count_value.as_usize()?;
812                
813                let mut list = Vec::with_capacity(count);
814                for _ in 0..count {
815                    let item = Self::read_binary_property_value_mmap_be(reader, item_type)?;
816                    list.push(item);
817                }
818                
819                Ok(PlyValue::List(list))
820            }
821        }
822    }
823    
824    /// Read and parse PLY header
825    fn read_header<R: BufRead>(reader: &mut R) -> Result<PlyHeader> {
826        let mut format = None;
827        let mut version = "1.0".to_string();
828        let mut elements = Vec::new();
829        let mut comments = Vec::new();
830        let mut obj_info = Vec::new();
831        
832        let mut line = String::new();
833        
834        // Read magic number
835        reader.read_line(&mut line)?;
836        if line.trim() != "ply" {
837            return Err(Error::InvalidData("Not a PLY file - missing magic number".to_string()));
838        }
839        
840        // Parse header lines
841        loop {
842            line.clear();
843            if reader.read_line(&mut line)? == 0 {
844                return Err(Error::InvalidData("Unexpected end of file in header".to_string()));
845            }
846            
847            let line = line.trim();
848            if line == "end_header" {
849                break;
850            }
851            
852            let parts: Vec<&str> = line.split_whitespace().collect();
853            if parts.is_empty() {
854                continue;
855            }
856            
857            match parts[0] {
858                "format" => {
859                    if parts.len() < 3 {
860                        return Err(Error::InvalidData("Invalid format line".to_string()));
861                    }
862                    format = Some(match parts[1] {
863                        "ascii" => PlyFormat::Ascii,
864                        "binary_little_endian" => PlyFormat::BinaryLittleEndian,
865                        "binary_big_endian" => PlyFormat::BinaryBigEndian,
866                        _ => return Err(Error::InvalidData(format!("Unknown format: {}", parts[1]))),
867                    });
868                    version = parts[2].to_string();
869                }
870                "comment" => {
871                    if parts.len() > 1 {
872                        comments.push(parts[1..].join(" "));
873                    }
874                }
875                "obj_info" => {
876                    if parts.len() > 1 {
877                        obj_info.push(parts[1..].join(" "));
878                    }
879                }
880                "element" => {
881                    if parts.len() < 3 {
882                        return Err(Error::InvalidData("Invalid element line".to_string()));
883                    }
884                    let name = parts[1].to_string();
885                    let count: usize = parts[2].parse()
886                        .map_err(|_| Error::InvalidData("Invalid element count".to_string()))?;
887                    elements.push(PlyElement {
888                        name,
889                        count,
890                        properties: Vec::new(),
891                    });
892                }
893                "property" => {
894                    if elements.is_empty() {
895                        return Err(Error::InvalidData("Property without element".to_string()));
896                    }
897                    let property = Self::parse_property(&parts[1..])?;
898                    elements.last_mut().unwrap().properties.push(property);
899                }
900                _ => {
901                    // Ignore unknown header lines
902                }
903            }
904        }
905        
906        let format = format.ok_or_else(|| Error::InvalidData("Missing format specification".to_string()))?;
907        
908        Ok(PlyHeader {
909            format,
910            version,
911            elements,
912            comments,
913            obj_info,
914        })
915    }
916    
917    /// Parse a property definition
918    fn parse_property(parts: &[&str]) -> Result<PlyProperty> {
919        if parts.is_empty() {
920            return Err(Error::InvalidData("Empty property definition".to_string()));
921        }
922        
923        let property_type = if parts[0] == "list" {
924            if parts.len() < 4 {
925                return Err(Error::InvalidData("Invalid list property definition".to_string()));
926            }
927            let count_type = Self::parse_scalar_type(parts[1])?;
928            let item_type = Self::parse_scalar_type(parts[2])?;
929            PlyPropertyType::List(Box::new(count_type), Box::new(item_type))
930        } else {
931            if parts.len() < 2 {
932                return Err(Error::InvalidData("Invalid property definition".to_string()));
933            }
934            Self::parse_scalar_type(parts[0])?
935        };
936        
937        let name = parts.last().unwrap().to_string();
938        
939        Ok(PlyProperty { name, property_type })
940    }
941    
942    /// Parse a scalar type
943    fn parse_scalar_type(type_str: &str) -> Result<PlyPropertyType> {
944        match type_str {
945            "char" | "int8" => Ok(PlyPropertyType::Char),
946            "uchar" | "uint8" => Ok(PlyPropertyType::UChar),
947            "short" | "int16" => Ok(PlyPropertyType::Short),
948            "ushort" | "uint16" => Ok(PlyPropertyType::UShort),
949            "int" | "int32" => Ok(PlyPropertyType::Int),
950            "uint" | "uint32" => Ok(PlyPropertyType::UInt),
951            "float" | "float32" => Ok(PlyPropertyType::Float),
952            "double" | "float64" => Ok(PlyPropertyType::Double),
953            _ => Err(Error::InvalidData(format!("Unknown property type: {}", type_str))),
954        }
955    }
956    
957    /// Read all elements according to header specification
958    fn read_elements<R: BufRead>(reader: &mut R, header: &PlyHeader) -> Result<HashMap<String, Vec<HashMap<String, PlyValue>>>> {
959        let mut elements = HashMap::new();
960        
961        for element_def in &header.elements {
962            let mut element_data = Vec::with_capacity(element_def.count);
963            
964            for _ in 0..element_def.count {
965                let mut instance = HashMap::new();
966                
967                match header.format {
968                    PlyFormat::Ascii => {
969                        let mut line = String::new();
970                        reader.read_line(&mut line)?;
971                        let values = line.trim().split_whitespace().collect::<Vec<_>>();
972                        let mut value_idx = 0;
973                        
974                        for property in &element_def.properties {
975                            let value = Self::read_ascii_property_value(&values, &mut value_idx, &property.property_type)?;
976                            instance.insert(property.name.clone(), value);
977                        }
978                    }
979                    PlyFormat::BinaryLittleEndian => {
980                        for property in &element_def.properties {
981                            let value = Self::read_binary_property_value::<LittleEndian, _>(reader, &property.property_type)?;
982                            instance.insert(property.name.clone(), value);
983                        }
984                    }
985                    PlyFormat::BinaryBigEndian => {
986                        for property in &element_def.properties {
987                            let value = Self::read_binary_property_value::<BigEndian, _>(reader, &property.property_type)?;
988                            instance.insert(property.name.clone(), value);
989                        }
990                    }
991                }
992                
993                element_data.push(instance);
994            }
995            
996            elements.insert(element_def.name.clone(), element_data);
997        }
998        
999        Ok(elements)
1000    }
1001    
1002    /// Read ASCII property value
1003    fn read_ascii_property_value(values: &[&str], value_idx: &mut usize, property_type: &PlyPropertyType) -> Result<PlyValue> {
1004        if *value_idx >= values.len() {
1005            return Err(Error::InvalidData("Not enough values in line".to_string()));
1006        }
1007        
1008        match property_type {
1009            PlyPropertyType::Char => {
1010                let val = values[*value_idx].parse::<i8>()
1011                    .map_err(|_| Error::InvalidData("Invalid char value".to_string()))?;
1012                *value_idx += 1;
1013                Ok(PlyValue::Char(val))
1014            }
1015            PlyPropertyType::UChar => {
1016                let val = values[*value_idx].parse::<u8>()
1017                    .map_err(|_| Error::InvalidData("Invalid uchar value".to_string()))?;
1018                *value_idx += 1;
1019                Ok(PlyValue::UChar(val))
1020            }
1021            PlyPropertyType::Short => {
1022                let val = values[*value_idx].parse::<i16>()
1023                    .map_err(|_| Error::InvalidData("Invalid short value".to_string()))?;
1024                *value_idx += 1;
1025                Ok(PlyValue::Short(val))
1026            }
1027            PlyPropertyType::UShort => {
1028                let val = values[*value_idx].parse::<u16>()
1029                    .map_err(|_| Error::InvalidData("Invalid ushort value".to_string()))?;
1030                *value_idx += 1;
1031                Ok(PlyValue::UShort(val))
1032            }
1033            PlyPropertyType::Int => {
1034                let val = values[*value_idx].parse::<i32>()
1035                    .map_err(|_| Error::InvalidData("Invalid int value".to_string()))?;
1036                *value_idx += 1;
1037                Ok(PlyValue::Int(val))
1038            }
1039            PlyPropertyType::UInt => {
1040                let val = values[*value_idx].parse::<u32>()
1041                    .map_err(|_| Error::InvalidData("Invalid uint value".to_string()))?;
1042                *value_idx += 1;
1043                Ok(PlyValue::UInt(val))
1044            }
1045            PlyPropertyType::Float => {
1046                let val = values[*value_idx].parse::<f32>()
1047                    .map_err(|_| Error::InvalidData("Invalid float value".to_string()))?;
1048                *value_idx += 1;
1049                Ok(PlyValue::Float(val))
1050            }
1051            PlyPropertyType::Double => {
1052                let val = values[*value_idx].parse::<f64>()
1053                    .map_err(|_| Error::InvalidData("Invalid double value".to_string()))?;
1054                *value_idx += 1;
1055                Ok(PlyValue::Double(val))
1056            }
1057            PlyPropertyType::List(count_type, item_type) => {
1058                let count_value = Self::read_ascii_property_value(values, value_idx, count_type)?;
1059                let count = count_value.as_usize()?;
1060                
1061                let mut list = Vec::with_capacity(count);
1062                for _ in 0..count {
1063                    let item = Self::read_ascii_property_value(values, value_idx, item_type)?;
1064                    list.push(item);
1065                }
1066                
1067                Ok(PlyValue::List(list))
1068            }
1069        }
1070    }
1071    
1072    /// Read binary property value
1073    fn read_binary_property_value<E: byteorder::ByteOrder, R: Read>(reader: &mut R, property_type: &PlyPropertyType) -> Result<PlyValue> {
1074        match property_type {
1075            PlyPropertyType::Char => Ok(PlyValue::Char(reader.read_i8()?)),
1076            PlyPropertyType::UChar => Ok(PlyValue::UChar(reader.read_u8()?)),
1077            PlyPropertyType::Short => Ok(PlyValue::Short(reader.read_i16::<E>()?)),
1078            PlyPropertyType::UShort => Ok(PlyValue::UShort(reader.read_u16::<E>()?)),
1079            PlyPropertyType::Int => Ok(PlyValue::Int(reader.read_i32::<E>()?)),
1080            PlyPropertyType::UInt => Ok(PlyValue::UInt(reader.read_u32::<E>()?)),
1081            PlyPropertyType::Float => Ok(PlyValue::Float(reader.read_f32::<E>()?)),
1082            PlyPropertyType::Double => Ok(PlyValue::Double(reader.read_f64::<E>()?)),
1083            PlyPropertyType::List(count_type, item_type) => {
1084                let count_value = Self::read_binary_property_value::<E, _>(reader, count_type)?;
1085                let count = count_value.as_usize()?;
1086                
1087                let mut list = Vec::with_capacity(count);
1088                for _ in 0..count {
1089                    let item = Self::read_binary_property_value::<E, _>(reader, item_type)?;
1090                    list.push(item);
1091                }
1092                
1093                Ok(PlyValue::List(list))
1094            }
1095        }
1096    }
1097}
1098
1099impl PlyValue {
1100    /// Convert PLY value to f32
1101    pub fn as_f32(&self) -> Result<f32> {
1102        match self {
1103            PlyValue::Char(v) => Ok(*v as f32),
1104            PlyValue::UChar(v) => Ok(*v as f32),
1105            PlyValue::Short(v) => Ok(*v as f32),
1106            PlyValue::UShort(v) => Ok(*v as f32),
1107            PlyValue::Int(v) => Ok(*v as f32),
1108            PlyValue::UInt(v) => Ok(*v as f32),
1109            PlyValue::Float(v) => Ok(*v),
1110            PlyValue::Double(v) => Ok(*v as f32),
1111            _ => Err(Error::InvalidData("Cannot convert list to f32".to_string())),
1112        }
1113    }
1114    
1115    /// Convert PLY value to usize
1116    pub fn as_usize(&self) -> Result<usize> {
1117        match self {
1118            PlyValue::UChar(v) => Ok(*v as usize),
1119            PlyValue::UShort(v) => Ok(*v as usize),
1120            PlyValue::UInt(v) => Ok(*v as usize),
1121            PlyValue::Int(v) if *v >= 0 => Ok(*v as usize),
1122            _ => Err(Error::InvalidData("Cannot convert value to usize".to_string())),
1123        }
1124    }
1125    
1126    /// Convert PLY value to Vec<usize> (for face indices)
1127    pub fn as_usize_list(&self) -> Result<Vec<usize>> {
1128        match self {
1129            PlyValue::List(values) => {
1130                values.iter().map(|v| v.as_usize()).collect()
1131            }
1132            _ => Err(Error::InvalidData("Value is not a list".to_string())),
1133        }
1134    }
1135}
1136
1137// Legacy PLY reader/writer using ply-rs for backward compatibility
1138pub struct PlyReader;
1139pub struct PlyWriter;
1140
1141// Implement the new unified traits
1142impl crate::registry::PointCloudReader for PlyReader {
1143    fn read_point_cloud(&self, path: &Path) -> Result<PointCloud<Point3f>> {
1144        // Use the robust reader for better format support
1145        let ply_data = RobustPlyReader::read_ply_file(path)?;
1146        
1147        let mut points = Vec::new();
1148        
1149        if let Some(vertex_elements) = ply_data.elements.get("vertex") {
1150            for vertex in vertex_elements {
1151                let x = vertex.get("x")
1152                    .ok_or_else(|| Error::InvalidData("Missing x coordinate".to_string()))?
1153                    .as_f32()?;
1154                let y = vertex.get("y")
1155                    .ok_or_else(|| Error::InvalidData("Missing y coordinate".to_string()))?
1156                    .as_f32()?;
1157                let z = vertex.get("z")
1158                    .ok_or_else(|| Error::InvalidData("Missing z coordinate".to_string()))?
1159                    .as_f32()?;
1160                
1161                points.push(Point3f::new(x, y, z));
1162            }
1163        }
1164        
1165        Ok(PointCloud::from_points(points))
1166    }
1167    
1168    fn can_read(&self, path: &Path) -> bool {
1169        // Check if file starts with "ply"
1170        if let Ok(mut file) = File::open(path) {
1171            let mut header = [0u8; 4];
1172            if let Ok(_) = file.read(&mut header) {
1173                return header.starts_with(b"ply");
1174            }
1175        }
1176        false
1177    }
1178    
1179    fn format_name(&self) -> &'static str {
1180        "ply"
1181    }
1182}
1183
1184impl crate::registry::MeshReader for PlyReader {
1185    fn read_mesh(&self, path: &Path) -> Result<TriangleMesh> {
1186        let ply_data = RobustPlyReader::read_ply_file(path)?;
1187        
1188        // Extract vertices
1189        let mut vertices = Vec::new();
1190        if let Some(vertex_elements) = ply_data.elements.get("vertex") {
1191            for vertex in vertex_elements {
1192                let x = vertex.get("x")
1193                    .ok_or_else(|| Error::InvalidData("Missing x coordinate".to_string()))?
1194                    .as_f32()?;
1195                let y = vertex.get("y")
1196                    .ok_or_else(|| Error::InvalidData("Missing y coordinate".to_string()))?
1197                    .as_f32()?;
1198                let z = vertex.get("z")
1199                    .ok_or_else(|| Error::InvalidData("Missing z coordinate".to_string()))?
1200                    .as_f32()?;
1201                
1202                vertices.push(Point3f::new(x, y, z));
1203            }
1204        }
1205        
1206        // Extract faces
1207        let mut faces = Vec::new();
1208        if let Some(face_elements) = ply_data.elements.get("face") {
1209            for face in face_elements {
1210                // Look for vertex_indices or vertex_index property
1211                let indices = if let Some(vertex_indices) = face.get("vertex_indices") {
1212                    vertex_indices.as_usize_list()?
1213                } else if let Some(vertex_index) = face.get("vertex_index") {
1214                    vertex_index.as_usize_list()?
1215                } else {
1216                    return Err(Error::InvalidData("Face missing vertex indices".to_string()));
1217                };
1218                
1219                // Convert to triangles (assuming triangular faces or taking first 3 vertices)
1220                if indices.len() >= 3 {
1221                    faces.push([indices[0], indices[1], indices[2]]);
1222                }
1223                // For quads and other polygons, we could triangulate here
1224                if indices.len() == 4 {
1225                    faces.push([indices[0], indices[2], indices[3]]);
1226                }
1227            }
1228        }
1229        
1230        // Extract normals if available
1231        let normals = if let Some(vertex_elements) = ply_data.elements.get("vertex") {
1232            let mut normals = Vec::new();
1233            let mut has_normals = true;
1234            
1235            for vertex in vertex_elements {
1236                if let (Some(nx), Some(ny), Some(nz)) = (
1237                    vertex.get("nx"),
1238                    vertex.get("ny"), 
1239                    vertex.get("nz"),
1240                ) {
1241                    normals.push(Vector3f::new(
1242                        nx.as_f32()?,
1243                        ny.as_f32()?,
1244                        nz.as_f32()?,
1245                    ));
1246                } else {
1247                    has_normals = false;
1248                    break;
1249                }
1250            }
1251            
1252            if has_normals {
1253                Some(normals)
1254            } else {
1255                None
1256            }
1257        } else {
1258            None
1259        };
1260        
1261        let mut mesh = TriangleMesh::from_vertices_and_faces(vertices, faces);
1262        if let Some(normals) = normals {
1263            mesh.set_normals(normals);
1264        }
1265        
1266        Ok(mesh)
1267    }
1268    
1269    fn can_read(&self, path: &Path) -> bool {
1270        // Check if file starts with "ply"
1271        if let Ok(mut file) = File::open(path) {
1272            let mut header = [0u8; 4];
1273            if let Ok(_) = file.read(&mut header) {
1274                return header.starts_with(b"ply");
1275            }
1276        }
1277        false
1278    }
1279    
1280    fn format_name(&self) -> &'static str {
1281        "ply"
1282    }
1283}
1284
1285impl crate::registry::PointCloudWriter for PlyWriter {
1286    fn write_point_cloud(&self, cloud: &PointCloud<Point3f>, path: &Path) -> Result<()> {
1287        // Use the legacy ply-rs for writing for now
1288        use ply_rs::{
1289            writer::Writer,
1290            ply::{Property, PropertyDef, PropertyType, ScalarType, ElementDef, Ply, Addable, DefaultElement},
1291        };
1292        
1293        let file = File::create(path)?;
1294        let mut writer = BufWriter::new(file);
1295        
1296        // Create PLY structure
1297        let mut ply = Ply::<DefaultElement>::new();
1298        
1299        // Define vertex element
1300        let mut vertex_element = ElementDef::new("vertex".to_string());
1301        vertex_element.count = cloud.len();
1302        vertex_element.properties.add(PropertyDef::new(
1303            "x".to_string(),
1304            PropertyType::Scalar(ScalarType::Float),
1305        ));
1306        vertex_element.properties.add(PropertyDef::new(
1307            "y".to_string(),
1308            PropertyType::Scalar(ScalarType::Float),
1309        ));
1310        vertex_element.properties.add(PropertyDef::new(
1311            "z".to_string(),
1312            PropertyType::Scalar(ScalarType::Float),
1313        ));
1314        
1315        ply.header.elements.add(vertex_element);
1316        
1317        // Add vertex data
1318        let mut vertices = Vec::new();
1319        for point in &cloud.points {
1320            let mut vertex = DefaultElement::new();
1321            vertex.insert("x".to_string(), Property::Float(point.x));
1322            vertex.insert("y".to_string(), Property::Float(point.y));
1323            vertex.insert("z".to_string(), Property::Float(point.z));
1324            vertices.push(vertex);
1325        }
1326        ply.payload.insert("vertex".to_string(), vertices);
1327        
1328        // Write PLY file
1329        let writer_instance = Writer::new();
1330        writer_instance.write_ply(&mut writer, &mut ply)?;
1331        
1332        Ok(())
1333    }
1334    
1335    fn format_name(&self) -> &'static str {
1336        "ply"
1337    }
1338}
1339
1340impl crate::registry::MeshWriter for PlyWriter {
1341    fn write_mesh(&self, mesh: &TriangleMesh, path: &Path) -> Result<()> {
1342        // Use the legacy ply-rs for writing for now
1343        use ply_rs::{
1344            writer::Writer,
1345            ply::{Property, PropertyDef, PropertyType, ScalarType, ElementDef, Ply, Addable, DefaultElement},
1346        };
1347        
1348        let file = File::create(path)?;
1349        let mut writer = BufWriter::new(file);
1350        
1351        // Create PLY structure
1352        let mut ply = Ply::<DefaultElement>::new();
1353        
1354        // Define vertex element
1355        let mut vertex_element = ElementDef::new("vertex".to_string());
1356        vertex_element.count = mesh.vertices.len();
1357        vertex_element.properties.add(PropertyDef::new(
1358            "x".to_string(),
1359            PropertyType::Scalar(ScalarType::Float),
1360        ));
1361        vertex_element.properties.add(PropertyDef::new(
1362            "y".to_string(),
1363            PropertyType::Scalar(ScalarType::Float),
1364        ));
1365        vertex_element.properties.add(PropertyDef::new(
1366            "z".to_string(),
1367            PropertyType::Scalar(ScalarType::Float),
1368        ));
1369        
1370        // Add normal properties if available
1371        if mesh.normals.is_some() {
1372            vertex_element.properties.add(PropertyDef::new(
1373                "nx".to_string(),
1374                PropertyType::Scalar(ScalarType::Float),
1375            ));
1376            vertex_element.properties.add(PropertyDef::new(
1377                "ny".to_string(),
1378                PropertyType::Scalar(ScalarType::Float),
1379            ));
1380            vertex_element.properties.add(PropertyDef::new(
1381                "nz".to_string(),
1382                PropertyType::Scalar(ScalarType::Float),
1383            ));
1384        }
1385        
1386        ply.header.elements.add(vertex_element);
1387        
1388        // Add vertex data
1389        let mut vertices = Vec::new();
1390        for (i, point) in mesh.vertices.iter().enumerate() {
1391            let mut vertex = DefaultElement::new();
1392            vertex.insert("x".to_string(), Property::Float(point.x));
1393            vertex.insert("y".to_string(), Property::Float(point.y));
1394            vertex.insert("z".to_string(), Property::Float(point.z));
1395            
1396            // Add normals if available
1397            if let Some(ref normals) = mesh.normals {
1398                if i < normals.len() {
1399                    vertex.insert("nx".to_string(), Property::Float(normals[i].x));
1400                    vertex.insert("ny".to_string(), Property::Float(normals[i].y));
1401                    vertex.insert("nz".to_string(), Property::Float(normals[i].z));
1402                }
1403            }
1404            
1405            vertices.push(vertex);
1406        }
1407        ply.payload.insert("vertex".to_string(), vertices);
1408        
1409        // Define face element if we have faces
1410        if !mesh.faces.is_empty() {
1411            let mut face_element = ElementDef::new("face".to_string());
1412            face_element.count = mesh.faces.len();
1413            face_element.properties.add(PropertyDef::new(
1414                "vertex_indices".to_string(),
1415                PropertyType::List(ScalarType::UChar, ScalarType::Int),
1416            ));
1417            
1418            ply.header.elements.add(face_element);
1419            
1420            // Add face data
1421            let mut faces = Vec::new();
1422            for face in &mesh.faces {
1423                let mut face_data = DefaultElement::new();
1424                let indices = vec![
1425                    face[0] as i32,
1426                    face[1] as i32,
1427                    face[2] as i32,
1428                ];
1429                face_data.insert("vertex_indices".to_string(), Property::ListInt(indices));
1430                faces.push(face_data);
1431            }
1432            ply.payload.insert("face".to_string(), faces);
1433        }
1434        
1435        // Write PLY file
1436        let writer_instance = Writer::new();
1437        writer_instance.write_ply(&mut writer, &mut ply)?;
1438        
1439        Ok(())
1440    }
1441    
1442    fn format_name(&self) -> &'static str {
1443        "ply"
1444    }
1445}
1446
1447// Keep the legacy trait implementations for backward compatibility
1448impl PointCloudReader for PlyReader {
1449    fn read_point_cloud<P: AsRef<Path>>(path: P) -> Result<PointCloud<Point3f>> {
1450        let reader = PlyReader;
1451        crate::registry::PointCloudReader::read_point_cloud(&reader, path.as_ref())
1452    }
1453}
1454
1455impl MeshReader for PlyReader {
1456    fn read_mesh<P: AsRef<Path>>(path: P) -> Result<TriangleMesh> {
1457        let reader = PlyReader;
1458        crate::registry::MeshReader::read_mesh(&reader, path.as_ref())
1459    }
1460}
1461
1462impl PointCloudWriter for PlyWriter {
1463    fn write_point_cloud<P: AsRef<Path>>(cloud: &PointCloud<Point3f>, path: P) -> Result<()> {
1464        let writer = PlyWriter;
1465        crate::registry::PointCloudWriter::write_point_cloud(&writer, cloud, path.as_ref())
1466    }
1467}
1468
1469impl MeshWriter for PlyWriter {
1470    fn write_mesh<P: AsRef<Path>>(mesh: &TriangleMesh, path: P) -> Result<()> {
1471        let writer = PlyWriter;
1472        crate::registry::MeshWriter::write_mesh(&writer, mesh, path.as_ref())
1473    }
1474}
1475
1476/// Streaming PLY reader for point clouds
1477pub struct PlyStreamingReader {
1478    reader: BufReader<File>,
1479    current_count: usize,
1480    current_index: usize,
1481    chunk_size: usize,
1482    buffer: Vec<u8>,
1483    format: PlyFormat,
1484}
1485
1486impl PlyStreamingReader {
1487    /// Create a new streaming PLY reader
1488    pub fn new<P: AsRef<Path>>(path: P, chunk_size: usize) -> Result<Self> {
1489        let file = File::open(path)?;
1490        let mut reader = BufReader::new(file);
1491        let header = RobustPlyReader::read_header(&mut reader)?;
1492        
1493        // Find vertex element
1494        let vertex_element = header.elements.iter()
1495            .find(|e| e.name == "vertex")
1496            .ok_or_else(|| Error::InvalidData("No vertex element found in PLY file".to_string()))?;
1497        
1498        Ok(Self {
1499            reader,
1500            current_count: vertex_element.count,
1501            current_index: 0,
1502            chunk_size,
1503            buffer: Vec::with_capacity(chunk_size * 12), // Estimate 12 bytes per vertex
1504            format: header.format,
1505        })
1506    }
1507}
1508
1509impl Iterator for PlyStreamingReader {
1510    type Item = Result<Point3f>;
1511    
1512    fn next(&mut self) -> Option<Self::Item> {
1513        if self.current_index >= self.current_count {
1514            return None;
1515        }
1516        
1517        // Read a chunk if buffer is empty
1518        if self.buffer.is_empty() {
1519            let remaining = self.current_count - self.current_index;
1520            let to_read = std::cmp::min(remaining, self.chunk_size);
1521            
1522            match self.format {
1523                PlyFormat::Ascii => {
1524                    // For ASCII, we need to read line by line
1525                    let mut line = String::new();
1526                    if let Err(e) = self.reader.read_line(&mut line) {
1527                        return Some(Err(Error::Io(e)));
1528                    }
1529                    
1530                    let values: Vec<&str> = line.trim().split_whitespace().collect();
1531                    if values.len() < 3 {
1532                        return Some(Err(Error::InvalidData("Not enough coordinates in vertex line".to_string())));
1533                    }
1534                    
1535                    let x = match values[0].parse::<f32>() {
1536                        Ok(v) => v,
1537                        Err(_) => return Some(Err(Error::InvalidData("Invalid x coordinate".to_string()))),
1538                    };
1539                    let y = match values[1].parse::<f32>() {
1540                        Ok(v) => v,
1541                        Err(_) => return Some(Err(Error::InvalidData("Invalid y coordinate".to_string()))),
1542                    };
1543                    let z = match values[2].parse::<f32>() {
1544                        Ok(v) => v,
1545                        Err(_) => return Some(Err(Error::InvalidData("Invalid z coordinate".to_string()))),
1546                    };
1547                    
1548                    self.current_index += 1;
1549                    return Some(Ok(Point3f::new(x, y, z)));
1550                }
1551                PlyFormat::BinaryLittleEndian => {
1552                    // For binary, read the chunk
1553                    let bytes_per_vertex = 12; // 3 * f32
1554                    let chunk_bytes = to_read * bytes_per_vertex;
1555                    self.buffer.resize(chunk_bytes, 0);
1556                    
1557                    if let Err(e) = self.reader.read_exact(&mut self.buffer[..chunk_bytes]) {
1558                        return Some(Err(Error::Io(e)));
1559                    }
1560                }
1561                PlyFormat::BinaryBigEndian => {
1562                    // For binary, read the chunk
1563                    let bytes_per_vertex = 12; // 3 * f32
1564                    let chunk_bytes = to_read * bytes_per_vertex;
1565                    self.buffer.resize(chunk_bytes, 0);
1566                    
1567                    if let Err(e) = self.reader.read_exact(&mut self.buffer[..chunk_bytes]) {
1568                        return Some(Err(Error::Io(e)));
1569                    }
1570                }
1571            }
1572        }
1573        
1574        // Extract point from buffer
1575        match self.format {
1576            PlyFormat::Ascii => {
1577                // Already handled above
1578                unreachable!()
1579            }
1580            PlyFormat::BinaryLittleEndian => {
1581                if self.buffer.len() < 12 {
1582                    return Some(Err(Error::InvalidData("Insufficient data in buffer".to_string())));
1583                }
1584                
1585                let x = f32::from_le_bytes([
1586                    self.buffer[0], self.buffer[1], self.buffer[2], self.buffer[3]
1587                ]);
1588                let y = f32::from_le_bytes([
1589                    self.buffer[4], self.buffer[5], self.buffer[6], self.buffer[7]
1590                ]);
1591                let z = f32::from_le_bytes([
1592                    self.buffer[8], self.buffer[9], self.buffer[10], self.buffer[11]
1593                ]);
1594                
1595                // Remove processed data from buffer
1596                self.buffer.drain(0..12);
1597                self.current_index += 1;
1598                
1599                Some(Ok(Point3f::new(x, y, z)))
1600            }
1601            PlyFormat::BinaryBigEndian => {
1602                if self.buffer.len() < 12 {
1603                    return Some(Err(Error::InvalidData("Insufficient data in buffer".to_string())));
1604                }
1605                
1606                let x = f32::from_be_bytes([
1607                    self.buffer[0], self.buffer[1], self.buffer[2], self.buffer[3]
1608                ]);
1609                let y = f32::from_be_bytes([
1610                    self.buffer[4], self.buffer[5], self.buffer[6], self.buffer[7]
1611                ]);
1612                let z = f32::from_be_bytes([
1613                    self.buffer[8], self.buffer[9], self.buffer[10], self.buffer[11]
1614                ]);
1615                
1616                // Remove processed data from buffer
1617                self.buffer.drain(0..12);
1618                self.current_index += 1;
1619                
1620                Some(Ok(Point3f::new(x, y, z)))
1621            }
1622        }
1623    }
1624}
1625
1626/// Streaming PLY reader for mesh faces
1627pub struct PlyMeshStreamingReader {
1628    reader: BufReader<File>,
1629    current_count: usize,
1630    current_index: usize,
1631    chunk_size: usize,
1632    buffer: Vec<u8>,
1633    format: PlyFormat,
1634    vertices: Vec<Point3f>, // We need to store vertices to convert face indices
1635}
1636
1637impl PlyMeshStreamingReader {
1638    /// Create a new streaming PLY mesh reader
1639    pub fn new<P: AsRef<Path>>(path: P, chunk_size: usize) -> Result<Self> {
1640        let file = File::open(path)?;
1641        let mut reader = BufReader::new(file);
1642        let header = RobustPlyReader::read_header(&mut reader)?;
1643        
1644        // Find face element
1645        let face_element = header.elements.iter()
1646            .find(|e| e.name == "face")
1647            .ok_or_else(|| Error::InvalidData("No face element found in PLY file".to_string()))?;
1648        
1649        // First, we need to read all vertices to convert face indices
1650        let vertex_element = header.elements.iter()
1651            .find(|e| e.name == "vertex")
1652            .ok_or_else(|| Error::InvalidData("No vertex element found in PLY file".to_string()))?;
1653        
1654        let mut vertices = Vec::with_capacity(vertex_element.count);
1655        
1656        // Read vertices first
1657        for _ in 0..vertex_element.count {
1658            let mut line = String::new();
1659            reader.read_line(&mut line)?;
1660            let values: Vec<&str> = line.trim().split_whitespace().collect();
1661            if values.len() >= 3 {
1662                let x = values[0].parse::<f32>()
1663                    .map_err(|_| Error::InvalidData("Invalid x coordinate".to_string()))?;
1664                let y = values[1].parse::<f32>()
1665                    .map_err(|_| Error::InvalidData("Invalid y coordinate".to_string()))?;
1666                let z = values[2].parse::<f32>()
1667                    .map_err(|_| Error::InvalidData("Invalid z coordinate".to_string()))?;
1668                vertices.push(Point3f::new(x, y, z));
1669            }
1670        }
1671        
1672        Ok(Self {
1673            reader,
1674            current_count: face_element.count,
1675            current_index: 0,
1676            chunk_size,
1677            buffer: Vec::with_capacity(chunk_size * 16), // Estimate 16 bytes per face
1678            format: header.format,
1679            vertices,
1680        })
1681    }
1682}
1683
1684impl Iterator for PlyMeshStreamingReader {
1685    type Item = Result<[usize; 3]>;
1686    
1687    fn next(&mut self) -> Option<Self::Item> {
1688        if self.current_index >= self.current_count {
1689            return None;
1690        }
1691        
1692        // Read a chunk if buffer is empty
1693        if self.buffer.is_empty() {
1694            let remaining = self.current_count - self.current_index;
1695            let to_read = std::cmp::min(remaining, self.chunk_size);
1696            
1697            match self.format {
1698                PlyFormat::Ascii => {
1699                    // For ASCII, we need to read line by line
1700                    let mut line = String::new();
1701                    if let Err(e) = self.reader.read_line(&mut line) {
1702                        return Some(Err(Error::Io(e)));
1703                    }
1704                    
1705                    let values: Vec<&str> = line.trim().split_whitespace().collect();
1706                    if values.len() < 4 { // count + 3 indices
1707                        return Some(Err(Error::InvalidData("Not enough values in face line".to_string())));
1708                    }
1709                    
1710                    let count = match values[0].parse::<usize>() {
1711                        Ok(v) => v,
1712                        Err(_) => return Some(Err(Error::InvalidData("Invalid face count".to_string()))),
1713                    };
1714                    
1715                    if count != 3 {
1716                        return Some(Err(Error::InvalidData("Only triangular faces supported".to_string())));
1717                    }
1718                    
1719                    let i1 = match values[1].parse::<usize>() {
1720                        Ok(v) => v,
1721                        Err(_) => return Some(Err(Error::InvalidData("Invalid face index".to_string()))),
1722                    };
1723                    let i2 = match values[2].parse::<usize>() {
1724                        Ok(v) => v,
1725                        Err(_) => return Some(Err(Error::InvalidData("Invalid face index".to_string()))),
1726                    };
1727                    let i3 = match values[3].parse::<usize>() {
1728                        Ok(v) => v,
1729                        Err(_) => return Some(Err(Error::InvalidData("Invalid face index".to_string()))),
1730                    };
1731                    
1732                    // Validate indices
1733                    if i1 >= self.vertices.len() || i2 >= self.vertices.len() || i3 >= self.vertices.len() {
1734                        return Some(Err(Error::InvalidData("Face index out of range".to_string())));
1735                    }
1736                    
1737                    self.current_index += 1;
1738                    return Some(Ok([i1, i2, i3]));
1739                }
1740                PlyFormat::BinaryLittleEndian | PlyFormat::BinaryBigEndian => {
1741                    // For binary, read the chunk
1742                    let bytes_per_face = 16; // 1 uchar count + 3 * u32 indices
1743                    let chunk_bytes = to_read * bytes_per_face;
1744                    self.buffer.resize(chunk_bytes, 0);
1745                    
1746                    if let Err(e) = self.reader.read_exact(&mut self.buffer[..chunk_bytes]) {
1747                        return Some(Err(Error::Io(e)));
1748                    }
1749                }
1750            }
1751        }
1752        
1753        // Extract face from buffer
1754        match self.format {
1755            PlyFormat::Ascii => {
1756                // Already handled above
1757                unreachable!()
1758            }
1759            PlyFormat::BinaryLittleEndian => {
1760                if self.buffer.len() < 16 {
1761                    return Some(Err(Error::InvalidData("Insufficient data in buffer".to_string())));
1762                }
1763                
1764                let count = self.buffer[0] as usize;
1765                if count != 3 {
1766                    return Some(Err(Error::InvalidData("Only triangular faces supported".to_string())));
1767                }
1768                
1769                let i1 = u32::from_le_bytes([
1770                    self.buffer[1], self.buffer[2], self.buffer[3], self.buffer[4]
1771                ]) as usize;
1772                let i2 = u32::from_le_bytes([
1773                    self.buffer[5], self.buffer[6], self.buffer[7], self.buffer[8]
1774                ]) as usize;
1775                let i3 = u32::from_le_bytes([
1776                    self.buffer[9], self.buffer[10], self.buffer[11], self.buffer[12]
1777                ]) as usize;
1778                
1779                // Validate indices
1780                if i1 >= self.vertices.len() || i2 >= self.vertices.len() || i3 >= self.vertices.len() {
1781                    return Some(Err(Error::InvalidData("Face index out of range".to_string())));
1782                }
1783                
1784                // Remove processed data from buffer
1785                self.buffer.drain(0..16);
1786                self.current_index += 1;
1787                
1788                Some(Ok([i1, i2, i3]))
1789            }
1790            PlyFormat::BinaryBigEndian => {
1791                if self.buffer.len() < 16 {
1792                    return Some(Err(Error::InvalidData("Insufficient data in buffer".to_string())));
1793                }
1794                
1795                let count = self.buffer[0] as usize;
1796                if count != 3 {
1797                    return Some(Err(Error::InvalidData("Only triangular faces supported".to_string())));
1798                }
1799                
1800                let i1 = u32::from_be_bytes([
1801                    self.buffer[1], self.buffer[2], self.buffer[3], self.buffer[4]
1802                ]) as usize;
1803                let i2 = u32::from_be_bytes([
1804                    self.buffer[5], self.buffer[6], self.buffer[7], self.buffer[8]
1805                ]) as usize;
1806                let i3 = u32::from_be_bytes([
1807                    self.buffer[9], self.buffer[10], self.buffer[11], self.buffer[12]
1808                ]) as usize;
1809                
1810                // Validate indices
1811                if i1 >= self.vertices.len() || i2 >= self.vertices.len() || i3 >= self.vertices.len() {
1812                    return Some(Err(Error::InvalidData("Face index out of range".to_string())));
1813                }
1814                
1815                // Remove processed data from buffer
1816                self.buffer.drain(0..16);
1817                self.current_index += 1;
1818                
1819                Some(Ok([i1, i2, i3]))
1820            }
1821        }
1822    }
1823}