use super::{PolyMesh, TetMesh, TriMesh};
#[derive(Copy, Clone, Debug, PartialEq)]
pub enum AxisPlaneOrientation {
XY,
YZ,
ZX,
}
#[derive(Copy, Clone, Debug, PartialEq)]
pub struct GridBuilder {
pub rows: usize,
pub cols: usize,
pub orientation: AxisPlaneOrientation,
}
impl GridBuilder {
pub fn build(self) -> PolyMesh<f64> {
let GridBuilder {
rows,
cols,
orientation,
} = self;
let mut positions = Vec::new();
for j in 0..=cols {
for i in 0..=rows {
let r = -1.0 + 2.0 * (i as f64) / rows as f64;
let c = -1.0 + 2.0 * (j as f64) / cols as f64;
let node_pos = match orientation {
AxisPlaneOrientation::XY => [r, c, 0.0],
AxisPlaneOrientation::YZ => [0.0, r, c],
AxisPlaneOrientation::ZX => [c, 0.0, r],
};
positions.push(node_pos);
}
}
let mut indices = Vec::new();
for i in 0..rows {
for j in 0..cols {
indices.push(4);
indices.push((rows + 1) * j + i);
indices.push((rows + 1) * j + i + 1);
indices.push((rows + 1) * (j + 1) + i + 1);
indices.push((rows + 1) * (j + 1) + i);
}
}
PolyMesh::new(positions, &indices)
}
}
#[derive(Copy, Clone, Debug, PartialEq)]
pub struct SolidBoxBuilder {
pub res: [usize; 3]
}
impl SolidBoxBuilder {
pub fn new() -> Self {
SolidBoxBuilder { res: [1, 1, 1] }
}
pub fn build(self) -> TetMesh<f64> {
let mut positions = Vec::new();
let [nx, ny, nz] = self.res;
for ix in 0..=nx {
for iy in 0..=ny {
for iz in 0..=nz {
let x = -1.0 + 2.0 * (ix as f64) / nx as f64;
let y = -1.0 + 2.0 * (iy as f64) / ny as f64;
let z = -1.0 + 2.0 * (iz as f64) / nz as f64;
positions.push([x, y, z]);
}
}
}
let mut indices = Vec::new();
for ix in 0..nx {
for iy in 0..ny {
for iz in 0..nz {
let index = |x, y, z| ((ix + x) * (ny + 1) + (iy + y)) * (nz + 1) + (iz + z);
let first = index(0, 0, 0);
let second = index(1, 1, 1);
indices.push(first);
indices.push(second);
indices.push(index(0, 1, 1));
indices.push(index(0, 1, 0));
indices.push(first);
indices.push(second);
indices.push(index(0, 1, 0));
indices.push(index(1, 1, 0));
indices.push(first);
indices.push(second);
indices.push(index(1, 1, 0));
indices.push(index(1, 0, 0));
indices.push(first);
indices.push(second);
indices.push(index(1, 0, 0));
indices.push(index(1, 0, 1));
indices.push(first);
indices.push(second);
indices.push(index(1, 0, 1));
indices.push(index(0, 0, 1));
indices.push(first);
indices.push(second);
indices.push(index(0, 0, 1));
indices.push(index(0, 1, 1));
}
}
}
TetMesh::new(positions, indices)
}
}
#[derive(Copy, Clone, Debug, PartialEq)]
pub struct PlatonicSolidBuilder { }
impl PlatonicSolidBuilder {
pub fn build_octahedron() -> TriMesh<f64> {
let vertices = vec![
[-0.5, 0.0, 0.0],
[0.5, 0.0, 0.0],
[0.0, -0.5, 0.0],
[0.0, 0.5, 0.0],
[0.0, 0.0, -0.5],
[0.0, 0.0, 0.5],
];
let indices = vec![
0, 5, 3, 4, 0, 3, 1, 4, 3, 5, 1, 3, 5, 0, 2, 0, 4, 2, 4, 1, 2, 1, 5, 2,
];
TriMesh::new(vertices, indices)
}
pub fn build_tetrahedron() -> TetMesh<f64> {
let sqrt_8_by_9 = f64::sqrt(8.0 / 9.0);
let sqrt_2_by_9 = f64::sqrt(2.0 / 9.0);
let sqrt_2_by_3 = f64::sqrt(2.0 / 3.0);
let vertices = vec![
[0.0, 1.0, 0.0],
[-sqrt_8_by_9, -1.0 / 3.0, 0.0],
[sqrt_2_by_9, -1.0 / 3.0, sqrt_2_by_3],
[sqrt_2_by_9, -1.0 / 3.0, -sqrt_2_by_3],
];
let indices = vec![3, 1, 0, 2];
TetMesh::new(vertices, indices)
}
pub fn build_icosahedron() -> TriMesh<f64> {
let sqrt5 = 5.0_f64.sqrt();
let a = 1.0 / sqrt5;
let w1 = 0.25 * (sqrt5 - 1.0);
let h1 = (0.125 * (5.0 + sqrt5)).sqrt();
let w2 = 0.25 * (sqrt5 + 1.0);
let h2 = (0.125 * (5.0 - sqrt5)).sqrt();
let vertices = vec![
[0.0, 0.0, 1.0],
[0.0, 2.0 * a, a],
[2.0 * a * h2, 2.0 * a * w2, -a],
[2.0 * a * h1, 2.0 * a * w1, a],
[2.0 * a * h1, -2.0 * a * w1, -a],
[2.0 * a * h2, -2.0 * a * w2, a],
[0.0, -2.0 * a, -a],
[-2.0 * a * h2, -2.0 * a * w2, a],
[-2.0 * a * h1, -2.0 * a * w1, -a],
[-2.0 * a * h1, 2.0 * a * w1, a],
[-2.0 * a * h2, 2.0 * a * w2, -a],
[0.0, 0.0, -1.0],
];
#[rustfmt::skip]
let indices = vec![
0, 1, 3,
0, 3, 5,
0, 5, 7,
0, 7, 9,
0, 9, 1,
1, 2, 3,
2, 4, 3,
3, 4, 5,
4, 6, 5,
5, 6, 7,
6, 8, 7,
7, 8, 9,
8, 10, 9,
9, 10, 1,
10, 2, 1,
11, 2, 10,
11, 4, 2,
11, 6, 4,
11, 8, 6,
11, 10, 8,
];
TriMesh::new(vertices, indices)
}
}
#[derive(Copy, Clone, Debug, PartialEq)]
pub struct TorusBuilder {
pub outer_radius: f32,
pub inner_radius: f32,
pub outer_divs: usize,
pub inner_divs: usize,
}
impl TorusBuilder {
pub fn new() -> Self {
TorusBuilder { outer_radius: 0.5, inner_radius: 0.25, outer_divs: 24, inner_divs: 12 }
}
pub fn build(self) -> PolyMesh<f64> {
let TorusBuilder {
outer_radius,
inner_radius,
outer_divs,
inner_divs
} = self;
let mut vertices = Vec::with_capacity(outer_divs * inner_divs);
let mut indices = Vec::with_capacity(5 * outer_divs * inner_divs);
let outer_step = 2.0 * std::f64::consts::PI / outer_divs as f64;
let inner_step = 2.0 * std::f64::consts::PI / inner_divs as f64;
for i in 0..outer_divs {
let theta = outer_step * i as f64;
for j in 0..inner_divs {
let phi = inner_step * j as f64;
let idx = vertices.len();
vertices.push([
theta.cos() * (outer_radius as f64 + phi.cos() * inner_radius as f64),
phi.sin() * inner_radius as f64,
theta.sin() * (outer_radius as f64 + phi.cos() * inner_radius as f64),
]);
indices.extend_from_slice(&[
4,
idx,
(((idx + 1) % inner_divs) + inner_divs * (idx / inner_divs))
% (inner_divs * outer_divs),
((1 + idx) % inner_divs + (1 + idx / inner_divs) * inner_divs)
% (inner_divs * outer_divs),
(idx % inner_divs + (1 + idx / inner_divs) * inner_divs)
% (inner_divs * outer_divs),
]);
}
}
PolyMesh::new(vertices, &indices)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::ops::*;
#[test]
fn icosahedron_unity_test() {
use approx::assert_relative_eq;
use crate::mesh::VertexPositions;
use math::Vector3;
let icosa = PlatonicSolidBuilder::build_icosahedron();
for &v in icosa.vertex_positions() {
assert_relative_eq!(Vector3::from(v).norm(), 1.0);
}
}
#[test]
fn grid_test() {
use crate::ops::*;
let grid = GridBuilder {
rows: 1,
cols: 1,
orientation: AxisPlaneOrientation::ZX,
}.build();
let bbox = grid.bounding_box();
assert_eq!(bbox.min_corner(), [-1.0, 0.0, -1.0]);
assert_eq!(bbox.max_corner(), [1.0, 0.0, 1.0]);
}
}