Skip to main content

oxihuman_morph/
proximity_pin.rs

1// Copyright (C) 2026 COOLJAPAN OU (Team KitaSan)
2// SPDX-License-Identifier: Apache-2.0
3#![allow(dead_code)]
4
5//! Proximity pinning constraint stub.
6
7/// A proximity pin entry that pins a vertex to a surface point.
8#[derive(Debug, Clone)]
9pub struct ProximityPin {
10    pub vertex_index: usize,
11    pub target_position: [f32; 3],
12    pub influence: f32,
13    pub enabled: bool,
14}
15
16impl ProximityPin {
17    pub fn new(vertex_index: usize, target: [f32; 3]) -> Self {
18        ProximityPin {
19            vertex_index,
20            target_position: target,
21            influence: 1.0,
22            enabled: true,
23        }
24    }
25}
26
27/// A set of proximity pins.
28#[derive(Debug, Clone, Default)]
29pub struct ProximityPinSet {
30    pub pins: Vec<ProximityPin>,
31}
32
33/// Create a new empty pin set.
34pub fn new_pin_set() -> ProximityPinSet {
35    ProximityPinSet::default()
36}
37
38/// Add a pin.
39pub fn pin_add(set: &mut ProximityPinSet, vertex_index: usize, target: [f32; 3]) {
40    set.pins.push(ProximityPin::new(vertex_index, target));
41}
42
43/// Remove a pin by index.
44pub fn pin_remove(set: &mut ProximityPinSet, pin_index: usize) {
45    if pin_index < set.pins.len() {
46        set.pins.remove(pin_index);
47    }
48}
49
50/// Set pin influence.
51pub fn pin_set_influence(set: &mut ProximityPinSet, pin_index: usize, influence: f32) {
52    if pin_index < set.pins.len() {
53        set.pins[pin_index].influence = influence.clamp(0.0, 1.0);
54    }
55}
56
57/// Enable or disable a pin.
58pub fn pin_set_enabled(set: &mut ProximityPinSet, pin_index: usize, enabled: bool) {
59    if pin_index < set.pins.len() {
60        set.pins[pin_index].enabled = enabled;
61    }
62}
63
64/// Return pin count.
65pub fn pin_count(set: &ProximityPinSet) -> usize {
66    set.pins.len()
67}
68
69/// Return enabled pin count.
70pub fn pin_enabled_count(set: &ProximityPinSet) -> usize {
71    set.pins.iter().filter(|p| p.enabled).count()
72}
73
74/// Return a JSON-like string.
75pub fn pin_set_to_json(set: &ProximityPinSet) -> String {
76    format!(
77        r#"{{"pins":{},"enabled":{}}}"#,
78        set.pins.len(),
79        set.pins.iter().filter(|p| p.enabled).count()
80    )
81}
82
83#[cfg(test)]
84mod tests {
85    use super::*;
86
87    #[test]
88    fn test_new_pin_set_empty() {
89        let s = new_pin_set();
90        assert_eq!(pin_count(&s), 0 /* new set should be empty */,);
91    }
92
93    #[test]
94    fn test_add_pin_increases_count() {
95        let mut s = new_pin_set();
96        pin_add(&mut s, 0, [0.0, 1.0, 0.0]);
97        assert_eq!(pin_count(&s), 1 /* count should increase after add */,);
98    }
99
100    #[test]
101    fn test_remove_pin_decreases_count() {
102        let mut s = new_pin_set();
103        pin_add(&mut s, 0, [0.0; 3]);
104        pin_remove(&mut s, 0);
105        assert_eq!(
106            pin_count(&s),
107            0, /* count should decrease after remove */
108        );
109    }
110
111    #[test]
112    fn test_pin_enabled_by_default() {
113        let mut s = new_pin_set();
114        pin_add(&mut s, 0, [0.0; 3]);
115        assert!(s.pins[0].enabled /* pins are enabled by default */,);
116    }
117
118    #[test]
119    fn test_set_enabled_false() {
120        let mut s = new_pin_set();
121        pin_add(&mut s, 0, [0.0; 3]);
122        pin_set_enabled(&mut s, 0, false);
123        assert!(!s.pins[0].enabled /* pin should be disabled */,);
124    }
125
126    #[test]
127    fn test_enabled_count() {
128        let mut s = new_pin_set();
129        pin_add(&mut s, 0, [0.0; 3]);
130        pin_add(&mut s, 1, [1.0, 0.0, 0.0]);
131        pin_set_enabled(&mut s, 0, false);
132        assert_eq!(
133            pin_enabled_count(&s),
134            1, /* only one pin should be enabled */
135        );
136    }
137
138    #[test]
139    fn test_set_influence_clamps() {
140        let mut s = new_pin_set();
141        pin_add(&mut s, 0, [0.0; 3]);
142        pin_set_influence(&mut s, 0, 3.0);
143        assert!((s.pins[0].influence - 1.0).abs() < 1e-5, /* influence clamped to 1 */);
144    }
145
146    #[test]
147    fn test_to_json_contains_pins() {
148        let s = new_pin_set();
149        let j = pin_set_to_json(&s);
150        assert!(j.contains("pins") /* JSON must contain pins */,);
151    }
152
153    #[test]
154    fn test_remove_out_of_bounds_ignored() {
155        let mut s = new_pin_set();
156        pin_remove(&mut s, 99);
157        assert_eq!(pin_count(&s), 0 /* out-of-bounds remove is no-op */,);
158    }
159
160    #[test]
161    fn test_vertex_index_stored() {
162        let mut s = new_pin_set();
163        pin_add(&mut s, 42, [0.0; 3]);
164        assert_eq!(
165            s.pins[0].vertex_index,
166            42, /* vertex index must match */
167        );
168    }
169}