feat(view): 전체 보기로 6기 콘크리트를 한 화면에 표시

- 교각별 축 위치(pivot E·N 차이)로 장면을 배치하는 pierOffset과 buildAllPiers 추가
- 지형 격자는 기준 교각(첫 교각) 좌표로 한 번만 그림, 교각별 지반면은 숨김
- 철근은 표시하지 않음 (콘크리트만). ① 형상 생성에서만 동작
- 전체 보기에서 교각을 클릭하면 그 교각을 활성으로 하고 단일 교각 보기로 전환
- 테스트: 위치 오프셋 부호, 피벗을 옮겨도 같은 월드 상자

Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
This commit is contained in:
ypshimandClaude Haiku 5.5 committed 2026-10-27 15:08:09 +09:00
1 parent ab04c19afb
commit a2ad0f2baf
4 files changed
+181 -3

No files matched your search

+52
View File
@@ -0,0 +1,52 @@
import * as THREE from 'three';
import type { PierModel, Vec2 } from '../core/geometry';
import type { TerrainGrid } from '../core/ground-model';
import { buildPierScene, type PierScene } from './scene';
import { terrainGeometry } from './terrain';
/**
* Scene offset of a pier's group inside the shared frame of the reference pier. Each pier's scene is centred on its own
* pivot, so moving it by the pivot difference puts it at its national-grid place (x = E, z = −N, as in stretchMatrix).
*/
export function pierOffset(pivot: Vec2, reference: Vec2): { x: number; z: number } {
return { x: pivot.e - reference.e, z: reference.n - pivot.n };
}
export interface AllPiers {
/** Holds every pier's group, placed by pierOffset. Used for the camera fit and picking. */
piers: THREE.Group;
/** Shared terrain grid in the reference frame, or null without a terrain model. */
terrain: THREE.Mesh | null;
/** Scene of each pier, keyed by the pier name. Parts are shown per pier. */
scenes: Map<string, PierScene>;
/** Scene with the same shape the single-pier view expects (root = pier group, size = whole span). */
view: PierScene;
}
/**
* Builds the concrete members of several piers in one frame (전체 보기, 콘크리트만: no rebar). The first entry is the
* reference. Each pier's own ground plane is hidden; the shared terrain grid is drawn once.
*/
export function buildAllPiers(entries: { name: string; model: PierModel }[], terrain?: TerrainGrid): AllPiers {
const piers = new THREE.Group();
const scenes = new Map<string, PierScene>();
const reference = entries[0].model.pivot;
for (const { name, model } of entries) {
const scene = buildPierScene(model);
scene.parts.ground.visible = false;
const { x, z } = pierOffset(model.pivot, reference);
scene.root.position.set(x, 0, z);
scene.root.userData.pier = name;
piers.add(scene.root);
scenes.set(name, scene);
}
let terrainMesh: THREE.Mesh | null = null;
if (terrain) {
const material = new THREE.MeshStandardMaterial({ color: 0xb8c7b0, roughness: 0.9, metalness: 0, transparent: true, opacity: 0.35, side: THREE.DoubleSide });
terrainMesh = new THREE.Mesh(terrainGeometry(terrain, reference), material);
}
const box = new THREE.Box3().setFromObject(piers);
const size = box.getSize(new THREE.Vector3()).length();
const view: PierScene = { root: piers, parts: scenes.get(entries[0].name)!.parts, size };
return { piers, terrain: terrainMesh, scenes, view };
}