use crate::BoxError;
pub fn query2box_distance(
query_center: &[f32],
query_offset: &[f32],
entity_point: &[f32],
alpha: f32,
) -> Result<f32, BoxError> {
let d = query_center.len();
if query_offset.len() != d || entity_point.len() != d {
return Err(BoxError::DimensionMismatch {
expected: d,
actual: query_offset.len().max(entity_point.len()),
});
}
let mut d_out = 0.0f32;
let mut d_in = 0.0f32;
for i in 0..d {
let lo = query_center[i] - query_offset[i];
let hi = query_center[i] + query_offset[i];
let v = entity_point[i];
if v < lo {
d_out += lo - v;
} else if v > hi {
d_out += v - hi;
} else {
d_in += (v - query_center[i]).abs();
}
}
Ok(d_out + alpha * d_in)
}
#[cfg(test)]
mod tests {
use super::*;
const EPS: f32 = 1e-5;
#[test]
fn query2box_entity_inside_box() {
let d = query2box_distance(&[1.0, 1.0], &[1.0, 1.0], &[1.0, 1.0], 0.02).unwrap();
assert!(
d.abs() < EPS,
"entity at center: distance should be 0, got {d}"
);
let d2 = query2box_distance(&[1.0, 1.0], &[1.0, 1.0], &[0.5, 0.5], 0.02).unwrap();
assert!(
d2 > 0.0,
"entity inside but off-center should have positive distance"
);
assert!((d2 - 0.02).abs() < EPS, "expected 0.02, got {d2}");
}
#[test]
fn query2box_entity_outside_box() {
let d = query2box_distance(&[1.0, 1.0], &[1.0, 1.0], &[5.0, 5.0], 0.02).unwrap();
assert!(
(d - 6.0).abs() < EPS,
"entity outside: expected 6.0, got {d}"
);
}
#[test]
fn query2box_entity_on_boundary() {
let d = query2box_distance(&[1.0, 1.0], &[1.0, 1.0], &[2.0, 2.0], 0.02).unwrap();
assert!(
(d - 0.04).abs() < EPS,
"on boundary: expected 0.04, got {d}"
);
}
#[test]
fn query2box_alpha_one_reduces_to_center_distance() {
let d = query2box_distance(&[1.0, 1.0], &[1.0, 1.0], &[0.5, 0.5], 1.0).unwrap();
assert!(
(d - 1.0).abs() < EPS,
"alpha=1, inside: expected L1 to center = 1.0, got {d}"
);
}
#[test]
fn query2box_alpha_zero_reduces_to_boundary_only() {
let d = query2box_distance(&[1.0, 1.0], &[1.0, 1.0], &[0.5, 0.5], 0.0).unwrap();
assert!(d.abs() < EPS, "alpha=0, inside: expected 0, got {d}");
let d2 = query2box_distance(&[1.0, 1.0], &[1.0, 1.0], &[5.0, 5.0], 0.0).unwrap();
assert!(
(d2 - 6.0).abs() < EPS,
"alpha=0, outside: expected 6.0, got {d2}"
);
}
#[test]
fn query2box_dim_mismatch() {
assert!(query2box_distance(&[1.0], &[1.0, 1.0], &[0.5], 0.02).is_err());
}
}