use crate::prelude::*;
use crate::shape::*;
use crate::svg::*;
use delaunator as del;
use rand::rngs::ThreadRng;
use std::collections::HashSet;
macro_rules! set {
{ $( $elem:expr ),* } => {
{
let mut set = HashSet::new();
$( set.insert($elem); )*
set
}
}
}
fn periodic_grid_tiling<F>(f: &Frame, gen: F, idir: Pos, jdir: Pos) -> Vec<(Pos, Path)>
where
F: Fn(Pos) -> Vec<(Pos, Path)>,
{
let mut items = Vec::new();
let center = f.center();
let mut set = set![center];
let mut stk = vec![center];
while let Some(pos) = stk.pop() {
if f.is_inside(pos) {
for item in gen(pos) {
items.push(item);
}
for &(i, j) in &[(0, 1), (0, -1), (1, 0), (-1, 0)] {
let p = pos + idir * i + jdir * j;
if !set.contains(&p) {
set.insert(p);
stk.push(p);
}
}
}
}
items
}
pub fn tile_hexagons(f: &Frame, size: f64, rot: isize) -> Vec<(Pos, Path)> {
let idir = Pos::polar(rot - 30, (size * 2.) * radians(30).cos());
let jdir = Pos::polar(rot + 30, (size * 2.) * radians(30).cos());
let m = Movable::hexagon(size, rot);
periodic_grid_tiling(f, |p| vec![m.render(p)], idir, jdir)
}
pub fn tile_triangles(f: &Frame, size: f64, rot: isize) -> Vec<(Pos, Path)> {
let idir = Pos::polar(rot - 30, (size * 2.) * radians(30).cos());
let jdir = Pos::polar(rot + 30, (size * 2.) * radians(30).cos());
let adjust = Pos::polar(rot + 60, size * radians(30).sin()) + idir * 0.5;
let m1 = Movable::triangle(size, rot + 60);
let m2 = Movable::triangle(size, rot);
periodic_grid_tiling(f, |p| vec![m1.render(p), m2.render(p + adjust)], idir, jdir)
}
pub fn tile_hybrid_hexagons_triangles(f: &Frame, size: f64, rot: isize) -> Vec<(Pos, Path)> {
let idir = Pos::polar(rot, size * 2.);
let jdir = Pos::polar(rot + 60, size * 2.);
let adjust = Pos::polar(rot + 30, size / radians(30).cos());
let m = [
Movable::hexagon(size, rot),
Movable::triangle(size * radians(30).sin(), rot + 30),
Movable::triangle(size * radians(30).sin(), rot + 90),
];
periodic_grid_tiling(
f,
|p| {
vec![
m[0].render(p),
m[1].render(p + adjust),
m[2].render(p - adjust),
]
},
idir,
jdir,
)
}
pub fn tile_hybrid_squares_triangles(f: &Frame, size: f64, rot: isize) -> Vec<(Pos, Path)> {
let a = size / 2_f64.sqrt();
let b = a * radians(30).tan();
let c = a / radians(30).cos();
let idir = Pos::polar(rot, c + a * 2. + 2. * b) + Pos::polar(rot + 60, c + a * 2. + 2. * b);
let jdir = Pos::polar(rot, c + a * 2. + 2. * b) + Pos::polar(rot - 60, c + a * 2. + 2. * b);
let mv = [
Movable::square(size, rot),
Movable::square(size, rot + 60),
Movable::square(size, rot - 60),
Movable::triangle(c, rot + 60),
Movable::triangle(c, rot),
Movable::triangle(c, rot + 90),
Movable::triangle(c, rot + 30),
];
periodic_grid_tiling(
f,
|pos| {
let mut items = vec![
mv[4].render(pos + Pos::polar(rot, c + 2. * b + 2. * a)),
mv[3].render(pos - Pos::polar(rot, c + 2. * b + 2. * a)),
];
for i in 0..6 {
items.push(mv[3 + (i as usize % 2)].render(pos + Pos::polar(rot + i * 60, c)));
items.push(mv[i as usize % 3].render(pos + Pos::polar(rot + i * 60, c + b + a)));
items.push(
mv[5 + (i as usize % 2)]
.render(pos + Pos::polar(rot + i * 60 + 30, 2. * a + c)),
);
}
items
},
idir,
jdir,
)
}
pub fn tile_rhombus(f: &Frame, ldiag: f64, sdiag: f64, rot: isize) -> Vec<(Pos, Path)> {
let idir = Pos::polar(rot, ldiag) + Pos::polar(rot + 90, sdiag);
let jdir = Pos::polar(rot, -ldiag) + Pos::polar(rot + 90, sdiag);
let m = Movable::rhombus(ldiag, sdiag, rot);
periodic_grid_tiling(f, |p| vec![m.render(p)], idir, jdir)
}
fn fast_triangulate(pts: &[Pos]) -> Vec<(Pos, Pos, Pos)> {
let points = pts
.iter()
.map(|&Pos(x, y)| del::Point { x, y })
.collect::<Vec<_>>();
let result = del::triangulate(&points)
.unwrap()
.triangles
.iter()
.map(|&i| pts[i])
.collect::<Vec<_>>();
let mut v = Vec::new();
for i in 0..result.len() / 3 {
v.push((result[i * 3], result[i * 3 + 1], result[i * 3 + 2]));
}
v
}
pub fn random_delaunay(f: &Frame, rng: &mut ThreadRng, n: usize) -> Vec<(Pos, Path)> {
let mut pts = Vec::new();
for _ in 0..n {
pts.push(Pos::random(f, rng));
}
let triangulation = fast_triangulate(&pts);
triangulation
.into_iter()
.map(|(a, b, c)| {
(
(a + b + c) * 0.33,
Path::new(Data::new(a).with_line_to(b).with_line_to(c)),
)
})
.collect::<Vec<_>>()
}
pub fn pentagons_type1(f: &Frame, size: f64, rot: isize) -> Vec<(Pos, Path)> {
let beta = 80;
let gamma = 180 - beta;
let alpha = 110;
let delta = 110;
let epsilon = 360 - alpha - delta;
let sizes = [size * 1.2, size, size * 0.9];
let angles = [alpha, beta, gamma, delta, epsilon];
let mv = [
Pentagon { sizes, rot, angles }.to_movable(),
Pentagon {
sizes,
rot: rot + 180,
angles,
}
.to_movable(),
];
let idir = mv[0].vertex(3) - mv[0].vertex(0);
let jdir = mv[0].vertex(0) - mv[1].vertex(4) + mv[1].vertex(2) - mv[0].vertex(1);
periodic_grid_tiling(
f,
|pos| {
vec![
mv[0].render(pos - mv[0].vertex(0) + mv[0].side(4) * 0.5),
mv[1].render(pos - mv[1].vertex(0) + mv[1].side(4) * 0.5),
]
},
idir,
jdir,
)
}
pub fn pentagons_type2(f: &Frame, size: f64, rot: isize) -> Vec<(Pos, Path)> {
let alpha = 110;
let beta = 110;
let gamma = 100;
let delta = 180 - beta;
let epsilon = 150;
let sizes = [size, size * 1.2, size];
let angles = [epsilon, delta, gamma, beta, alpha];
let rangles = [beta, gamma, delta, epsilon, alpha];
let mv = [
Pentagon { sizes, rot, angles }.to_movable(),
Pentagon {
sizes,
rot: rot + 180,
angles,
}
.to_movable(),
Pentagon {
sizes,
rot: rot + 180,
angles: rangles,
}
.to_movable(),
Pentagon {
sizes,
rot,
angles: rangles,
}
.to_movable(),
];
let idir = mv[0].vertex(0) - mv[2].vertex(1) + mv[2].vertex(2) - mv[0].vertex(3);
let jdir = mv[0].vertex(4) - mv[3].vertex(1) + mv[3].vertex(4) - mv[0].vertex(2);
periodic_grid_tiling(
f,
|pos| {
vec![
mv[0].render(pos),
mv[1].render(pos + mv[0].vertex(2) + mv[3].side(4) - mv[1].vertex(1)),
mv[2].render(pos + mv[0].vertex(0) - mv[2].vertex(1)),
mv[3].render(pos + mv[0].vertex(2) - mv[3].vertex(4)),
]
},
idir,
jdir,
)
}
struct Pentagon {
rot: isize,
sizes: [f64; 3],
angles: [usize; 5],
}
impl Pentagon {
fn to_movable(&self) -> Movable {
assert_eq!(0, {
let mut s = 0;
for i in 0..5 {
s += 180 - self.angles[i];
}
s.rem_euclid(360)
});
let mut pts = Vec::new();
pts.push(Pos::zero());
let mut running_angle = self.rot;
for i in 0..=2 {
let latest = pts[i];
pts.push(latest + Pos::polar(running_angle, self.sizes[i]));
running_angle += 180 - self.angles[i + 1] as isize;
}
let latest = pts[3];
pts.push(Pos::intersect(
(Pos::zero(), self.rot + self.angles[0] as isize),
(latest, running_angle),
));
let mid = pts.iter().fold(Pos::zero(), |acc, item| acc + *item) * 0.2;
Movable::from(pts.into_iter().map(|p| p - mid).collect::<Vec<_>>())
}
}