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:
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user