#![warn(missing_docs, missing_debug_implementations)]
use glam::{dvec2, DMat3, DVec2};
pub mod data;
mod iterators;
mod utils;
use data::{tiling_type_data, TilingTypeData};
use iterators::{FillAlgorithm, TilingShapeIterator, TilingShapePartIterator};
use utils::{fill_matrix, fill_vector, r#match};
pub use data::get_tiling_type;
#[derive(Debug, Default, Clone, Copy)]
pub struct TilingType(usize);
impl std::fmt::Display for TilingType {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "IH{:02}", self.0)
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum EdgeShape {
J,
U,
S,
I,
}
#[derive(Debug, Default)]
pub struct IsohedralTiling {
tiling_type: TilingType,
num_params: usize,
parameters: [f64; 6],
verts: [DVec2; 6],
edges: [DMat3; 6],
reversals: [bool; 6],
aspects: [DMat3; 12],
t1: DVec2,
t2: DVec2,
ttd: &'static TilingTypeData,
}
impl IsohedralTiling {
pub fn new(ihtype: TilingType) -> Self {
let mut tiling = Self::default();
tiling.reset(ihtype);
tiling
}
pub fn reset(&mut self, ihtype: TilingType) {
self.tiling_type = ihtype;
let ttd = &tiling_type_data[ihtype.0];
self.num_params = ttd.num_params;
self.ttd = ttd;
self.parameters[..ttd.num_params].copy_from_slice(ttd.default_params);
self.recompute();
}
pub fn tiling_type(&self) -> TilingType {
self.tiling_type
}
pub fn num_params(&self) -> usize {
self.num_params
}
pub fn num_edge_shapes(&self) -> usize {
self.ttd.num_edge_shapes
}
pub fn num_vertices(&self) -> usize {
self.ttd.num_vertices
}
pub fn edge_shape(&self, idx: usize) -> EdgeShape {
self.ttd.edge_shapes[idx]
}
pub fn vertex(&self, idx: usize) -> &DVec2 {
&self.verts[idx]
}
pub fn num_aspects(&self) -> usize {
self.ttd.num_aspects
}
pub fn aspect_transform(&self, idx: usize) -> &DMat3 {
&self.aspects[idx]
}
pub fn colour(&self, t1: isize, t2: isize, aspect: usize) -> u8 {
let nc = self.ttd.colouring[18] as isize;
let mut mt1 = t1 % nc;
if mt1 < 0 {
mt1 += nc;
}
let mut mt2 = t2 % nc;
if mt2 < 0 {
mt2 += nc;
}
let mut col = self.ttd.colouring[aspect];
for _ in 0..mt1 {
col = self.ttd.colouring[12 + col as usize];
}
for _ in 0..mt2 {
col = self.ttd.colouring[15 + col as usize];
}
col
}
pub fn t1(&self) -> &DVec2 {
&self.t1
}
pub fn t2(&self) -> &DVec2 {
&self.t2
}
pub fn shapes(&self) -> TilingShapeIterator {
TilingShapeIterator {
idx: 0,
tiling: self,
}
}
pub fn parts(&self) -> TilingShapePartIterator {
TilingShapePartIterator {
idx: 0,
tiling: self,
second: false,
}
}
pub fn fill_region(&self, xmin: f64, ymin: f64, xmax: f64, ymax: f64) -> FillAlgorithm<'_> {
FillAlgorithm::new(
self,
dvec2(xmin, ymin),
dvec2(xmax, ymin),
dvec2(xmax, ymax),
dvec2(xmin, ymax),
)
}
pub fn parameters(&self, params: &mut [f64; 6]) {
params.copy_from_slice(&self.parameters);
}
pub fn set_parameters(&mut self, params: &[f64; 6]) {
self.parameters.copy_from_slice(params);
self.recompute();
}
pub fn vertices(&self) -> &[DVec2] {
&self.verts[0..self.num_vertices()]
}
fn recompute(&mut self) {
let ntv = self.ttd.num_vertices;
let mut data = self.ttd.tiling_vertex_coeffs;
for idx in 0..ntv {
fill_vector(
data,
&self.parameters,
self.num_params,
&mut self.verts[idx],
);
data = &data[(2 * (self.num_params + 1))..];
}
for idx in 0..ntv {
let fl = self.ttd.edge_orientations[2 * idx];
let ro = self.ttd.edge_orientations[2 * idx + 1];
self.reversals[idx] = fl != ro;
self.edges[idx] = r#match(&self.verts[idx], &self.verts[(idx + 1) % ntv])
* utils::M_ORIENTS[2 * (fl as usize) + (ro as usize)];
}
data = self.ttd.aspect_xform_coeffs;
let sz = self.ttd.num_aspects;
for idx in 0..sz {
fill_matrix(
data,
&self.parameters,
self.num_params,
&mut self.aspects[idx],
);
data = &data[(6 * (self.num_params + 1))..];
}
data = self.ttd.translation_vertex_coeffs;
fill_vector(data, &self.parameters, self.num_params, &mut self.t1);
fill_vector(
&data[(2 * (self.num_params + 1))..],
&self.parameters,
self.num_params,
&mut self.t2,
);
}
}
#[cfg(test)]
mod tests {
use crate::*;
#[test]
fn it_works() {
let tiling = IsohedralTiling::new(TilingType(1));
println!("{:?}", tiling.aspect_transform(1).to_cols_array());
let mut cnt = 0;
for v in tiling.fill_region(-5.0, -5.0, 5.0, 5.0).iter() {
println!(
"t1={}, t2={}, transform={:?}",
v.t1,
v.t2,
v.transform.to_cols_array()
);
cnt += 1;
}
println!("Got {} tiles", cnt);
}
}