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(¢er1);
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; sum += fs.avg_x() * TIEBREAKER_WEIGHT; }
305
306 sum / 6.0
307 }
308}