paper-divisor-avatars.rs
4.3 kB · rust · 145 lines
1use mrlycore::errors::Result;2use mrlyfig::{ink, iso, save, Board, Frame};3use mrlymath::three::designs;4use mrlymath::three::faces::quads;56const SIDE: usize = 3;7const HALF: f64 = SIDE as f64 / 2.0;89enum Mark {10 Face(Vec<(f64, f64)>, usize),11 Cage(Vec<[(f64, f64); 2]>),12}1314fn even(c: &[u8]) -> usize {15 c.iter().filter(|&&v| v == 1).count()16}1718fn corners() -> Vec<Vec<u8>> {19 let mut out = Vec::new();20 for i in 0..SIDE {21 for j in 0..SIDE {22 for k in 0..SIDE {23 out.push(vec![i as u8, j as u8, k as u8]);24 }25 }26 }27 out28}2930fn node(i: f64, j: f64, k: f64) -> (f64, f64) {31 iso::project((i - HALF) / HALF, (j - HALF) / HALF, (k - HALF) / HALF)32}3334fn cage(c: &[u8]) -> Vec<[(f64, f64); 2]> {35 let (x, y, z) = (c[0] as f64, c[1] as f64, c[2] as f64);36 let corner = |b: usize| {37 node(38 x + (b & 1) as f64,39 y + ((b >> 1) & 1) as f64,40 z + ((b >> 2) & 1) as f64,41 )42 };43 let mut out = Vec::new();44 for a in 0usize..8 {45 for b in (a + 1)..8 {46 if (a ^ b).count_ones() == 1 {47 out.push([corner(a), corner(b)]);48 }49 }50 }51 out52}5354fn place(marks: &[(f64, Mark)], frame: Frame) -> impl Fn((f64, f64)) -> (f64, f64) {55 let mut lo = (f64::MAX, f64::MAX);56 let mut hi = (f64::MIN, f64::MIN);57 for (_, mark) in marks {58 if let Mark::Face(pts, _) = mark {59 for p in pts {60 lo.0 = lo.0.min(p.0);61 lo.1 = lo.1.min(p.1);62 hi.0 = hi.0.max(p.0);63 hi.1 = hi.1.max(p.1);64 }65 }66 }67 let (sx, sy) = (hi.0 - lo.0, hi.1 - lo.1);68 let scale = (frame.w / sx).min(frame.h / sy);69 let ox = frame.x + (frame.w - sx * scale) / 2.0;70 let oy = frame.y + (frame.h - sy * scale) / 2.0;71 move |p: (f64, f64)| (ox + (p.0 - lo.0) * scale, oy + (p.1 - lo.1) * scale)72}7374fn main() -> Result<()> {75 let mut board = Board::square();76 let frame = board.frame(0.08);77 let all = corners();78 let kept: Vec<Vec<u8>> = all.iter().filter(|c| even(c) <= 1).cloned().collect();79 let drilled: Vec<Vec<u8>> = all.iter().filter(|c| even(c) >= 2).cloned().collect();80 assert_eq!(kept.len(), 20);81 assert_eq!(drilled.len(), 7);82 let solid = designs::from_corners(&kept, SIDE, 1, SIDE)?;83 assert_eq!(solid.types().sum(), 20);84 let mut marks: Vec<(f64, Mark)> = Vec::new();85 for quad in quads(&solid) {86 let n = (87 quad.normal.x as f64,88 quad.normal.y as f64,89 quad.normal.z as f64,90 );91 if n.0 + n.1 + n.2 <= 0.0 {92 continue;93 }94 let tone = if n.2 > 0.0 {95 096 } else if n.1 > 0.0 {97 198 } else {99 2100 };101 let pts: Vec<(f64, f64)> = quad102 .verts103 .iter()104 .map(|v| iso::project(v.x as f64, v.y as f64, v.z as f64))105 .collect();106 let depth = quad107 .verts108 .iter()109 .map(|v| (v.x + v.y + v.z) as f64)110 .sum::<f64>()111 / 4.0;112 marks.push((depth, Mark::Face(pts, tone)));113 }114 for cell in &drilled {115 let mid = cell.iter().map(|&v| v as f64 + 0.5).sum::<f64>();116 let depth = (mid - 3.0 * HALF) / HALF;117 marks.push((depth, Mark::Cage(cage(cell))));118 }119 marks.sort_by(|a, b| a.0.total_cmp(&b.0));120 let put = place(&marks, frame);121 let gold = [122 ink::yellow(),123 ink::mix(ink::yellow(), ink::ground(), 0.4),124 ink::mix(ink::yellow(), ink::ground(), 0.66),125 ];126 let unit = frame.h / (SIDE as f64 * 2.0);127 for (_, mark) in &marks {128 match mark {129 Mark::Face(pts, tone) => {130 let screen: Vec<(f64, f64)> = pts.iter().map(|p| put(*p)).collect();131 board.polygon(&screen, gold[*tone]);132 let mut ring = screen.clone();133 ring.push(screen[0]);134 board.polyline(&ring, unit / 70.0, ink::line());135 }136 Mark::Cage(edges) => {137 for edge in edges {138 board.segment(put(edge[0]), put(edge[1]), unit / 34.0, ink::dim());139 }140 }141 }142 }143 save("paper-divisor-avatars", &board)?;144 Ok(())145}