fix(viewcube): deterministic horizon for obliques, generic opposite

Top Front Right/Left kept Y (north) as up when starting from Top,
tilting the base because orbit is turntable (never banks). Make
snap_to_direction deterministic: world +Z horizon except near +-Z
where north +Y is used, sign-preserved only for intentionally
inverted views. Expand opposite() from faces-only to all 26 regions
so already-there flip works for edges/corners.
This commit is contained in:
Karim Jerbi committed 2026-09-19 09:37:56 +01:00
1 parent 5ba72ec3e4
commit 4fdef48502
3 files changed
+50 -49

No files matched your search

+27 -12
View File
@@ -142,24 +142,39 @@ impl CubeRegion {
/// Unit eye-direction vector (from target toward the camera) that
/// looks straight at this region. Used by `Camera::snap_to_direction`
/// which derives the full orientation by re-using the current
/// camera's up vector, projected onto the plane perpendicular to
/// this direction — so clicking an edge spins the cube around the
/// edge without rolling the user's "up" sense.
/// / `snap_to_face` which derive a deterministic horizon (world +Z,
/// except top/bottom where north +Y is used) so every cube click
/// lands repeatably.
pub fn snap_direction(self) -> glam::Vec3 {
let c = region_centroids()[self.id()];
glam::Vec3::new(c[0], c[1], c[2]).normalize_or(glam::Vec3::Z)
}
pub fn opposite(self) -> CubeRegion {
match self {
CubeRegion::Face(FACE_TOP) => CubeRegion::Face(FACE_BOTTOM),
CubeRegion::Face(FACE_BOTTOM) => CubeRegion::Face(FACE_TOP),
CubeRegion::Face(FACE_FRONT) => CubeRegion::Face(FACE_BACK),
CubeRegion::Face(FACE_BACK) => CubeRegion::Face(FACE_FRONT),
CubeRegion::Face(FACE_RIGHT) => CubeRegion::Face(FACE_LEFT),
CubeRegion::Face(FACE_LEFT) => CubeRegion::Face(FACE_RIGHT),
other => other,
// Generic opposite: the region whose centroid is nearest to -self.
// Covers faces, edges and corners so "already there → flip" works
// for every cube click, not just faces.
let centroids = region_centroids();
let c = centroids[self.id()];
let neg = [-c[0], -c[1], -c[2]];
let mut best = self.id();
let mut best_d = f32::MAX;
for (i, cc) in centroids.iter().enumerate() {
let dx = cc[0] - neg[0];
let dy = cc[1] - neg[1];
let dz = cc[2] - neg[2];
let d = dx * dx + dy * dy + dz * dz;
if d < best_d {
best_d = d;
best = i;
}
}
if best < 6 {
CubeRegion::Face(best)
} else if best < 18 {
CubeRegion::Edge(best)
} else {
CubeRegion::Corner(best)
}
}