use crate::hat::geometry::{
hex_pt, intersect, match_two, mul, padd, psub, pt, rot_about, trans_pt, trot, ttrans,
HAT_OUTLINE, HR3, H_OUTLINE, IDENT,
};
use image::{Rgb, RgbImage};
use nalgebra::Vector2;
use std::any::Any;
use std::rc::Rc;
#[derive(Clone)]
pub struct HatTile {
pub label: String,
pub shape: [Vector2<f64>; 13], pub children: Vec<Child>,
}
impl HatTile {
pub fn new(label: &str) -> Self {
HatTile {
label: label.to_string(),
shape: HAT_OUTLINE,
children: Vec::new(),
}
}
pub fn draw(&self, s: &[f64; 6], level: i32, draw: &mut dyn DrawContext, img: &RgbImage) {
if level > 0 {
return;
}
let poly: Vec<Vector2<f64>> = self.shape.iter().map(|p| trans_pt(s, *p)).collect();
let poly_i: Vec<(i32, i32)> = poly
.iter()
.map(|p| ((p[0] + 0.5) as i32, (p[1] + 0.5) as i32))
.collect();
let fill = average_color(img, &poly_i);
draw.polygon(&poly_i, Some(fill), None);
}
pub fn as_any(&self) -> &dyn Any {
self
}
}
#[derive(Clone)]
pub struct MetaTile {
pub shape: Vec<Vector2<f64>>,
pub width: i32,
pub children: Vec<Child>,
}
#[derive(Clone)]
pub struct Child {
pub t: [f64; 6],
pub geom: Rc<dyn Tile>,
}
pub trait Tile {
fn draw(&self, s: &[f64; 6], level: i32, draw: &mut dyn DrawContext, img: &RgbImage);
fn shape(&self) -> &[Vector2<f64>];
fn as_any(&self) -> &dyn Any;
}
impl Tile for HatTile {
fn draw(&self, s: &[f64; 6], level: i32, draw: &mut dyn DrawContext, img: &RgbImage) {
if level > 0 {
for ch in &self.children {
let s2 = mul(s, &ch.t);
ch.geom.draw(&s2, level - 1, draw, img);
}
} else {
let poly: Vec<Vector2<f64>> = self.shape.iter().map(|p| trans_pt(s, *p)).collect();
let poly_i: Vec<(i32, i32)> = poly
.iter()
.map(|p| ((p[0] + 0.5) as i32, (p[1] + 0.5) as i32))
.collect();
draw.polygon(&poly_i, None, Some((0, 0, 0)));
}
}
fn shape(&self) -> &[Vector2<f64>] {
&self.shape
}
fn as_any(&self) -> &dyn Any {
self
}
}
impl Tile for MetaTile {
fn draw(&self, s: &[f64; 6], level: i32, draw: &mut dyn DrawContext, img: &RgbImage) {
if level > 0 {
for ch in &self.children {
let s2 = mul(s, &ch.t);
ch.geom.draw(&s2, level - 1, draw, img);
}
} else {
let poly: Vec<Vector2<f64>> = self.shape.iter().map(|p| trans_pt(s, *p)).collect();
let poly_i: Vec<(i32, i32)> = poly
.iter()
.map(|p| ((p[0] + 0.5) as i32, (p[1] + 0.5) as i32))
.collect();
draw.polygon(&poly_i, None, Some((0, 0, 0)));
}
}
fn shape(&self) -> &[Vector2<f64>] {
&self.shape
}
fn as_any(&self) -> &dyn Any {
self
}
}
impl MetaTile {
pub fn new(outline: Vec<Vector2<f64>>, width: i32) -> Self {
MetaTile {
shape: outline,
width,
children: Vec::new(),
}
}
pub fn add_child(&mut self, t: [f64; 6], geom: Rc<dyn Tile>) {
self.children.push(Child { t, geom });
}
pub fn eval_child(&self, n: usize, i: usize) -> Vector2<f64> {
trans_pt(&self.children[n].t, self.children[n].geom.shape()[i])
}
pub fn recenter(&mut self) {
let n = self.shape.len() as f64;
let cx = self.shape.iter().map(|p| p[0]).sum::<f64>() / n;
let cy = self.shape.iter().map(|p| p[1]).sum::<f64>() / n;
let shift = Vector2::new(-cx, -cy);
self.shape = self.shape.iter().map(|p| padd(*p, shift)).collect();
let m = ttrans(-cx, -cy);
for ch in &mut self.children {
ch.t = mul(&m, &ch.t);
}
}
}
pub trait DrawContext {
fn polygon(
&mut self,
points: &[(i32, i32)],
fill: Option<(u8, u8, u8)>,
outline: Option<(u8, u8, u8)>,
);
}
pub fn average_color(img: &RgbImage, poly: &[(i32, i32)]) -> (u8, u8, u8) {
if poly.is_empty() {
return (0, 0, 0);
}
let mut r_sum: u64 = 0;
let mut g_sum: u64 = 0;
let mut b_sum: u64 = 0;
let mut count: u64 = 0;
let min_x = poly.iter().map(|p| p.0).min().unwrap_or(0).max(0) as u32;
let max_x = poly
.iter()
.map(|p| p.0)
.max()
.unwrap_or(0)
.min(img.width() as i32 - 1) as u32;
let min_y = poly.iter().map(|p| p.1).min().unwrap_or(0).max(0) as u32;
let max_y = poly
.iter()
.map(|p| p.1)
.max()
.unwrap_or(0)
.min(img.height() as i32 - 1) as u32;
for x in min_x..=max_x {
for y in min_y..=max_y {
let pixel = img.get_pixel(x, y);
r_sum += pixel[0] as u64;
g_sum += pixel[1] as u64;
b_sum += pixel[2] as u64;
count += 1;
}
}
if count == 0 {
return (0, 0, 0);
}
(
(r_sum / count) as u8,
(g_sum / count) as u8,
(b_sum / count) as u8,
)
}
pub fn construct_prototiles() -> (MetaTile, MetaTile, MetaTile, MetaTile) {
let h1_hat = Rc::new(HatTile::new("H1")) as Rc<dyn Tile>;
let h_hat = Rc::new(HatTile::new("H")) as Rc<dyn Tile>;
let t_hat = Rc::new(HatTile::new("T")) as Rc<dyn Tile>;
let p_hat = Rc::new(HatTile::new("P")) as Rc<dyn Tile>;
let f_hat = Rc::new(HatTile::new("F")) as Rc<dyn Tile>;
let mut h_init = MetaTile::new(H_OUTLINE.to_vec(), 2);
h_init.add_child(
match_two(HAT_OUTLINE[5], HAT_OUTLINE[7], H_OUTLINE[5], H_OUTLINE[0]),
h_hat.clone(),
);
h_init.add_child(
match_two(HAT_OUTLINE[9], HAT_OUTLINE[11], H_OUTLINE[1], H_OUTLINE[2]),
h_hat.clone(),
);
h_init.add_child(
match_two(HAT_OUTLINE[5], HAT_OUTLINE[7], H_OUTLINE[3], H_OUTLINE[4]),
h_hat.clone(),
);
h_init.add_child(
mul(
&ttrans(2.5, HR3),
&mul(
&[-0.5, -HR3, 0.0, HR3, -0.5, 0.0],
&[0.5, 0.0, 0.0, 0.0, -0.5, 0.0],
),
),
h1_hat.clone(),
);
let t_outline = vec![pt(0.0, 0.0), pt(3.0, 0.0), pt(1.5, 3.0 * HR3)];
let mut t_init = MetaTile::new(t_outline, 2);
t_init.add_child([0.5, 0.0, 0.5, 0.0, 0.5, HR3], t_hat.clone());
let p_outline = vec![
pt(0.0, 0.0),
pt(4.0, 0.0),
pt(3.0, 2.0 * HR3),
pt(-1.0, 2.0 * HR3),
];
let mut p_init = MetaTile::new(p_outline, 2);
p_init.add_child([0.5, 0.0, 1.5, 0.0, 0.5, HR3], p_hat.clone());
p_init.add_child(
mul(
&ttrans(0.0, 2.0 * HR3),
&mul(
&[0.5, HR3, 0.0, -HR3, 0.5, 0.0],
&[0.5, 0.0, 0.0, 0.0, 0.5, 0.0],
),
),
p_hat.clone(),
);
let f_outline = vec![
pt(0.0, 0.0),
pt(3.0, 0.0),
pt(3.5, HR3),
pt(3.0, 2.0 * HR3),
pt(-1.0, 2.0 * HR3),
];
let mut f_init = MetaTile::new(f_outline, 2);
f_init.add_child([0.5, 0.0, 1.5, 0.0, 0.5, HR3], f_hat.clone());
f_init.add_child(
mul(
&ttrans(0.0, 2.0 * HR3),
&mul(
&[0.5, HR3, 0.0, -HR3, 0.5, 0.0],
&[0.5, 0.0, 0.0, 0.0, 0.5, 0.0],
),
),
f_hat.clone(),
);
(h_init, t_init, p_init, f_init)
}
pub fn construct_patch(h: &MetaTile, t: &MetaTile, p: &MetaTile, f: &MetaTile) -> MetaTile {
let rules: Vec<Vec<Rule>> = vec![
vec![Rule::Label("H".to_string())],
vec![Rule::Four(0, 0, "P".to_string(), 2)],
vec![Rule::Four(1, 0, "H".to_string(), 2)],
vec![Rule::Four(2, 0, "P".to_string(), 2)],
vec![Rule::Four(3, 0, "H".to_string(), 2)],
vec![Rule::Four(4, 4, "P".to_string(), 2)],
vec![Rule::Four(0, 4, "F".to_string(), 3)],
vec![Rule::Four(2, 4, "F".to_string(), 3)],
vec![Rule::Six(4, 1, 3, 2, "F".to_string(), 0)],
vec![Rule::Four(8, 3, "H".to_string(), 0)],
vec![Rule::Four(9, 2, "P".to_string(), 0)],
vec![Rule::Four(10, 2, "H".to_string(), 0)],
vec![Rule::Four(11, 4, "P".to_string(), 2)],
vec![Rule::Four(12, 0, "H".to_string(), 2)],
vec![Rule::Four(13, 0, "F".to_string(), 3)],
vec![Rule::Four(14, 2, "F".to_string(), 1)],
vec![Rule::Four(15, 3, "H".to_string(), 4)],
vec![Rule::Four(8, 2, "F".to_string(), 1)],
vec![Rule::Four(17, 3, "H".to_string(), 0)],
vec![Rule::Four(18, 2, "P".to_string(), 0)],
vec![Rule::Four(19, 2, "H".to_string(), 2)],
vec![Rule::Four(20, 4, "F".to_string(), 3)],
vec![Rule::Four(20, 0, "P".to_string(), 2)],
vec![Rule::Four(22, 0, "H".to_string(), 2)],
vec![Rule::Four(23, 4, "F".to_string(), 3)],
vec![Rule::Four(23, 0, "F".to_string(), 3)],
vec![Rule::Four(16, 0, "P".to_string(), 2)],
vec![Rule::Six(9, 4, 0, 2, "T".to_string(), 2)],
vec![Rule::Four(4, 0, "F".to_string(), 3)],
];
let mut ret = MetaTile::new(vec![], h.width);
let shapes: std::collections::HashMap<String, Rc<dyn Tile>> = [
("H".to_string(), Rc::new(h.clone()) as Rc<dyn Tile>),
("T".to_string(), Rc::new(t.clone()) as Rc<dyn Tile>),
("P".to_string(), Rc::new(p.clone()) as Rc<dyn Tile>),
("F".to_string(), Rc::new(f.clone()) as Rc<dyn Tile>),
]
.into_iter()
.collect();
for r in rules {
match r.as_slice() {
[Rule::Label(label)] => {
ret.add_child(IDENT, shapes[label].clone());
}
[Rule::Four(c, i, label, j)] => {
let poly = ret.children[*c].geom.shape();
let t0 = ret.children[*c].t;
let ppt = trans_pt(&t0, poly[(i + 1) % poly.len()]);
let qpt = trans_pt(&t0, poly[*i]);
let nshp = shapes[label].clone();
ret.add_child(
match_two(
nshp.shape()[*j],
nshp.shape()[(*j + 1) % nshp.shape().len()],
ppt,
qpt,
),
nshp,
);
}
[Rule::Six(c_p, i, c_q, j, label, k)] => {
let ppt = trans_pt(&ret.children[*c_q].t, ret.children[*c_q].geom.shape()[*j]);
let qpt = trans_pt(&ret.children[*c_p].t, ret.children[*c_p].geom.shape()[*i]);
let nshp = shapes[label].clone();
ret.add_child(
match_two(
nshp.shape()[*k],
nshp.shape()[(*k + 1) % nshp.shape().len()],
ppt,
qpt,
),
nshp,
);
}
_ => unreachable!(),
}
}
ret
}
pub fn construct_metatiles(patch: &MetaTile) -> (MetaTile, MetaTile, MetaTile, MetaTile) {
let bps1 = patch.eval_child(8, 2);
let bps2 = patch.eval_child(21, 2);
let rbps = trans_pt(&rot_about(bps1, -2.0 * std::f64::consts::PI / 3.0), bps2);
let p72 = patch.eval_child(7, 2);
let p252 = patch.eval_child(25, 2);
let llc = intersect(bps1, rbps, patch.eval_child(6, 2), p72);
let w = psub(patch.eval_child(6, 2), llc);
let mut new_h_outline = vec![llc, bps1];
let w = trans_pt(&trot(-std::f64::consts::PI / 3.0), w);
new_h_outline.push(padd(new_h_outline[1], w));
new_h_outline.push(patch.eval_child(14, 2));
let w = trans_pt(&trot(-std::f64::consts::PI / 3.0), w);
new_h_outline.push(psub(new_h_outline[3], w));
new_h_outline.push(patch.eval_child(6, 2));
let new_h_outline_clone = new_h_outline.clone();
let mut new_h = MetaTile::new(new_h_outline, patch.width * 2);
for idx in [0, 9, 16, 27, 26, 6, 1, 8, 10, 15] {
let ch = patch.children[idx].clone();
new_h.add_child(ch.t, ch.geom);
}
let new_p_outline = vec![p72, padd(p72, psub(bps1, llc)), bps1, llc];
let mut new_p = MetaTile::new(new_p_outline, patch.width * 2);
for idx in [7, 2, 3, 4, 28] {
let ch = patch.children[idx].clone();
new_p.add_child(ch.t, ch.geom);
}
let new_f_outline = vec![
bps2,
patch.eval_child(24, 2),
patch.eval_child(25, 0),
p252,
padd(p252, psub(llc, bps1)),
];
let mut new_f = MetaTile::new(new_f_outline, patch.width * 2);
for idx in [21, 20, 22, 23, 24, 25] {
let ch = patch.children[idx].clone();
new_f.add_child(ch.t, ch.geom);
}
let aaa = new_h_outline_clone[2];
let bbb = padd(
new_h_outline_clone[1],
psub(new_h_outline_clone[4], new_h_outline_clone[5]),
);
let ccc = trans_pt(&rot_about(bbb, -std::f64::consts::PI / 3.0), aaa);
let new_t_outline = vec![bbb, ccc, aaa];
let mut new_t = MetaTile::new(new_t_outline, patch.width * 2);
let ch = patch.children[11].clone();
new_t.add_child(ch.t, ch.geom);
new_h.recenter();
new_t.recenter();
new_p.recenter();
new_f.recenter();
(new_h, new_t, new_p, new_f)
}
#[derive(Clone)]
pub enum Rule {
Label(String),
Four(usize, usize, String, usize),
Six(usize, usize, usize, usize, String, usize),
}
pub fn collect_hats(node: &Rc<dyn Tile>, t_parent: [f64; 6]) -> Vec<(Rc<HatTile>, [f64; 6])> {
let mut hats = Vec::new();
if let Some(hat_tile) = node.as_any().downcast_ref::<HatTile>() {
hats.push((Rc::new(hat_tile.clone()), t_parent));
return hats;
}
if let Some(meta_tile) = node.as_any().downcast_ref::<MetaTile>() {
for ch in &meta_tile.children {
let t_abs = mul(&t_parent, &ch.t);
hats.extend(collect_hats(&ch.geom, t_abs));
}
}
hats
}