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
1224/// Positions straight from the file when the vertex element comes first and
1225/// has only scalar properties (the usual layout). `None` means the general
1226/// reader is needed.
1227fn read_positions_fast(path: &Path) -> Result<Option<Vec<Point3f>>> {
1228    use crate::records::{read_ascii_xyz, read_binary_xyz, BinaryXyz, Scalar};
1229    let mut reader = BufReader::new(File::open(path)?);
1230    let header = RobustPlyReader::read_header(&mut reader)?;
1231    let Some(vertex) = header.elements.first().filter(|e| e.name == "vertex") else {
1232        return Ok(None);
1233    };
1234    let mut offset = 0;
1235    let mut found = [None; 3];
1236    for (column, property) in vertex.properties.iter().enumerate() {
1237        let scalar = match property.property_type {
1238            PlyPropertyType::Char => Scalar::I8,
1239            PlyPropertyType::UChar => Scalar::U8,
1240            PlyPropertyType::Short => Scalar::I16,
1241            PlyPropertyType::UShort => Scalar::U16,
1242            PlyPropertyType::Int => Scalar::I32,
1243            PlyPropertyType::UInt => Scalar::U32,
1244            PlyPropertyType::Float => Scalar::F32,
1245            PlyPropertyType::Double => Scalar::F64,
1246            PlyPropertyType::List(..) => return Ok(None),
1247        };
1248        if let Some(axis) = ["x", "y", "z"].iter().position(|n| *n == property.name) {
1249            found[axis] = Some((column, offset, scalar));
1250        }
1251        offset += scalar.size();
1252    }
1253    let [Some(x), Some(y), Some(z)] = found else {
1254        return Err(Error::InvalidData(
1255            "PLY vertex element is missing x, y or z".to_string(),
1256        ));
1257    };
1258    let points = match header.format {
1259        PlyFormat::Ascii => read_ascii_xyz(&mut reader, [x.0, y.0, z.0], vertex.count)?,
1260        PlyFormat::BinaryLittleEndian | PlyFormat::BinaryBigEndian => {
1261            let layout = BinaryXyz {
1262                record_size: offset,
1263                xyz: [(x.1, x.2), (y.1, y.2), (z.1, z.2)],
1264                big_endian: header.format == PlyFormat::BinaryBigEndian,
1265            };
1266            read_binary_xyz(&mut reader, &layout, vertex.count)?
1267        }
1268    };
1269    Ok(Some(points))
1270}
1271
1272impl crate::registry::PointCloudReader for PlyReader {
1273    fn read_point_cloud(&self, path: &Path) -> Result<PointCloud<Point3f>> {
1274        if let Some(points) = read_positions_fast(path)? {
1275            return Ok(PointCloud::from_points(points));
1276        }
1277        // Use the robust reader for better format support
1278        let ply_data = RobustPlyReader::read_ply_file(path)?;
1279
1280        let mut points = Vec::new();
1281
1282        if let Some(vertex_elements) = ply_data.elements.get("vertex") {
1283            for vertex in vertex_elements {
1284                let x = vertex
1285                    .get("x")
1286                    .ok_or_else(|| Error::InvalidData("Missing x coordinate".to_string()))?
1287                    .as_f32()?;
1288                let y = vertex
1289                    .get("y")
1290                    .ok_or_else(|| Error::InvalidData("Missing y coordinate".to_string()))?
1291                    .as_f32()?;
1292                let z = vertex
1293                    .get("z")
1294                    .ok_or_else(|| Error::InvalidData("Missing z coordinate".to_string()))?
1295                    .as_f32()?;
1296
1297                points.push(Point3f::new(x, y, z));
1298            }
1299        }
1300
1301        Ok(PointCloud::from_points(points))
1302    }
1303
1304    fn can_read(&self, path: &Path) -> bool {
1305        // Check if file starts with "ply"
1306        if let Ok(mut file) = File::open(path) {
1307            let mut header = [0u8; 4];
1308            if let Ok(_) = file.read(&mut header) {
1309                return header.starts_with(b"ply");
1310            }
1311        }
1312        false
1313    }
1314
1315    fn format_name(&self) -> &'static str {
1316        "ply"
1317    }
1318}
1319
1320impl crate::registry::MeshReader for PlyReader {
1321    fn read_mesh(&self, path: &Path) -> Result<TriangleMesh> {
1322        let ply_data = RobustPlyReader::read_ply_file(path)?;
1323
1324        // Extract vertices
1325        let mut vertices = Vec::new();
1326        if let Some(vertex_elements) = ply_data.elements.get("vertex") {
1327            for vertex in vertex_elements {
1328                let x = vertex
1329                    .get("x")
1330                    .ok_or_else(|| Error::InvalidData("Missing x coordinate".to_string()))?
1331                    .as_f32()?;
1332                let y = vertex
1333                    .get("y")
1334                    .ok_or_else(|| Error::InvalidData("Missing y coordinate".to_string()))?
1335                    .as_f32()?;
1336                let z = vertex
1337                    .get("z")
1338                    .ok_or_else(|| Error::InvalidData("Missing z coordinate".to_string()))?
1339                    .as_f32()?;
1340
1341                vertices.push(Point3f::new(x, y, z));
1342            }
1343        }
1344
1345        // Extract faces
1346        let mut faces = Vec::new();
1347        if let Some(face_elements) = ply_data.elements.get("face") {
1348            for face in face_elements {
1349                // Look for vertex_indices or vertex_index property
1350                let indices = if let Some(vertex_indices) = face.get("vertex_indices") {
1351                    vertex_indices.as_usize_list()?
1352                } else if let Some(vertex_index) = face.get("vertex_index") {
1353                    vertex_index.as_usize_list()?
1354                } else {
1355                    return Err(Error::InvalidData(
1356                        "Face missing vertex indices".to_string(),
1357                    ));
1358                };
1359
1360                // Convert to triangles (assuming triangular faces or taking first 3 vertices)
1361                if indices.len() >= 3 {
1362                    faces.push([indices[0], indices[1], indices[2]]);
1363                }
1364                // For quads and other polygons, we could triangulate here
1365                if indices.len() == 4 {
1366                    faces.push([indices[0], indices[2], indices[3]]);
1367                }
1368            }
1369        }
1370
1371        // Extract normals if available
1372        let normals = if let Some(vertex_elements) = ply_data.elements.get("vertex") {
1373            let mut normals = Vec::new();
1374            let mut has_normals = true;
1375
1376            for vertex in vertex_elements {
1377                if let (Some(nx), Some(ny), Some(nz)) =
1378                    (vertex.get("nx"), vertex.get("ny"), vertex.get("nz"))
1379                {
1380                    normals.push(Vector3f::new(nx.as_f32()?, ny.as_f32()?, nz.as_f32()?));
1381                } else {
1382                    has_normals = false;
1383                    break;
1384                }
1385            }
1386
1387            if has_normals {
1388                Some(normals)
1389            } else {
1390                None
1391            }
1392        } else {
1393            None
1394        };
1395
1396        let mut mesh = TriangleMesh::from_vertices_and_faces(vertices, faces);
1397        if let Some(normals) = normals {
1398            mesh.set_normals(normals);
1399        }
1400
1401        Ok(mesh)
1402    }
1403
1404    fn can_read(&self, path: &Path) -> bool {
1405        // Check if file starts with "ply"
1406        if let Ok(mut file) = File::open(path) {
1407            let mut header = [0u8; 4];
1408            if let Ok(_) = file.read(&mut header) {
1409                return header.starts_with(b"ply");
1410            }
1411        }
1412        false
1413    }
1414
1415    fn format_name(&self) -> &'static str {
1416        "ply"
1417    }
1418}
1419
1420impl crate::registry::PointCloudWriter for PlyWriter {
1421    fn write_point_cloud(&self, cloud: &PointCloud<Point3f>, path: &Path) -> Result<()> {
1422        // Use the legacy ply-rs for writing for now
1423        use ply_rs::{
1424            ply::{
1425                Addable, DefaultElement, ElementDef, Ply, Property, PropertyDef, PropertyType,
1426                ScalarType,
1427            },
1428            writer::Writer,
1429        };
1430
1431        let file = File::create(path)?;
1432        let mut writer = BufWriter::new(file);
1433
1434        // Create PLY structure
1435        let mut ply = Ply::<DefaultElement>::new();
1436
1437        // Define vertex element
1438        let mut vertex_element = ElementDef::new("vertex".to_string());
1439        vertex_element.count = cloud.len();
1440        vertex_element.properties.add(PropertyDef::new(
1441            "x".to_string(),
1442            PropertyType::Scalar(ScalarType::Float),
1443        ));
1444        vertex_element.properties.add(PropertyDef::new(
1445            "y".to_string(),
1446            PropertyType::Scalar(ScalarType::Float),
1447        ));
1448        vertex_element.properties.add(PropertyDef::new(
1449            "z".to_string(),
1450            PropertyType::Scalar(ScalarType::Float),
1451        ));
1452
1453        ply.header.elements.add(vertex_element);
1454
1455        // Add vertex data
1456        let mut vertices = Vec::new();
1457        for point in &cloud.points {
1458            let mut vertex = DefaultElement::new();
1459            vertex.insert("x".to_string(), Property::Float(point.x));
1460            vertex.insert("y".to_string(), Property::Float(point.y));
1461            vertex.insert("z".to_string(), Property::Float(point.z));
1462            vertices.push(vertex);
1463        }
1464        ply.payload.insert("vertex".to_string(), vertices);
1465
1466        // Write PLY file
1467        let writer_instance = Writer::new();
1468        writer_instance.write_ply(&mut writer, &mut ply)?;
1469
1470        Ok(())
1471    }
1472
1473    fn format_name(&self) -> &'static str {
1474        "ply"
1475    }
1476}
1477
1478impl crate::registry::MeshWriter for PlyWriter {
1479    fn write_mesh(&self, mesh: &TriangleMesh, path: &Path) -> Result<()> {
1480        // Use the legacy ply-rs for writing for now
1481        use ply_rs::{
1482            ply::{
1483                Addable, DefaultElement, ElementDef, Ply, Property, PropertyDef, PropertyType,
1484                ScalarType,
1485            },
1486            writer::Writer,
1487        };
1488
1489        let file = File::create(path)?;
1490        let mut writer = BufWriter::new(file);
1491
1492        // Create PLY structure
1493        let mut ply = Ply::<DefaultElement>::new();
1494
1495        // Define vertex element
1496        let mut vertex_element = ElementDef::new("vertex".to_string());
1497        vertex_element.count = mesh.vertices.len();
1498        vertex_element.properties.add(PropertyDef::new(
1499            "x".to_string(),
1500            PropertyType::Scalar(ScalarType::Float),
1501        ));
1502        vertex_element.properties.add(PropertyDef::new(
1503            "y".to_string(),
1504            PropertyType::Scalar(ScalarType::Float),
1505        ));
1506        vertex_element.properties.add(PropertyDef::new(
1507            "z".to_string(),
1508            PropertyType::Scalar(ScalarType::Float),
1509        ));
1510
1511        // Add normal properties if available
1512        if mesh.normals.is_some() {
1513            vertex_element.properties.add(PropertyDef::new(
1514                "nx".to_string(),
1515                PropertyType::Scalar(ScalarType::Float),
1516            ));
1517            vertex_element.properties.add(PropertyDef::new(
1518                "ny".to_string(),
1519                PropertyType::Scalar(ScalarType::Float),
1520            ));
1521            vertex_element.properties.add(PropertyDef::new(
1522                "nz".to_string(),
1523                PropertyType::Scalar(ScalarType::Float),
1524            ));
1525        }
1526
1527        ply.header.elements.add(vertex_element);
1528
1529        // Add vertex data
1530        let mut vertices = Vec::new();
1531        for (i, point) in mesh.vertices.iter().enumerate() {
1532            let mut vertex = DefaultElement::new();
1533            vertex.insert("x".to_string(), Property::Float(point.x));
1534            vertex.insert("y".to_string(), Property::Float(point.y));
1535            vertex.insert("z".to_string(), Property::Float(point.z));
1536
1537            // Add normals if available
1538            if let Some(ref normals) = mesh.normals {
1539                if i < normals.len() {
1540                    vertex.insert("nx".to_string(), Property::Float(normals[i].x));
1541                    vertex.insert("ny".to_string(), Property::Float(normals[i].y));
1542                    vertex.insert("nz".to_string(), Property::Float(normals[i].z));
1543                }
1544            }
1545
1546            vertices.push(vertex);
1547        }
1548        ply.payload.insert("vertex".to_string(), vertices);
1549
1550        // Define face element if we have faces
1551        if !mesh.faces.is_empty() {
1552            let mut face_element = ElementDef::new("face".to_string());
1553            face_element.count = mesh.faces.len();
1554            face_element.properties.add(PropertyDef::new(
1555                "vertex_indices".to_string(),
1556                PropertyType::List(ScalarType::UChar, ScalarType::Int),
1557            ));
1558
1559            ply.header.elements.add(face_element);
1560
1561            // Add face data
1562            let mut faces = Vec::new();
1563            for face in &mesh.faces {
1564                let mut face_data = DefaultElement::new();
1565                let indices = vec![face[0] as i32, face[1] as i32, face[2] as i32];
1566                face_data.insert("vertex_indices".to_string(), Property::ListInt(indices));
1567                faces.push(face_data);
1568            }
1569            ply.payload.insert("face".to_string(), faces);
1570        }
1571
1572        // Write PLY file
1573        let writer_instance = Writer::new();
1574        writer_instance.write_ply(&mut writer, &mut ply)?;
1575
1576        Ok(())
1577    }
1578
1579    fn format_name(&self) -> &'static str {
1580        "ply"
1581    }
1582}
1583
1584// Keep the legacy trait implementations for backward compatibility
1585impl PointCloudReader for PlyReader {
1586    fn read_point_cloud<P: AsRef<Path>>(path: P) -> Result<PointCloud<Point3f>> {
1587        let reader = PlyReader;
1588        crate::registry::PointCloudReader::read_point_cloud(&reader, path.as_ref())
1589    }
1590}
1591
1592impl MeshReader for PlyReader {
1593    fn read_mesh<P: AsRef<Path>>(path: P) -> Result<TriangleMesh> {
1594        let reader = PlyReader;
1595        crate::registry::MeshReader::read_mesh(&reader, path.as_ref())
1596    }
1597}
1598
1599impl PointCloudWriter for PlyWriter {
1600    fn write_point_cloud<P: AsRef<Path>>(cloud: &PointCloud<Point3f>, path: P) -> Result<()> {
1601        let writer = PlyWriter;
1602        crate::registry::PointCloudWriter::write_point_cloud(&writer, cloud, path.as_ref())
1603    }
1604}
1605
1606impl MeshWriter for PlyWriter {
1607    fn write_mesh<P: AsRef<Path>>(mesh: &TriangleMesh, path: P) -> Result<()> {
1608        let writer = PlyWriter;
1609        crate::registry::MeshWriter::write_mesh(&writer, mesh, path.as_ref())
1610    }
1611}
1612
1613/// Streaming PLY reader for point clouds
1614pub struct PlyStreamingReader {
1615    reader: BufReader<File>,
1616    current_count: usize,
1617    current_index: usize,
1618    chunk_size: usize,
1619    buffer: Vec<u8>,
1620    format: PlyFormat,
1621}
1622
1623impl PlyStreamingReader {
1624    /// Create a new streaming PLY reader
1625    pub fn new<P: AsRef<Path>>(path: P, chunk_size: usize) -> Result<Self> {
1626        let file = File::open(path)?;
1627        let mut reader = BufReader::new(file);
1628        let header = RobustPlyReader::read_header(&mut reader)?;
1629
1630        // Find vertex element
1631        let vertex_element = header
1632            .elements
1633            .iter()
1634            .find(|e| e.name == "vertex")
1635            .ok_or_else(|| Error::InvalidData("No vertex element found in PLY file".to_string()))?;
1636
1637        Ok(Self {
1638            reader,
1639            current_count: vertex_element.count,
1640            current_index: 0,
1641            chunk_size,
1642            buffer: Vec::with_capacity(chunk_size * 12), // Estimate 12 bytes per vertex
1643            format: header.format,
1644        })
1645    }
1646}
1647
1648impl Iterator for PlyStreamingReader {
1649    type Item = Result<Point3f>;
1650
1651    fn next(&mut self) -> Option<Self::Item> {
1652        if self.current_index >= self.current_count {
1653            return None;
1654        }
1655
1656        // Read a chunk if buffer is empty
1657        if self.buffer.is_empty() {
1658            let remaining = self.current_count - self.current_index;
1659            let to_read = std::cmp::min(remaining, self.chunk_size);
1660
1661            match self.format {
1662                PlyFormat::Ascii => {
1663                    // For ASCII, we need to read line by line
1664                    let mut line = String::new();
1665                    if let Err(e) = self.reader.read_line(&mut line) {
1666                        return Some(Err(Error::Io(e)));
1667                    }
1668
1669                    let values: Vec<&str> = line.trim().split_whitespace().collect();
1670                    if values.len() < 3 {
1671                        return Some(Err(Error::InvalidData(
1672                            "Not enough coordinates in vertex line".to_string(),
1673                        )));
1674                    }
1675
1676                    let x = match values[0].parse::<f32>() {
1677                        Ok(v) => v,
1678                        Err(_) => {
1679                            return Some(Err(Error::InvalidData(
1680                                "Invalid x coordinate".to_string(),
1681                            )))
1682                        }
1683                    };
1684                    let y = match values[1].parse::<f32>() {
1685                        Ok(v) => v,
1686                        Err(_) => {
1687                            return Some(Err(Error::InvalidData(
1688                                "Invalid y coordinate".to_string(),
1689                            )))
1690                        }
1691                    };
1692                    let z = match values[2].parse::<f32>() {
1693                        Ok(v) => v,
1694                        Err(_) => {
1695                            return Some(Err(Error::InvalidData(
1696                                "Invalid z coordinate".to_string(),
1697                            )))
1698                        }
1699                    };
1700
1701                    self.current_index += 1;
1702                    return Some(Ok(Point3f::new(x, y, z)));
1703                }
1704                PlyFormat::BinaryLittleEndian => {
1705                    // For binary, read the chunk
1706                    let bytes_per_vertex = 12; // 3 * f32
1707                    let chunk_bytes = to_read * bytes_per_vertex;
1708                    self.buffer.resize(chunk_bytes, 0);
1709
1710                    if let Err(e) = self.reader.read_exact(&mut self.buffer[..chunk_bytes]) {
1711                        return Some(Err(Error::Io(e)));
1712                    }
1713                }
1714                PlyFormat::BinaryBigEndian => {
1715                    // For binary, read the chunk
1716                    let bytes_per_vertex = 12; // 3 * f32
1717                    let chunk_bytes = to_read * bytes_per_vertex;
1718                    self.buffer.resize(chunk_bytes, 0);
1719
1720                    if let Err(e) = self.reader.read_exact(&mut self.buffer[..chunk_bytes]) {
1721                        return Some(Err(Error::Io(e)));
1722                    }
1723                }
1724            }
1725        }
1726
1727        // Extract point from buffer
1728        match self.format {
1729            PlyFormat::Ascii => {
1730                // Already handled above
1731                unreachable!()
1732            }
1733            PlyFormat::BinaryLittleEndian => {
1734                if self.buffer.len() < 12 {
1735                    return Some(Err(Error::InvalidData(
1736                        "Insufficient data in buffer".to_string(),
1737                    )));
1738                }
1739
1740                let x = f32::from_le_bytes([
1741                    self.buffer[0],
1742                    self.buffer[1],
1743                    self.buffer[2],
1744                    self.buffer[3],
1745                ]);
1746                let y = f32::from_le_bytes([
1747                    self.buffer[4],
1748                    self.buffer[5],
1749                    self.buffer[6],
1750                    self.buffer[7],
1751                ]);
1752                let z = f32::from_le_bytes([
1753                    self.buffer[8],
1754                    self.buffer[9],
1755                    self.buffer[10],
1756                    self.buffer[11],
1757                ]);
1758
1759                // Remove processed data from buffer
1760                self.buffer.drain(0..12);
1761                self.current_index += 1;
1762
1763                Some(Ok(Point3f::new(x, y, z)))
1764            }
1765            PlyFormat::BinaryBigEndian => {
1766                if self.buffer.len() < 12 {
1767                    return Some(Err(Error::InvalidData(
1768                        "Insufficient data in buffer".to_string(),
1769                    )));
1770                }
1771
1772                let x = f32::from_be_bytes([
1773                    self.buffer[0],
1774                    self.buffer[1],
1775                    self.buffer[2],
1776                    self.buffer[3],
1777                ]);
1778                let y = f32::from_be_bytes([
1779                    self.buffer[4],
1780                    self.buffer[5],
1781                    self.buffer[6],
1782                    self.buffer[7],
1783                ]);
1784                let z = f32::from_be_bytes([
1785                    self.buffer[8],
1786                    self.buffer[9],
1787                    self.buffer[10],
1788                    self.buffer[11],
1789                ]);
1790
1791                // Remove processed data from buffer
1792                self.buffer.drain(0..12);
1793                self.current_index += 1;
1794
1795                Some(Ok(Point3f::new(x, y, z)))
1796            }
1797        }
1798    }
1799}
1800
1801/// Streaming PLY reader for mesh faces
1802pub struct PlyMeshStreamingReader {
1803    reader: BufReader<File>,
1804    current_count: usize,
1805    current_index: usize,
1806    chunk_size: usize,
1807    buffer: Vec<u8>,
1808    format: PlyFormat,
1809    vertices: Vec<Point3f>, // We need to store vertices to convert face indices
1810}
1811
1812impl PlyMeshStreamingReader {
1813    /// Create a new streaming PLY mesh reader
1814    pub fn new<P: AsRef<Path>>(path: P, chunk_size: usize) -> Result<Self> {
1815        let file = File::open(path)?;
1816        let mut reader = BufReader::new(file);
1817        let header = RobustPlyReader::read_header(&mut reader)?;
1818
1819        // Find face element
1820        let face_element = header
1821            .elements
1822            .iter()
1823            .find(|e| e.name == "face")
1824            .ok_or_else(|| Error::InvalidData("No face element found in PLY file".to_string()))?;
1825
1826        // First, we need to read all vertices to convert face indices
1827        let vertex_element = header
1828            .elements
1829            .iter()
1830            .find(|e| e.name == "vertex")
1831            .ok_or_else(|| Error::InvalidData("No vertex element found in PLY file".to_string()))?;
1832
1833        let mut vertices = Vec::with_capacity(crate::capacity_hint(vertex_element.count));
1834
1835        // Read vertices first
1836        for _ in 0..vertex_element.count {
1837            let mut line = String::new();
1838            reader.read_line(&mut line)?;
1839            let values: Vec<&str> = line.trim().split_whitespace().collect();
1840            if values.len() >= 3 {
1841                let x = values[0]
1842                    .parse::<f32>()
1843                    .map_err(|_| Error::InvalidData("Invalid x coordinate".to_string()))?;
1844                let y = values[1]
1845                    .parse::<f32>()
1846                    .map_err(|_| Error::InvalidData("Invalid y coordinate".to_string()))?;
1847                let z = values[2]
1848                    .parse::<f32>()
1849                    .map_err(|_| Error::InvalidData("Invalid z coordinate".to_string()))?;
1850                vertices.push(Point3f::new(x, y, z));
1851            }
1852        }
1853
1854        Ok(Self {
1855            reader,
1856            current_count: face_element.count,
1857            current_index: 0,
1858            chunk_size,
1859            buffer: Vec::with_capacity(chunk_size * 16), // Estimate 16 bytes per face
1860            format: header.format,
1861            vertices,
1862        })
1863    }
1864}
1865
1866impl Iterator for PlyMeshStreamingReader {
1867    type Item = Result<[usize; 3]>;
1868
1869    fn next(&mut self) -> Option<Self::Item> {
1870        if self.current_index >= self.current_count {
1871            return None;
1872        }
1873
1874        // Read a chunk if buffer is empty
1875        if self.buffer.is_empty() {
1876            let remaining = self.current_count - self.current_index;
1877            let to_read = std::cmp::min(remaining, self.chunk_size);
1878
1879            match self.format {
1880                PlyFormat::Ascii => {
1881                    // For ASCII, we need to read line by line
1882                    let mut line = String::new();
1883                    if let Err(e) = self.reader.read_line(&mut line) {
1884                        return Some(Err(Error::Io(e)));
1885                    }
1886
1887                    let values: Vec<&str> = line.trim().split_whitespace().collect();
1888                    if values.len() < 4 {
1889                        // count + 3 indices
1890                        return Some(Err(Error::InvalidData(
1891                            "Not enough values in face line".to_string(),
1892                        )));
1893                    }
1894
1895                    let count = match values[0].parse::<usize>() {
1896                        Ok(v) => v,
1897                        Err(_) => {
1898                            return Some(Err(Error::InvalidData("Invalid face count".to_string())))
1899                        }
1900                    };
1901
1902                    if count != 3 {
1903                        return Some(Err(Error::InvalidData(
1904                            "Only triangular faces supported".to_string(),
1905                        )));
1906                    }
1907
1908                    let i1 = match values[1].parse::<usize>() {
1909                        Ok(v) => v,
1910                        Err(_) => {
1911                            return Some(Err(Error::InvalidData("Invalid face index".to_string())))
1912                        }
1913                    };
1914                    let i2 = match values[2].parse::<usize>() {
1915                        Ok(v) => v,
1916                        Err(_) => {
1917                            return Some(Err(Error::InvalidData("Invalid face index".to_string())))
1918                        }
1919                    };
1920                    let i3 = match values[3].parse::<usize>() {
1921                        Ok(v) => v,
1922                        Err(_) => {
1923                            return Some(Err(Error::InvalidData("Invalid face index".to_string())))
1924                        }
1925                    };
1926
1927                    // Validate indices
1928                    if i1 >= self.vertices.len()
1929                        || i2 >= self.vertices.len()
1930                        || i3 >= self.vertices.len()
1931                    {
1932                        return Some(Err(Error::InvalidData(
1933                            "Face index out of range".to_string(),
1934                        )));
1935                    }
1936
1937                    self.current_index += 1;
1938                    return Some(Ok([i1, i2, i3]));
1939                }
1940                PlyFormat::BinaryLittleEndian | PlyFormat::BinaryBigEndian => {
1941                    // For binary, read the chunk
1942                    let bytes_per_face = 16; // 1 uchar count + 3 * u32 indices
1943                    let chunk_bytes = to_read * bytes_per_face;
1944                    self.buffer.resize(chunk_bytes, 0);
1945
1946                    if let Err(e) = self.reader.read_exact(&mut self.buffer[..chunk_bytes]) {
1947                        return Some(Err(Error::Io(e)));
1948                    }
1949                }
1950            }
1951        }
1952
1953        // Extract face from buffer
1954        match self.format {
1955            PlyFormat::Ascii => {
1956                // Already handled above
1957                unreachable!()
1958            }
1959            PlyFormat::BinaryLittleEndian => {
1960                if self.buffer.len() < 16 {
1961                    return Some(Err(Error::InvalidData(
1962                        "Insufficient data in buffer".to_string(),
1963                    )));
1964                }
1965
1966                let count = self.buffer[0] as usize;
1967                if count != 3 {
1968                    return Some(Err(Error::InvalidData(
1969                        "Only triangular faces supported".to_string(),
1970                    )));
1971                }
1972
1973                let i1 = u32::from_le_bytes([
1974                    self.buffer[1],
1975                    self.buffer[2],
1976                    self.buffer[3],
1977                    self.buffer[4],
1978                ]) as usize;
1979                let i2 = u32::from_le_bytes([
1980                    self.buffer[5],
1981                    self.buffer[6],
1982                    self.buffer[7],
1983                    self.buffer[8],
1984                ]) as usize;
1985                let i3 = u32::from_le_bytes([
1986                    self.buffer[9],
1987                    self.buffer[10],
1988                    self.buffer[11],
1989                    self.buffer[12],
1990                ]) as usize;
1991
1992                // Validate indices
1993                if i1 >= self.vertices.len()
1994                    || i2 >= self.vertices.len()
1995                    || i3 >= self.vertices.len()
1996                {
1997                    return Some(Err(Error::InvalidData(
1998                        "Face index out of range".to_string(),
1999                    )));
2000                }
2001
2002                // Remove processed data from buffer
2003                self.buffer.drain(0..16);
2004                self.current_index += 1;
2005
2006                Some(Ok([i1, i2, i3]))
2007            }
2008            PlyFormat::BinaryBigEndian => {
2009                if self.buffer.len() < 16 {
2010                    return Some(Err(Error::InvalidData(
2011                        "Insufficient data in buffer".to_string(),
2012                    )));
2013                }
2014
2015                let count = self.buffer[0] as usize;
2016                if count != 3 {
2017                    return Some(Err(Error::InvalidData(
2018                        "Only triangular faces supported".to_string(),
2019                    )));
2020                }
2021
2022                let i1 = u32::from_be_bytes([
2023                    self.buffer[1],
2024                    self.buffer[2],
2025                    self.buffer[3],
2026                    self.buffer[4],
2027                ]) as usize;
2028                let i2 = u32::from_be_bytes([
2029                    self.buffer[5],
2030                    self.buffer[6],
2031                    self.buffer[7],
2032                    self.buffer[8],
2033                ]) as usize;
2034                let i3 = u32::from_be_bytes([
2035                    self.buffer[9],
2036                    self.buffer[10],
2037                    self.buffer[11],
2038                    self.buffer[12],
2039                ]) as usize;
2040
2041                // Validate indices
2042                if i1 >= self.vertices.len()
2043                    || i2 >= self.vertices.len()
2044                    || i3 >= self.vertices.len()
2045                {
2046                    return Some(Err(Error::InvalidData(
2047                        "Face index out of range".to_string(),
2048                    )));
2049                }
2050
2051                // Remove processed data from buffer
2052                self.buffer.drain(0..16);
2053                self.current_index += 1;
2054
2055                Some(Ok([i1, i2, i3]))
2056            }
2057        }
2058    }
2059}