diff --git a/B05_Profile/B05_Profile_UI_Corridor.ts b/B05_Profile/B05_Profile_UI_Corridor.ts index d3eb0c0b..42b919c1 100644 --- a/B05_Profile/B05_Profile_UI_Corridor.ts +++ b/B05_Profile/B05_Profile_UI_Corridor.ts @@ -84,7 +84,7 @@ function fnv1a(text: string): string { * (2026-08-23 사용자 보고: "원복이 안 된 것 같다가 갑자기 반영됨"). 판번호를 해시에 * 섞어 두면 배포와 동시에 저장본이 만료된다. */ -const BUILD_VERSION = 36; // 세월교 날개 투영 커브를 노선 흐름에 맞춤(2026-08-26). +const BUILD_VERSION = 37; // 날개벽 바깥 세로선을 구체 측면에 붙임(2026-08-26). /** 종횡단 정본에서 코리도에 영향을 주는 입력만 요약해 해시 — 갱신 감지 기준. */ export function corridorHash(detail: SectionDetailResponse, routePoints: RoutePoint[]): string { diff --git a/B05_Profile/B05_Profile_UI_Corridor_Structures_Wing.ts b/B05_Profile/B05_Profile_UI_Corridor_Structures_Wing.ts index 08abbefa..558bfefc 100644 --- a/B05_Profile/B05_Profile_UI_Corridor_Structures_Wing.ts +++ b/B05_Profile/B05_Profile_UI_Corridor_Structures_Wing.ts @@ -129,12 +129,25 @@ export function buildWingSolids( const wingNorm = Math.hypot(wingX, wingY) || 1; const ux = wingX / wingNorm; const uy = wingY / wingNorm; + // 판을 축 방향으로 **밀어넣는 양**(2026-08-26 사용자: 커브에 단이 진다). + // + // 판은 축 위에 가운데 정렬이라 뿌리를 구체 측면에 놓으면 절반(두께/2)이 측면 + // 밖으로 삐져나와 단이 생긴다. **바깥쪽 세로선이 측면에 닿도록** 축을 따라 + // 뒤로 민다 — 측면(법선 = 진행 방향)까지의 거리를 0으로 만드는 양은 + // (두께/2) × |cos각| / sin각 = (두께/2) × cot각이다. 각도 틸팅은 그대로 둔다. + const alongDot = ux * dirX * along + uy * dirY * along; // = sin(각) + const edgeDot = -uy * dirX * along + ux * dirY * along; // = ±cos(각) + const push = alongDot > 1e-6 ? ((thicknessM / 2) * Math.abs(edgeDot)) / alongDot : 0; + // 날개 길이의 절반을 넘겨 밀지 않는다 — 각이 아주 작으면 cot이 발산한다. + const inset = Math.min(push, length / 2); + const rootX = startX - ux * inset; + const rootY = startY - uy * inset; const rings: StructureFrame[] = []; const polygons: SectionPolygon[] = []; for (let i = 0; i <= WING_STEPS; i += 1) { const t = (length * i) / WING_STEPS; // 패널 두께 방향 = 날개 축의 법선. - rings.push({ cx: startX + ux * t, cy: startY + uy * t, leftX: -uy, leftY: ux, dz: 0 }); + rings.push({ cx: rootX + ux * t, cy: rootY + uy * t, leftX: -uy, leftY: ux, dz: 0 }); const top = anchor.bottomElevation + height0 + ((height1 - height0) * i) / WING_STEPS; polygons.push([ [-thicknessM / 2, top], @@ -160,8 +173,8 @@ export function buildWingSolids( const t = i / WING_STEPS; // 링 좌향 = 뿌리→바닥판 끝 방향. 폭을 |P0P2| → 0으로 좁히면 P0·P2·P1 삼각형이다. apronRings.push({ - cx: startX + ux * length * t, - cy: startY + uy * length * t, + cx: rootX + ux * length * t, + cy: rootY + uy * length * t, leftX: base.leftX * spread, leftY: base.leftY * spread, dz: 0,