use super::{GeoClass, Scratchpad};
use crate::StrError;
use russell_lab::Vector;
impl Scratchpad {
pub fn triangulate<F>(&mut self, x_work: &mut Vector, mut f: F) -> Result<(), StrError>
where
F: FnMut(usize, usize, usize, &Vector),
{
let kind = self.kind;
let class = kind.class();
let tri_or_qua = class == GeoClass::Tri || class == GeoClass::Qua;
if !tri_or_qua {
return Err("triangulate works with Tri and Qua classes only");
}
let space_ndim = self.get_space_ndim();
if x_work.dim() != space_ndim {
return Err("x_work.dim() must equal space_ndim");
}
let nnode = kind.nnode();
for t in 0..kind.triangulate_ntriangle() {
for i in 0..3 {
let m = kind.triangulate_triangle_nodes(t, i);
if m >= nnode {
let k = m - nnode;
let ksi = kind.triangulate_extra_coords(k);
self.calc_coords(x_work, ksi)?;
} else {
for j in 0..space_ndim {
x_work[j] = self.xxt.get(j, m);
}
};
f(t, i, m, x_work);
}
}
Ok(())
}
}
#[cfg(test)]
mod tests {
use crate::geometry::{Point2d, Triangle2d};
use crate::shapes::scratchpad_testing::aux;
use crate::shapes::{GeoClass, GeoKind};
use plotpy::{Canvas, Plot};
use russell_lab::math::PI;
use russell_lab::{approx_eq, Vector};
const SAVE_FIGURE: bool = false;
#[test]
fn pad_triangulate_works() {
let ninety = PI / 2.0;
let forty_five = PI / 4.0;
let space_ndim = 2;
let mut x_work = Vector::new(space_ndim);
for kind in GeoKind::VALUES {
let ntriangle = kind.triangulate_ntriangle();
if ntriangle > 0 {
let edge_nnode = kind.edge_nnode();
let n_quads = (edge_nnode - 1) * (edge_nnode - 1);
if kind.class() == GeoClass::Tri {
assert_eq!(ntriangle, n_quads);
} else {
assert_eq!(ntriangle, n_quads * 2);
}
let expected_area = if kind.class() == GeoClass::Tri {
2.0 / (ntriangle as f64)
} else {
4.0 / (ntriangle as f64)
};
let mut pad = aux::gen_scratchpad_with_coords_aligned(kind);
let nnode_total = kind.nnode() + kind.triangulate_extra_nnode();
let mut xx = vec![0.0; nnode_total];
let mut yy = vec![0.0; nnode_total];
let mut triangles = vec![vec![0; 3]; ntriangle];
pad.triangulate(&mut x_work, |t, i, m, x| {
xx[m] = x[0];
yy[m] = x[1];
triangles[t][i] = m;
})
.unwrap();
for t in 0..ntriangle {
let a = triangles[t][0];
let b = triangles[t][1];
let c = triangles[t][2];
let xa = Point2d::from_slice(&[xx[a], yy[a]]);
let xb = Point2d::from_slice(&[xx[b], yy[b]]);
let xc = Point2d::from_slice(&[xx[c], yy[c]]);
let tri = Triangle2d::from_points(&xa, &xb, &xc);
approx_eq(tri.signed_area(), expected_area, 1e-14);
let (h0, h1, h2) = tri.internal_angles();
if kind.class() == GeoClass::Tri {
let mut angles = vec![h0, h1, h2];
angles.sort_by(|a, b| a.partial_cmp(b).unwrap());
let (smallest, middle, largest) = (angles[0], angles[1], angles[2]);
approx_eq(smallest, forty_five, 1e-14);
approx_eq(middle, forty_five, 1e-14);
approx_eq(largest, ninety, 1e-14);
} else {
if t % 2 == 0 {
approx_eq(h0, ninety, 1e-14);
approx_eq(h1, forty_five, 1e-14);
approx_eq(h2, forty_five, 1e-14);
} else {
approx_eq(h0, forty_five, 1e-14);
approx_eq(h1, ninety, 1e-14);
approx_eq(h2, forty_five, 1e-14);
}
}
}
if SAVE_FIGURE {
let mut canvas = Canvas::new();
canvas.draw_triangles(&xx, &yy, &triangles);
let mut plot = Plot::new();
pad.draw_shape(&mut plot, "", true, false).unwrap();
plot.add(&canvas);
plot.set_figure_size_points(600.0, 600.0)
.set_equal_axes(true)
.save(format!("/tmp/gemlab/test_pad_triangulation_works_{}.svg", kind.to_string()).as_str())
.unwrap();
}
}
}
}
#[test]
fn pad_triangulate_works_2() {
let space_ndim = 3; let mut x_work = Vector::new(space_ndim);
for kind in GeoKind::VALUES {
let ntriangle = kind.triangulate_ntriangle();
if ntriangle > 0 {
let mut pad = aux::gen_scratchpad_with_coords(space_ndim, kind);
let nnode_total = kind.nnode() + kind.triangulate_extra_nnode();
let mut xx = vec![0.0; nnode_total];
let mut yy = vec![0.0; nnode_total];
let mut zz = vec![0.0; nnode_total];
let mut triangles = vec![vec![0; 3]; ntriangle];
pad.triangulate(&mut x_work, |t, i, m, x| {
xx[m] = x[0];
yy[m] = x[1];
zz[m] = x[2];
triangles[t][i] = m;
})
.unwrap();
for t in 0..ntriangle {
let a = triangles[t][0];
let b = triangles[t][1];
let c = triangles[t][2];
let xa = Point2d::from_slice(&[xx[a], yy[a]]);
let xb = Point2d::from_slice(&[xx[b], yy[b]]);
let xc = Point2d::from_slice(&[xx[c], yy[c]]);
let tri = Triangle2d::from_points(&xa, &xb, &xc);
assert!(tri.signed_area() > 0.0);
}
if SAVE_FIGURE {
let mut canvas = Canvas::new();
canvas.draw_triangles_3d(&xx, &yy, &zz, &triangles);
let mut plot = Plot::new();
pad.draw_shape(&mut plot, "", true, false).unwrap();
plot.add(&canvas);
plot.set_figure_size_points(600.0, 600.0)
.set_equal_axes(true)
.save(format!("/tmp/gemlab/test_pad_triangulation_works_2_{}.svg", kind.to_string()).as_str())
.unwrap();
}
}
}
}
}