fix(B04,B05): 탑뷰를 극점에서 2° 띄워 첫 드래그 화면 뒤집힘 제거

앞선 두 커밋(c3f643b, 7d50ded)으로도 증상이 남아 헤드리스 크롬으로 실제 앱에 로그인해
B05 3D를 끌어 보며 측정했다. 원인은 회전 계산이 아니라 **탑뷰 카메라 자리**였다.

카메라를 정확히 수직으로 세우면(기존 top = [0, distance, 0.001], 극각 1e-6도) lookAt이
화면의 가로·세로 방향을 정하지 못한다. 그 자리에서 벗어나는 순간 화면 축이 180° 돌아간다.

실측(2px 드래그 6걸음):
  before  극각 0.00° -> 0.86°, 1걸음째 화면축 반전, 이후 같은 방향 드래그 먹통
  after   극각 2.00° -> 1.15°, 반전 0회, 반대 방향 20걸음도 반전 0회(1.15° -> 130°)

- TOP_VIEW_TILT(2°) 신설 — 화면상 위에서 내려다보는 그림과 구분되지 않으면서 여유각
  (POLAR_EPSILON, 약 1.15°)보다 바깥이라 첫 드래그부터 회전이 이어진다.
- B05 노선 뷰어, B04 지형·포인트클라우드 뷰어의 탑뷰 세 곳에 모두 적용.

typecheck·prettier 통과, 정적 번들 재빌드.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-08-08 18:45:42 +09:00
co-authored by Claude Opus 5
parent 7d50ded7b7
commit 1e905c257f
4 changed files with 21 additions and 4 deletions
@@ -11,6 +11,7 @@ import {
bindCursorPivotControls,
bindSurfaceViewerTheme,
getTopFitDistance,
TOP_VIEW_TILT,
niceScaleDistance,
SURFACE_CAMERA_FOV,
targetPlaneMetersPerPixel,
@@ -305,7 +306,8 @@ export function createSurfaceTerrainViewer(): SurfaceTerrainViewer {
const aspect = viewerArea.clientWidth / Math.max(viewerArea.clientHeight, 1);
const distance = referenceBounds ? getTopFitDistance(referenceBounds, aspect) : span * 1.2;
controls.target.set(0, 0, 0);
camera.position.set(0, distance, 0.001);
// 정확히 수직이면 lookAt이 화면 방향을 못 정해 첫 드래그에 화면이 뒤집힌다.
camera.position.set(0, distance, distance * TOP_VIEW_TILT);
camera.near = Math.max(distance / 10000, 0.01);
camera.far = distance * 100;
camera.updateProjectionMatrix();