use crate::data::constrained_section::DofConstraint;
use crate::data::section::Section;
use crate::data::storage::Storage;
use crate::mesh_error::MeshSieveError;
use crate::topology::anchors::TopologicalAnchors;
use crate::topology::cache::InvalidateCache;
use crate::topology::point::PointId;
use std::collections::BTreeMap;
#[derive(Clone, Debug, PartialEq)]
pub struct LinearConstraintTerm<V> {
pub point: PointId,
pub index: usize,
pub weight: V,
}
impl<V> LinearConstraintTerm<V> {
pub fn new(point: PointId, index: usize, weight: V) -> Self {
Self {
point,
index,
weight,
}
}
}
#[derive(Clone, Debug, PartialEq)]
pub struct HangingDofConstraint<V> {
pub index: usize,
pub terms: Vec<LinearConstraintTerm<V>>,
}
impl<V> HangingDofConstraint<V> {
pub fn new(index: usize, terms: Vec<LinearConstraintTerm<V>>) -> Self {
Self { index, terms }
}
}
#[derive(Clone, Debug)]
pub struct HangingNodeConstraints<V> {
constraints: BTreeMap<PointId, Vec<HangingDofConstraint<V>>>,
}
impl<V> Default for HangingNodeConstraints<V> {
fn default() -> Self {
Self {
constraints: BTreeMap::new(),
}
}
}
impl<V> HangingNodeConstraints<V> {
pub fn constraints(&self) -> &BTreeMap<PointId, Vec<HangingDofConstraint<V>>> {
&self.constraints
}
pub fn constraints_mut(&mut self) -> &mut BTreeMap<PointId, Vec<HangingDofConstraint<V>>> {
&mut self.constraints
}
pub fn constraints_for(&self, point: PointId) -> Option<&[HangingDofConstraint<V>]> {
self.constraints
.get(&point)
.map(|constraints| constraints.as_slice())
}
pub fn is_constrained_dof(&self, point: PointId, index: usize) -> bool {
self.constraints.get(&point).is_some_and(|constraints| {
constraints
.iter()
.any(|constraint| constraint.index == index)
})
}
pub fn to_dof_constraint_mask(&self, value: V) -> BTreeMap<PointId, Vec<DofConstraint<V>>>
where
V: Clone,
{
self.constraints
.iter()
.map(|(point, constraints)| {
(
*point,
constraints
.iter()
.map(|constraint| DofConstraint {
index: constraint.index,
value: value.clone(),
})
.collect(),
)
})
.collect()
}
pub fn insert_constraint(
&mut self,
point: PointId,
index: usize,
terms: Vec<LinearConstraintTerm<V>>,
) {
let entry = self.constraints.entry(point).or_default();
if let Some(existing) = entry.iter_mut().find(|c| c.index == index) {
existing.terms = terms;
} else {
entry.push(HangingDofConstraint { index, terms });
}
}
pub fn clear_constraints_for_point(&mut self, point: PointId) {
self.constraints.remove(&point);
}
pub fn clear_constraints(&mut self) {
self.constraints.clear();
}
}
pub fn constraints_from_topological_anchors(
anchors: &TopologicalAnchors,
dofs_per_point: usize,
) -> HangingNodeConstraints<f64> {
let mut constraints = HangingNodeConstraints::default();
if dofs_per_point == 0 {
return constraints;
}
for (point, anchor) in anchors.iter() {
if !anchor.is_constrained() || anchor.parents.is_empty() {
continue;
}
let weight = 1.0 / anchor.parents.len() as f64;
for dof in 0..dofs_per_point {
let terms = anchor
.parents
.iter()
.map(|parent| LinearConstraintTerm::new(*parent, dof, weight))
.collect();
constraints.insert_constraint(point, dof, terms);
}
}
constraints
}
pub fn apply_hanging_constraints_to_section<V, S>(
section: &mut Section<V, S>,
constraints: &HangingNodeConstraints<V>,
) -> Result<(), MeshSieveError>
where
V: Clone + Default + core::ops::AddAssign + core::ops::Mul<Output = V>,
S: Storage<V>,
{
let mut updates: Vec<(PointId, usize, V)> = Vec::new();
for (point, list) in constraints.constraints.iter() {
let len = section.try_restrict(*point)?.len();
for constraint in list {
if constraint.index >= len {
return Err(MeshSieveError::ConstraintIndexOutOfBounds {
point: *point,
index: constraint.index,
len,
});
}
let mut value = V::default();
for term in &constraint.terms {
let source = section.try_restrict(term.point)?;
let src_len = source.len();
if term.index >= src_len {
return Err(MeshSieveError::ConstraintIndexOutOfBounds {
point: term.point,
index: term.index,
len: src_len,
});
}
value += source[term.index].clone() * term.weight.clone();
}
updates.push((*point, constraint.index, value));
}
}
for (point, index, value) in updates {
let slice = section.try_restrict_mut(point)?;
slice[index] = value;
}
section.invalidate_cache();
Ok(())
}