From d3440e9158b483e1df0cc47a5870f92c1d4b5360 Mon Sep 17 00:00:00 2001 From: ypshim Date: Tue, 3 Nov 2026 17:37:43 +0900 Subject: [PATCH] =?UTF-8?q?fix(view):=20=EA=B5=90=EB=8C=80=EB=A7=8C=20?= =?UTF-8?q?=EA=B7=B8=EB=A0=A4=EC=A7=84=20=EC=83=81=ED=83=9C=EC=97=90?= =?UTF-8?q?=EC=84=9C=20=EC=A0=95=EB=A9=B4=C2=B7=EC=B8=A1=EB=A9=B4=C2=B7?= =?UTF-8?q?=ED=8F=89=EB=A9=B4=20=EB=B2=84=ED=8A=BC=EC=9D=B4=20=EB=8F=99?= =?UTF-8?q?=EC=9E=91=ED=95=98=EB=8F=84=EB=A1=9D=20=ED=95=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 원인: 교대를 고르면 교각 장면이 없어서 setView가 바로 반환. 정면 버튼이 아무 동작도 하지 않았음 - 교대만 보일 때는 교대 그룹의 좌표계(+x = 교대축, +z = 전면)에서 카메라 방향을 잡고, 보이는 교대 전체를 맞춤 - 테스트 640건 통과, 타입 검사·빌드 통과. 브라우저 시각 확인은 미실시 Co-Authored-By: Claude Haiku 5.5 --- src/main.ts | 34 ++++++++++++++++++++++++++++++++-- 1 file changed, 32 insertions(+), 2 deletions(-) diff --git a/src/main.ts b/src/main.ts index 2455083..a820be5 100644 --- a/src/main.ts +++ b/src/main.ts @@ -479,9 +479,12 @@ function setView(kind: ViewKind): void { $('views-toggle').textContent = `${VIEWS.find((v) => v.kind === kind)?.label ?? ''} ▾`; setMenuOpen('views', false); const scene = allPiers ? allPiers.view : state.scene; - if (!scene) return; const pier = activePier(); - if (!pier?.model) return; + if (!scene || !pier?.model) { + // No pier is drawn (an abutment is selected in ②): the views follow the abutment's own frame (2026-11-03). + abutView(kind); + return; + } const framed = viewPosition(scene, kind, pier.model); // Keep the chosen view direction but fit every model (piers and abutments) in the window (2026-11-03). const fitted = fitToModels(framed.position, framed.target); @@ -490,6 +493,33 @@ function setView(kind: ViewKind): void { controls.update(); } +/** Camera for the drawn abutments alone: the directions come from the abutment group (local +x = 교대축 IP1→IP3, +z = 전면). */ +function abutView(kind: ViewKind): void { + const shown = abutGroups.filter((g) => g.visible); + if (shown.length === 0) return; + const box = new THREE.Box3(); + for (const g of shown) { + g.updateWorldMatrix(true, true); + box.expandByObject(g); + } + const sphere = box.getBoundingSphere(new THREE.Sphere()); + const ref = shown[0]; + const axisX = new THREE.Vector3(1, 0, 0).transformDirection(ref.matrixWorld); + const front = new THREE.Vector3(0, 0, 1).transformDirection(ref.matrixWorld); + const lift = new THREE.Vector3(0, 0.15, 0); + const dirs: Record = { + iso: new THREE.Vector3(1, 0.7, 1).normalize(), + front: front.clone().add(lift).normalize(), + side: axisX.clone().add(lift).normalize(), + top: new THREE.Vector3(0, 1, 0.001).normalize(), + }; + const target = sphere.center.clone(); + const dist = sphere.radius * 2.6; + camera.position.copy(target.clone().add(dirs[kind].multiplyScalar(dist))); + controls.target.copy(target); + controls.update(); +} + /** Bounding sphere of everything drawn (the piers or the all-piers view, plus the abutments) fitted to the camera. */ function fitToModels(position: THREE.Vector3, target: THREE.Vector3): { position: THREE.Vector3; target: THREE.Vector3 } { const box = new THREE.Box3();