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