use crate::grid::index::{Dims, Ijk};
use crate::grid::point::Point3;
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct Pillar {
pub top: Point3,
pub bottom: Point3,
}
impl Pillar {
#[inline]
fn xy_at(self, z: f64) -> (f64, f64) {
let dz = self.bottom.z - self.top.z;
if dz.abs() < f64::EPSILON {
return (self.top.x, self.top.y);
}
let t = (z - self.top.z) / dz;
(
self.top.x + t * (self.bottom.x - self.top.x),
self.top.y + t * (self.bottom.y - self.top.y),
)
}
}
#[derive(Debug, Clone)]
pub struct CornerPointGeom {
dims: Dims,
coord: Vec<Pillar>,
zcorn: Vec<f64>,
vertical: bool,
}
impl CornerPointGeom {
#[must_use]
pub fn new(dims: Dims, coord: Vec<Pillar>, zcorn: Vec<f64>) -> Self {
assert_eq!(coord.len(), dims.pillar_count(), "coord length mismatch");
assert_eq!(zcorn.len(), dims.cell_count() * 8, "zcorn length mismatch");
let vertical = coord
.iter()
.all(|p| p.top.x == p.bottom.x && p.top.y == p.bottom.y);
Self {
dims,
coord,
zcorn,
vertical,
}
}
#[must_use]
#[inline]
pub fn cell_volume_at(&self, lin: usize) -> f64 {
crate::grid::volume::hexahedron_volume(&self.cell_corners_at(lin))
}
#[inline]
fn cell_corners_at(&self, lin: usize) -> [Point3; 8] {
let per_layer = self.dims.ni * self.dims.nj;
let layer = lin / per_layer;
let rem = lin % per_layer;
let j = rem / self.dims.ni;
let i = rem % self.dims.ni;
if !self.vertical {
return self.cell_corners(Ijk::new(i, j, layer));
}
let z = &self.zcorn[lin * 8..lin * 8 + 8];
let mut corners = [Point3::new(0.0, 0.0, 0.0); 8];
for (idx, slot) in corners.iter_mut().enumerate() {
let di = idx & 1;
let dj = (idx >> 1) & 1;
let top = self.coord[self.dims.pillar_linear(i + di, j + dj)].top;
*slot = Point3::new(top.x, top.y, z[idx]);
}
corners
}
#[must_use]
pub fn take_buffers(&mut self) -> (Vec<Pillar>, Vec<f64>) {
(
std::mem::take(&mut self.coord),
std::mem::take(&mut self.zcorn),
)
}
pub fn install(&mut self, dims: Dims, coord: Vec<Pillar>, zcorn: Vec<f64>) {
assert_eq!(coord.len(), dims.pillar_count(), "coord length mismatch");
assert_eq!(zcorn.len(), dims.cell_count() * 8, "zcorn length mismatch");
let vertical = coord
.iter()
.all(|p| p.top.x == p.bottom.x && p.top.y == p.bottom.y);
self.dims = dims;
self.coord = coord;
self.zcorn = zcorn;
self.vertical = vertical;
}
#[must_use]
pub fn dims(&self) -> Dims {
self.dims
}
#[must_use]
pub fn coord(&self) -> &[Pillar] {
&self.coord
}
#[must_use]
pub fn zcorn(&self) -> &[f64] {
&self.zcorn
}
#[must_use]
pub fn is_vertical(&self) -> bool {
self.vertical
}
#[must_use]
#[inline]
pub fn centroid_z_at(&self, lin: usize) -> f64 {
let z = &self.zcorn[lin * 8..lin * 8 + 8];
(z[0] + z[1] + z[2] + z[3] + z[4] + z[5] + z[6] + z[7]) / 8.0
}
#[must_use]
#[inline]
pub fn cell_corners(&self, c: Ijk) -> [Point3; 8] {
let cell = self.dims.linear(c).expect("cell index out of bounds");
let z = &self.zcorn[cell * 8..cell * 8 + 8];
let mut corners = [Point3::new(0.0, 0.0, 0.0); 8];
for (idx, slot) in corners.iter_mut().enumerate() {
let di = idx & 1;
let dj = (idx >> 1) & 1;
let pillar = self.coord[self.dims.pillar_linear(c.i + di, c.j + dj)];
let zc = z[idx];
let (x, y) = pillar.xy_at(zc);
*slot = Point3::new(x, y, zc);
}
corners
}
}