use nalgebra::{allocator::Allocator, DefaultAllocator, DimName, DimNameDiff, DimNameSub, U1};
use crate::{
misc::FloatingPoint, prelude::NurbsSurface, surface::UVDirection,
tessellation::adaptive_tessellation_node::AdaptiveTessellationNode,
};
use super::{
adaptive_tessellation_node::{DividableDirection, NeighborDirection},
adaptive_tessellation_option::AdaptiveTessellationOptions,
};
pub struct AdaptiveTessellationProcessor<'a, T: FloatingPoint, D: DimName>
where
D: DimNameSub<U1>,
DefaultAllocator: Allocator<D>,
DefaultAllocator: Allocator<DimNameDiff<D, U1>>,
{
surface: &'a NurbsSurface<T, D>,
nodes: Vec<AdaptiveTessellationNode<T, D>>,
}
impl<'a, T: FloatingPoint, D: DimName> AdaptiveTessellationProcessor<'a, T, D>
where
D: DimNameSub<U1>,
DefaultAllocator: Allocator<D>,
DefaultAllocator: Allocator<DimNameDiff<D, U1>>,
{
pub fn new(
surface: &'a NurbsSurface<T, D>,
nodes: Vec<AdaptiveTessellationNode<T, D>>,
) -> Self {
Self { surface, nodes }
}
pub fn into_nodes(self) -> Vec<AdaptiveTessellationNode<T, D>> {
self.nodes
}
pub fn divide<F>(&mut self, id: usize, options: &AdaptiveTessellationOptions<T, D, F>)
where
F: Fn(&AdaptiveTessellationNode<T, D>) -> Option<DividableDirection> + Copy,
{
let direction = if self.surface.u_degree() > 1 {
UVDirection::U
} else {
UVDirection::V
};
self.iterate(id, options, 0, direction);
}
fn iterate<F>(
&mut self,
id: usize,
options: &AdaptiveTessellationOptions<T, D, F>,
current_depth: usize,
direction: UVDirection,
) where
F: Fn(&AdaptiveTessellationNode<T, D>) -> Option<DividableDirection> + Copy,
{
let next_node_id_0 = self.nodes.len();
let next_node_id_1 = next_node_id_0 + 1;
let node = self.nodes.get_mut(id).unwrap();
let dividable_direction = if current_depth < options.min_depth {
Some(DividableDirection::Both)
} else if current_depth >= options.max_depth {
None
} else {
if let Some(ref min_edge_length) = options.min_edge_length {
let edge = node.max_edge_length();
if edge < *min_edge_length {
return;
}
}
let normal_criterion = options
.divider
.and_then(|f| f(node))
.or(node.should_divide(options.norm_tolerance));
let edge_criterion = options.max_edge_length.as_ref().and_then(|max_len| {
if node.max_edge_length() <= *max_len {
return None;
}
let v_dir_ok = node.corners.iter().all(|c| !c.is_u_constrained());
let u_dir_ok = node.corners.iter().all(|c| !c.is_v_constrained());
match (u_dir_ok, v_dir_ok) {
(true, true) => Some(DividableDirection::Both),
(true, false) => Some(DividableDirection::U),
(false, true) => Some(DividableDirection::V),
(false, false) => None,
}
});
normal_criterion.or(edge_criterion)
};
node.direction = match dividable_direction {
Some(DividableDirection::Both) => direction,
Some(DividableDirection::U) => UVDirection::U,
Some(DividableDirection::V) => UVDirection::V,
None => {
return;
}
};
let (c0, c1) = {
match node.direction {
UVDirection::U => {
let east_mid = node.evaluate_mid_point(self.surface, NeighborDirection::East);
let west_mid = node.evaluate_mid_point(self.surface, NeighborDirection::West);
let bottom = [
node.corners[0].clone(), node.corners[1].clone(), east_mid.clone(),
west_mid.clone(),
];
let top = [
west_mid,
east_mid,
node.corners[2].clone(), node.corners[3].clone(), ];
node.assign_children([next_node_id_0, next_node_id_1]);
let bottom_neighbors = [
*node.neighbors.at(NeighborDirection::South),
*node.neighbors.at(NeighborDirection::East),
Some(next_node_id_1), *node.neighbors.at(NeighborDirection::West),
];
let top_neighbors = [
Some(next_node_id_0), *node.neighbors.at(NeighborDirection::East),
*node.neighbors.at(NeighborDirection::North),
*node.neighbors.at(NeighborDirection::West),
];
(
AdaptiveTessellationNode::new(next_node_id_0, bottom, bottom_neighbors),
AdaptiveTessellationNode::new(next_node_id_1, top, top_neighbors),
)
}
UVDirection::V => {
let south_mid = node.evaluate_mid_point(self.surface, NeighborDirection::South);
let north_mid = node.evaluate_mid_point(self.surface, NeighborDirection::North);
let left = [
node.corners[0].clone(), south_mid.clone(),
north_mid.clone(),
node.corners[3].clone(), ];
let right = [
south_mid,
node.corners[1].clone(), node.corners[2].clone(), north_mid,
];
node.assign_children([next_node_id_0, next_node_id_1]);
let left_neighbors = [
*node.neighbors.at(NeighborDirection::South),
Some(next_node_id_1), *node.neighbors.at(NeighborDirection::North),
*node.neighbors.at(NeighborDirection::West),
];
let right_neighbors = [
*node.neighbors.at(NeighborDirection::South),
*node.neighbors.at(NeighborDirection::East),
*node.neighbors.at(NeighborDirection::North),
Some(next_node_id_0), ];
(
AdaptiveTessellationNode::new(next_node_id_0, left, left_neighbors),
AdaptiveTessellationNode::new(next_node_id_1, right, right_neighbors),
)
}
}
};
self.nodes.push(c0);
self.nodes.push(c1);
for next_id in [next_node_id_0, next_node_id_1] {
self.iterate(next_id, options, current_depth + 1, direction.opposite());
}
}
}