viewer: Pan + ZoomExtents + 표준 뷰 단축키

camera.rs:
- pan(dx, dy): Shift+중간버튼으로 target 이동 (화면 평면 이동)
- zoom_extents(mn, mx): E키로 전체 씬 맞춤
- set_standard_view(StandardView): 축 고정 뷰
- StandardView: Top(7) / Front(1) / Right(3) / Left(4) / Iso(Home)

lib.rs:
- shift_pressed 추적 (ModifiersChanged)
- Shift+중간버튼 드래그 → pan
- E → ZoomExtents
- 1/3/4/7/Home → 표준 뷰 스냅
- scene_mn/mx 저장 → zoom_extents에 전달

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
minsung
2026-04-14 20:11:07 +09:00
parent 3359475879
commit 096fc133c4
2 changed files with 114 additions and 13 deletions

View File

@@ -22,6 +22,16 @@ impl CameraUniform {
// ─── Camera ──────────────────────────────────────────────────────────────────
/// Standard axis-aligned views (Revit numpad convention).
#[derive(Debug, Clone, Copy)]
pub enum StandardView {
Top, // Numpad 7
Front, // Numpad 1
Right, // Numpad 3
Left, // Numpad 4 / Ctrl+Numpad3
Iso, // Home
}
/// Orbit camera — spherical coordinates around a fixed target point.
///
/// All distances in millimetres (scene units).
@@ -94,6 +104,42 @@ impl Camera {
self.radius = (self.radius * (1.0 - delta * 0.1)).max(100.0);
}
/// Pan target by screen-space delta (Shift + middle mouse).
///
/// Moves the orbit centre so the object stays under the cursor.
pub fn pan(&mut self, delta_x: f32, delta_y: f32) {
let eye = self.eye();
let view_dir = (self.target - eye).normalize();
let right = view_dir.cross(Vec3::Y).normalize();
let up = right.cross(view_dir).normalize();
let scale = self.radius * 0.001; // pan speed proportional to zoom
self.target -= right * delta_x * scale;
self.target += up * delta_y * scale;
}
/// Zoom-extents: fit the given bounding box into view.
pub fn zoom_extents(&mut self, mn: [f32; 3], mx: [f32; 3]) {
let c = Vec3::new(
(mn[0] + mx[0]) * 0.5,
(mn[1] + mx[1]) * 0.5,
(mn[2] + mx[2]) * 0.5,
);
let diag = Vec3::new(mx[0]-mn[0], mx[1]-mn[1], mx[2]-mn[2]).length();
self.target = c;
self.radius = (diag * 0.75).max(1000.0);
}
/// Snap to a standard axis-aligned view (Revit numpad convention).
pub fn set_standard_view(&mut self, view: StandardView) {
match view {
StandardView::Top => { self.yaw = 0.0; self.pitch = std::f32::consts::FRAC_PI_2 - 0.01; }
StandardView::Front => { self.yaw = std::f32::consts::PI; self.pitch = 0.0; }
StandardView::Right => { self.yaw = std::f32::consts::FRAC_PI_2; self.pitch = 0.0; }
StandardView::Left => { self.yaw = -std::f32::consts::FRAC_PI_2; self.pitch = 0.0; }
StandardView::Iso => { self.yaw = std::f32::consts::FRAC_PI_4; self.pitch = 0.35; }
}
}
/// Update aspect ratio on window resize.
pub fn resize(&mut self, width: u32, height: u32) {
self.aspect = width as f32 / height.max(1) as f32;