#![allow(dead_code)]
#[allow(dead_code)]
#[derive(Debug, Clone, Copy)]
pub struct BspLine {
pub a: f32,
pub b: f32,
pub c: f32,
}
#[allow(dead_code)]
pub fn bsp_line_side(line: &BspLine, p: [f32; 2]) -> f32 {
line.a * p[0] + line.b * p[1] + line.c
}
#[allow(dead_code)]
#[derive(Debug, Clone)]
pub struct BspPolygon {
pub vertices: Vec<[f32; 2]>,
pub label: String,
}
#[allow(dead_code)]
#[derive(Debug, Clone)]
pub enum BspNode {
Leaf(Vec<BspPolygon>),
Branch {
splitter: BspLine,
front: Box<BspNode>,
back: Box<BspNode>,
},
}
#[allow(dead_code)]
#[derive(Debug, Clone, Default)]
pub struct BspTree2D {
root: Option<Box<BspNode>>,
}
#[allow(dead_code)]
pub fn new_bsp_tree() -> BspTree2D {
BspTree2D::default()
}
#[allow(dead_code)]
pub fn bsp_split_polygon(poly: &[[f32; 2]], line: &BspLine) -> (Vec<[f32; 2]>, Vec<[f32; 2]>) {
let mut front = Vec::new();
let mut back = Vec::new();
let n = poly.len();
if n == 0 {
return (front, back);
}
for i in 0..n {
let curr = poly[i];
let next = poly[(i + 1) % n];
let d_curr = bsp_line_side(line, curr);
let d_next = bsp_line_side(line, next);
if d_curr >= 0.0 {
front.push(curr);
} else {
back.push(curr);
}
if (d_curr > 0.0 && d_next < 0.0) || (d_curr < 0.0 && d_next > 0.0) {
let t = d_curr / (d_curr - d_next);
let ix = curr[0] + t * (next[0] - curr[0]);
let iy = curr[1] + t * (next[1] - curr[1]);
front.push([ix, iy]);
back.push([ix, iy]);
}
}
(front, back)
}
#[allow(dead_code)]
pub fn bsp_build(polygons: Vec<BspPolygon>) -> BspNode {
if polygons.is_empty() {
return BspNode::Leaf(Vec::new());
}
if polygons.len() == 1 {
return BspNode::Leaf(polygons);
}
let splitter = {
let verts = &polygons[0].vertices;
if verts.len() < 2 {
return BspNode::Leaf(polygons);
}
let a = verts[0];
let b = verts[1];
let dx = b[0] - a[0];
let dy = b[1] - a[1];
let len = (dx * dx + dy * dy).sqrt().max(1e-10);
BspLine {
a: -dy / len,
b: dx / len,
c: (dy * a[0] - dx * a[1]) / len,
}
};
let mut front_polys = Vec::new();
let mut back_polys = Vec::new();
let input_count = polygons.len();
for poly in polygons {
let (f, b) = bsp_split_polygon(&poly.vertices, &splitter);
let f_ok = f.len() >= 3;
let b_ok = b.len() >= 3;
if f_ok {
front_polys.push(BspPolygon {
vertices: f,
label: poly.label.clone(),
});
}
if b_ok {
back_polys.push(BspPolygon {
vertices: b,
label: poly.label.clone(),
});
}
if !f_ok && !b_ok {
front_polys.push(poly);
}
}
if front_polys.len() == input_count && back_polys.is_empty() {
return BspNode::Leaf(front_polys);
}
if back_polys.len() == input_count && front_polys.is_empty() {
return BspNode::Leaf(back_polys);
}
BspNode::Branch {
splitter,
front: Box::new(bsp_build(front_polys)),
back: Box::new(bsp_build(back_polys)),
}
}
#[allow(dead_code)]
pub fn bsp_polygon_count(node: &BspNode) -> usize {
match node {
BspNode::Leaf(polys) => polys.len(),
BspNode::Branch { front, back, .. } => bsp_polygon_count(front) + bsp_polygon_count(back),
}
}
#[allow(dead_code)]
pub fn bsp_depth(node: &BspNode) -> usize {
match node {
BspNode::Leaf(_) => 0,
BspNode::Branch { front, back, .. } => 1 + bsp_depth(front).max(bsp_depth(back)),
}
}
#[allow(dead_code)]
pub fn bsp_collect_polygons(node: &BspNode) -> Vec<&BspPolygon> {
let mut result = Vec::new();
bsp_collect_recursive(node, &mut result);
result
}
fn bsp_collect_recursive<'a>(node: &'a BspNode, out: &mut Vec<&'a BspPolygon>) {
match node {
BspNode::Leaf(polys) => {
for p in polys {
out.push(p);
}
}
BspNode::Branch { front, back, .. } => {
bsp_collect_recursive(front, out);
bsp_collect_recursive(back, out);
}
}
}
#[allow(dead_code)]
pub fn bsp_is_leaf(node: &BspNode) -> bool {
matches!(node, BspNode::Leaf(_))
}
#[allow(dead_code)]
pub fn bsp_set_root(tree: &mut BspTree2D, node: BspNode) {
tree.root = Some(Box::new(node));
}
#[allow(dead_code)]
pub fn bsp_get_root(tree: &BspTree2D) -> Option<&BspNode> {
tree.root.as_deref()
}
#[cfg(test)]
mod tests {
use super::*;
fn make_quad(x: f32, y: f32, w: f32, h: f32, label: &str) -> BspPolygon {
BspPolygon {
vertices: vec![[x, y], [x + w, y], [x + w, y + h], [x, y + h]],
label: label.to_string(),
}
}
#[test]
fn test_new_tree_empty() {
let tree = new_bsp_tree();
assert!(bsp_get_root(&tree).is_none());
}
#[test]
fn test_build_single_polygon() {
let poly = make_quad(0.0, 0.0, 1.0, 1.0, "a");
let node = bsp_build(vec![poly]);
assert!(bsp_is_leaf(&node));
}
#[test]
fn test_build_two_polygons() {
let p1 = make_quad(-1.0, -1.0, 2.0, 2.0, "a");
let p2 = make_quad(-1.0, 1.0, 2.0, 2.0, "b");
let node = bsp_build(vec![p1, p2]);
let count = bsp_polygon_count(&node);
assert!(count >= 1);
}
#[test]
fn test_bsp_line_side() {
let line = BspLine {
a: 1.0,
b: 0.0,
c: 0.0,
};
assert!(bsp_line_side(&line, [1.0, 0.0]) > 0.0);
assert!(bsp_line_side(&line, [-1.0, 0.0]) < 0.0);
}
#[test]
fn test_split_polygon_horizontal() {
let line = BspLine {
a: 0.0,
b: 1.0,
c: -0.5,
};
let poly = vec![[0.0, 0.0], [1.0, 0.0], [1.0, 1.0], [0.0, 1.0]];
let (f, b) = bsp_split_polygon(&poly, &line);
assert!(!f.is_empty());
assert!(!b.is_empty());
}
#[test]
fn test_collect_polygons() {
let p1 = make_quad(0.0, 0.0, 1.0, 1.0, "a");
let p2 = make_quad(2.0, 0.0, 1.0, 1.0, "b");
let node = bsp_build(vec![p1, p2]);
let collected = bsp_collect_polygons(&node);
assert!(!collected.is_empty());
}
#[test]
fn test_bsp_depth_single() {
let poly = make_quad(0.0, 0.0, 1.0, 1.0, "a");
let node = bsp_build(vec![poly]);
assert_eq!(bsp_depth(&node), 0);
}
#[test]
fn test_bsp_set_and_get_root() {
let mut tree = new_bsp_tree();
let poly = make_quad(0.0, 0.0, 1.0, 1.0, "a");
let node = bsp_build(vec![poly]);
bsp_set_root(&mut tree, node);
assert!(bsp_get_root(&tree).is_some());
}
#[test]
fn test_empty_build() {
let node = bsp_build(vec![]);
assert!(bsp_is_leaf(&node));
assert_eq!(bsp_polygon_count(&node), 0);
}
}