Skip to main content

oxihuman_viewer/
virtual_camera_view.rs

1// Copyright (C) 2026 COOLJAPAN OU (Team KitaSan)
2// SPDX-License-Identifier: Apache-2.0
3#![allow(dead_code)]
4
5//! Virtual cinema camera setup stub.
6
7/// Aspect ratio preset.
8#[derive(Debug, Clone, Copy, PartialEq)]
9pub enum AspectPreset {
10    Wide235,
11    Standard185,
12    Academy137,
13    Square,
14}
15
16/// Virtual cinema camera configuration.
17#[derive(Debug, Clone)]
18pub struct VirtualCameraView {
19    pub focal_length_mm: f32,
20    pub sensor_width_mm: f32,
21    pub aperture_fstop: f32,
22    pub aspect: AspectPreset,
23    pub enabled: bool,
24}
25
26impl VirtualCameraView {
27    pub fn new() -> Self {
28        VirtualCameraView {
29            focal_length_mm: 50.0,
30            sensor_width_mm: 36.0,
31            aperture_fstop: 2.8,
32            aspect: AspectPreset::Wide235,
33            enabled: true,
34        }
35    }
36}
37
38impl Default for VirtualCameraView {
39    fn default() -> Self {
40        Self::new()
41    }
42}
43
44/// Create a new virtual camera view.
45pub fn new_virtual_camera_view() -> VirtualCameraView {
46    VirtualCameraView::new()
47}
48
49/// Set focal length in mm.
50pub fn vcv_set_focal_length(vcv: &mut VirtualCameraView, mm: f32) {
51    vcv.focal_length_mm = mm.max(1.0);
52}
53
54/// Set aperture f-stop.
55pub fn vcv_set_aperture(vcv: &mut VirtualCameraView, fstop: f32) {
56    vcv.aperture_fstop = fstop.max(0.7);
57}
58
59/// Compute horizontal FOV in degrees.
60pub fn vcv_hfov_deg(vcv: &VirtualCameraView) -> f32 {
61    /* Stub: 2 * arctan(sensor_width / (2 * focal_length)) */
62    let r = vcv.sensor_width_mm / (2.0 * vcv.focal_length_mm);
63    2.0 * r.atan().to_degrees()
64}
65
66/// Set aspect ratio preset.
67pub fn vcv_set_aspect(vcv: &mut VirtualCameraView, aspect: AspectPreset) {
68    vcv.aspect = aspect;
69}
70
71/// Enable or disable.
72pub fn vcv_set_enabled(vcv: &mut VirtualCameraView, enabled: bool) {
73    vcv.enabled = enabled;
74}
75
76/// Serialize to JSON-like string.
77pub fn vcv_to_json(vcv: &VirtualCameraView) -> String {
78    format!(
79        r#"{{"focal_length_mm":{},"sensor_width_mm":{},"aperture_fstop":{},"enabled":{}}}"#,
80        vcv.focal_length_mm, vcv.sensor_width_mm, vcv.aperture_fstop, vcv.enabled
81    )
82}
83
84#[cfg(test)]
85mod tests {
86    use super::*;
87
88    #[test]
89    fn test_default_focal_length() {
90        let v = new_virtual_camera_view();
91        assert!((v.focal_length_mm - 50.0).abs() < 1e-5, /* default focal length must be 50 */);
92    }
93
94    #[test]
95    fn test_set_focal_length() {
96        let mut v = new_virtual_camera_view();
97        vcv_set_focal_length(&mut v, 85.0);
98        assert!((v.focal_length_mm - 85.0).abs() < 1e-5, /* focal length must be set */);
99    }
100
101    #[test]
102    fn test_focal_length_minimum() {
103        let mut v = new_virtual_camera_view();
104        vcv_set_focal_length(&mut v, -10.0);
105        assert!((v.focal_length_mm - 1.0).abs() < 1e-5, /* focal length clamped to 1 */);
106    }
107
108    #[test]
109    fn test_set_aperture() {
110        let mut v = new_virtual_camera_view();
111        vcv_set_aperture(&mut v, 5.6);
112        assert!((v.aperture_fstop - 5.6).abs() < 1e-5, /* aperture must be set */);
113    }
114
115    #[test]
116    fn test_hfov_positive() {
117        let v = new_virtual_camera_view();
118        let hfov = vcv_hfov_deg(&v);
119        assert!(hfov > 0.0 /* horizontal FOV must be positive */,);
120    }
121
122    #[test]
123    fn test_hfov_decreases_with_focal_length() {
124        let mut v = new_virtual_camera_view();
125        let hfov_50 = vcv_hfov_deg(&v);
126        vcv_set_focal_length(&mut v, 100.0);
127        let hfov_100 = vcv_hfov_deg(&v);
128        assert!(hfov_50 > hfov_100, /* longer focal length must yield narrower FOV */);
129    }
130
131    #[test]
132    fn test_set_aspect() {
133        let mut v = new_virtual_camera_view();
134        vcv_set_aspect(&mut v, AspectPreset::Square);
135        assert_eq!(v.aspect, AspectPreset::Square /* aspect must be set */,);
136    }
137
138    #[test]
139    fn test_set_enabled() {
140        let mut v = new_virtual_camera_view();
141        vcv_set_enabled(&mut v, false);
142        assert!(!v.enabled /* must be disabled */,);
143    }
144
145    #[test]
146    fn test_to_json_contains_focal_length() {
147        let v = new_virtual_camera_view();
148        let j = vcv_to_json(&v);
149        assert!(j.contains("\"focal_length_mm\""), /* json must contain focal_length_mm */);
150    }
151
152    #[test]
153    fn test_enabled_default() {
154        let v = new_virtual_camera_view();
155        assert!(v.enabled /* must be enabled by default */,);
156    }
157}