gistools/geometry/wm/
convert.rs

1use crate::{
2    build_sq_dists, clip_line, BBox3D, ClipLineResultWithBBox, Face, Feature, Geometry, LonLat,
3    S2Feature, S2Point, STPoint, VectorFeature, VectorGeometry, VectorGeometryType,
4    VectorLineString, VectorLineStringGeometry, VectorMultiLineStringGeometry,
5    VectorMultiPointGeometry, VectorMultiPolygonGeometry, VectorPoint, VectorPointGeometry,
6    VectorPolygon, VectorPolygonGeometry,
7};
8
9use alloc::collections::BTreeSet;
10use alloc::vec;
11use alloc::vec::Vec;
12
13// TODO: We are cloning geometry twice at times. Let's optimize (check "to_vec" and "clone" cases)
14
15impl<M: Clone> Feature<M> {
16    /// Convert a GeoJSON Feature to a GeoJSON Vector Feature
17    pub fn to_vector(data: &Feature<M>, build_bbox: Option<bool>) -> VectorFeature<M> {
18        let build_bbox = build_bbox.unwrap_or(false);
19        let Feature { id, properties, metadata, geometry, .. } = data;
20        let vector_geo = convert_geometry(geometry, build_bbox);
21        VectorFeature::new_wm(*id, properties.clone(), vector_geo, metadata.clone())
22    }
23}
24
25impl<M: Clone> VectorFeature<M> {
26    /// Reproject GeoJSON geometry coordinates from lon-lat to a 0->1 coordinate system in place
27    pub fn to_unit_scale(&mut self, tolerance: Option<f64>, maxzoom: Option<u8>) {
28        let mut bbox: Option<BBox3D> = None;
29        match &mut self.geometry {
30            VectorGeometry::Point(geo) => {
31                geo.coordinates.project(&mut bbox);
32                geo.vec_bbox = bbox;
33            }
34            VectorGeometry::LineString(geo) | VectorGeometry::MultiPoint(geo) => {
35                geo.coordinates.iter_mut().for_each(|p| p.project(&mut bbox));
36                geo.vec_bbox = bbox;
37            }
38            VectorGeometry::Polygon(geo) | VectorGeometry::MultiLineString(geo) => {
39                geo.coordinates
40                    .iter_mut()
41                    .for_each(|p| p.iter_mut().for_each(|p| p.project(&mut bbox)));
42                geo.vec_bbox = bbox;
43            }
44            VectorGeometry::MultiPolygon(geo) => {
45                geo.coordinates.iter_mut().for_each(|p| {
46                    p.iter_mut().for_each(|p| p.iter_mut().for_each(|p| p.project(&mut bbox)))
47                });
48                geo.vec_bbox = bbox;
49            }
50        }
51
52        if let Some(tolerance) = tolerance {
53            build_sq_dists(&mut self.geometry, tolerance, maxzoom);
54        }
55    }
56
57    /// Reproject GeoJSON geometry coordinates from lon-lat to a 0->1 coordinate system in place
58    pub fn to_ll(&mut self) {
59        match &mut self.geometry {
60            VectorGeometry::Point(geo) => {
61                geo.coordinates.unproject();
62            }
63            VectorGeometry::LineString(geo) | VectorGeometry::MultiPoint(geo) => {
64                geo.coordinates.iter_mut().for_each(|p| p.unproject());
65            }
66            VectorGeometry::Polygon(geo) | VectorGeometry::MultiLineString(geo) => {
67                geo.coordinates.iter_mut().for_each(|p| p.iter_mut().for_each(|p| p.unproject()));
68            }
69            VectorGeometry::MultiPolygon(geo) => {
70                geo.coordinates.iter_mut().for_each(|p| {
71                    p.iter_mut().for_each(|p| p.iter_mut().for_each(|p| p.unproject()))
72                });
73            }
74        }
75    }
76
77    /// Convet a GeoJSON Feature to an S2Feature
78    pub fn to_s2(&self, tolerance: Option<f64>, maxzoom: Option<u8>) -> Vec<S2Feature<M>> {
79        let VectorFeature { _type, id, properties, metadata, geometry, .. } = self;
80        let mut res: Vec<S2Feature<M>> = vec![];
81
82        if _type == "S2Feature" {
83            res.push(self.clone());
84        } else {
85            let vector_geo = convert_geometry_wm_to_s2(geometry, tolerance, maxzoom);
86            for ConvertedGeometry { geometry, face } in vector_geo {
87                res.push(S2Feature::<M>::new_s2(
88                    *id,
89                    face,
90                    properties.clone(),
91                    geometry,
92                    metadata.clone(),
93                ));
94            }
95        }
96
97        res
98    }
99}
100
101/// Convert a GeoJSON Geometry to an Vector Geometry
102fn convert_geometry(geometry: &Geometry, _build_bbox: bool) -> VectorGeometry {
103    // TODO: build a bbox if user wants it
104    match geometry {
105        Geometry::Point(geo) => {
106            let mut coordinates: VectorPoint = (&geo.coordinates).into();
107            coordinates.m = geo.m_values.clone();
108            VectorGeometry::Point(VectorPointGeometry {
109                _type: VectorGeometryType::Point,
110                is_3d: false,
111                coordinates,
112                bbox: geo.bbox.as_ref().map(|bbox| bbox.into()),
113                offset: None,
114                vec_bbox: None,
115                indices: None,
116                tesselation: None,
117            })
118        }
119        Geometry::Point3D(geo) => {
120            let mut coordinates: VectorPoint = (&geo.coordinates).into();
121            coordinates.m = geo.m_values.clone();
122            VectorGeometry::Point(VectorPointGeometry {
123                _type: VectorGeometryType::Point,
124                is_3d: true,
125                coordinates,
126                bbox: geo.bbox,
127                offset: None,
128                vec_bbox: None,
129                indices: None,
130                tesselation: None,
131            })
132        }
133        Geometry::MultiPoint(geo) => VectorGeometry::MultiPoint(VectorMultiPointGeometry {
134            _type: VectorGeometryType::MultiPoint,
135            is_3d: false,
136            coordinates: geo
137                .coordinates
138                .iter()
139                .enumerate()
140                .map(|(i, p)| {
141                    let mut vp = VectorPoint::from(p);
142                    if let Some(m) = &geo.m_values {
143                        vp.m = Some(m[i].clone());
144                    }
145                    vp
146                })
147                .collect(),
148            bbox: geo.bbox.as_ref().map(|bbox| bbox.into()),
149            ..Default::default()
150        }),
151        Geometry::MultiPoint3D(geo) => VectorGeometry::MultiPoint(VectorMultiPointGeometry {
152            _type: VectorGeometryType::MultiPoint,
153            is_3d: true,
154            coordinates: geo
155                .coordinates
156                .iter()
157                .enumerate()
158                .map(|(i, p)| {
159                    let mut vp = VectorPoint::from(p);
160                    if let Some(m) = &geo.m_values {
161                        vp.m = Some(m[i].clone());
162                    }
163                    vp
164                })
165                .collect(),
166            bbox: geo.bbox,
167            ..Default::default()
168        }),
169        Geometry::LineString(geo) => VectorGeometry::LineString(VectorLineStringGeometry {
170            _type: VectorGeometryType::LineString,
171            is_3d: false,
172            coordinates: geo
173                .coordinates
174                .iter()
175                .enumerate()
176                .map(|(i, p)| {
177                    let mut vp = VectorPoint::from(p);
178                    if let Some(m) = &geo.m_values {
179                        vp.m = Some(m[i].clone());
180                    }
181                    vp
182                })
183                .collect(),
184            bbox: geo.bbox.as_ref().map(|bbox| bbox.into()),
185            ..Default::default()
186        }),
187        Geometry::LineString3D(geo) => VectorGeometry::LineString(VectorLineStringGeometry {
188            _type: VectorGeometryType::LineString,
189            is_3d: true,
190            coordinates: geo
191                .coordinates
192                .iter()
193                .enumerate()
194                .map(|(i, p)| {
195                    let mut vp = VectorPoint::from(p);
196                    if let Some(m) = &geo.m_values {
197                        vp.m = Some(m[i].clone());
198                    }
199                    vp
200                })
201                .collect(),
202            bbox: geo.bbox,
203            ..Default::default()
204        }),
205        Geometry::MultiLineString(geo) => {
206            VectorGeometry::MultiLineString(VectorMultiLineStringGeometry {
207                _type: VectorGeometryType::MultiLineString,
208                is_3d: false,
209                coordinates: geo
210                    .coordinates
211                    .iter()
212                    .enumerate()
213                    .map(|(i, line)| {
214                        line.iter()
215                            .enumerate()
216                            .map(|(j, p)| {
217                                let mut vp = VectorPoint::from(p);
218                                if let Some(m) = &geo.m_values {
219                                    vp.m = Some(m[i][j].clone());
220                                }
221                                vp
222                            })
223                            .collect()
224                    })
225                    .collect(),
226                bbox: geo.bbox.as_ref().map(|bbox| bbox.into()),
227                ..Default::default()
228            })
229        }
230        Geometry::MultiLineString3D(geo) => {
231            VectorGeometry::MultiLineString(VectorMultiLineStringGeometry {
232                _type: VectorGeometryType::MultiLineString,
233                is_3d: true,
234                coordinates: geo
235                    .coordinates
236                    .iter()
237                    .enumerate()
238                    .map(|(i, line)| {
239                        line.iter()
240                            .enumerate()
241                            .map(|(j, p)| {
242                                let mut vp = VectorPoint::from(p);
243                                if let Some(m) = &geo.m_values {
244                                    vp.m = Some(m[i][j].clone());
245                                }
246                                vp
247                            })
248                            .collect()
249                    })
250                    .collect(),
251                bbox: geo.bbox,
252                ..Default::default()
253            })
254        }
255        Geometry::Polygon(geo) => VectorGeometry::Polygon(VectorPolygonGeometry {
256            _type: VectorGeometryType::Polygon,
257            is_3d: false,
258            coordinates: geo
259                .coordinates
260                .iter()
261                .enumerate()
262                .map(|(i, line)| {
263                    line.iter()
264                        .enumerate()
265                        .map(|(j, p)| {
266                            let mut vp = VectorPoint::from(p);
267                            if let Some(m) = &geo.m_values {
268                                vp.m = Some(m[i][j].clone());
269                            }
270                            vp
271                        })
272                        .collect()
273                })
274                .collect(),
275            bbox: geo.bbox.as_ref().map(|bbox| bbox.into()),
276            ..Default::default()
277        }),
278        Geometry::Polygon3D(geo) => VectorGeometry::Polygon(VectorPolygonGeometry {
279            _type: VectorGeometryType::Polygon,
280            is_3d: true,
281            coordinates: geo
282                .coordinates
283                .iter()
284                .enumerate()
285                .map(|(i, line)| {
286                    line.iter()
287                        .enumerate()
288                        .map(|(j, p)| {
289                            let mut vp = VectorPoint::from(p);
290                            if let Some(m) = &geo.m_values {
291                                vp.m = Some(m[i][j].clone());
292                            }
293                            vp
294                        })
295                        .collect()
296                })
297                .collect(),
298            bbox: geo.bbox,
299            ..Default::default()
300        }),
301        Geometry::MultiPolygon(geo) => VectorGeometry::MultiPolygon(VectorMultiPolygonGeometry {
302            _type: VectorGeometryType::MultiPolygon,
303            is_3d: false,
304            coordinates: geo
305                .coordinates
306                .iter()
307                .enumerate()
308                .map(|(i, polygon)| {
309                    polygon
310                        .iter()
311                        .enumerate()
312                        .map(|(j, line)| {
313                            line.iter()
314                                .enumerate()
315                                .map(|(k, p)| {
316                                    let mut vp = VectorPoint::from(p);
317                                    if let Some(m) = &geo.m_values {
318                                        vp.m = Some(m[i][j][k].clone());
319                                    }
320                                    vp
321                                })
322                                .collect()
323                        })
324                        .collect()
325                })
326                .collect(),
327            bbox: geo.bbox.as_ref().map(|bbox| bbox.into()),
328            ..Default::default()
329        }),
330        Geometry::MultiPolygon3D(geo) => VectorGeometry::MultiPolygon(VectorMultiPolygonGeometry {
331            _type: VectorGeometryType::MultiPolygon,
332            is_3d: true,
333            coordinates: geo
334                .coordinates
335                .iter()
336                .enumerate()
337                .map(|(i, polygon)| {
338                    polygon
339                        .iter()
340                        .enumerate()
341                        .map(|(j, line)| {
342                            line.iter()
343                                .enumerate()
344                                .map(|(k, p)| {
345                                    let mut vp = VectorPoint::from(p);
346                                    if let Some(m) = &geo.m_values {
347                                        vp.m = Some(m[i][j][k].clone());
348                                    }
349                                    vp
350                                })
351                                .collect()
352                        })
353                        .collect()
354                })
355                .collect(),
356            bbox: geo.bbox,
357            ..Default::default()
358        }),
359    }
360}
361
362/// The resultant geometry after conversion
363pub struct ConvertedGeometry {
364    pub geometry: VectorGeometry,
365    pub face: Face,
366}
367pub type ConvertedGeometryList = Vec<ConvertedGeometry>;
368
369/// Underlying conversion mechanic to move GeoJSON Geometry to S2Geometry
370fn convert_geometry_wm_to_s2(
371    geometry: &VectorGeometry,
372    tolerance: Option<f64>,
373    maxzoom: Option<u8>,
374) -> ConvertedGeometryList {
375    let mut res: ConvertedGeometryList = vec![];
376
377    match geometry {
378        VectorGeometry::Point(geo) => {
379            res.extend(convert_geometry_point(geo));
380        }
381        VectorGeometry::MultiPoint(geo) => {
382            res.extend(convert_geometry_multipoint(geo));
383        }
384        VectorGeometry::LineString(geo) => {
385            res.extend(convert_geometry_linestring(geo));
386        }
387        VectorGeometry::MultiLineString(geo) => {
388            res.extend(convert_geometry_multilinestring(geo));
389        }
390        VectorGeometry::Polygon(geo) => {
391            res.extend(convert_geometry_polygon(geo));
392        }
393        VectorGeometry::MultiPolygon(geo) => {
394            res.extend(convert_geometry_multipolygon(geo));
395        }
396    }
397
398    if let Some(tolerance) = tolerance {
399        for c_geo in &mut res {
400            build_sq_dists(&mut c_geo.geometry, tolerance, maxzoom);
401        }
402    }
403
404    res
405}
406
407// /**
408//  * @param geometry - GeoJSON PointGeometry
409//  * @returns - S2 PointGeometry
410//  */
411/// Convert a GeoJSON PointGeometry to a S2 PointGeometry
412fn convert_geometry_point(geometry: &VectorPointGeometry) -> ConvertedGeometryList {
413    let VectorPointGeometry { _type, is_3d, coordinates, bbox, .. } = geometry;
414    let mut new_point = coordinates.clone();
415    let ll: S2Point = (&LonLat::new(new_point.x, new_point.y)).into();
416    let (face, s, t) = ll.to_face_st();
417    new_point.x = s;
418    new_point.y = t;
419    let vec_bbox = Some(BBox3D::from_point(&new_point));
420    vec![ConvertedGeometry {
421        face: face.into(),
422        geometry: VectorGeometry::Point(VectorPointGeometry {
423            _type: VectorGeometryType::Point,
424            coordinates: new_point,
425            is_3d: *is_3d,
426            bbox: *bbox,
427            vec_bbox,
428            offset: None,
429            indices: None,
430            tesselation: None,
431        }),
432    }]
433}
434
435// /**
436//  * @param geometry - GeoJSON PointGeometry
437//  * @returns - S2 PointGeometry
438//  */
439fn convert_geometry_multipoint(geometry: &VectorMultiPointGeometry) -> ConvertedGeometryList {
440    let VectorMultiPointGeometry { is_3d, coordinates, bbox, .. } = geometry;
441    coordinates
442        .iter()
443        .flat_map(|coordinates| {
444            convert_geometry_point(&VectorPointGeometry {
445                _type: VectorGeometryType::Point,
446                is_3d: *is_3d,
447                coordinates: coordinates.clone(),
448                bbox: *bbox,
449                offset: None,
450                vec_bbox: None,
451                indices: None,
452                tesselation: None,
453            })
454        })
455        .collect()
456}
457
458/// Convert a GeoJSON LineStringGeometry to S2 LineStringGeometry
459fn convert_geometry_linestring(geometry: &VectorLineStringGeometry) -> ConvertedGeometryList {
460    let VectorLineStringGeometry { _type, is_3d, coordinates, bbox, .. } = geometry;
461
462    convert_line_string(coordinates, false)
463        .into_iter()
464        .map(|cline| {
465            let ConvertedLineString { face, line, offset, vec_bbox } = cline;
466            ConvertedGeometry {
467                face,
468                geometry: VectorGeometry::LineString(VectorLineStringGeometry {
469                    _type: VectorGeometryType::LineString,
470                    is_3d: *is_3d,
471                    coordinates: line.to_vec(),
472                    bbox: *bbox,
473                    offset: Some(offset),
474                    vec_bbox: Some(vec_bbox),
475                    ..Default::default()
476                }),
477            }
478        })
479        .collect()
480}
481
482/// Convert a GeoJSON MultiLineStringGeometry to S2 MultiLineStringGeometry
483fn convert_geometry_multilinestring(
484    geometry: &VectorMultiLineStringGeometry,
485) -> ConvertedGeometryList {
486    let VectorMultiLineStringGeometry { is_3d, coordinates, bbox, .. } = geometry;
487
488    coordinates
489        .iter()
490        .flat_map(|line| convert_line_string(line, false))
491        .map(|ConvertedLineString { face, line, offset, vec_bbox }| ConvertedGeometry {
492            face,
493            geometry: VectorGeometry::LineString(VectorLineStringGeometry {
494                _type: VectorGeometryType::LineString,
495                is_3d: *is_3d,
496                coordinates: line,
497                bbox: *bbox,
498                offset: Some(offset),
499                vec_bbox: Some(vec_bbox),
500                ..Default::default()
501            }),
502        })
503        .collect()
504}
505
506/// Convert a GeoJSON PolygonGeometry to S2 PolygonGeometry
507fn convert_geometry_polygon(geometry: &VectorPolygonGeometry) -> ConvertedGeometryList {
508    let VectorPolygonGeometry { _type, is_3d, coordinates, bbox, .. } = geometry;
509    let mut res: ConvertedGeometryList = vec![];
510
511    // conver all lines
512    let mut outer_ring = convert_line_string(&coordinates[0], true);
513    let mut inner_rings = coordinates[1..].iter().flat_map(|line| convert_line_string(line, true));
514
515    // for each face, build a new polygon
516    for ConvertedLineString { face, line, offset, vec_bbox: poly_bbox } in &mut outer_ring {
517        let mut polygon: VectorPolygon = vec![line.to_vec()];
518        let mut polygon_offsets = vec![*offset];
519        let mut poly_bbox = *poly_bbox;
520        for ConvertedLineString {
521            face: inner_face,
522            line: inner_line,
523            offset: inner_offset,
524            vec_bbox,
525        } in &mut inner_rings
526        {
527            if inner_face == *face {
528                polygon.push(inner_line);
529                polygon_offsets.push(inner_offset);
530                poly_bbox = poly_bbox.merge(&vec_bbox);
531            }
532        }
533
534        res.push(ConvertedGeometry {
535            face: *face,
536            geometry: VectorGeometry::Polygon(VectorPolygonGeometry {
537                _type: VectorGeometryType::Polygon,
538                is_3d: *is_3d,
539                coordinates: polygon,
540                bbox: *bbox,
541                offset: Some(polygon_offsets),
542                vec_bbox: Some(poly_bbox),
543                ..Default::default()
544            }),
545        });
546    }
547
548    res
549}
550
551/// Convert a GeoJSON MultiPolygonGeometry to S2 MultiPolygonGeometry
552fn convert_geometry_multipolygon(geometry: &VectorMultiPolygonGeometry) -> ConvertedGeometryList {
553    let VectorMultiPolygonGeometry { is_3d, coordinates, bbox, offset, .. } = geometry;
554    coordinates
555        .iter()
556        .enumerate()
557        .flat_map(|(i, polygon)| {
558            let offset: Option<Vec<f64>> = offset.as_ref().map(|offset| offset[i].clone());
559            convert_geometry_polygon(&VectorPolygonGeometry {
560                _type: VectorGeometryType::Polygon,
561                is_3d: *is_3d,
562                coordinates: polygon.to_vec(),
563                bbox: *bbox,
564                offset,
565                ..Default::default()
566            })
567        })
568        .collect()
569}
570
571/// LineString converted from WM to S2
572pub struct ConvertedLineString {
573    face: Face,
574    line: VectorLineString,
575    offset: f64,
576    vec_bbox: BBox3D,
577}
578
579/// Convert WM LineString to S2
580fn convert_line_string(line: &VectorLineString, is_polygon: bool) -> Vec<ConvertedLineString> {
581    let mut res: Vec<ConvertedLineString> = vec![];
582    // first re-project all the coordinates to S2
583    let mut new_geometry: Vec<STPoint> = vec![];
584    for VectorPoint { x: lon, y: lat, z, m, .. } in line {
585        let ll: S2Point = (&LonLat::new(*lon, *lat)).into();
586        let (face, s, t) = ll.to_face_st();
587        new_geometry.push(STPoint { face: face.into(), s, t, z: *z, m: m.clone() });
588    }
589    // find all the faces that exist in the line
590    let mut faces = BTreeSet::<Face>::new();
591    new_geometry.iter().for_each(|stpoint| {
592        faces.insert(stpoint.face);
593    });
594    // for each face, build a line
595    for face in faces {
596        let mut line: VectorLineString = vec![];
597        for st_point in &new_geometry {
598            line.push(st_point_to_face(face, st_point));
599        }
600        let clipped_lines = clip_line(&line, BBox3D::default(), is_polygon, None, None);
601        for ClipLineResultWithBBox { line, offset, vec_bbox } in clipped_lines {
602            res.push(ConvertedLineString { face, line, offset, vec_bbox });
603        }
604    }
605
606    res
607}
608
609/// Given a face, rotate the point into it's 0->1 coordinate system
610fn st_point_to_face(target_face: Face, stp: &STPoint) -> VectorPoint {
611    let cur_face = stp.face;
612    if target_face == cur_face {
613        return VectorPoint { x: stp.s, y: stp.t, z: stp.z, m: stp.m.clone(), t: None };
614    }
615
616    let (rot, x, y) = &FACE_RULE_SET[target_face as usize][cur_face as usize];
617    let (new_s, new_t) = rotate(*rot, stp.s, stp.t);
618
619    VectorPoint { x: new_s + *x as f64, y: new_t + *y as f64, z: stp.z, m: stp.m.clone(), t: None }
620}
621
622/**
623 * @param rot - rotation
624 * @param s - input s
625 * @param t - input t
626 * @returns - new [s, t] after rotating
627 */
628fn rotate(rot: Rotation, s: f64, t: f64) -> (f64, f64) {
629    match rot {
630        Rotation::_0 => (s, t),
631        Rotation::_90 => (t, 1. - s),
632        Rotation::_Neg90 => (1. - t, s),
633    }
634}
635
636#[derive(Debug, PartialEq, Copy, Clone)]
637pub enum Rotation {
638    _0,
639    _90,
640    _Neg90,
641}
642
643/// Ruleset for converting an S2Point from a face to another.
644/// While this this set includes opposite side faces, without axis mirroring,
645/// it is not technically accurate and shouldn't be used. Instead, data should let two points travel
646/// further than a full face width.
647/// FACE_RULE_SET[target_face][currentFace] = [rot, x, y]
648pub const FACE_RULE_SET: [[(Rotation, i8, i8); 6]; 6] = [
649    // Target Face 0
650    [
651        (Rotation::_0, 0, 0),      // Current Face 0
652        (Rotation::_0, 1, 0),      // Current Face 1
653        (Rotation::_90, 0, 1),     // Current Face 2
654        (Rotation::_Neg90, 2, 0),  // Current Face 3
655        (Rotation::_Neg90, -1, 0), //  Current Face 4
656        (Rotation::_0, 0, -1),     //  Current Face 5
657    ],
658    // Target Face 1
659    [
660        (Rotation::_0, -1, 0),    // Current Face 0
661        (Rotation::_0, 0, 0),     // Current Face 1
662        (Rotation::_0, 0, 1),     // Current Face 2
663        (Rotation::_Neg90, 1, 0), // Current Face 3
664        (Rotation::_Neg90, 2, 0), // Current Face 4
665        (Rotation::_90, 0, -1),   // Current Face 5
666    ],
667    // Target Face 2
668    [
669        (Rotation::_Neg90, -1, 0), // Current Face 0
670        (Rotation::_0, 0, -1),     // Current Face 1
671        (Rotation::_0, 0, 0),      // Current Face 2
672        (Rotation::_0, 1, 0),      // Current Face 3
673        (Rotation::_90, 0, 1),     // Current Face 4
674        (Rotation::_Neg90, 2, 0),  // Current Face 5
675    ],
676    // Target Face 3
677    [
678        (Rotation::_Neg90, 2, 0), // Current Face 0
679        (Rotation::_90, 0, -1),   // Current Face 1
680        (Rotation::_0, -1, 0),    // Current Face 2
681        (Rotation::_0, 0, 0),     // Current Face 3
682        (Rotation::_0, 0, 1),     // Current Face 4
683        (Rotation::_Neg90, 1, 0), // Current Face 5
684    ],
685    // Target Face 4
686    [
687        (Rotation::_90, 0, 1),     // Current Face 0
688        (Rotation::_Neg90, 2, 0),  // Current Face 1
689        (Rotation::_Neg90, -1, 0), // Current Face 2
690        (Rotation::_0, 0, -1),     // Current Face 3
691        (Rotation::_0, 0, 0),      // Current Face 4
692        (Rotation::_0, 1, 0),      // Current Face 5
693    ],
694    // Target Face 5
695    [
696        (Rotation::_0, 0, 1),     // Current Face 0
697        (Rotation::_Neg90, 1, 0), // Current Face 1
698        (Rotation::_Neg90, 2, 0), // Current Face 2
699        (Rotation::_90, 0, -1),   // Current Face 3
700        (Rotation::_0, -1, 0),    // Current Face 4
701        (Rotation::_0, 0, 0),     // Current Face 5
702    ],
703];