Skip to main content

oxihuman_export/
abc_pointcloud_export.rs

1// Copyright (C) 2026 COOLJAPAN OU (Team KitaSan)
2// SPDX-License-Identifier: Apache-2.0
3#![allow(dead_code)]
4
5//! Alembic point cloud export stub.
6
7/// Configuration for Alembic point cloud export.
8#[derive(Debug, Clone)]
9pub struct AbcPointcloudConfig {
10    pub object_path: String,
11    pub fps: f32,
12    pub frame_start: i32,
13    pub frame_end: i32,
14}
15
16impl Default for AbcPointcloudConfig {
17    fn default() -> Self {
18        Self {
19            object_path: "/pointcloud".to_string(),
20            fps: 24.0,
21            frame_start: 0,
22            frame_end: 1,
23        }
24    }
25}
26
27/// An Alembic point cloud export.
28#[derive(Debug, Clone)]
29pub struct AbcPointcloudExport {
30    pub config: AbcPointcloudConfig,
31    pub frames: Vec<AbcPointcloudFrame>,
32}
33
34/// A single frame of point cloud data.
35#[derive(Debug, Clone)]
36pub struct AbcPointcloudFrame {
37    pub frame: i32,
38    pub positions: Vec<[f32; 3]>,
39    pub radii: Vec<f32>,
40}
41
42/// Create a new Alembic point cloud export.
43pub fn new_abc_pointcloud(config: AbcPointcloudConfig) -> AbcPointcloudExport {
44    AbcPointcloudExport {
45        config,
46        frames: Vec::new(),
47    }
48}
49
50/// Add a frame of positions to the export.
51pub fn add_abc_frame(
52    export: &mut AbcPointcloudExport,
53    frame: i32,
54    positions: Vec<[f32; 3]>,
55    radii: Vec<f32>,
56) {
57    export.frames.push(AbcPointcloudFrame {
58        frame,
59        positions,
60        radii,
61    });
62}
63
64/// Return the total number of frames.
65pub fn frame_count(export: &AbcPointcloudExport) -> usize {
66    export.frames.len()
67}
68
69/// Return the total number of points across all frames.
70pub fn total_point_count(export: &AbcPointcloudExport) -> usize {
71    export.frames.iter().map(|f| f.positions.len()).sum()
72}
73
74/// Estimate the export size in bytes (stub).
75pub fn estimate_abc_size_bytes(export: &AbcPointcloudExport) -> usize {
76    /* Rough: 12 bytes per position + 4 bytes per radius */
77    export.frames.iter().map(|f| f.positions.len() * 16).sum()
78}
79
80/// Validate the export (check FPS > 0, at least one frame).
81pub fn validate_abc_pointcloud(export: &AbcPointcloudExport) -> bool {
82    export.config.fps > 0.0 && !export.frames.is_empty()
83}
84
85/// Serialize the config as a JSON-like string (stub).
86pub fn abc_pointcloud_config_to_json(config: &AbcPointcloudConfig) -> String {
87    format!(
88        r#"{{"object_path":"{}","fps":{},"frame_start":{},"frame_end":{}}}"#,
89        config.object_path, config.fps, config.frame_start, config.frame_end
90    )
91}
92
93/// Return the point count for a specific frame index.
94pub fn frame_point_count(export: &AbcPointcloudExport, frame_idx: usize) -> usize {
95    export
96        .frames
97        .get(frame_idx)
98        .map(|f| f.positions.len())
99        .unwrap_or(0)
100}
101
102#[cfg(test)]
103mod tests {
104    use super::*;
105
106    fn sample_export() -> AbcPointcloudExport {
107        let cfg = AbcPointcloudConfig::default();
108        let mut exp = new_abc_pointcloud(cfg);
109        add_abc_frame(
110            &mut exp,
111            0,
112            vec![[0.0, 0.0, 0.0], [1.0, 0.0, 0.0]],
113            vec![0.1, 0.1],
114        );
115        exp
116    }
117
118    #[test]
119    fn test_frame_count() {
120        let exp = sample_export();
121        assert_eq!(frame_count(&exp), 1);
122    }
123
124    #[test]
125    fn test_total_point_count() {
126        let exp = sample_export();
127        assert_eq!(total_point_count(&exp), 2);
128    }
129
130    #[test]
131    fn test_estimate_size() {
132        let exp = sample_export();
133        assert!(estimate_abc_size_bytes(&exp) > 0);
134    }
135
136    #[test]
137    fn test_validate_valid() {
138        let exp = sample_export();
139        assert!(validate_abc_pointcloud(&exp));
140    }
141
142    #[test]
143    fn test_validate_empty_frames() {
144        let cfg = AbcPointcloudConfig::default();
145        let exp = new_abc_pointcloud(cfg);
146        assert!(!validate_abc_pointcloud(&exp));
147    }
148
149    #[test]
150    fn test_config_to_json() {
151        let cfg = AbcPointcloudConfig::default();
152        let json = abc_pointcloud_config_to_json(&cfg);
153        assert!(json.contains("pointcloud"));
154    }
155
156    #[test]
157    fn test_frame_point_count() {
158        let exp = sample_export();
159        assert_eq!(frame_point_count(&exp, 0), 2);
160        assert_eq!(frame_point_count(&exp, 99), 0);
161    }
162
163    #[test]
164    fn test_default_fps() {
165        let cfg = AbcPointcloudConfig::default();
166        assert!((cfg.fps - 24.0).abs() < 1e-6);
167    }
168
169    #[test]
170    fn test_add_multiple_frames() {
171        let cfg = AbcPointcloudConfig::default();
172        let mut exp = new_abc_pointcloud(cfg);
173        add_abc_frame(&mut exp, 0, vec![[0.0, 0.0, 0.0]], vec![0.1]);
174        add_abc_frame(
175            &mut exp,
176            1,
177            vec![[1.0, 0.0, 0.0], [2.0, 0.0, 0.0]],
178            vec![0.1, 0.2],
179        );
180        assert_eq!(total_point_count(&exp), 3);
181    }
182}