fix(viewcube): snap to direction with nearest-canonical up axis

ViewCube tıklaması artık sabit (yaw, pitch) tablosu yerine bir yön
vektörüne (eye direction) snap ediyor. Up vektörü şöyle seçiliyor:

  1. Mevcut up alın.
  2. ±X / ±Y / ±Z arasından (yeni gaze ile (anti-)paralel olmayanlar)
     mevcut up'a en yüksek dot-product veren cardinal axis'i seç.
  3. Bu axis'i yeni gaze yönüne dik plane'e projekte et ve up olarak
     kullan.

Sonuç:
- Küçük tilt'ler ilgili canonical axis'e snap olarak temizleniyor.
- TOP yukarıda iken TOP-LEFT edge tıklayınca TOP yukarıda kalıyor;
  cube edge'e dönüyor ama kullanıcının "yukarı" hissi korunuyor.
- Upside-down orbit edilmişse -Z up olarak seçilir, snap baş aşağı
  yapılandırmayı bozmuyor.

CubeRegion::snap_angles ve Camera::snap_to_angles yerini
snap_direction / snap_to_direction aldı.
snap_active_viewport_to_direction viewport'un view_direction'ını
günceller, twist_angle'a dokunmaz.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
Hakan Seven 2026-05-27 08:09:38 +03:00
commit 451239c4f9
4 changed files with 135 additions and 68 deletions

View file

@ -2568,34 +2568,25 @@ impl OpenCADStudio {
Message::ViewCubeSnap(region) => {
let i = self.active_tab;
let mut region = region;
let (yaw, pitch) = {
let (target_yaw, target_pitch) = region.snap_angles();
// Check current orientation to detect "already there → flip to opposite".
let already_there = if let Some((cur_yaw, cur_pitch)) =
self.tabs[i].scene.active_viewport_yaw_pitch()
{
angle_close(cur_yaw, target_yaw, 0.01)
&& angle_close(cur_pitch, target_pitch, 0.01)
} else if self.tabs[i].scene.active_viewport.is_some() {
false
} else {
let cam = self.tabs[i].scene.camera.borrow();
angle_close(cam.yaw, target_yaw, 0.01)
&& angle_close(cam.pitch, target_pitch, 0.01)
};
if already_there {
region = region.opposite();
}
region.snap_angles()
// "Already there → flip to opposite" check: compare the
// current gaze direction with the region's target gaze.
let target_dir = region.snap_direction();
let cur_dir = {
let cam = self.tabs[i].scene.camera.borrow();
cam.rotation * glam::Vec3::Z
};
if cur_dir.dot(target_dir) > 0.9999 {
region = region.opposite();
}
let eye_dir = region.snap_direction();
if self.tabs[i].scene.active_viewport.is_some() {
self.tabs[i]
.scene
.snap_active_viewport_to_angles(yaw, pitch);
.snap_active_viewport_to_direction(eye_dir);
} else {
let mut cam = self.tabs[i].scene.camera.borrow_mut();
cam.snap_to_angles(yaw, pitch);
cam.snap_to_direction(eye_dir);
}
self.tabs[i].scene.camera_generation += 1;
self.command_line