1use super::renderer::*;
2use super::GridPos;
3use crate::block::{Block, Rotation};
4use bobbin_bits::U4;
5
6#[cfg(test)]
7macro_rules! dir {
8 (^) => {
9 crate::block::Rotation::Up
10 };
11 (v) => {
12 crate::block::Rotation::Down
13 };
14 (<) => {
15 crate::block::Rotation::Left
16 };
17 (>) => {
18 crate::block::Rotation::Right
19 };
20}
21#[cfg(test)]
22macro_rules! conv {
23 (_) => {
24 None
25 };
26 ($dir:tt) => {
27 Some((&crate::block::CONVEYOR, crate::data::autotile::dir!($dir)))
28 };
29}
30#[cfg(test)]
31macro_rules! define {
32 ($a:tt,$b:tt,$c:tt,$d:tt) => {
33 [
34 crate::data::autotile::conv!($a),
35 crate::data::autotile::conv!($b),
36 crate::data::autotile::conv!($c),
37 crate::data::autotile::conv!($d),
38 ]
39 };
40}
41
42#[cfg(test)]
43pub(crate) use conv;
44#[cfg(test)]
45pub(crate) use define;
46#[cfg(test)]
47pub(crate) use dir;
48
49pub type Cross = [Option<(&'static Block, Rotation)>; 4];
50#[derive(Copy, Clone)]
52pub struct RenderingContext {
53 pub cross: Cross,
54 pub position: PositionContext,
55}
56
57#[derive(Copy, Clone, Eq, PartialEq)]
59pub struct PositionContext {
60 pub position: GridPos,
61 pub width: usize,
62 pub height: usize,
63}
64
65impl std::fmt::Debug for PositionContext {
66 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
67 write!(
68 f,
69 "PC<{:?} ({}/{})>",
70 self.position, self.width, self.height
71 )
72 }
73}
74
75#[cfg(test)]
76fn print_crosses(v: Vec<Cross>, height: usize) -> String {
77 let mut s = String::new();
78 for c in v.chunks(height) {
79 for c in c {
80 s.push(c[0].map_or('_', |(_, r)| r.ch()));
81 for c in &c[1..] {
82 s.push(',');
83 s.push(c.map_or('_', |(_, r)| r.ch()));
84 }
85 s.push(' ');
86 }
87 s.push('\n');
88 }
89 s
90}
91
92pub fn tile(ctx: &RenderingContext, name: &str, rot: Rotation, s: Scale) -> ImageHolder<4> {
93 mask2tile(mask(ctx, rot, name), rot, name, s)
94}
95
96pub fn mask2tile(mask: U4, rot: Rotation, name: &str, scale: Scale) -> ImageHolder<4> {
97 use U4::*;
98 macro_rules! p {
99 ($image:literal) => {
100 load!(concat $image => name which is ["reinforced-conduit" | "armored-duct" | "pulse-conduit" | "plated-conduit" | "conduit" | "conveyor" | "titanium-conveyor" | "armored-conveyor" | "duct"], scale)
101 };
102 }
103
104 match mask {
105 B0001 => match rot {
107 Rotation::Down => p!("1-1-h"), Rotation::Right => p!("0-0"), Rotation::Up => p!("1-3"), Rotation::Left => unreachable!(),
111 },
112 B0010 => match rot {
114 Rotation::Left => p!("1-2"), Rotation::Right => p!("1-2-h"), Rotation::Up => p!("0-3"), Rotation::Down => unreachable!(),
118 },
119 B0011 => match rot {
121 Rotation::Right => p!("2-0"), Rotation::Up => p!("2-3-h"), _ => unreachable!(),
124 },
125 B0100 => match rot {
127 Rotation::Left => p!("0-2"), Rotation::Down => p!("1-1"), Rotation::Up => p!("1-1-v"), Rotation::Right => unreachable!(),
131 },
132 B0101 => match rot {
134 Rotation::Up => p!("4-3"), Rotation::Down => p!("4-1"), _ => unreachable!(),
137 },
138 B0110 => match rot {
140 Rotation::Up => p!("2-3"), Rotation::Left => p!("2-0-h"), _ => unreachable!(),
143 },
144 B0111 => match rot {
146 Rotation::Up => p!("3-3"), _ => unreachable!(),
148 },
149 B1000 => match rot {
151 Rotation::Down => p!("0-1"), Rotation::Left => p!("1-0-h"), Rotation::Right => p!("1-0"), Rotation::Up => unreachable!(),
155 },
156 B1001 => match rot {
158 Rotation::Right => p!("2-0-v"), Rotation::Down => p!("2-1"), _ => unreachable!(),
161 },
162 B1010 => match rot {
164 Rotation::Right => p!("4-0"), Rotation::Left => p!("4-3"), _ => unreachable!(),
167 },
168 B1011 => match rot {
170 Rotation::Right => p!("3-0"), _ => unreachable!(),
172 },
173 B1100 => match rot {
175 Rotation::Down => p!("2-1-h"), Rotation::Left => p!("2-2"), _ => unreachable!(),
178 },
179 B1101 => match rot {
181 Rotation::Down => p!("3-1"), _ => unreachable!(),
183 },
184 B1110 => match rot {
186 Rotation::Left => p!("3-0-h"), _ => unreachable!(),
188 },
189 B0000 => match rot {
190 Rotation::Left => p!("0-2"),
191 Rotation::Right => p!("0-0"),
192 Rotation::Down => p!("0-1"),
193 Rotation::Up => p!("0-3"),
194 },
195 B1111 => unreachable!(),
196 }
197}
198
199pub fn mask(ctx: &RenderingContext, rot: Rotation, n: &str) -> U4 {
200 macro_rules! c {
201 ($in: expr, $srot: expr, $name: expr, $at: expr) => {{
202 if let Some((b, rot)) = $in {
203 if b.name() == $name {
204 (rot == $at && rot.mirrored(true, true) != $srot) as u8
206 } else {
207 0
208 }
209 } else {
210 0
211 }
212 }};
213 }
214 use Rotation::{Down, Left, Right, Up};
215 let mut x = 0b0000;
216
217 x |= 8 * c!(ctx.cross[0], rot, n, Down);
218 x |= 4 * c!(ctx.cross[1], rot, n, Left);
219 x |= 2 * c!(ctx.cross[2], rot, n, Up);
220 x |= c!(ctx.cross[3], rot, n, Right);
221 U4::from(x)
222}
223
224pub trait RotationState {
225 fn get_rotation(&self) -> Option<Rotation>;
226}
227pub trait BlockState {
228 fn get_block(&self) -> Option<&'static Block>;
229}
230pub trait Crossable {
231 fn cross(&self, j: usize, c: &PositionContext) -> Cross;
232}
233
234#[test]
235fn test_cross() {
236 macro_rules! test {
237 ($schem: literal => $($a:tt,$b:tt,$c:tt,$d:tt)*) => {
238 let s = crate::Schematic::deserialize_base64($schem).unwrap();
239 let mut c = vec![];
240 println!("{:#?}", s.blocks);
241 for (position, _) in s.block_iter() {
242 let pctx = PositionContext {
243 position,
244 width: s.width,
245 height: s.height,
246 };
247 c.push(s.cross(&pctx));
248 }
249 let n = s.tags.get("name").map_or("<unknown>", |x| &x);
250 let cc: Vec<Cross> = vec![
251 $(define!($a,$b,$c,$d),)*
252 ];
253 if cc != c {
254 let a = print_crosses(cc, s.height as usize);
255 let b = print_crosses(c, s.height as usize);
256 for diff in diff::lines(&a, &b) {
257 match diff {
258 diff::Result::Left(l) => println!("\x1b[38;5;1m{}", l),
259 diff::Result::Right(r) => println!("\x1b[38;5;2m{}", r),
260 diff::Result::Both(l, _) => println!("\x1b[0m{}", l),
261 }
262 }
263 print!("\x1b[0m");
264 panic!("test {n} \x1b[38;5;1mfailed\x1b[0m")
274 }
275 println!("test {n} \x1b[38;5;2mpassed\x1b[0m");
276 };
277 }
278 test!("bXNjaAF4nGNgYmBiZmDJS8xNZWBNSizOTGbgTkktTi7KLCjJzM9jYGBgy0lMSs0pZmCNfr9gTSwjA0dyfl5ZamV+EVCOhQEBGGEEM4hiZGAGAOb+EWA=" =>
286 >,v,_,_ _,v,>,_
289 v,_,_,> _,_,v,>
291 );
292 test!("bXNjaAF4nDWK4QqAIBCDd6dE0SNGP8zuh2CeaAS9fZk0xvjGBgNjYJM7BDaqy5h3qb6EfAZNAIboNokVvKyE0Wu65NbyDhM+cQv6mTtTM/WFYfqLm6m3lx9MAg7n" =>
297 >,^,_,_ <,>,<,_ _,v,>,_
298 >,<,_,< v,v,^,> _,>,>,<
299 v,_,_,^ >,_,<,> _,_,v,v
300 );
301 test!("bXNjaAF4nGNgYmBiZmDJS8xNZWApzkvNZuBOSS1OLsosKMnMz2NgYGDLSUxKzSlmYIqOZWTgSM7PK0utzC8CSrAwIAAjEIIQhGJkYAIARA0Ozg==" =>
305 ^,^,_,_ _,<,>,_
306 <,_,_,> _,_,^,^
307 );
308
309 test!("bXNjaAF4nCWJQQqAIBREx69E0Lq994oWph8STEMj6fZpzcDjDQMCSahoDsZsdN1TYB25aucz28uniMlxsdmf3wCGYDYOBbSsAqNN8eYn5XYofJEdAtSB31tfaoIVGw==" =>
311 <,>,_,_ _,^,v,_
312 ^,_,_,v _,_,>,<
313 );
314 test!("bXNjaAF4nEXJwQqAIBAE0HGVCPrE6GC2B0HdcCPw78MKnMMwj4EFWbjiM8N5bRnLwRpqPK8oBcCU/M5JQetmMAcpNzep/cCIAfX69yv6RF0PFy0O4Q==" =>
318 <,>,_,_ _,<,>,_
319 <,>,_,> _,<,>,<
320 <,_,_,> _,_,>,<
322 );
323
324 test!("bXNjaAF4nEWOUQ7CIBBEh2VZTbyCx/A2xg9a+WiC0LTGxNvb7Wjk5wEzb7M4QCO05UdBqj3PF5zuZR2XaX5OvQGwmodSV8j1FnAce3uVd1+24Iz/CYQQ8fcVHYEQIjqEXWEm9LwgX9kR+PLSbm2BMlN6Sk/3LhJnJu6S6CVmxl2MntEzv38AchUPug==" =>
330 >,v,_,_ >,v,>,_ >,v,>,_ _,v,>,_
331 v,<,_,> v,v,v,> v,>,v,> _,<,v,>
332 v,>,_,v >,<,<,v <,^,v,v _,v,>,v
333 <,>,_,< ^,v,>,v v,>,<,> _,^,^,<
334 v,<,_,> >,>,>,< ^,^,v,^ _,^,>,v
335 >,v,_,> ^,v,<,v ^,>,>,> _,>,^,^
336 v,_,_,< >,_,v,> >,_,v,^ _,_,>,^
340 );
341}
342
343#[test]
344fn test_mask() {
345 macro_rules! assert {
346 ($a:tt,$b:tt,$c:tt,$d:tt => $rot: tt => $expect: expr) => {
347 assert_eq!(mask!(define!($a, $b, $c, $d), $rot), $expect)
348 };
349 }
350 macro_rules! mask {
351 ($cross:expr, $rot: tt) => {
352 mask(
353 &RenderingContext {
354 position: PositionContext {
355 position: GridPos(5, 5),
356 width: 10,
357 height: 10,
358 },
359 cross: $cross,
360 },
361 dir!($rot),
362 "conveyor",
363 )
364 };
365 }
366 assert!(_,_,_,_ => ^ => U4::B0000);
367 assert!(v,_,_,_ => > => U4::B1000);
368 assert!(v,v,_,_ => v => U4::B1000);
369 assert!(_,v,>,_ => > => U4::B0000);
370 assert!(v,>,<,> => ^ => U4::B0001);
371 assert!(v,>,>,_ => > => U4::B1000);
372}