diff --git a/B05_Profile/B05_Profile_UI_Corridor.ts b/B05_Profile/B05_Profile_UI_Corridor.ts index 43cc3bfd..d3eb0c0b 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 = 35; // 세월교 날개벽 벌어짐 방향 정정(2026-08-26). +const BUILD_VERSION = 36; // 세월교 날개 투영 커브를 노선 흐름에 맞춤(2026-08-26). /** 종횡단 정본에서 코리도에 영향을 주는 입력만 요약해 해시 — 갱신 감지 기준. */ export function corridorHash(detail: SectionDetailResponse, routePoints: RoutePoint[]): string { diff --git a/B05_Profile/B05_Profile_UI_Corridor_Plan_Footprint.ts b/B05_Profile/B05_Profile_UI_Corridor_Plan_Footprint.ts index 5b3035fa..3b2ecd6e 100644 --- a/B05_Profile/B05_Profile_UI_Corridor_Plan_Footprint.ts +++ b/B05_Profile/B05_Profile_UI_Corridor_Plan_Footprint.ts @@ -209,14 +209,17 @@ export function wingBoxLoops( ): Array> { if (toe.length < 2) return []; const sign = side === "left" ? 1 : -1; - const dx = axis.leftX * sign; - const dy = axis.leftY * sign; const loops: Array> = []; for (const structure of set) { const rings = structure.rings; // 날개벽(revet)만 — 세월교 바닥 에이프런(basin)도 `wing` 표식을 달지만 박스는 없다. if (!structure.wing || structure.kind !== "revet" || !rings || rings.length < 2) continue; if (wingSide(rings, axis) !== side) continue; + // 옆선 방향 — 세월교는 그 날개가 붙은 자리의 노선 좌향(구조물이 실어 보낸다), + // BOX암거는 기준 측점의 좌향 그대로다(2026-08-26 사용자: "박스 암거는 유지"). + const [ax, ay] = structure.wingAxis ?? [axis.leftX, axis.leftY]; + const dx = ax * sign; + const dy = ay * sign; const start = rings[0]; const end = rings[rings.length - 1]; const startHit = castToToe(start.cx, start.cy, dx, dy, toe); diff --git a/B05_Profile/B05_Profile_UI_Corridor_Structures.ts b/B05_Profile/B05_Profile_UI_Corridor_Structures.ts index 9137ceb5..11854805 100644 --- a/B05_Profile/B05_Profile_UI_Corridor_Structures.ts +++ b/B05_Profile/B05_Profile_UI_Corridor_Structures.ts @@ -77,8 +77,18 @@ export interface CorridorStructure { * 날개벽 표식 — 링이 **노선이 아니라 날개 축**을 탄다(2026-08-26). 그래서 링 * 폴리곤의 편거리는 노선 편거리가 아니라 **패널 두께**다. 탑뷰 투영에서 도로부 * 클립을 적용하면 안 되고(링이 이미 노견 밖), 성토끝 박스를 하나 더 그린다. + * + * `"station"` = 기준 측점 프레임에서 직선으로 뻗은 날개(BOX암거). + * `"route"` = 측벽 끝단부 자리의 노선 프레임을 탄 날개(세월교). */ - wing?: true; + wing?: "station" | "route"; + /** + * 성토끝 박스 옆선을 그을 **바깥 방향**(단위벡터, 좌향 기준) — 세월교 전용. + * 없으면 기준 측점의 좌향을 쓴다(BOX암거 현행 — 2026-08-26 사용자 "박스 암거는 + * 유지"). 세월교는 날개마다 그 자리 노선 프레임의 좌향이 들어와 커브가 노선 + * 흐름을 탄다. + */ + wingAxis?: [number, number]; /** 배관 전용 — 관 하단 시·끝점(offset/표고)과 관경(m), 관벽 두께(m·중공 튜브). */ pipe?: { start: [number, number]; diff --git a/B05_Profile/B05_Profile_UI_Corridor_Structures_Wing.ts b/B05_Profile/B05_Profile_UI_Corridor_Structures_Wing.ts index 84ee9c78..08abbefa 100644 --- a/B05_Profile/B05_Profile_UI_Corridor_Structures_Wing.ts +++ b/B05_Profile/B05_Profile_UI_Corridor_Structures_Wing.ts @@ -143,7 +143,11 @@ export function buildWingSolids( [-thicknessM / 2, anchor.bottomElevation], ]); } - solids.push({ chainage_m: chainageM, kind, polygons, rings, wing: true }); + // 노선 프레임을 탄 날개(세월교)는 그 프레임 좌향을 투영 축으로 실어 보낸다 — + // 성토끝 박스 옆선이 노선 흐름을 타게 된다(2026-08-26 사용자). BOX는 안 싣는다. + const wingMode = end ? "route" : "station"; + const wingAxis: [number, number] | undefined = end ? [base.leftX, base.leftY] : undefined; + solids.push({ chainage_m: chainageM, kind, polygons, rings, wing: wingMode, wingAxis }); const apron = anchor.apron; if (!apron) continue; // 바닥판 종방향 끝의 계류측 모서리까지의 폭 — 뿌리에서 그만큼 벌어져 있다. @@ -175,7 +179,7 @@ export function buildWingSolids( kind: "basin", polygons: apronPolygons, rings: apronRings, - wing: true, + wing: wingMode, }); } }