cube_core/cube/
slice.rs

1use crate::{
2    utils::{
3        cube_utils::{Axis, Color},
4        geometry::Point3D
5    },
6    cube::{
7        cube::Face,
8        core::{
9            grid::{GridSide, MoveDirection}
10        }
11    },
12    game::render::{AnyFace, Renderable}
13};
14
15const TIEBREAKER_WEIGHT: f32 = 0.001;
16const MAGIC_THINGY: f32 = 0.2;
17
18#[derive(Clone)]
19pub struct FaceSlice {
20    corners: [Point3D; 4],
21    pub markers: Vec<Point3D>,
22    pub colors: [Color; 3],
23}
24
25impl FaceSlice {
26    fn new(corners: [Point3D; 4], colors: [Color; 3]) -> FaceSlice {
27        let mut markers = Vec::with_capacity(8);
28        let diff = corners[3].subtract(&corners[0]).scalar_multiply(1.0 / 3.0);
29        for i in 0..4 {
30            markers.push(corners[0].add(&diff.scalar_multiply(i as f32))); 
31            markers.push(corners[1].add(&diff.scalar_multiply(i as f32)));
32        };
33
34        FaceSlice { corners, markers, colors }
35    }
36
37    fn avg_x(&self) -> f32 {
38        (self.corners[0].x.abs() + self.corners[1].x.abs() + self.corners[2].x.abs() + self.corners[3].x.abs()) / 4.0
39    }
40
41    fn avg_y(&self) -> f32 {
42        (self.corners[0].y.abs() + self.corners[1].y.abs() + self.corners[2].y.abs() + self.corners[3].y.abs()) / 4.0
43    }
44
45    pub fn avg_z(&self) -> f32 {
46        (self.corners[0].z + self.corners[1].z + self.corners[2].z + self.corners[3].z) / 4.0 + MAGIC_THINGY
47    }
48}
49
50#[derive(Debug, Clone)]
51pub struct CubeMove {
52    pub axis: Axis,
53    pub grid_side: GridSide,
54    pub order: CubeSliceOrder,
55    pub direction: MoveDirection,
56}
57
58impl CubeMove {
59    pub fn from_side(grid_side: GridSide, direction: MoveDirection) -> CubeMove {
60        CubeMove { axis: grid_side.axis(), grid_side, order: grid_side.order(), direction }
61    }
62
63    pub fn from_str(mv: &str) -> Result<(GridSide, MoveDirection), String> {
64        let (side_char, suffix) = mv.split_at(1);
65
66        let grid_side= match side_char {
67            "R" => GridSide::Right,
68            "L" => GridSide::Left,
69            "U" => GridSide::Top,
70            "D" => GridSide::Bottom,
71            "F" => GridSide::Front,
72            "B" => GridSide::Back,
73            "M" => GridSide::MiddleX,
74            "E" => GridSide::MiddleY,
75            "S" => GridSide::MiddleZ,
76            _ => return Err(format!("Incorrect move '{}'", mv)),
77        };
78        let direction = match suffix {
79            "" => Ok(MoveDirection::Clockwise),
80            "'" => Ok(MoveDirection::CounterClockwise),
81            "2" => Ok(MoveDirection::Double),
82            _ => Err(format!("Incorrect move '{}'", mv)),
83        }?;
84
85        Ok((grid_side, direction))
86    }
87}
88
89#[derive(Debug, Clone)]
90pub enum CubeSliceOrder {
91    FIRST,
92    MIDDLE,
93    LAST,
94}
95
96impl CubeSliceOrder {
97    pub fn idx(&self) -> usize {
98        match self {
99            Self::FIRST => 0,
100            Self::MIDDLE => 1,
101            Self::LAST => 2,
102        }
103    }
104}
105
106#[derive(Clone)]
107pub struct CubeSlice {
108    pub global_cube_position: Point3D,
109    pub face_1: Face,
110    pub face_2: Face,
111    pub face_slices: [FaceSlice; 4],
112}
113
114impl CubeSlice {
115    pub fn new(
116        global_cube_position: Point3D,
117        face_1: Face, 
118        face_2: Face, 
119        mut colors: Vec<[Color; 3]>, 
120        axis: &Axis, 
121        order: CubeSliceOrder
122    ) -> CubeSlice {
123
124        Self::flip_colors(&mut colors, axis, order);
125
126        let face_slices = [
127            FaceSlice::new(
128                [
129                    face_1.corners[0],
130                    face_2.corners[1],
131                    face_2.corners[0],
132                    face_1.corners[1],
133                ],
134                *colors.get(0).unwrap()
135            ),
136            FaceSlice::new(
137                [
138                    face_1.corners[1],
139                    face_2.corners[0],
140                    face_2.corners[3],
141                    face_1.corners[2],
142                ],
143                *colors.get(1).unwrap()
144            ),
145            FaceSlice::new(
146                [
147                    face_1.corners[2],
148                    face_2.corners[3],
149                    face_2.corners[2],
150                    face_1.corners[3],
151                ],
152                *colors.get(2).unwrap()
153            ),
154            FaceSlice::new(
155                [
156                    face_1.corners[3],
157                    face_2.corners[2],
158                    face_2.corners[1],
159                    face_1.corners[0],
160                ],
161                *colors.get(3).unwrap()
162            ),
163        ];
164
165        CubeSlice { global_cube_position, face_1, face_2, face_slices }
166    }
167
168    fn flip_colors(
169        colors: &mut Vec<[Color; 3]>, 
170        axis: &Axis, 
171        order: CubeSliceOrder
172    ) {
173        match axis {
174            Axis::X => {
175                if let CubeSliceOrder::LAST = &order { } else {
176                    for i in 0..4 {
177                        if let Some(color) = colors.get_mut(i) {
178                            color.reverse();
179                        }
180                    }
181                }
182            },
183            Axis::Y => {
184                if let CubeSliceOrder::LAST = &order {
185                    colors.rotate_right(2);
186                } else {
187                    for i in 0..4 {
188                        if let Some(color) = colors.get_mut(i) {
189                            color.reverse();
190                        }
191                    }
192                }
193            },
194            Axis::Z => {
195                if let CubeSliceOrder::LAST = &order { } else {
196                    for i in 0..4 {
197                        if let Some(color) = colors.get_mut(i) {
198                            color.reverse();
199                        }
200                    }
201                }
202            }
203        }
204    }
205
206    pub fn rotate_around_own_axis(&mut self, angle_rad: f32) {
207        let center1 = self.face_1.center();
208        let center2 = self.face_2.center();
209        let axis = center2.subtract(&center1);
210
211        for p in &mut self.face_1.corners {
212            *p = p.rotate_around_axis(axis, center1, angle_rad);
213        }
214        for p in &mut self.face_1.markers {
215            *p = p.rotate_around_axis(axis, center1, angle_rad);
216        }
217
218        for p in &mut self.face_2.corners {
219            *p = p.rotate_around_axis(axis, center1, angle_rad);
220        }
221        for p in &mut self.face_2.markers {
222            *p = p.rotate_around_axis(axis, center1, angle_rad);
223        }
224
225        for slice in &mut self.face_slices {
226            for p in &mut slice.corners {
227                *p = p.rotate_around_axis(axis, center1, angle_rad);
228            }
229            for p in &mut slice.markers {
230                *p = p.rotate_around_axis(axis, center1, angle_rad);
231            }
232        }
233    }
234
235    pub fn rotate(&mut self, axis: Axis, angle: f32) {
236        let rotate_fn: Box<dyn Fn(Point3D) -> Point3D> = match axis {
237            Axis::X => Box::new(move |p| p.rotate_x(angle)),
238            Axis::Y => Box::new(move |p| p.rotate_y(angle)),
239            Axis::Z => Box::new(move |p| p.rotate_z(angle)),
240        };
241
242        let flipped_offset = self.global_cube_position.scalar_multiply(-1.0);
243        for p in &mut self.face_1.corners {
244            *p = p.translate(flipped_offset);
245            *p = rotate_fn(*p);
246            *p = p.translate(self.global_cube_position);
247        }
248        for p in &mut self.face_1.markers {
249            *p = p.translate(flipped_offset);
250            *p = rotate_fn(*p);
251            *p = p.translate(self.global_cube_position);
252        }
253        
254        for p in &mut self.face_2.corners {
255            *p = p.translate(flipped_offset);
256            *p = rotate_fn(*p);
257            *p = p.translate(self.global_cube_position);
258        }
259        for p in &mut self.face_2.markers {
260            *p = p.translate(flipped_offset);
261            *p = rotate_fn(*p);
262            *p = p.translate(self.global_cube_position);
263        }
264        
265        for slice in &mut self.face_slices {
266            for p in &mut slice.corners {
267                *p = p.translate(flipped_offset);
268                *p = rotate_fn(*p);
269                *p = p.translate(self.global_cube_position);
270            }
271            for p in &mut slice.markers {
272                *p = p.translate(flipped_offset);
273                *p = rotate_fn(*p);
274                *p = p.translate(self.global_cube_position);
275            }
276        }
277    }
278}
279
280impl Renderable for CubeSlice {
281    fn get_visible_faces(&self) -> Vec<AnyFace> {
282        let mut faces = vec![
283            AnyFace::Face(self.face_1.clone()),
284            AnyFace::Face(self.face_2.clone()),
285        ];
286
287        faces.extend(self.face_slices.iter()
288            .cloned()
289            .map(AnyFace::FaceSlice));
290
291        faces.sort_by(|a, b| a.avg_z().partial_cmp(&b.avg_z()).unwrap());
292        faces
293    }
294
295    fn dist(&self) -> f32 {
296        let mut sum = 0.0;
297        
298        sum += self.face_1.avg_z();
299        sum += self.face_2.avg_z();
300
301        for fs in &self.face_slices {
302            sum += fs.avg_z();
303            sum += fs.avg_y() * TIEBREAKER_WEIGHT; // secret sauce
304            sum += fs.avg_x() * TIEBREAKER_WEIGHT; // secret sauce
305        }
306
307        sum / 6.0
308    }
309}