fix(B05): 세월교 날개벽이 오므라들던 것을 벌어지게 정정

위에서 봤을 때 아령처럼 유입·유출부가 커져야 물을 받는다(사용자). 그런데 노선
프레임 경로에서 앞뒤 기준이 뒤집혀 세월교 날개가 오므라들었다.

뿌리는 frameAt(측점 + along × 반폭)으로 누가거리 기준으로 잡는데, 벌어지는
방향은 dir × along으로 측점 로컬 기준을 쓴다. 이 프레임의 dir은 누가거리가
줄어드는 쪽을 향해 둘이 반대였다 — 뿌리가 벌어짐 방향의 반대편에 앉는다.
실측: 149.73 날개가 (−3.65, 종 3.88) → (−4.48, 종 2.06)으로 종방향이 0에
가까워졌다. BOX암거는 뿌리·방향이 같은 기준이라 영향이 없었다.

부호를 가정하지 않는다. 측점 ± 반폭 두 자리를 다 뽑아 `along` 방향에 놓인 쪽을
고른다(`endFrameOf`) — 누가거리 부호와 진행 방향 부호가 반대인 노선에도 맞는다.
`BUILD_VERSION` 34 → 35.

실측: 같은 측 두 날개의 종방향 간격이 뿌리 → 끝으로 커진다. 149.73 좌 10.89 →
14.19m, 우 8.74 → 10.92m. BOX 200.92는 2.40 → 5.22m로 정정 전과 동일(무변화).
파생물 회귀 없음 — 에이프런 4장(기준 직선 이탈 0.0000m), 성토끝 박스 4/4·4/4.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-08-26 18:10:50 +09:00
co-authored by Claude Opus 5
parent bad291f720
commit 24bc9480a5
2 changed files with 37 additions and 2 deletions
+1 -1
View File
@@ -84,7 +84,7 @@ function fnv1a(text: string): string {
* (2026-08-23 사용자 보고: "원복이 안 된 것 같다가 갑자기 반영됨"). 판번호를 해시에
* 섞어 두면 배포와 동시에 저장본이 만료된다.
*/
const BUILD_VERSION = 34; // 세월교 날개벽 노선 추종 + 바닥 에이프런(2026-08-26).
const BUILD_VERSION = 35; // 세월교 날개벽 벌어짐 방향 정정(2026-08-26).
/** 종횡단 정본에서 코리도에 영향을 주는 입력만 요약해 해시 — 갱신 감지 기준. */
export function corridorHash(detail: SectionDetailResponse, routePoints: RoutePoint[]): string {
@@ -46,6 +46,35 @@ export interface WingAnchor {
/** 날개 축 방향 분할 수 — 높이 체감을 담을 최소 해상도. */
const WING_STEPS = 4;
/**
* 측벽 끝단부 프레임 — 측점 ± 반폭 두 자리 중 **`along` 방향에 놓인 쪽**을 고른다.
* 누가거리 부호와 진행 방향 부호가 반대인 노선이 있어 부호를 가정하면 안 된다.
*/
function endFrameOf(
frame: StructureFrame,
endFrameAt: ((chainageM: number) => RouteFrame | null) | undefined,
chainageM: number,
halfSpanM: number,
along: number,
): RouteFrame | null {
if (!endFrameAt) return null;
const dirX = -frame.leftY;
const dirY = frame.leftX;
let best: RouteFrame | null = null;
let bestReach = 0;
for (const step of [halfSpanM, -halfSpanM]) {
const candidate = endFrameAt(chainageM + step);
if (!candidate) continue;
// 기준 측점에서 그 자리까지의 진행 방향 성분 — `along`과 부호가 같아야 한다.
const reach = ((candidate.cx - frame.cx) * dirX + (candidate.cy - frame.cy) * dirY) * along;
if (reach > bestReach) {
bestReach = reach;
best = candidate;
}
}
return best;
}
/**
* 날개벽 4매(좌·우 × 전·후). `anchors`가 빈 배열이거나 제원이 꺼져 있으면 안 만든다.
*
@@ -79,7 +108,13 @@ export function buildWingSolids(
const height1 = Math.max(wing.height_m ?? height0, 0.3);
for (const along of [1, -1]) {
// 뿌리를 낼 프레임 — 세월교는 측벽 끝단 자리의 노선 프레임, BOX는 기준 측점.
const end = endFrameAt?.(chainageM + along * halfSpanM) ?? null;
//
// 누가거리 부호와 `dir`(좌향을 90° 돌린 진행 방향) 부호가 같다는 보장이 없다.
// 실제로 이 프레임의 `dir`은 누가거리가 **줄어드는** 쪽을 향해, 그냥
// `chainageM + along * halfSpanM`으로 잡으면 뿌리가 벌어짐 방향의 반대편에
// 앉아 날개가 오므라든다(2026-08-26 사용자: "아령같이 커져야 물을 받는다").
// 그래서 앞뒤 두 자리를 다 뽑아 **`along` 방향에 놓인 쪽**을 고른다.
const end = endFrameOf(frame, endFrameAt, chainageM, halfSpanM, along);
const base = end ?? frame;
// 노선 진행 방향 = 좌향 벡터를 90° 돌린 것.
const dirX = -base.leftY;