Skip to main content

feagi_brain_development/connectivity/rules/
vectors.rs

1// Copyright 2025 Neuraville Inc.
2// SPDX-License-Identifier: Apache-2.0
3
4/*!
5Vector-based connectivity - offset-based connection patterns.
6*/
7
8use crate::types::{BduResult, Position};
9
10type Dimensions = (usize, usize, usize);
11
12#[cfg(feature = "parallel")]
13use rayon::prelude::*;
14
15/// Apply vector offset to source position.
16///
17/// Returns `None` if the offset falls outside destination dimensions.
18pub fn apply_vector_offset(
19    src_position: Position,
20    vector: (i32, i32, i32),
21    morphology_scalar: f32,
22    dst_dimensions: Dimensions,
23) -> Option<Position> {
24    let (src_x, src_y, src_z) = src_position;
25    let (vec_x, vec_y, vec_z) = vector;
26
27    // Apply scalar
28    let scaled_x = (vec_x as f32 * morphology_scalar) as i32;
29    let scaled_y = (vec_y as f32 * morphology_scalar) as i32;
30    let scaled_z = (vec_z as f32 * morphology_scalar) as i32;
31
32    // Apply offset without clamping (cast to i32 for signed arithmetic)
33    let dst_x = src_x as i32 + scaled_x;
34    let dst_y = src_y as i32 + scaled_y;
35    let dst_z = src_z as i32 + scaled_z;
36
37    let dims_x = dst_dimensions.0 as i32;
38    let dims_y = dst_dimensions.1 as i32;
39    let dims_z = dst_dimensions.2 as i32;
40
41    if dst_x < 0 || dst_x >= dims_x || dst_y < 0 || dst_y >= dims_y || dst_z < 0 || dst_z >= dims_z
42    {
43        return None;
44    }
45
46    let dst_x = dst_x as u32;
47    let dst_y = dst_y as u32;
48    let dst_z = dst_z as u32;
49
50    Some((dst_x, dst_y, dst_z))
51}
52
53/// Apply vector offset to source position with clamping to destination dimensions.
54pub(super) fn apply_vector_offset_clamped(
55    src_position: Position,
56    vector: (i32, i32, i32),
57    morphology_scalar: f32,
58    dst_dimensions: Dimensions,
59) -> BduResult<Position> {
60    let (src_x, src_y, src_z) = src_position;
61    let (vec_x, vec_y, vec_z) = vector;
62
63    // Apply scalar
64    let scaled_x = (vec_x as f32 * morphology_scalar) as i32;
65    let scaled_y = (vec_y as f32 * morphology_scalar) as i32;
66    let scaled_z = (vec_z as f32 * morphology_scalar) as i32;
67
68    // Apply offset and clamp to dimensions (cast to i32 for signed arithmetic)
69    let dst_x = (src_x as i32 + scaled_x)
70        .max(0)
71        .min(dst_dimensions.0 as i32 - 1) as u32;
72    let dst_y = (src_y as i32 + scaled_y)
73        .max(0)
74        .min(dst_dimensions.1 as i32 - 1) as u32;
75    let dst_z = (src_z as i32 + scaled_z)
76        .max(0)
77        .min(dst_dimensions.2 as i32 - 1) as u32;
78
79    Ok((dst_x, dst_y, dst_z))
80}
81
82/// Match vectors - apply vector offset to source positions
83pub fn match_vectors_batch(
84    src_positions: &[Position],
85    vector: (i32, i32, i32),
86    morphology_scalar: f32,
87    dst_dimensions: Dimensions,
88) -> BduResult<Vec<Position>> {
89    // Parallel processing for large batches (sequential fallback for WASM)
90    #[cfg(feature = "parallel")]
91    let results: Vec<_> = src_positions
92        .par_iter()
93        .filter_map(|&src_pos| {
94            apply_vector_offset(src_pos, vector, morphology_scalar, dst_dimensions)
95        })
96        .collect();
97
98    #[cfg(not(feature = "parallel"))]
99    let results: Vec<_> = src_positions
100        .iter()
101        .filter_map(|&src_pos| {
102            apply_vector_offset(src_pos, vector, morphology_scalar, dst_dimensions)
103        })
104        .collect();
105
106    Ok(results)
107}
108
109#[cfg(test)]
110mod tests {
111    use super::*;
112
113    #[test]
114    fn test_vector_offset() {
115        let result = apply_vector_offset((5, 5, 5), (1, 0, 0), 1.0, (10, 10, 10));
116        assert_eq!(result, Some((6, 5, 5)));
117    }
118
119    #[test]
120    fn test_vector_offset_with_scalar() {
121        let result = apply_vector_offset((5, 5, 5), (2, 2, 2), 2.0, (20, 20, 20));
122        assert_eq!(result, Some((9, 9, 9)));
123    }
124
125    #[test]
126    fn test_vector_offset_out_of_bounds() {
127        // Should error when offset is out of bounds
128        let result = apply_vector_offset((8, 8, 8), (5, 5, 5), 1.0, (10, 10, 10));
129        assert_eq!(result, None);
130    }
131
132    #[test]
133    fn test_vector_offset_clamped() {
134        // Clamped variant should stay in bounds
135        let result = apply_vector_offset_clamped((8, 8, 8), (5, 5, 5), 1.0, (10, 10, 10));
136        assert!(result.is_ok());
137        assert_eq!(result.unwrap(), (9, 9, 9)); // Clamped to max-1
138    }
139
140    #[test]
141    fn test_batch_vectors() {
142        let positions = vec![(0, 0, 0), (1, 1, 1), (2, 2, 2)];
143
144        let results = match_vectors_batch(&positions, (1, 0, 0), 1.0, (10, 10, 10));
145
146        assert!(results.is_ok());
147        let results = results.unwrap();
148        assert_eq!(results.len(), 3);
149        assert_eq!(results[0], (1, 0, 0));
150        assert_eq!(results[1], (2, 1, 1));
151        assert_eq!(results[2], (3, 2, 2));
152    }
153
154    #[test]
155    fn test_batch_vectors_skips_out_of_bounds() {
156        let positions = vec![(8, 0, 0), (9, 0, 0)];
157        let results = match_vectors_batch(&positions, (1, 0, 0), 1.0, (10, 10, 10));
158        assert!(results.is_ok());
159        let results = results.unwrap();
160        assert_eq!(results.len(), 1);
161        assert_eq!(results[0], (9, 0, 0));
162    }
163}