1use super::GridPos;
2use super::renderer::*;
3use crate::block::{Block, Rotation, content::Type};
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];
50pub type Corners = [Option<(&'static Block, Rotation)>; 4];
51#[derive(Copy, Clone)]
53pub struct RenderingContext {
54 pub cross: Cross,
55 pub corners: Corners,
56 pub position: PositionContext,
57}
58
59#[derive(Copy, Clone, Eq, PartialEq, Default)]
61pub struct PositionContext {
62 pub position: GridPos,
63 pub width: usize,
64 pub height: usize,
65}
66
67impl std::fmt::Debug for PositionContext {
68 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
69 write!(
70 f,
71 "PC<{:?} ({}/{})>",
72 self.position, self.width, self.height
73 )
74 }
75}
76
77#[cfg(test)]
78fn print_crosses(v: Vec<Cross>, height: usize) -> String {
79 let mut s = String::new();
80 for c in v.chunks(height) {
81 for c in c {
82 s.push(c[0].map_or('_', |(_, r)| r.ch()));
83 for c in &c[1..] {
84 s.push(',');
85 s.push(c.map_or('_', |(_, r)| r.ch()));
86 }
87 s.push(' ');
88 }
89 s.push('\n');
90 }
91 s
92}
93
94pub fn tile(ctx: &RenderingContext, name: &str, rot: Rotation, s: Scale) -> ImageHolder<4> {
95 mask2tile(mask(ctx, rot, name), rot, name, s)
96}
97
98pub fn mask2tile(mask: U4, rot: Rotation, name: &str, scale: Scale) -> ImageHolder<4> {
99 use U4::*;
100 macro_rules! p {
101 ($image:literal) => {
102 load!(concat $image => name which is ["reinforced-conduit" | "armored-duct" | "pulse-conduit" | "plated-conduit" | "conduit" | "conveyor" | "titanium-conveyor" | "armored-conveyor" | "duct"], scale)
103 };
104 }
105
106 match mask {
107 B0001 => match rot {
109 Rotation::Down => p!("1-1-h"), Rotation::Right => p!("0-0"), Rotation::Up => p!("1-3"), Rotation::Left => unreachable!(),
113 },
114 B0010 => match rot {
116 Rotation::Left => p!("1-2"), Rotation::Right => p!("1-2-h"), Rotation::Up => p!("0-3"), Rotation::Down => unreachable!(),
120 },
121 B0011 => match rot {
123 Rotation::Right => p!("2-0"), Rotation::Up => p!("2-3-h"), _ => unreachable!(),
126 },
127 B0100 => match rot {
129 Rotation::Left => p!("0-2"), Rotation::Down => p!("1-1"), Rotation::Up => p!("1-1-v"), Rotation::Right => unreachable!(),
133 },
134 B0101 => match rot {
136 Rotation::Up => p!("4-3"), Rotation::Down => p!("4-1"), _ => unreachable!(),
139 },
140 B0110 => match rot {
142 Rotation::Up => p!("2-3"), Rotation::Left => p!("2-0-h"), _ => unreachable!(),
145 },
146 B0111 => match rot {
148 Rotation::Up => p!("3-3"), _ => unreachable!(),
150 },
151 B1000 => match rot {
153 Rotation::Down => p!("0-1"), Rotation::Left => p!("1-0-h"), Rotation::Right => p!("1-0"), Rotation::Up => unreachable!(),
157 },
158 B1001 => match rot {
160 Rotation::Right => p!("2-0-v"), Rotation::Down => p!("2-1"), _ => unreachable!(),
163 },
164 B1010 => match rot {
166 Rotation::Right => p!("4-0"), Rotation::Left => p!("4-3"), _ => unreachable!(),
169 },
170 B1011 => match rot {
172 Rotation::Right => p!("3-0"), _ => unreachable!(),
174 },
175 B1100 => match rot {
177 Rotation::Down => p!("2-1-h"), Rotation::Left => p!("2-2"), _ => unreachable!(),
180 },
181 B1101 => match rot {
183 Rotation::Down => p!("3-1"), _ => unreachable!(),
185 },
186 B1110 => match rot {
188 Rotation::Left => p!("3-0-h"), _ => unreachable!(),
190 },
191 B0000 => match rot {
192 Rotation::Left => p!("0-2"),
193 Rotation::Right => p!("0-0"),
194 Rotation::Down => p!("0-1"),
195 Rotation::Up => p!("0-3"),
196 },
197 B1111 => unreachable!(),
198 }
199}
200
201pub fn mask(ctx: &RenderingContext, rot: Rotation, n: &str) -> U4 {
202 macro_rules! c {
203 ($in: expr, $srot: expr, $name: expr, $at: expr) => {{
204 if let Some((b, rot)) = $in {
205 if b.name() == $name {
206 (rot == $at && rot.mirrored(true, true) != $srot) as u8
208 } else {
209 0
210 }
211 } else {
212 0
213 }
214 }};
215 }
216 use Rotation::{Down, Left, Right, Up};
217 let mut x = 0b0000;
218
219 x |= 8 * c!(ctx.cross[0], rot, n, Down);
220 x |= 4 * c!(ctx.cross[1], rot, n, Left);
221 x |= 2 * c!(ctx.cross[2], rot, n, Up);
222 x |= c!(ctx.cross[3], rot, n, Right);
223 U4::from(x)
224}
225
226pub fn nbors(corners: [Option<Type>; 4], cross: [Option<Type>; 4], t: Type) -> u8 {
227 let x = [
228 cross[1], corners[3], cross[0], corners[2], cross[3], corners[0], cross[2], corners[1],
229 ];
230
231 use std::simd::prelude::*;
232 u8x8::from_array(x.map(|x| (x == Some(t)) as u8))
233 .simd_eq(u8x8::splat(1))
234 .to_bitmask() as u8
235}
236
237macro_rules! g {
238 ($name:literal, $($i:literal,)+) => { paste::paste! { [
239 $(load!([<$name _ $i>]),)+
240 ] }};
241}
242
243pub fn select(n: &str, mask: u8) -> [Image<&'static [u8], 4>; 3] {
244 macro_rules! autotiled {
245 ($($name:literal)+) => {{ paste::paste! { $(const [<$name:snake:upper>]: [[Image<&[u8], 4>; 3]; 256] = g![$name,
24639, 36, 39, 36, 27, 16, 27, 24, 39, 36, 39, 36, 27, 16, 27, 24,
24738, 37, 38, 37, 17, 41, 17, 43, 38, 37, 38, 37, 26, 21, 26, 25,
24839, 36, 39, 36, 27, 16, 27, 24, 39, 36, 39, 36, 27, 16, 27, 24,
24938, 37, 38, 37, 17, 41, 17, 43, 38, 37, 38, 37, 26, 21, 26, 25,
250 3, 4, 3, 4, 15, 40, 15, 20, 3, 4, 3, 4, 15, 40, 15, 20,
251 5, 28, 5, 28, 29, 10, 29, 23, 5, 28, 5, 28, 31, 11, 31, 32,
252 3, 4, 3, 4, 15, 40, 15, 20, 3, 4, 3, 4, 15, 40, 15, 20,
253 2, 30, 2, 30, 9, 46, 9, 22, 2, 30, 2, 30, 14, 44, 14, 6,
25439, 36, 39, 36, 27, 16, 27, 24, 39, 36, 39, 36, 27, 16, 27, 24,
25538, 37, 38, 37, 17, 41, 17, 43, 38, 37, 38, 37, 26, 21, 26, 25,
25639, 36, 39, 36, 27, 16, 27, 24, 39, 36, 39, 36, 27, 16, 27, 24,
25738, 37, 38, 37, 17, 41, 17, 43, 38, 37, 38, 37, 26, 21, 26, 25,
258 3, 0, 3, 0, 15, 42, 15, 12, 3, 0, 3, 0, 15, 42, 15, 12,
259 5, 8, 5, 8, 29, 35, 29, 33, 5, 8, 5, 8, 31, 34, 31, 7,
260 3, 0, 3, 0, 15, 42, 15, 12, 3, 0, 3, 0, 15, 42, 15, 12,
261 2, 1, 2, 1, 9, 45, 9, 19, 2, 1, 2, 1, 14, 18, 14, 13,];)+ match n {
262 $($name => { [<$name:snake:upper>][mask as usize] },)+
263 x => unreachable!("{x}")
264 }}}};
265}
266 autotiled!("colored-floor" "colored-wall"
267 "metal-tiles-1" "metal-tiles-2" "metal-tiles-3" "metal-tiles-4" "metal-tiles-5""metal-tiles-6" "metal-tiles-7" "metal-tiles-8" "metal-tiles-9" "metal-tiles-10" "metal-tiles-11" "metal-tiles-12" "metal-tiles-13"
268 "metal-wall-1" "metal-wall-2" "metal-wall-3"
269 )
270}
271
272pub trait RotationState {
273 fn get_rotation(&self) -> Option<Rotation>;
274}
275pub trait BlockState {
276 fn get_block(&self) -> Option<&'static Block>;
277}
278
279#[test]
280fn test_cross() {
281 macro_rules! test {
282 ($schem: literal => $($a:tt,$b:tt,$c:tt,$d:tt)*) => {
283 let s = crate::Schematic::deserialize_base64($schem).unwrap();
284 let mut c = vec![];
285 println!("{:#?}", s.blocks);
286 for (position, _) in s.block_iter() {
287 let pctx = PositionContext {
288 position,
289 width: s.width,
290 height: s.height,
291 };
292 c.push(s.cross(&pctx).0);
293 }
294 let n = s.tags.get("name").map_or("<unknown>", |x| &x);
295 let cc: Vec<Cross> = vec![
296 $(define!($a,$b,$c,$d),)*
297 ];
298 if cc != c {
299 let a = print_crosses(cc, s.height as usize);
300 let b = print_crosses(c, s.height as usize);
301 for diff in diff::lines(&a, &b) {
302 match diff {
303 diff::Result::Left(l) => println!("\x1b[38;5;1m{}", l),
304 diff::Result::Right(r) => println!("\x1b[38;5;2m{}", r),
305 diff::Result::Both(l, _) => println!("\x1b[0m{}", l),
306 }
307 }
308 print!("\x1b[0m");
309 panic!("test {n} \x1b[38;5;1mfailed\x1b[0m")
319 }
320 println!("test {n} \x1b[38;5;2mpassed\x1b[0m");
321 };
322 }
323 test!("bXNjaAF4nGNgYmBiZmDJS8xNZWBNSizOTGbgTkktTi7KLCjJzM9jYGBgy0lMSs0pZmCNfr9gTSwjA0dyfl5ZamV+EVCOhQEBGGEEM4hiZGAGAOb+EWA=" =>
331 >,v,_,_ _,v,>,_
334 v,_,_,> _,_,v,>
336 );
337 test!("bXNjaAF4nDWK4QqAIBCDd6dE0SNGP8zuh2CeaAS9fZk0xvjGBgNjYJM7BDaqy5h3qb6EfAZNAIboNokVvKyE0Wu65NbyDhM+cQv6mTtTM/WFYfqLm6m3lx9MAg7n" =>
342 >,^,_,_ <,>,<,_ _,v,>,_
343 >,<,_,< v,v,^,> _,>,>,<
344 v,_,_,^ >,_,<,> _,_,v,v
345 );
346 test!("bXNjaAF4nGNgYmBiZmDJS8xNZWApzkvNZuBOSS1OLsosKMnMz2NgYGDLSUxKzSlmYIqOZWTgSM7PK0utzC8CSrAwIAAjEIIQhGJkYAIARA0Ozg==" =>
350 ^,^,_,_ _,<,>,_
351 <,_,_,> _,_,^,^
352 );
353
354 test!("bXNjaAF4nCWJQQqAIBREx69E0Lq994oWph8STEMj6fZpzcDjDQMCSahoDsZsdN1TYB25aucz28uniMlxsdmf3wCGYDYOBbSsAqNN8eYn5XYofJEdAtSB31tfaoIVGw==" =>
356 <,>,_,_ _,^,v,_
357 ^,_,_,v _,_,>,<
358 );
359 test!("bXNjaAF4nEXJwQqAIBAE0HGVCPrE6GC2B0HdcCPw78MKnMMwj4EFWbjiM8N5bRnLwRpqPK8oBcCU/M5JQetmMAcpNzep/cCIAfX69yv6RF0PFy0O4Q==" =>
363 <,>,_,_ _,<,>,_
364 <,>,_,> _,<,>,<
365 <,_,_,> _,_,>,<
367 );
368
369 test!("bXNjaAF4nEWOUQ7CIBBEh2VZTbyCx/A2xg9a+WiC0LTGxNvb7Wjk5wEzb7M4QCO05UdBqj3PF5zuZR2XaX5OvQGwmodSV8j1FnAce3uVd1+24Iz/CYQQ8fcVHYEQIjqEXWEm9LwgX9kR+PLSbm2BMlN6Sk/3LhJnJu6S6CVmxl2MntEzv38AchUPug==" =>
375 >,v,_,_ >,v,>,_ >,v,>,_ _,v,>,_
376 v,<,_,> v,v,v,> v,>,v,> _,<,v,>
377 v,>,_,v >,<,<,v <,^,v,v _,v,>,v
378 <,>,_,< ^,v,>,v v,>,<,> _,^,^,<
379 v,<,_,> >,>,>,< ^,^,v,^ _,^,>,v
380 >,v,_,> ^,v,<,v ^,>,>,> _,>,^,^
381 v,_,_,< >,_,v,> >,_,v,^ _,_,>,^
385 );
386}
387
388#[test]
389fn test_mask() {
390 macro_rules! assert {
391 ($a:tt,$b:tt,$c:tt,$d:tt => $rot: tt => $expect: expr) => {
392 assert_eq!(mask!(define!($a, $b, $c, $d), $rot), $expect)
393 };
394 }
395 macro_rules! mask {
396 ($cross:expr, $rot: tt) => {
397 mask(
398 &RenderingContext {
399 position: PositionContext {
400 position: GridPos(5, 5),
401 width: 10,
402 height: 10,
403 },
404 corners: Default::default(),
405 cross: $cross,
406 },
407 dir!($rot),
408 "conveyor",
409 )
410 };
411 }
412 assert!(_,_,_,_ => ^ => U4::B0000);
413 assert!(v,_,_,_ => > => U4::B1000);
414 assert!(v,v,_,_ => v => U4::B1000);
415 assert!(_,v,>,_ => > => U4::B0000);
416 assert!(v,>,<,> => ^ => U4::B0001);
417 assert!(v,>,>,_ => > => U4::B1000);
418}