use serde::{Deserialize, Serialize};
use crate::{block::Block, block_face_functions::Face};
pub type FaceKey = (usize, usize, usize, usize, usize, usize, usize);
#[derive(Clone, Debug, Serialize)]
pub struct MatchPoint {
pub i1: usize,
pub j1: usize,
pub k1: usize,
pub i2: usize,
pub j2: usize,
pub k2: usize,
}
pub fn match_point_bounds(
points: &[MatchPoint],
use_block1: bool,
) -> (usize, usize, usize, usize, usize, usize) {
if use_block1 {
(
points.iter().map(|p| p.i1).min().unwrap(),
points.iter().map(|p| p.i1).max().unwrap(),
points.iter().map(|p| p.j1).min().unwrap(),
points.iter().map(|p| p.j1).max().unwrap(),
points.iter().map(|p| p.k1).min().unwrap(),
points.iter().map(|p| p.k1).max().unwrap(),
)
} else {
(
points.iter().map(|p| p.i2).min().unwrap(),
points.iter().map(|p| p.i2).max().unwrap(),
points.iter().map(|p| p.j2).min().unwrap(),
points.iter().map(|p| p.j2).max().unwrap(),
points.iter().map(|p| p.k2).min().unwrap(),
points.iter().map(|p| p.k2).max().unwrap(),
)
}
}
#[derive(Clone, Debug, Serialize)]
pub struct FaceRecord {
pub block_index: usize,
pub il: usize,
pub jl: usize,
pub kl: usize,
pub ih: usize,
pub jh: usize,
pub kh: usize,
pub id: Option<usize>,
#[serde(default)]
pub u_physical: Option<(char, bool)>,
#[serde(default)]
pub v_physical: Option<(char, bool)>,
}
impl FaceRecord {
pub(crate) fn from_match_points(
block_index: usize,
points: &[MatchPoint],
first: bool,
) -> Option<Self> {
if points.is_empty() {
return None;
}
let il = points
.iter()
.map(|p| if first { p.i1 } else { p.i2 })
.min()?;
let jl = points
.iter()
.map(|p| if first { p.j1 } else { p.j2 })
.min()?;
let kl = points
.iter()
.map(|p| if first { p.k1 } else { p.k2 })
.min()?;
let ih = points
.iter()
.map(|p| if first { p.i1 } else { p.i2 })
.max()?;
let jh = points
.iter()
.map(|p| if first { p.j1 } else { p.j2 })
.max()?;
let kh = points
.iter()
.map(|p| if first { p.k1 } else { p.k2 })
.max()?;
Some(Self {
block_index,
il,
jl,
kl,
ih,
jh,
kh,
id: None,
u_physical: None,
v_physical: None,
})
}
pub fn from_face(face: &Face) -> Self {
Self {
block_index: face.block_index().unwrap_or(usize::MAX),
il: face.imin(),
jl: face.jmin(),
kl: face.kmin(),
ih: face.imax(),
jh: face.jmax(),
kh: face.kmax(),
id: face.id(),
u_physical: None,
v_physical: None,
}
}
#[inline]
pub fn i_lo(&self) -> usize {
self.il.min(self.ih)
}
#[inline]
pub fn i_hi(&self) -> usize {
self.il.max(self.ih)
}
#[inline]
pub fn j_lo(&self) -> usize {
self.jl.min(self.jh)
}
#[inline]
pub fn j_hi(&self) -> usize {
self.jl.max(self.jh)
}
#[inline]
pub fn k_lo(&self) -> usize {
self.kl.min(self.kh)
}
#[inline]
pub fn k_hi(&self) -> usize {
self.kl.max(self.kh)
}
#[inline]
pub fn i_reversed(&self) -> bool {
self.il > self.ih
}
#[inline]
pub fn j_reversed(&self) -> bool {
self.jl > self.jh
}
#[inline]
pub fn k_reversed(&self) -> bool {
self.kl > self.kh
}
#[inline]
pub fn bounds(&self) -> ([usize; 3], [usize; 3]) {
(
[self.i_lo(), self.j_lo(), self.k_lo()],
[self.i_hi(), self.j_hi(), self.k_hi()],
)
}
#[inline]
pub fn constant_axis(&self) -> Option<usize> {
let (lo, hi) = self.bounds();
(0..3).find(|&d| lo[d] == hi[d])
}
pub fn face_dims(&self) -> (usize, usize) {
let mut spans = [
self.il.abs_diff(self.ih),
self.jl.abs_diff(self.jh),
self.kl.abs_diff(self.kh),
];
spans.sort();
(spans[1], spans[2])
}
pub fn compute_direction(&mut self, block: &Block) {
let i_const = self.i_lo() == self.i_hi();
let j_const = self.j_lo() == self.j_hi();
let k_const = self.k_lo() == self.k_hi();
let (ilo, jlo, klo) = (self.i_lo(), self.j_lo(), self.k_lo());
let (ihi, jhi, khi) = (self.i_hi(), self.j_hi(), self.k_hi());
let (u_min_ijk, u_max_ijk, v_min_ijk, v_max_ijk) = if k_const || !i_const && !j_const {
(
(ilo, jlo, klo),
(ihi, jlo, klo),
(ilo, jlo, klo),
(ilo, jhi, klo),
)
} else if j_const {
(
(ilo, jlo, klo),
(ihi, jlo, klo),
(ilo, jlo, klo),
(ilo, jlo, khi),
)
} else {
(
(ilo, jlo, klo),
(ilo, jhi, klo),
(ilo, jlo, klo),
(ilo, jlo, khi),
)
};
let (ux0, uy0, uz0) = block.xyz(u_min_ijk.0, u_min_ijk.1, u_min_ijk.2);
let (ux1, uy1, uz1) = block.xyz(u_max_ijk.0, u_max_ijk.1, u_max_ijk.2);
let (vx0, vy0, vz0) = block.xyz(v_min_ijk.0, v_min_ijk.1, v_min_ijk.2);
let (vx1, vy1, vz1) = block.xyz(v_max_ijk.0, v_max_ijk.1, v_max_ijk.2);
let du = [(ux1 - ux0), (uy1 - uy0), (uz1 - uz0)];
let abs_du = [du[0].abs(), du[1].abs(), du[2].abs()];
let u_axis_idx = if abs_du[0] >= abs_du[1] && abs_du[0] >= abs_du[2] {
0
} else if abs_du[1] >= abs_du[2] {
1
} else {
2
};
let u_axis = ['x', 'y', 'z'][u_axis_idx];
let u_increasing = du[u_axis_idx] >= 0.0;
let dv = [(vx1 - vx0), (vy1 - vy0), (vz1 - vz0)];
let abs_dv = [dv[0].abs(), dv[1].abs(), dv[2].abs()];
let v_axis_idx = if abs_dv[0] >= abs_dv[1] && abs_dv[0] >= abs_dv[2] {
0
} else if abs_dv[1] >= abs_dv[2] {
1
} else {
2
};
let v_axis = ['x', 'y', 'z'][v_axis_idx];
let v_increasing = dv[v_axis_idx] >= 0.0;
self.u_physical = Some((u_axis, u_increasing));
self.v_physical = Some((v_axis, v_increasing));
}
pub fn scale_indices(&mut self, factor: usize) {
if factor <= 1 {
return;
}
self.il *= factor;
self.jl *= factor;
self.kl *= factor;
self.ih *= factor;
self.jh *= factor;
self.kh *= factor;
}
pub fn divide_indices(&mut self, divisor: usize) {
if divisor <= 1 {
return;
}
self.il /= divisor;
self.jl /= divisor;
self.kl /= divisor;
self.ih /= divisor;
self.jh /= divisor;
self.kh /= divisor;
}
#[inline]
pub fn index_key(&self) -> FaceKey {
(
self.block_index,
self.il,
self.jl,
self.kl,
self.ih,
self.jh,
self.kh,
)
}
pub fn to_face(&self, blocks: &[Block]) -> Option<Face> {
let block = blocks.get(self.block_index)?;
let mut face = crate::block_face_functions::create_face_from_diagonals(
block,
self.i_lo(),
self.j_lo(),
self.k_lo(),
self.i_hi(),
self.j_hi(),
self.k_hi(),
);
face.set_block_index(self.block_index);
if let Some(id) = self.id {
face.set_id(id);
}
Some(face)
}
}
pub trait FaceRecordTraits {
fn print(&self);
}
impl FaceRecordTraits for [FaceRecord] {
fn print(&self) {
for face in self {
println!(
"face block{} id {:?}: [{},{},{} → {},{},{}]",
face.block_index, face.id, face.il, face.jl, face.kl, face.ih, face.jh, face.kh
);
}
}
}
impl FaceRecordTraits for Vec<FaceRecord> {
fn print(&self) {
self.as_slice().print();
}
}
pub const PERMUTATION_MATRICES: [[[i8; 2]; 2]; 8] = [
[[1, 0], [0, 1]], [[-1, 0], [0, 1]], [[1, 0], [0, -1]], [[-1, 0], [0, -1]], [[0, 1], [1, 0]], [[0, -1], [1, 0]], [[0, 1], [-1, 0]], [[0, -1], [-1, 0]], ];
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "kebab-case")]
pub enum OrientationPlane {
InPlane,
CrossPlane,
}
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
pub struct Orientation {
pub permutation_index: u8,
pub plane: OrientationPlane,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub permutation_matrix: Option<[[i8; 2]; 2]>,
}
impl Orientation {
pub fn from_flags(
u_reversed: bool,
v_reversed: bool,
swapped: bool,
plane: OrientationPlane,
) -> Self {
let index = (u_reversed as u8) | ((v_reversed as u8) << 1) | ((swapped as u8) << 2);
Self {
permutation_index: index,
plane,
permutation_matrix: None,
}
}
pub fn u_reversed(&self) -> bool {
self.permutation_index & 1 != 0
}
pub fn v_reversed(&self) -> bool {
self.permutation_index & 2 != 0
}
pub fn swapped(&self) -> bool {
self.permutation_index & 4 != 0
}
pub fn matrix(&self) -> &[[i8; 2]; 2] {
&PERMUTATION_MATRICES[self.permutation_index as usize]
}
pub fn index_from_permutation_matrix(m: [[i8; 2]; 2]) -> Option<u8> {
PERMUTATION_MATRICES
.iter()
.position(|c| *c == m)
.map(|i| i as u8)
}
}
#[derive(Clone, Debug, Serialize)]
pub struct FaceMatch {
pub block1: FaceRecord,
pub block2: FaceRecord,
pub points: Vec<MatchPoint>,
#[serde(default)]
pub orientation: Option<Orientation>,
}
impl FaceMatch {
pub fn divide_indices(&mut self, divisor: usize) {
self.block1.divide_indices(divisor);
self.block2.divide_indices(divisor);
}
pub fn scale_indices(&mut self, factor: usize) {
self.block1.scale_indices(factor);
self.block2.scale_indices(factor);
}
}
pub trait FaceMatchPrinter {
fn print(&self);
}
impl FaceMatchPrinter for [FaceMatch] {
fn print(&self) {
for (idx, m) in self.iter().enumerate() {
let block1 = &m.block1;
let block2 = &m.block2;
let node_count = m.points.len();
let node_label = if node_count == 1 { "node" } else { "nodes" };
println!(
"match #{idx}: block{block1_idx:02} [{il1:03},{jl1:03},{kl1:03} -> {ih1:03},{jh1:03},{kh1:03}] <-> block{block2_idx:02} [{il2:03},{jl2:03},{kl2:03} -> {ih2:03},{jh2:03},{kh2:03}] ({node_count} {node_label})",
block1_idx = block1.block_index,
il1 = block1.il,
jl1 = block1.jl,
kl1 = block1.kl,
ih1 = block1.ih,
jh1 = block1.jh,
kh1 = block1.kh,
block2_idx = block2.block_index,
il2 = block2.il,
jl2 = block2.jl,
kl2 = block2.kl,
ih2 = block2.ih,
jh2 = block2.jh,
kh2 = block2.kh,
node_count = node_count,
node_label = node_label,
);
}
}
}
impl FaceMatchPrinter for Vec<FaceMatch> {
fn print(&self) {
self.as_slice().print();
}
}
pub type PeriodicPair = FaceMatch;