use std::collections::{BTreeMap, HashMap, HashSet};
use std::ops::RangeBounds;
use crate::algs::submesh::{SubmeshMaps, SubmeshSelection, extract_by_label};
use crate::data::storage::Storage;
use crate::io::MeshData;
use crate::mesh_error::MeshSieveError;
use crate::topology::cell_type::CellType;
use crate::topology::point::PointId;
use crate::topology::sieve::{OrientedMeshSieve, OrientedSieve, Sieve};
#[derive(Clone, Debug, Default)]
pub struct LabelSet {
labels: HashMap<String, HashMap<PointId, i32>>,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum LabelIterationOrder {
PointAscending,
ValueThenPointAscending,
}
impl LabelSet {
pub fn new() -> Self {
Self::default()
}
pub fn set_label(&mut self, point: PointId, name: &str, value: i32) -> Option<i32> {
self.labels
.entry(name.to_string())
.or_default()
.insert(point, value)
}
pub fn get_label(&self, point: PointId, name: &str) -> Option<i32> {
self.labels
.get(name)
.and_then(|map| map.get(&point).copied())
}
pub fn points_with_label<'a>(
&'a self,
name: &'a str,
value: i32,
) -> impl Iterator<Item = PointId> + 'a {
self.labels.get(name).into_iter().flat_map(move |map| {
map.iter()
.filter_map(move |(&point, &label_value)| (label_value == value).then_some(point))
})
}
pub fn stratum_size(&self, name: &str, value: i32) -> usize {
self.labels.get(name).map_or(0, |map| {
map.values()
.filter(|&&label_value| label_value == value)
.count()
})
}
pub fn stratum_points(&self, name: &str, value: i32) -> Vec<PointId> {
let mut points: Vec<_> = self.points_with_label(name, value).collect();
points.sort_unstable();
points
}
pub fn stratum_values(&self, name: &str) -> Vec<i32> {
let mut values: Vec<i32> = self
.labels
.get(name)
.map_or_else(Vec::new, |map| map.values().copied().collect());
values.sort_unstable();
values.dedup();
values
}
pub fn values(&self, name: &str) -> Vec<i32> {
self.stratum_values(name)
}
pub fn strata_by_value(&self, name: &str) -> BTreeMap<i32, Vec<PointId>> {
let Some(map) = self.labels.get(name) else {
return BTreeMap::new();
};
let mut strata: BTreeMap<i32, Vec<PointId>> = BTreeMap::new();
for (&point, &value) in map {
strata.entry(value).or_default().push(point);
}
for points in strata.values_mut() {
points.sort_unstable();
}
strata
}
pub fn stratum_points_in_range<R>(&self, name: &str, range: R) -> Vec<PointId>
where
R: RangeBounds<i32>,
{
let strata = self.strata_by_value(name);
let mut points = Vec::new();
for (_, stratum_points) in strata.range(range) {
points.extend(stratum_points.iter().copied());
}
points
}
pub fn stratum_union(&self, other: &LabelSet, name: &str, value: i32) -> Vec<PointId> {
let mut union: HashSet<PointId> = self.points_with_label(name, value).collect();
union.extend(other.points_with_label(name, value));
let mut points: Vec<_> = union.into_iter().collect();
points.sort_unstable();
points
}
pub fn stratum_intersection(&self, other: &LabelSet, name: &str, value: i32) -> Vec<PointId> {
let other_points: HashSet<PointId> = other.points_with_label(name, value).collect();
let mut points: Vec<_> = self
.points_with_label(name, value)
.filter(|point| other_points.contains(point))
.collect();
points.sort_unstable();
points
}
pub fn stratum_difference(&self, other: &LabelSet, name: &str, value: i32) -> Vec<PointId> {
let other_points: HashSet<PointId> = other.points_with_label(name, value).collect();
let mut points: Vec<_> = self
.points_with_label(name, value)
.filter(|point| !other_points.contains(point))
.collect();
points.sort_unstable();
points
}
pub fn clear_label_value(&mut self, name: &str, value: i32) -> usize {
let Some(map) = self.labels.get_mut(name) else {
return 0;
};
let before = map.len();
map.retain(|_, label_value| *label_value != value);
let removed = before - map.len();
if map.is_empty() {
self.labels.remove(name);
}
removed
}
pub fn filtered_to_points<I>(&self, points: I) -> Self
where
I: IntoIterator<Item = PointId>,
{
let keep: HashSet<PointId> = points.into_iter().collect();
if keep.is_empty() {
return Self::default();
}
let mut out = LabelSet::new();
for (name, values) in &self.labels {
for (&point, &value) in values {
if keep.contains(&point) {
out.set_label(point, name, value);
}
}
}
out
}
pub fn is_empty(&self) -> bool {
self.labels.is_empty()
}
pub fn clear_points<I>(&mut self, points: I)
where
I: IntoIterator<Item = PointId>,
{
let targets: HashSet<PointId> = points.into_iter().collect();
if targets.is_empty() {
return;
}
let mut empty_labels = Vec::new();
for (name, map) in &mut self.labels {
map.retain(|point, _| !targets.contains(point));
if map.is_empty() {
empty_labels.push(name.clone());
}
}
for name in empty_labels {
self.labels.remove(&name);
}
}
pub fn iter(&self) -> impl Iterator<Item = (&str, PointId, i32)> + '_ {
self.labels.iter().flat_map(|(name, map)| {
map.iter()
.map(move |(&point, &value)| (name.as_str(), point, value))
})
}
pub fn apply_to_closure<S>(
&mut self,
sieve: &S,
name: &str,
value: i32,
seeds: impl IntoIterator<Item = PointId>,
) where
S: Sieve<Point = PointId>,
{
for point in sieve.closure_iter(seeds) {
self.set_label(point, name, value);
}
}
pub fn apply_to_star<S>(
&mut self,
sieve: &S,
name: &str,
value: i32,
seeds: impl IntoIterator<Item = PointId>,
) where
S: Sieve<Point = PointId>,
{
for point in sieve.star_iter(seeds) {
self.set_label(point, name, value);
}
}
pub fn union_into(
&mut self,
other: &LabelSet,
src_name: &str,
src_value: i32,
dst_name: &str,
dst_value: i32,
) {
for point in other.stratum_points(src_name, src_value) {
self.set_label(point, dst_name, dst_value);
}
}
pub fn intersect_with(
&mut self,
other: &LabelSet,
src_name: &str,
src_value: i32,
dst_name: &str,
dst_value: i32,
) {
let keep: HashSet<PointId> = other
.stratum_points(src_name, src_value)
.into_iter()
.collect();
let to_remove: Vec<PointId> = self
.stratum_points(dst_name, dst_value)
.into_iter()
.filter(|p| !keep.contains(p))
.collect();
for p in to_remove {
if let Some(map) = self.labels.get_mut(dst_name) {
map.remove(&p);
}
}
}
pub fn subtract(
&mut self,
other: &LabelSet,
src_name: &str,
src_value: i32,
dst_name: &str,
dst_value: i32,
) {
let remove: HashSet<PointId> = other
.stratum_points(src_name, src_value)
.into_iter()
.collect();
for p in remove {
if self.get_label(p, dst_name) == Some(dst_value)
&& let Some(map) = self.labels.get_mut(dst_name)
{
map.remove(&p);
}
}
}
pub fn remap_value(&mut self, name: &str, from: i32, to: i32) {
for p in self.stratum_points(name, from) {
self.set_label(p, name, to);
}
}
}
pub fn complete_label_value<S>(sieve: &S, labels: &LabelSet, name: &str, value: i32) -> LabelSet
where
S: Sieve<Point = PointId>,
{
propagate_label_value_closure(sieve, labels, name, value)
}
pub fn complete_label_set<S>(sieve: &S, labels: &LabelSet) -> LabelSet
where
S: Sieve<Point = PointId>,
{
propagate_label_set_closure(sieve, labels)
}
pub fn propagate_label_value_closure<S>(
sieve: &S,
labels: &LabelSet,
name: &str,
value: i32,
) -> LabelSet
where
S: Sieve<Point = PointId>,
{
let mut out = labels.clone();
let seeds: Vec<PointId> = labels.points_with_label(name, value).collect();
if seeds.is_empty() {
return out;
}
for point in sieve.closure_iter(seeds) {
out.set_label(point, name, value);
}
out
}
pub fn propagate_label_value_star<S>(
sieve: &S,
labels: &LabelSet,
name: &str,
value: i32,
) -> LabelSet
where
S: Sieve<Point = PointId>,
{
let mut out = labels.clone();
let seeds: Vec<PointId> = labels.points_with_label(name, value).collect();
if seeds.is_empty() {
return out;
}
for point in sieve.star_iter(seeds) {
out.set_label(point, name, value);
}
out
}
pub fn propagate_label_set_closure<S>(sieve: &S, labels: &LabelSet) -> LabelSet
where
S: Sieve<Point = PointId>,
{
let mut out = labels.clone();
let mut names: HashSet<String> = HashSet::new();
for (name, _, _) in labels.iter() {
names.insert(name.to_string());
}
for name in names {
for value in labels.stratum_values(&name) {
out = propagate_label_value_closure(sieve, &out, &name, value);
}
}
out
}
pub fn propagate_label_set_star<S>(sieve: &S, labels: &LabelSet) -> LabelSet
where
S: Sieve<Point = PointId>,
{
let mut out = labels.clone();
let mut names: HashSet<String> = HashSet::new();
for (name, _, _) in labels.iter() {
names.insert(name.to_string());
}
for name in names {
for value in labels.stratum_values(&name) {
out = propagate_label_value_star(sieve, &out, &name, value);
}
}
out
}
pub fn extract_submesh_from_label<S, V, St, CtSt>(
mesh: &MeshData<S, V, St, CtSt>,
labels: &LabelSet,
label_name: &str,
label_value: i32,
) -> Result<
(
MeshData<OrientedMeshSieve<PointId, S::Payload>, V, St, CtSt>,
SubmeshMaps,
),
MeshSieveError,
>
where
S: OrientedSieve<Point = PointId, Orient = i32>,
S::Payload: Clone,
V: Clone + Default,
St: Storage<V> + Clone,
CtSt: Storage<CellType> + Clone,
{
let completed = complete_label_value(&mesh.sieve, labels, label_name, label_value);
extract_by_label(
mesh,
&completed,
label_name,
label_value,
SubmeshSelection::FullClosure,
)
}