feat(B05): 3D 원근 · 직교 두 단추 · 기본 직교 · FRONT · SIDE 수평 보기
- 「원근으로/직교로」 토글을 「원근」 「직교」 두 단추로 · 켜진 쪽 강조 - 카메라 기본 = 직교 - FRONT = 남에서 북 수평 · SIDE = 동에서 서 수평 · 앞 가장자리 반폭만큼 물려 화면맞춤 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EYJJRCfHfdCGNhmXTjQKV7
This commit is contained in:
@@ -33,19 +33,29 @@ export function createViewControls(callbacks: ViewControlCallbacks): {
|
||||
(["iso", "top", "front", "side"] as const).forEach((preset) =>
|
||||
viewButtons.append(button(preset.toUpperCase(), () => callbacks.onView(preset), "glass")),
|
||||
);
|
||||
// 직교/원근 전환(2026-09-04 사용자 지시) — 기본은 원근이고, 단추 글자는 **바뀔 쪽**을
|
||||
// 가리킨다(누르면 그쪽으로 간다).
|
||||
let projection: "perspective" | "ortho" = "perspective";
|
||||
const projectionButton = button(
|
||||
"직교로",
|
||||
() => {
|
||||
projection = projection === "perspective" ? "ortho" : "perspective";
|
||||
projectionButton.textContent = projection === "perspective" ? "직교로" : "원근으로";
|
||||
callbacks.onProjection(projection);
|
||||
},
|
||||
"glass",
|
||||
);
|
||||
viewButtons.append(projectionButton);
|
||||
// 원근·직교 두 단추 — 지금 켜진 쪽을 강조한다. 기본은 직교(카메라 기본과 같음).
|
||||
const projectionButtons = (
|
||||
[
|
||||
["perspective", "원근"],
|
||||
["ortho", "직교"],
|
||||
] as const
|
||||
).map(([kind, label]) => {
|
||||
const element = button(
|
||||
label,
|
||||
() => {
|
||||
projectionButtons.forEach(({ kind: other, element: el }) => {
|
||||
el.classList.toggle("is-active", other === kind);
|
||||
el.setAttribute("aria-pressed", String(other === kind));
|
||||
});
|
||||
callbacks.onProjection(kind);
|
||||
},
|
||||
"glass",
|
||||
);
|
||||
element.classList.toggle("is-active", kind === "ortho");
|
||||
element.setAttribute("aria-pressed", String(kind === "ortho"));
|
||||
return { kind, element };
|
||||
});
|
||||
viewButtons.append(...projectionButtons.map(({ element }) => element));
|
||||
const visibilityButtons = document.createElement("div");
|
||||
visibilityButtons.className = "b05-route__view-group";
|
||||
visibilityButtons.append(
|
||||
|
||||
@@ -289,8 +289,9 @@ export function createRouteViewer(): RouteViewer {
|
||||
iso: [distance, distance, distance],
|
||||
// 정확히 수직이면 lookAt이 화면 방향을 못 정해 첫 드래그에 화면이 뒤집힌다.
|
||||
top: [0, distance, distance * TOP_VIEW_TILT],
|
||||
front: [0, distance * 0.25, distance],
|
||||
side: [distance, distance * 0.25, 0],
|
||||
// 정면 = 남에서 북을, 측면 = 동에서 서를 수평으로 본다(장면 +z 가 남, +x 가 동).
|
||||
front: [0, 0, distance],
|
||||
side: [distance, 0, 0],
|
||||
} as const;
|
||||
const [x, y, z] = positions[view];
|
||||
const camera = cameraRig.camera();
|
||||
@@ -309,7 +310,9 @@ export function createRouteViewer(): RouteViewer {
|
||||
if (!bounds) return;
|
||||
const width = bounds.x[1] - bounds.x[0];
|
||||
const depth = bounds.y[1] - bounds.y[0];
|
||||
placeCamera(new THREE.Vector3(0, 0, 0), Math.max(width, depth, 20) * 1.35, view);
|
||||
// 수평 보기는 지형 앞 가장자리가 카메라에 가까워 원근에서 잘린다 — 그 반폭만큼 물린다.
|
||||
const nearHalf = view === "front" ? depth / 2 : view === "side" ? width / 2 : 0;
|
||||
placeCamera(new THREE.Vector3(0, 0, 0), Math.max(width, depth, 20) * 1.35 + nearHalf, view);
|
||||
}
|
||||
|
||||
async function reloadContours(interval: number): Promise<void> {
|
||||
|
||||
@@ -2,8 +2,7 @@
|
||||
* B05_Profile_UI_Viewer_Camera.ts
|
||||
* B05 뷰어의 **원근/직교 두 카메라**와 그 사이 갈아 끼우기.
|
||||
*
|
||||
* 기본은 원근(시야각 45°) — B04 지표면 화면과 같은 조작감이다(2026-09-04 사용자 확정).
|
||||
* 탑뷰에서 크기를 정밀하게 대조할 때만 직교로 바꾼다. 갈아 끼울 때 위치·시선·근평면·
|
||||
* 기본은 직교 — 원근(시야각 45°)은 단추로 고른다. 갈아 끼울 때 위치·시선·근평면·
|
||||
* 먼평면과 **보이는 크기**를 그대로 옮기므로 화면이 튀지 않는다.
|
||||
*
|
||||
* 카메라 객체가 바뀌므로 쓰는 쪽은 붙잡아 두지 말고 `camera()`로 그때그때 읽을 것.
|
||||
@@ -22,7 +21,7 @@ export function createCameraRig() {
|
||||
const perspective = new THREE.PerspectiveCamera(FOV, 1, 0.1, 100000);
|
||||
perspective.position.set(100, 120, 100);
|
||||
const ortho = new THREE.OrthographicCamera(-100, 100, 100, -100, 0.1, 100000);
|
||||
let kind: ProjectionKind = "perspective";
|
||||
let kind: ProjectionKind = "ortho";
|
||||
let aspect = 1;
|
||||
let halfHeight = 100;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user