fix(B05): 날개벽 바깥 세로선을 구체 측면에 붙여 단을 없앤다
날개 판이 축 위에 가운데 정렬이라, 뿌리를 구체 측면에 놓으면 절반(두께/2)이 측면 밖으로 삐져나와 투영 커브에 단이 졌다. 각도 틸팅 방식은 그대로 두고 판을 축을 따라 뒤로 민다 — 바깥쪽 세로선이 측면에 닿는 데 필요한 양은 (두께/2) × |cos각| / sin각 = (두께/2) × cot각이다. 각이 아주 작으면 cot이 발산하므로 날개 길이의 절반으로 자른다. 세월교·BOX암거 둘 다 적용한다(사용자가 둘 다 지목). 에이프런도 새 뿌리를 따른다. `BUILD_VERSION` 36 → 37. 실측: 뿌리 링의 바깥 세로선이 구체 측면 평면에서 떨어진 거리가 세월교 4장·BOX 4장 전부 0.0000m. 안쪽 세로선은 0.1414m 안으로 박힌다(= 두께 0.2 × cos45°). 회귀 없음 — 아령 벌어짐 유지(149.73 좌 10.73 → 14.02m, 우 8.63 → 10.81m, 200.92 2.26 → 5.08m), 성토끝 박스 4/4·4/4, 에이프런 4장·0장, 기준 직선 이탈 0. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -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 {
|
||||
|
||||
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user