Skip to main content

oxihuman_export/
tooth_export.rs

1// Copyright (C) 2026 COOLJAPAN OU (Team KitaSan)
2// SPDX-License-Identifier: Apache-2.0
3#![allow(dead_code)]
4
5pub struct Tooth {
6    pub id: u8,
7    pub pos: [f32; 3],
8    pub orientation: [f32; 4],
9    pub crown_height_mm: f32,
10    pub root_length_mm: f32,
11    pub shade: String,
12}
13
14pub fn new_tooth(id: u8, pos: [f32; 3]) -> Tooth {
15    Tooth {
16        id,
17        pos,
18        orientation: [0.0, 0.0, 0.0, 1.0],
19        crown_height_mm: 8.5,
20        root_length_mm: 13.0,
21        shade: String::from("A2"),
22    }
23}
24
25pub fn tooth_total_length(t: &Tooth) -> f32 {
26    t.crown_height_mm + t.root_length_mm
27}
28
29pub fn tooth_to_json(t: &Tooth) -> String {
30    format!(
31        "{{\"id\":{},\"pos\":[{:.4},{:.4},{:.4}],\"crown_mm\":{:.2},\"root_mm\":{:.2},\"shade\":\"{}\"}}",
32        t.id,
33        t.pos[0],
34        t.pos[1],
35        t.pos[2],
36        t.crown_height_mm,
37        t.root_length_mm,
38        t.shade
39    )
40}
41
42pub fn teeth_to_json(teeth: &[Tooth]) -> String {
43    let items: Vec<String> = teeth.iter().map(tooth_to_json).collect();
44    format!("{{\"teeth\":[{}]}}", items.join(","))
45}
46
47/// Molar: id 6, 7, 8, 14, 15, 16 per arch (FDI or UNS 1-based).
48pub fn tooth_is_molar(t: &Tooth) -> bool {
49    matches!(t.id, 6 | 7 | 8 | 14 | 15 | 16)
50}
51
52pub fn tooth_count(teeth: &[Tooth]) -> usize {
53    teeth.len()
54}
55
56#[cfg(test)]
57mod tests {
58    use super::*;
59
60    #[test]
61    fn test_new_tooth() {
62        /* construction */
63        let t = new_tooth(1, [0.0, 0.0, 0.0]);
64        assert_eq!(t.id, 1);
65        assert!((t.crown_height_mm - 8.5).abs() < 1e-6);
66    }
67
68    #[test]
69    fn test_total_length() {
70        /* crown + root */
71        let t = new_tooth(1, [0.0; 3]);
72        assert!((tooth_total_length(&t) - 21.5).abs() < 1e-6);
73    }
74
75    #[test]
76    fn test_to_json() {
77        /* JSON contains id */
78        let t = new_tooth(5, [1.0, 2.0, 3.0]);
79        let json = tooth_to_json(&t);
80        assert!(json.contains("\"id\":5"));
81    }
82
83    #[test]
84    fn test_teeth_to_json() {
85        /* array in JSON */
86        let teeth = vec![new_tooth(1, [0.0; 3]), new_tooth(2, [1.0; 3])];
87        let json = teeth_to_json(&teeth);
88        assert!(json.contains("teeth"));
89    }
90
91    #[test]
92    fn test_is_molar() {
93        /* id 6 is molar, id 1 is not */
94        let molar = new_tooth(6, [0.0; 3]);
95        let incisor = new_tooth(1, [0.0; 3]);
96        assert!(tooth_is_molar(&molar));
97        assert!(!tooth_is_molar(&incisor));
98    }
99
100    #[test]
101    fn test_count() {
102        /* count */
103        let teeth: Vec<Tooth> = (1..=4).map(|i| new_tooth(i, [0.0; 3])).collect();
104        assert_eq!(tooth_count(&teeth), 4);
105    }
106}