Skip to main content

oxihuman_export/
light_export.rs

1// Copyright (C) 2026 COOLJAPAN OU (Team KitaSan)
2// SPDX-License-Identifier: Apache-2.0
3#![allow(dead_code)]
4
5//! Export light data to JSON-compatible format.
6
7pub const LIGHT_POINT: u8 = 0;
8pub const LIGHT_DIRECTIONAL: u8 = 1;
9pub const LIGHT_SPOT: u8 = 2;
10
11/* ── legacy API (kept) ── */
12
13#[derive(Debug, Clone)]
14pub struct LightExport {
15    pub name: String,
16    pub light_type: u8,
17    pub position: [f32; 3],
18    pub direction: [f32; 3],
19    pub color: [f32; 3],
20    pub intensity: f32,
21    pub radius: f32,
22    pub cast_shadow: bool,
23}
24
25pub fn default_point_light_export(name: &str) -> LightExport {
26    LightExport {
27        name: name.to_string(),
28        light_type: LIGHT_POINT,
29        position: [0.0; 3],
30        direction: [0.0, -1.0, 0.0],
31        color: [1.0; 3],
32        intensity: 1.0,
33        radius: 0.0,
34        cast_shadow: true,
35    }
36}
37
38/* ── spec functions (wave 150B) ── */
39
40/// Spec-style light data.
41#[derive(Debug, Clone)]
42pub struct LightData {
43    pub name: String,
44    pub light_type: String,
45    pub color: [f32; 3],
46    pub energy: f32,
47    pub shadow: bool,
48}
49
50/// Create a new `LightData`.
51pub fn new_light_data(name: &str, light_type: &str, energy: f32) -> LightData {
52    LightData {
53        name: name.to_string(),
54        light_type: light_type.to_string(),
55        color: [1.0, 1.0, 1.0],
56        energy,
57        shadow: true,
58    }
59}
60
61/// Serialize a `LightData` to JSON.
62pub fn light_to_json(l: &LightData) -> String {
63    format!(
64        "{{\"name\":\"{}\",\"type\":\"{}\",\"energy\":{},\"shadow\":{}}}",
65        l.name, l.light_type, l.energy, l.shadow
66    )
67}
68
69/// Serialize multiple lights to a JSON array.
70pub fn lights_to_json(lights: &[LightData]) -> String {
71    let inner: Vec<String> = lights.iter().map(light_to_json).collect();
72    format!("[{}]", inner.join(","))
73}
74
75/// Lux-equivalent illuminance at a given distance (point light: E = energy / (4π d²)).
76pub fn light_lux_at_distance(l: &LightData, distance: f32) -> f32 {
77    use std::f32::consts::PI;
78    if distance < f32::EPSILON {
79        return f32::MAX;
80    }
81    l.energy / (4.0 * PI * distance * distance)
82}
83
84/// Returns true if the light is directional.
85pub fn light_is_directional(l: &LightData) -> bool {
86    l.light_type.eq_ignore_ascii_case("SUN") || l.light_type.eq_ignore_ascii_case("DIRECTIONAL")
87}
88
89#[cfg(test)]
90mod tests {
91    use super::*;
92
93    #[test]
94    fn test_new_light_data() {
95        let l = new_light_data("sun", "SUN", 5.0);
96        assert_eq!(l.name, "sun");
97    }
98
99    #[test]
100    fn test_light_to_json() {
101        let l = new_light_data("pt", "POINT", 1.0);
102        let j = light_to_json(&l);
103        assert!(j.contains("POINT"));
104    }
105
106    #[test]
107    fn test_light_lux_at_distance() {
108        let l = new_light_data("l", "POINT", 4.0);
109        let lux = light_lux_at_distance(&l, 1.0);
110        assert!(lux > 0.0);
111    }
112
113    #[test]
114    fn test_light_is_directional_true() {
115        let l = new_light_data("sun", "SUN", 1.0);
116        assert!(light_is_directional(&l));
117    }
118
119    #[test]
120    fn test_light_is_directional_false() {
121        let l = new_light_data("pt", "POINT", 1.0);
122        assert!(!light_is_directional(&l));
123    }
124}