투영 커브 경계 주변에 미세한 성토 메쉬가 남던 문제(사용자 보고). 원인은
`clipCellByChord`가 자를 직선을 못 세운 셀을 **통째로 남기던** 폴백이다.
저장 코리도 실좌표로 셀을 재현해 보니 절단 실패 셀 627개(우 314·좌 313) 중
**622개가 셀 안쪽 비율 1.00** — 속은 통째로 커브 안인데 귀퉁이가 경계에 딱 붙어
`inCount === 4` 판정을 못 받고 걸친 셀로 내려간 뒤, 바깥 귀퉁이가 없어 `keepSign`을
못 잡은 셀들이었다(no-keepsign 491 · degenerate-chord 122 · 교점<2 14).
- Cut: `coverage()` 추가(4×4 이중선형 표본) — 절단 실패 셀만 안쪽 비율로 가른다.
0.5 이상이면 마스크, 미만이면 종전대로 남긴다. 정상 절단 경로·정점은 불변.
- Corridor: BUILD_VERSION 50 → 51(기하 변경 — 안 올리면 옛 저장본이 복원된다).
검증:
- 실빌드 데이터(공용 브라우저 `__corridorBuild.raw`): 안쪽인데 남은 셀 **0**(좌·우),
커브 밖인데 지운 셀 **0**. 마스크 셀 우 6,611·좌 6,237, 조각 삼각형 711·728.
- 같은 카메라 전후 스크린샷: 바뀐 픽셀의 수정 전 색이 성토면 청록 5,831 → 수정 후 14.
투영선·스케치선 픽셀은 그대로 — 지워진 건 성토 메쉬뿐이다.
- 저장본 시뮬레이션: 잔여 우 2.17㎡·좌 4.50㎡ → 0.00㎡, 과절단 합 0.0011㎡.
- npm run typecheck 통과, prettier 적용.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
빨간 투영선이 감싸는 자리의 성토면을 지운다. 지울 영역은 결국 **상단 평면 스케치가
덮는 XY 영역** 하나이고 투영선이 그 XY를 그대로 복사한 것이므로, 구조물 종류를
가리지 않고 한 가지 방법으로 자른다(사용자 확정).
- Cut(신규): 격자 셀 중심·귀퉁이로 안팎을 가려 안쪽 셀은 마스크하고, 걸친 셀은
교점 두 개가 만드는 직선으로 잘라 바깥 조각만 삼각형으로 되살린다. 정점은
하나도 안 건드린다. 교점이 2개 미만이면 셀을 통째로 남긴다 — 모자라게 자를지언정
더 자르지 않는다.
- Build: CorridorRibbon에 cellMask·trimTris·trimUvs 추가.
- Mesh: 마스크된 셀의 삼각형을 빼고, 조각 삼각형을 뒤에 이어 붙인다.
- Plan: BOX암거 커브의 **안쪽 끝도 박스 축**으로 쏜다(boxInner + castToToe).
종전엔 바깥만 박스 축이고 안쪽은 노선 흐름이라 가로대가 비스듬했다 —
평면 스케치의 횡단 방향은 박스가 놓인 전역 단방향이어야 한다(사용자 확정).
- Corridor: 저장본에 마스크·조각 삼각형을 담고 BUILD_VERSION을 올린다.
검증(공용 브라우저 캐시 데이터 실측):
- BOX 커브 가로대 각도차 0.00°(좌·우) — 전역 단방향 확인. 다른 세트는 노선 흐름대로
1.1~48.8°.
- 걸친 셀에서 되살린 조각 면적이 기대치와 일치 — 우 2.07㎡ vs 2.07, 좌 4.60 vs 4.61.
- 커브 밖을 지운 셀 0. 도로부·측구·절토 리본 행 수 불변(1768/1768/1768/15/26/23/13).
남은 것: 걸친 셀 1,230개 중 약 600개가 교점 부족으로 통째로 남아 경계에 잔여가
생긴다(우 2.09㎡·좌 4.36㎡). 더 잘리는 문제는 없다.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
구조물 상단 +5m 평면에 눕혀 두던 파란 스케치(slope-original, 유입 실선·유출 파선)의
XY를 그대로 쓰고 z만 그 자리 절·성토면에서 읽어 빨간 투영선(slope-projected)으로
낸다. 스케치는 평면에 그대로 남는다. 투영선이 격자 모서리에 앉도록 구조물 점유
구간 경계를 코리도 행으로 끼워 넣는다.
- Plan: slope-projected 소스 추가. 리본을 받아 `측|누가거리` 색인을 만들고 그 행의
열 폴리라인에서 z를 읽는다. 절토·성토 모두 대상. 밑에 면이 없으면 점을 버려 끊는다.
- Plan: 파란 스케치 안쪽 기준을 소유 측점 노견 편거리 → **그 행 절·성토면이 실제로
시작하는 자리**로 바꿨다. 종전에는 노폭 연장·측구를 못 따라가 커브가 면 밖(도로 위)에
얹혔다 — 275.71 좌측은 노견 4m 확장분만큼 안쪽이었다.
- Plan: 비탈 리본 열 순서가 좌우 반대(조각은 편거리 오름차순, +offset=좌측)라 우측에서
안쪽 끝을 반대로 집었다. 색인 단계에서 안쪽→바깥으로 방향을 통일한다.
- Mesh: 커브를 점의 z로 그린다(평면 커브는 z에 planZ가 들어 있어 동작 불변).
투영선 색 = 빨강 0xff3b30, 서피스에서 0.02m 띄운다.
- Build: 구조물 구간 [from, to]를 행 경계로 강제 삽입. 안쪽 블록 걸음은 상수로 뺐다
(지금은 바깥과 같은 0.2m — 경계 행만 새로 생긴다).
- Corridor: 저장본은 입력 해시로만 무효화되어 기하 코드를 고쳐도 옛 저장본이 계속
복원된다. ENVELOPE_VERSION을 올리고 그 함정을 주석으로 박았다.
- Viewer: 검증용 __corridorBuild 훅(기존 __corridorClip과 같은 용도).
검증(공용 브라우저 캐시 데이터 실측):
- 3D 회귀 없음 — 외곽선 행 1756→1768, 기존 1756행 좌표까지 전부 불변, 추가된 12행은
구조물 구간 경계뿐(79.3·83.3·85.3·89.3·144.73·154.73·199.72·202.12·270.71·
274.71·276.71·280.71). 측구·절토 리본 행 수 불변.
- 투영선 안쪽 끝 8벌 전부 서피스 시작점과 XY·Z 정확 일치(gap 0, dz 0).
- 바깥 끝은 XY 일치, z만 최대 1.01m 차 — 세월교 날개벽·다단 기슭막이 앞면처럼
거의 수직인 면이라 한 XY에 z가 여럿인 구간이다. 규칙 확정은 후속.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
원인은 종단경사 보정(dz)이었다. 세월교 본체(측벽·바닥판)는 dz를 타서 양끝이
들린 사발 모양인데(앞끝 +0.0907 / 측점 0 / 뒤끝 +0.1613) 날개벽·에이프런은
dz = 0으로 측점 표고에 평평히 놓였다. 접합부 틈 0.0907·0.1613m이 그 dz와
소수점까지 일치했다. 표고 자체는 이미 같았다 — 어긋난 건 dz뿐이다.
본체는 그대로 두고 날개·에이프런을 경사에 태운다(사용자 선택).
- `buildWingSolids`에 선택 인자 `baseElevationM` 추가 — 넘기면 날개·에이프런
링이 붙는 자리의 dz(end.designZ − baseElevationM)를 탄다. 세월교만 넘기고
BOX암거는 안 넘겨 dz = 0 현행을 유지한다.
- 에이프런을 본 바닥판과 한 장으로 만든다. ① 밑변을 판이 밀려들어간 뿌리가
아니라 바닥판 끝면 그 자리에서 시작한다. ② 단면을 직사각형 근사 대신 바닥판
폴리곤 그대로 쓴다(뿌리 offset 기준으로 다시 잡아 끝으로 갈수록 0으로 줄인다).
종전엔 사다리꼴 바닥판(2.669~4.283)을 직사각형(2.747~4.283)으로 근사해
안쪽 모서리가 0.078m 어긋났다.
- `BUILD_VERSION` 39 → 40.
실측: 에이프런 밑변 4꼭짓점이 본 바닥판 끝면 꼭짓점과 거리 0.0000m(4장 전부).
날개·에이프런 dz가 붙는 자리 값을 그대로 타고 같은 자리의 날개와 에이프런이
같은 dz다. 회귀 없음 — 아령 벌어짐 유지, 날개 바깥 세로선↔구체 측면 8장 전부
0.0000m, 성토끝 박스 4/4·4/4.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
원본·변형 성토면 투영 커브는 행마다 그 자리 노선 프레임을 써서, 곡선 구간이면
가로 방향이 행마다 틀어져 커브가 뒤틀린다. BOX암거는 날개벽처럼 박스 방향
하나로 그어야 한다(사용자).
BOX암거 세트만(`section.box`) 행 자리는 노선 위 그대로 두고 가로 방향만 기준
측점 좌향으로 고정한다. 세월교·배수관은 종전대로 그 행의 노선 프레임을 쓴다.
원본 커브의 바깥 끝은 그 행의 코리도 윤곽 점을 그대로 썼는데 가로 방향이
고정되면 어긋나므로, 고정 방향으로 쏜 선이 성토 끝선과 만나는 자리를 바깥
끝으로 삼는다(`castToToe`를 공개로 돌려 재사용). 끝은 여전히 실제 성토 끝선
위다. `BUILD_VERSION` 37 → 38.
실측(한 커브 안 행별 가로 방향 분산, ±180 감김 보정): BOX 200.92는 원본·변형
× 좌·우 4개 모두 0.00°. 배수관 84.3 11.42~50.80°, 세월교 149.73 22.84~23.02°,
배수관 275.71 2.28~8.95°로 그대로다. 행 손실 없음 — BOX 변형 6행·원본 13행.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
날개 판이 축 위에 가운데 정렬이라, 뿌리를 구체 측면에 놓으면 절반(두께/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>
성토끝 박스 옆선이 날개 4장 전부 기준 측점의 고정 좌향을 쓰고 있었다. 세월교는
날개가 저마다 다른 자리의 노선 프레임에 붙는데 커브만 한 방향이라 흐름과 어긋난다.
박스암거는 그 고정 방향이 맞으므로 유지한다(사용자).
방향을 구조물에 실어 보낸다 — `CorridorStructure.wingAxis`(단위벡터). 세월교
날개를 만들 때 그 자리 노선 프레임의 좌향을 담고, `wingBoxLoops`가
`structure.wingAxis ?? 측점 좌향`으로 고른다. Plan 쪽에 새 배관이 없다.
`wing` 표식은 `true` → `"station" | "route"`로 바꿔 어느 규칙으로 만든 날개인지
남긴다(문자열이라 기존 truthy 검사는 그대로 통한다). `BUILD_VERSION` 35 → 36.
실측: 149.73 날개 4장 모두 route, 실어 보낸 축이 기준 측점 좌향과 −20.45°/+2.57°
어긋난다. 박스 옆선 각도가 그 값과 정확히 일치(좌 −20.45°·2.57°, 우 159.55°·
−177.43° = 같은 축 반대 방향). 200.92는 4장 모두 station, 옆선 0°·−180°로 무변화.
박스 4/4·4/4, 에이프런 4장·0장, 기준 직선 이탈 0.0000m.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
위에서 봤을 때 아령처럼 유입·유출부가 커져야 물을 받는다(사용자). 그런데 노선
프레임 경로에서 앞뒤 기준이 뒤집혀 세월교 날개가 오므라들었다.
뿌리는 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>
세월교 측벽·바닥판은 ringsOf(half, half)로 노선 스플라인을 따라가는데 날개벽만
기준 측점 프레임에서 직선으로 ±월류폭/2 자리에 뿌리를 잡아, 곡선 구간에서 측벽
끝단과 어긋났다. BOX암거는 직선 규칙이 맞으므로 건드리지 않는다(사용자 ②).
- `buildWingSolids`에 선택 인자 `endFrameAt` 추가 — 넘기면 날개 뿌리와 축을
측벽 끝단부 자리의 노선 프레임에서 뽑는다. 세월교만 `frameAt`을 넘긴다.
안 넘기면 계산이 옛 식과 완전히 같다(BOX 무변화).
- `WingAnchor.apron` 추가 — 세월교 바닥 에이프런. 날개 끝점과 바닥판 종방향
끝점을 이은 직선을 외곽으로 하는 평면 삼각형을 바닥 두께로 세운다(사용자 ④).
링을 뿌리→날개끝으로 깔고 링 좌향을 뿌리→바닥판끝으로 잡아 폭을 |P0P2| → 0으로
좁히는 방식이라 기존 로프트 모델을 그대로 쓴다.
- 에이프런도 `wing` 표식을 달므로 성토끝 박스 대상은 `kind === "revet"`로 좁혔다.
- `BUILD_VERSION` 33 → 34.
실측: BOX 200.92 날개 뿌리 이동량 0.000m·에이프런 0개(무변화 확인). 세월교
149.73 뿌리 이동량 0.102~1.119m, 뿌리가 측벽 끝단 링 횡단선 위(종방향 잔차 0).
에이프런 4장, 바깥 모서리가 기준 직선 위 이탈 0.0000m. 성토끝 박스는 두 세트
모두 4/4 — 앞서 막혀 있던 149.73 우측 날개 1장도 뿌리 이동으로 풀렸다.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
날개벽 링은 노선이 아니라 날개 축을 탄다. 그래서 링 폴리곤의 편거리가 노선
편거리가 아니라 패널 두께(±0.10m)라 노견 클립 max(−0.10, +2)에 걸려 날개벽
투영 고리가 0개였다. 실측: 링 좌향과 노선 좌향 정렬 align=0.71, 링 중심의 노선
편거리 2.74~4.16(좌)·−4.01~−2.60(우) — 이미 노견 밖이다.
- `CorridorStructure.wing` 표식 신설, `_Structures_Wing.ts`가 붙인다.
- 날개벽은 도로부 클립을 생략하고 링 위치로 측을 가린다 — 판 두께 × 날개 길이
직사각형이 나온다.
- `wing-box` 커브 신설(주황) — 밑변은 날개 시작~끝 모서리, 양 옆선은 구조물 축
(측점 좌향 벡터)과 평행한 고정 직선, 바깥 변은 원본 성토 최하단 끝선 그대로.
노선 곡률을 따라 각도가 바뀌지 않는다(사용자 확정).
- 성토 끝선은 세트 ±40m만 잘라 쓰고 사거리 15m로 조인다 — 안 조이면 헤어핀
건너편 끝선에 걸려 −29m짜리 박스(점 115개)가 나온다.
- 교점 탐색은 광선이 아니라 양방향 직선이다 — 세월교 날개는 끝이 성토 끝선
밖이라(4.16 vs 2.60) 바깥으로만 쏘면 통째로 빠진다.
- `BUILD_VERSION` 32 → 33.
실측: 판 고리가 측당 2장씩 n=11로 살아났다. 박스는 200.92 4/4, 149.73 3/4.
박스 바깥 변의 점은 전부 성토 끝선 위(이탈 0.000m). 149.73 우측 날개 1장은
그 자리에 끝선이 없어 안 그린다 — 처리 방침은 사용자 확정 대기.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
화면이 안 바뀐 원인은 저장본 캐시였다. `BUILD_VERSION`을 안 올려 코리도 해시가
그대로였고 서버 저장본(planCurves 20개)이 재사용됐다. 실측으로 확인 — 저장본
hash 95cafe53 vs 새 코드 hash 0e67c470. 31 → 32로 올려 만료시킨다.
그리고 겹친 부재를 하나로 머지하지 않는다(사용자: "구조물은 머지하지 말고
표현할 것, 나중에 한번에 진행 예정"). 링 경로 그룹핑·구간 병합을 빼고 부재마다
고리 하나를 낸다.
배관은 투영 면적에서 계속 뺀다 — 다른 부재가 감싸고 있는 구조라는 사용자 확정.
실측(링 횡단선 되투영, 이탈 최대 0.0000m): 84.3 고리 4개 — 집수정 상·하단
2.000~2.650 각 31점, 기슭막이 상단 −2.946~−2.000·하단 −3.250~−2.000 각 47점.
149.73 8개, 200.92 10개(날개벽 4장 각각), 275.71 4개.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
기하 합집합(`_Corridor_Union.ts`)이 겹친 스트립에서 무너졌다. "같은 토막이
짝수 번 나오면 버린다"는 내부 칸막이 제거 규칙이 **외곽의 공유 변까지** 지웠다.
4+4.3(84.3m)에서 집수정 두 단은 탑뷰 사각형이 완전히 같아 네 변 전부 소멸해
좌측 커브가 0개였고, 기슭막이 두 단은 안쪽 변 −1.98을 공유해 23칸이 소멸,
0.34×0.92m 쪼가리만 남았다.
합집합을 버리고 구조물이 스윕 솔리드라는 성질을 그대로 쓴다 — 링 경로가 같은
부재끼리 묶고, 링마다 편거리 창을 도로 밖으로 자른 뒤 1차원 병합해 레인으로
잇는다. 기하 불리언이 없어 퇴화 케이스가 원리적으로 없다.
- `_Corridor_Plan_Footprint.ts` 신규 — 위 알고리즘.
- `_Corridor_Plan.ts` — `structureLoops` 제거, 신규 모듈 호출.
- `_Corridor_Carve.ts` — 죽은 `outlineLoops()` 제거(`outerAt`은 유지).
- `_Corridor_Union.ts` — 죽은 `unionOutline`·`insideQuad`·`splitEdges` 제거.
`UnionQuad`/`unionQuadOf`는 `outerAt`이 계속 쓴다. 189 → 34줄.
실측: 커브 각 점을 가장 가까운 링 횡단선에 되투영해 편거리를 읽었다 — 링선
이탈 최대 0.0000m. 84.3 좌(집수정) 2.000~2.650 31점, 우(기슭막이) −3.250~−2.000
47점으로 전 링이 살아난다. 275.71·149.73·200.92 세트도 날개벽까지 각각 나온다.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
실루엣은 소유 측점 단면 한 장이라 그대로 밀면 구간 내내 같은 폭이 됐다.
0.5m 행마다 종단경사만큼 올린 뒤 `trimSlopeToGround`로 그 행 지반과 만나는
자리까지 잘라 실제 발끝을 따라가게 한다. 표기 커브만 바뀐다 — 서피스 절단·
패치는 손대지 않았다(`CARVE_APPLY` 계속 false, 사용자 지시).
4+4.3 우측 실측: 바깥끝 offset −3.25 고정 → −2.96~−4.42(지형 따라), 행 11 → 21.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
구조물 추가 뒤 3D 투영 영역이 제대로 안 만들어지던 문제. 평면고가 구조물과
무관한 모델 전체 최고표고였고, 구조물 커브와 비탈 커브가 다른 기준으로 따로
나왔으며, 성토면 표면 위에도 절단 투영선이 겹쳐 그려져 이중 표기였다.
- `_Corridor_Plan.ts` 신규 — 구조물 세트(같은 측점 소유)별로 투영 평면을
그 세트 구조물 최고표고 +5m에 잡고, 유입/유출을 나눠 커브를 만든다.
세트당 구조물·비탈(변형 실루엣)·비탈(원본 성토선) × 유입/유출.
도로부(노견 안쪽)는 전 커브에서 빼 좌우가 끊긴 두 루프가 된다.
- `_Corridor_Carve.ts` — 투영 전용 함수(풋프린트 평면 투영·유입유출 판정·
절단 트레이스 조립) 삭제, `ringEdges` 공개. 패치 리본 경로만 남겼다.
- `_Corridor_Build.ts` — 성토면 위 절단 투영선(`carveTraces`) 생성 삭제,
`structurePlan`/`fillPlan` → `planCurves` 하나로 교체.
- `_Corridor_Mesh.ts` — 표면 위 투영선 그리기 삭제. 커브를 각자 평면고에
종류별 색(구조물 노랑/변형 분홍/원본 하늘), 유입 실선·유출 파선으로 그린다.
- `_Corridor.ts` — 저장 코리도 봉투에 `planCurves` 포함, 형식 버전 1→2.
안 담으면 저장본으로 복원한 코리도에서 커브가 통째로 사라진다.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
날개벽 시작점을 측점 중앙 프레임으로 잡아 곡선 구간에서 구체 끝과 0.15m 어긋났다.
그 모서리 링의 프레임으로 잡아 정확히 붙인다.
또 에이프런이 평면에서 직사각형이라 각도만큼 벌어진 날개벽 발치가 바닥 밖으로
나갔다. 날개벽 선을 따라 안쪽 변이 물러나는 **사다리꼴**로 바꿔 발치를 받는다
(2026-08-25 사용자 지적).
검증(10+0.9): 날개벽 4매 모두 구체 모서리와 거리 0.000m,
에이프런 링별 안쪽 변 [4.158, 2.744, 2.744, 4.158] — 바깥 링에서 폭 0으로 닫힌다.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
사용자 확정 모델: 초기 계산값은 **원복용으로 그대로 두고**, 사용자가 제어한 수정
1세트가 최종본이다. 여러 세트는 두지 않는다. 두 곳이 이 모델을 어기고 있었다.
① 지운 구조물이 되살아난다 — B05는 그려질 때마다 종단 정본의 비정규 측점을 모아
`/structures/migrate`를 부른다. 서버의 "멱등" 기준이 **지금 그 자리에 구조물이
있는가**여서, 사용자가 지우면 자리가 비고 다음 진입에서 같은 구조물이 다시
생성됐다(진행단계 오버레이로 오가면 매번). 옮긴 자리를 `structures.json`의
`migrated_legacy`에 **이력으로** 남기고, 이력에 있으면 구조물이 없어도 다시 만들지
않는다. 원천(비정규 측점)은 원복용으로 손대지 않는다. 이력은 일반 저장 경로에서도
보존한다 — 사라지면 삭제분이 부활한다.
② 임시저장이 캐시 수정분을 버린다 — B05 [임시저장]이 `cross_patches`를 보내지 않고
`invalidateSectionDetail`로 공유 캐시를 비웠다. B06에서 만져 캐시에 얹힌 구조물
조정(4축·다단·연동·표시 반폭)이 영구저장소에 못 가고 사라졌다. 계획선·비정규 측점
저장 **뒤에** 캐시 수정분을 `saveSections`로 남기고, 그 다음에 캐시를 비운다 —
순서가 뒤바뀌면 재계산이 사용자 수정을 덮는다.
`saveCachedCrossPatches`·`crossPatchesFromCache`는 공유 캐시 모듈에 뒀다 — B05 페이지
파일이 이미 700줄을 넘겨 더 불리지 않기 위함이다(기존 부채).
tsc/ruff/prettier 통과, pytest 200 passed(기존 실패 1건 유지).
신규 검증: tmp/tests/test_b05_structures_migration_history.py 4건.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
사용자 보고: 기슭막이 위치(본·연동)를 바꿔도, 추가(다단) 기슭막이를 세워도 3D가
바뀌지 않는다. 형상은 성토선 > 본 기슭막이 > 추가 성토선 > 추가 기슭막이 순이어야
한다. 원인이 둘이었다.
① 조작값이 캐시에 안 실렸다 — 사용자 확정 흐름은 "영구저장소 → 브라우저 캐시 →
3D는 캐시로 플롯, 편집하면 캐시를 갱신하고 그 캐시로 다시 플롯"이다. 그런데
조정창 조작값(4축·다단 단 수·유입 구조물·집수정·연동 플래그)은 sessionStorage에만
있고 확정·임시저장 때만 design에 실렸다. 이제 편집 즉시 `section.design`(B05·B06이
공유하는 같은 객체)에 함께 기록한다. 세션은 리로드 복원용 사본으로 남는다.
② 기하 캐시가 낡았다 — `culvertLayoutOf`의 WeakMap이 측점 객체만 키로 써서, 캐시를
제자리 고치면 옛 기하가 그대로 살아남는다. 입력 요약 해시를 함께 담아 무효화한다.
③ 비탈 실루엣이 순서를 잃었다 — 수집한 점을 편거리로 정렬한 뒤 재추출했다. 기슭막이
앞면이 거의 수직이라 편거리 정렬은 순서를 보장하지 못하고, 다단 계단은 안팎을 오가
뒤섞인다. 유출측은 수집 순서를 그대로 잇고, 추가 성토선과 추가 기슭막이를 번갈아
밀어 계단 순서를 맞춘다. 유입측(집수정·절토)은 범위 밖이라 종전 정렬 유지.
④ 성토 조각 해상도 20 → 48열 — 호길이 균등 재추출이 수직면을 뭉갠다. 열을 꼭짓점에
맞춰 배분하면 측점마다 열 수가 달라 리본이 꼬이므로, 파라미터화는 그대로 두고
해상도로 해결했다. 절토는 20 유지(다단이 붙지 않는다).
성토 물매 불변: 이번 수정은 trimSlope·outletFill 점을 만들지 않는다 — 잇는 순서와
재추출 열 수만 바꿨고, 물매 산출 경로(layout.fillSlope.ratio)는 손대지 않았다.
BUILD_VERSION 17 → 18(저장 코리도 만료).
tsc/prettier 통과, pytest 196 passed(기존 실패 1건 유지).
작업이력·자체검증 기록: docs/raw/PLAN.md §4-10.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
기슭막이 범위(길이·기준측점 전/후)를 B06 횡단도 조정창에서 지정하게 하고, 그
범위가 덮는 옆 측점의 기슭막이를 "연동" 파생물로 다루도록 체계를 세웠다.
값의 원천은 배수관 옵션(pipe_points) 하나다 — 같은 숫자를 B06 정본에 따로 담으면
B05 배수관 카드와 갈려 어느 쪽이 참인지 알 수 없다.
① 집수정 전/후 필드 신설 — inlet_basin_before_m·after_m(기본 각 1m), 길이 2m 유지.
종방향 구간 계산을 revetSpanOfSpec·basinSpanOfSpec 한 곳으로 모아 2D 링크 판정과
3D 스윕이 같은 값을 쓰게 했다.
② B06 조정창에 길이·전·후 행 — 기슭막이(유입·유출)·집수정 모두. 캐시를 고쳐 즉시
보여주고 저장은 조작이 멎으면 한 번만 나간다(세부유역 재계산이 매번 돌지 않게).
길이를 바꾸면 늘어난 몫만 지금 비율대로 나눠 담는다 — 총길이에서 비율로 다시
계산하면 0.1m 반올림이 쌓여 5.0/5.0이 5.6/5.4로 어긋난다.
③ 링크 카드 선택 허용 — "소유 측점 통째 복사"에서 "위치 4축만 물려받고 원지반
접합은 이 측점 지형으로 재계산"으로 바꿨다. 구조물 형식은 언제나 소유 측점 값이다.
손대기 전에도 어긋나지 않게 소유 측점이 실제로 적용한 4축(revetShift)을 명시값으로
물려준다.
④ 연동(측점별)·경사(전체 공통) 토글 — 연동 조작은 소유 측점 키에 실리고 그 연장이
덮는 측점을 함께 다시 그린다. 경사는 3D 전용 스위치다(끄면 스윕 프레임 dz=0).
횡단도까지 -dz로 밀면 노견에서 출발하는 성토선까지 내려가 노견과 벌어진다.
⑤ 가림 구간 선 정리 — 유입부 접속선과 유출부 접지 후 절토선을 2D·3D 모두에서
빼되 기하는 남긴다. 면적·수량 계산은 건드리지 않았다(전면 개편 대상).
⑥ 집수정은 소유 측점 횡단도 하나에만 — 링크 카드에서 집수정·계류측 다단을 뺀다.
⑦ 3D 구간 겹침 정리 — 측점 간격이 구조물 연장보다 좁으면 두 구간 제어점이 번갈아
서서 짧은 쪽(집수정 2m)이 잘려 보였다. 두 소유 측점의 중간에서 끊는다.
⑧ 조정창 정리 — 십자·9키를 맨 아래로, 창을 위쪽 기준 오버레이로(낮은 횡단도에서
제목이 잘리던 문제), 연동·경사를 1행 토글 버튼으로, 스크롤 자리 유지.
파일 분리: B06_Section_UI_Cross_View_Structure.ts(조정창 배선),
B06_Section_Api_Culvert_Options.ts(구간값 저장) — 700줄 제한.
BUILD_VERSION 14 → 17(저장 코리도 만료).
tsc/ruff/prettier 통과, pytest 196 passed(기존 실패 1건 유지).
작업이력·자체검증 기록: docs/raw/PLAN.md §4-9.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
3D 예상형상은 종단·횡단 확정 뒤의 최종 산출물이고 3D 쪽 편집 경로는 없다
(2026-08-24 사용자 확정). 그런데 구조물(비정규) 측점은 횡단 정본 자체가 없어
조회할 때마다 프리뷰 기본값이 재계산됐고, 조정창 조작값은 세션에서 사라졌다.
정본 경로를 열고, 그 위에서 코리도 단차를 걷어냈다.
① 구조물 측점 정본화 — 확정·임시저장이 종단 정본 stations 중 DB 행이 없는
측점을 대상에 넣어 upsert. cross_sections 19 → 23행.
② 조정창 조작값 정본화 — 기슭막이 4축(x·d·h·m)·다단 단 수를 design.revet_adjust
/ extra_wall_counts로 저장하고, 3D culvertLayoutOf가 그 값을 기하 입력으로 쓴다.
카드 우선순위는 세션 → 정본 → 자동.
③ 노폭 연장의 노견 편입 — PIECE_COLS.shoulder 2→3으로 원 노견 끝 꺾임을 남기고,
조기 return을 없애 확폭이 구조물 구간 내내 유지되게 했다.
④ 종단 기울기 반영 — 구조물 구간 복사 단면에 계획고 차이를 전량 얹는다.
노견 표고 단차 0.49~0.61m 제거.
⑤ 접속 테이퍼 — 구간 경계를 제어점으로 세워 "복사 vs 보간" 두 규칙을 하나로
통합. 노폭 3.0m·성토비탈 8.1m 단차 제거. 접속 길이 = 인접 측점까지.
제어점 생성은 B05_Profile_UI_Corridor_Frames.ts로 분리(Build 699 → 635줄).
⑥ 인접 측점 링크 — 기슭막이 연장(기본 10m)이 덮는 옆 측점 횡단도에도 소유 측점
단면을 계획고 차이만 얹어 그린다. 요소별 연장 판정(관·집수정 제외), 표시 전용.
BUILD_VERSION 11 → 14 (저장 코리도 만료).
tsc/ruff/prettier 통과, pytest 192 passed (기존 실패 1건 유지).
작업이력·자체검증 기록: docs/raw/PLAN.md §4-8.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
B06 횡단(computeCulvertLayout)을 그대로 재사용해 3D 코리도에 기슭막이·집수정·
배관을 세우고, 그 구간 성토/절토 단면을 횡단도와 같은 형상으로 다시 그린다.
구조물 솔리드(_Corridor_Structures.ts 신규)
- 기슭막이: 단면 폴리곤을 노선 따라 1m 간격 로프트 스윕(직선 압출 아님)
- 집수정: 부재 외곽 직육면체, 연장 basin_length_m(기본 2m) 전후 균등
- 배관: 관 하단선 축 원통(관경 지름)
- 스윕 프레임에 계획고 차(dz)를 실어 종단 경사 반영
- flat 법선 + EdgesGeometry 모서리 검은선(성토부·노폭 리본과 같은 규칙)
단면 재구성(_Corridor_Station.ts)
- 구조물 측 비탈 = 노견 → (노폭 연장) → 구조물 상단 → 전면 → 벽 하단 성토부선
→ 다단 기슭막이 → 지반/절토선 끝 한 줄(PIECE_COLS cut·fill 9 -> 20)
- 성토로 늘어난 노폭은 비탈이 아니라 노견 리본을 넓힌다
- 집수정 계류측 cutLine까지 이어 원지반을 그만큼 걷어낸다
구간 처리(_Corridor_Build.ts)
- 구조물 점유 구간(기준측점 전/후)이 다음 측점을 넘어가도 같은 단면 유지
- 그 구간은 지반 트림 생략, 비탈 앵커를 노견 조각 바깥 끝으로 통일
길이·전후 입력
- 레지스트리 pipe 옵션 추가: inlet/outlet_revet_before_m·after_m(5/5),
inlet_basin_length_m(2)
- B05 배수관 서브폼에 기준측점 전/후 칸(전+후=길이 연동)·집수정 길이 칸
- _side_spec이 전/후·집수정 길이를 CulvertSideSpec으로 전달
비정규 측점 프레임 정정(_Page_Helpers.ts)
- left_xy 선형보간(단위길이 깨짐·방향이 현 쪽으로 누움) -> 노선 폴리라인 접선에서
직접 산출(left = (-ty, tx)), 폴백은 각도 보간
BUILD_VERSION 10 — 저장본 자동 만료
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-23 사용자 보고: 종단 그래프의 계획고 변화가 B06 횡단도에 반영 안 됨.
원인: 횡단 설계는 계산 당시 계획고(design.design_elevation_m) 기준으로 설계선
좌표를 굳혀 둔다. 계획선이 뒤에 바뀌면(사용자 편집·확정, 배수 최소고 같은
백엔드 규칙 도입) 설계선은 옛 자리에 남는데, 화면의 계획고 십자선과 3D
예상형상은 최신 계획선을 쓴다 - 두 기준이 어긋난다. 실측으로 한 노선에서
최대 2.21m 어긋남 확인. 지금까지 이 보정은 B06 진입 때만 돌아서, B05의 횡단
기준 유토곡선과 3D는 옛 설계선을 계속 썼다.
- staleDesignChainages() 신설(B06_Section_UI_Section_Common) - 계획선 보간값과
계산 기준의 차이로 재계산 대상을 판정하는 B05·B06 공용 규칙.
- B05 Profile_Panel: 진입 렌더에서 어긋남이 있으면 프리뷰 1회로 맞춘다.
- B06 Page: 같은 공용 규칙을 쓰도록 통일(옛 암 2단계 필드 누락 조건은 유지).
검증: tsc 통과 · pytest 191 passed(신규 stale 판정 6건) · 공용 브라우저 실측 -
계획고·지반고 배치 대조(전 측점 일치, 렌더 역산 RMS 0.004~0.03m), 측점 중심
좌표가 노선 폴리라인과 거리 0, 인위 stale 1건을 B05 진입으로 자동 복구(stale
1 to 0, 계획고 540.895 복원).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-23 사용자 개념 확정 반영.
- 틸트는 그 지점을 라운드로 주변 직선과 잇는 조작이고, 측점·횡단배수 지점의
수직선에는 라운드 중심점이 놓인다. 따라서 최소고 판정도 제어점 z 기준이어야
한다(controlElevationAt) - 곡선 샘플 계획고로 재던 탓에 배수 지점 옆을
틸팅해 라운드 형상만 바뀌어도 락이 걸렸다.
- 쉬프트 재정의(shiftMovingPoints): 구간 끝이 사용자 틸팅점이면 그 점이 직접
움직이고, 고정점(BP·EP·횡단배수 앵커)이면 움직이지 않는다. 대신 구간 안쪽
미틸트 측점의 외곽 2개를 힌지로 승격해 라운드를 만들고 그 사이 직선을
평행이동한다. 힌지 둘을 못 만드는 구간(안쪽 측점 2개 미만)은 쉬프트 대상이
아니므로 ⬆⬇ 버튼을 만들지 않는다(canShift).
- shiftSegment 되먹임 보정 - 라운드 중앙종거가 낀 자리에서도 화면 계획고가
정확히 1스텝 움직인다.
검증: tsc 통과 · pytest 185 passed(힌지 규칙 4건 신규) · 공용 브라우저 실측 -
구간 0~84 ⬆에서 힌지(20·80)만 이동하고 BP·앵커 오프셋 미생성, 두 힌지 계획고
정확히 +0.1000m(구배 보존), 앵커 이웃 80.0m ▼ 차단 해제, 앵커 84.3m ▼ 차단 유지.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-23 사용자 보고: 횡단배수 최소고를 어기고 구간 쉬프트가 되는 경우
있음(가드가 측점 ▼에만, 관경 고정 산식).
- 가드 개편(blocksMinCover): 편집 후보로 정렬을 계산해 시설별 최소고
(minCoverPoints 원천 - 배수관 관경+토피, BOX암거 구체높이+토피, 세월교
+물넘이, 물넘이포장 제외) 위반이 새로 생기거나 커지면 차단. 측점 ▲▼와
구간 ⇧⇩ 모두 적용, 이웃 틸트가 종단곡선을 거쳐 배관 계획고를 내리는
경로까지 커버. 이미 위반이면 악화만 차단(복구 편집 허용). 기존
blocksPipeLowering(측점 ▼ 전용·관경 1000 고정)은 제거.
- shiftSegment 시딩 수정: 초기 오프셋을 곡선 샘플 계획고(PVI z - 중앙종거)
대신 변화점 실제 z로 계산 - 첫 이동에서 변화점이 내려앉아 상승 후 하강
복귀가 비대칭이던 화면 실측 버그 해소.
- MinCover 낡은 주석 정정(구현은 세월교 포함, 물넘이포장만 제외).
검증: tsc 통과 · pytest 182 passed(가드·시딩 신규 8건) · 공용 브라우저
실조작 - 배수관 앵커 구간 ⬇ 즉시 차단(+1.5), BOX암거 구간 차단(+2.5),
⬆/⬇ 대칭 복귀, 검증 상태 전부 원복.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-23 사용자 지시 3건:
- 절성토 완전 분리: 전환 구간을 상대 측점의 폭 0 축퇴점으로 모핑하던 방식
제거. 비탈을 측구와 같은 도로부 앵커 상대 좌표로 보간하고, 행마다 두 측점
지반선을 섞은 행 지반선과 직접 교차시켜 종결(trimSlopeToGround). 지반이
반대편인 행은 조각 드롭 - 절토가 지반으로 내려가거나 서피스끼리 침범하던
문제 해소. 클리핑 외곽도 행 실점유(비탈-측구-노견)로 일치.
700줄 제한: 측점 분류·트림 유틸을 Corridor_Station.ts로 분리(Build 494줄).
- 그랩 팬: 가운데 드래그를 커서 아래 지형점이 1:1로 따라오는 팬으로 직접
구현(B04_PreProcess_UI_Camera). OrbitControls 기본 PAN은 카메라-target
거리 비례라 커서 피벗 회전·줌 후 극단적으로 느려졌다. B04 뷰어도 공용.
- 측점 그룹 갱신: Profile_Panel이 라이브 alignment.samples 노출, Page의
측점 바 계획고·코리도 designSamples가 그걸 사용, 종단 편집 디바운스에서
renderStationLines 동반 호출. BUILD_VERSION 4-5로 저장본 만료.
검증: tsc 통과 · pytest 175 passed(신규 separation 8건) · 사용자 화면 확인.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
측점 사이 어딘가에서 계획선이 지면선을 통과한다 - 그 자리가 절토+측구의 종점이고
거기서부터 성토로 넘어간다(2026-08-23 사용자 확정). 임시로 쓰던 "측점 사이 중간에서
끊기"를 이 계산으로 대체한다.
- buildCrossingFinder(): 종단 계획선 샘플의 (계획고 - 지반고) 부호가 뒤집히는 구간을
선형보간해 전환 누가거리를 낸다. 구간 안쪽 교차만 인정 - BP·EP는 정의상 지반과
만나므로 경계의 0을 거르지 않으면 측구·절토가 0.4m 만에 끝난다(실측으로 확인)
- 측구는 그 지점까지 그리고 마감벽으로 닫는다(위치는 계속 노견을 따라간다)
- 절·성토는 전환점에서 폭 0이 되도록 보간 구간을 다시 잡는다. 구간 전체에 걸쳐
서서히 사라지던 것을 실제 절토가 끝나고 성토가 시작되는 자리에 맞춘다
- 전환점을 못 찾으면(전 구간 절토 또는 전 구간 성토) 종전대로 중간에서 나눈다
측구 형식(일반/L형)은 3D에 이미 반영되고 있음을 확인했다 - 폭은 형식별 필드
(top_width_m / width_m)를 구분해 쓰고, 단면 형상은 엔진이 만든 design_line을
잘라오므로 사다리꼴과 L형이 그대로 올라온다.
BUILD_VERSION 4로 올려 옛 저장본을 만료시킨다.
검증: 실측 측구·절토 유효구간 0.00~2.80m(계산 교차점 2.83m, 0.2m 세분 격자),
이전 0.00~10.00m. pytest 167 passed(전환점 경계·측구 형식 5건 신규), tsc 통과
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
측구가 노견에서 떨어지던 문제
- 측구는 있는 측점과 없는 측점이 갈려 절대 좌표로 따로 보간되고 있었다. 도로 끝은
다음 측점을 향해 내려가는데 측구만 제자리에 남아, 측점에서 멀어질수록 벌어졌다
(실측 ch2.0 0.18m / ch5.0 0.46m / ch9.8 0.90m - 측구 폭 0.69m보다 큰 틈)
- 측구 조각을 road edge 기준 상대 좌표로 들고 있다가 프레임의 도로 끝에 얹는다.
"측점 사이 중간에서 끊기" 규칙은 표시 구간만 자르고 위치는 항상 노견을 따라간다
- 비탈 안쪽 끝도 그 프레임의 도로부 바깥 끝(측구 끝 또는 노견 끝)에 스냅.
종단 보정분을 얹어 노면과 높이를 맞춘다
- 실측: 차도-노견, 노견-측구, 측구-절토 경계가 전부 0.0000 m.
측구 단면(폭 0.690 / 깊이 0.300)은 구간 전체에서 유지
저장본 캐시 만료
- 해시가 종횡단 입력만 요약해, 빌드 코드를 고쳐도 입력이 같으면 옛 저장본을 그대로
다시 썼다(사용자 보고: 수정이 반영 안 되다 갑자기 반영됨). BUILD_VERSION을 해시에
섞어 기하를 바꾸는 수정마다 저장본이 만료되게 한다
검증: pytest 162 passed, tsc 통과, 화면 실측
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
steps 계산의 Math.max(0.5, step)가 사용자가 지정한 0.1m 세분을 통째로
무시하고 있었다. 저장본이 702행(=350m/0.5m)으로 남아 있어 확인했다.
하한을 0으로 나누는 것만 막는 값(0.01)으로 낮춘다.
검증: 저장본 3,503행 · 평균 간격 0.100m 확인, 줌인 시 곡선 구간이
매끄럽게 이어짐. 편집 시 최대 프레임 갭 348ms(200ms 초과 1회)
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
세월교는 배관을 여러 개 묶어 다리 형태로 만든 것이라 배수관과 같은 산식을 쓰되,
그 위에 물넘이가 얹히므로 0.5m를 더 얹는다(2026-08-23 사용자 확정).
물넘이포장은 도로 위에 그대로 만드는 시설이라 들어 올릴 이유가 없다.
- 세월교 Ø1000 → 1.0 + 0.5(토피) + 0.5(물넘이) = 2.0
- 물넘이포장 → 0.0
- 앞서 쓰던 월류 높이(ford_height_m) 기준은 폐기
검증: 계획선 재생성 실측 — 세월교 지점 계획고-지반고 2.00 일치
(배수관 1.50/1.30, BOX 2.50도 유지). pytest 6/6, tsc 통과
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
클리핑 정상화 (절토가 원지반에 묻혀 안 보이던 원인)
- 정점 버퍼를 count 길이로 잘라 복사 — 'offset is out of bounds'로 클리핑
전체가 죽어 원지반이 하나도 안 잘리고 있었다(삼각형 485,358 → 477,239 확인)
- 정점색을 원본 배열 타입·normalized 그대로 복사(GLB는 정규화 Uint8 RGBA)
- 원본에 법선이 없으면 클리핑본도 만들지 않는다 — 직접 계산하면 감기 방향이
원본과 달라 지형이 새까맣게 렌더됐다
표현 규칙
- 측구가 한쪽 측점에만 있으면 측점 사이 중간에서 끊는다(사용자 확정).
폭 0 축퇴로 잇던 방식을 걷어내 없는 자리의 서피스·윤곽선 제거
- 리본을 조각이 존재하는 연속 구간별로 분리 생성
- 종단 세분 2m → 1m, 측점 사이 표고를 종단 계획선(종단곡선 포함) 기준으로
보정 — 도로·측구는 전량, 비탈은 지반 접점까지 감쇠
- 측점 가로선을 예상형상 표시 중에는 계획고 위로 올림
- 지형 기본 표시를 흑백으로
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
- Corridor_Build: design_line을 차도/노견/측구/절토/성토 5종으로 분류,
catch point(설계선-지반 교차)로 점유 폭 산출, 노선 폴리라인 2m 세분
중간 프레임 보간 (계산식 재구현 없이 백엔드 design_line 소비만)
- Corridor_Mesh: 종류별 색 리본 BufferGeometry 조합(멀티 스윕)
- Corridor_Clip: catch line 스트립으로 지형 삼각형 경계 재절단(셀 단위
판정·정점색 보간), 원본 보존 — [예상형상] 토글 OFF 시 원지반 완전체 복원
- Viewer: 원본/클리핑 지형 두 벌 스왑, 비탈 최외곽 z 지형 투영(심 보정),
클립 rAF 비동기, 흑백·dispose 연동
- Panel: [예상형상] 토글 (기본 ON)
- Corridor 엔트리: 설계 입력 FNV-1a 해시 버전, base64 Float32 JSON 직렬화,
최초 빌드 즉시 저장 + 임시저장·페이지 이동 시 dirty 저장, SPA 메모리 캐시
- Router_Corridor(신규)+main 등록: GET/PUT corridor 파일 보관
(B05_Profile/corridor/, atomic, 64MB 상한)
- 검증: tsc·ruff·pytest 5/5, 공용 브라우저 실조작(토글·B06 변경 왕복·
저장 왕복 네트워크 실측), 저장본 리본 수치 대조
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
- 종단 상단 표시줄을 법정 판정 3항목으로 축소: 최대 기울기/상한(별표2 Ⅰ.2.라),
절·성토 불균형(Ⅰ.1.나.(4)(다)), 종단곡선 필요 n곳(Ⅰ.2.마 — 대수차 5% 초과인데
곡선이 빠진 변화점). 변화점·종단곡선 개수·기본 곡선길이 L·중복 경고칩은 제거.
- [초기선 복원] → [편집 되돌리기] — 실제 동작은 화면 편집 델타 삭제이지 저장 지점
복귀가 아니다. 툴팁에 동작과 [초기화]와의 차이를 적었다.
- 3D 뷰포트 안내 라벨 삭제(로딩 완료 후 남던 조작법 문구).
- 배수유역 버튼 정비: 유역 다시 나누기 / 선택한 관 삭제 / 배관 배치 초기화 +
각 툴팁에 실제 동작 명시(선택 삭제는 툴팁 자체가 없었다).
- 유토곡선 요약 12칩 → 5칩(절토·성토·잉여|부족·운반·검산). 토질 3종 내역·다짐
환산·블록 수·장거리 운반·사토는 해당 칩 툴팁으로. B05·B06 공용 함수라 동시 반영.
- 설계속도 축 도입(임도는 속도를 낼 수 없는 노선 — 기본 20km/h):
· 임도 종류는 프로젝트 등록값(projects.road_type)을 읽어 B05가 읽기 전용 표시
(main→간선임도, fire→산불진화임도, 그 외→작업임도). 화면에서 고치지 않는다.
· 설계속도 선택(간선·산불진화 20/30/40, 작업 20 고정) 신설 — 종단기울기 상한과
종단곡선 반경이 등급이 아니라 이 값으로 정해진다(지식DB 설계제원_총괄 §6·§7).
· B02 임도 종류 선택지를 현행 3종으로 정리(지선 폐지·계류보전은 사방 구분).
· 백엔드: ROUTE_GRADE_CLASSES 3종+branch 호환, selectable_design_speeds 신설,
resolve_design_speed 신설, legal_grade_limit_pct·resolve_grade_options에
설계속도 인자, sections/context가 road_type 제공(B06도 저장값 승계).
· 계획선 정책은 그릴 때마다 현재 기준으로 동기화 — 저장분의 옛 상한이 남지 않는다.
- 검증: pytest 125 통과(설계속도 12건 신규), tsc, 헤드 브라우저 — 진입 시 상한 9%,
40km/h 전환 시 7% 즉시 반영, 원복 9%, 안내문 '간선임도 · 설계속도 20km/h ·
일반지형', 유토곡선 5칩, 유역 버튼 새 이름·툴팁, 3D 라벨 빈 문자열 확인.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- 기슭막이는 기슭(사면)을 막는 공정이라 그 아래 돌붙임이 붙을 자리가 아니다
(2026-08-19 사용자 지시). 배관 유입·유출부와 독립 기슭막이에서 보호공
(돌붙임 찰/메·도수로)·보호공 면적·도수로 폭 옵션을 전면 삭제.
- 바닥막이(bed_sill) 부활 — D군 점형, 형태(돌붙임 찰/메)·면적·높이
(기본 메·10㎡·0.3m). 바닥 보호는 이 구조물이 담당한다.
- 독립 기슭막이(D4)를 시설 서브폼에서 떼어 C군과 같은 레지스트리 옵션 방식으로
전환: 측점 범위 + 기준측점 전/후, 기본 돌쌓기(메)·높이 2.5·길이 10·전 5·후 5.
- 페이지 설정의 지형 구분·등급·최대 종단기울기를 바꾸면 안내문·placeholder와
종단 그래프 위반 판정이 즉시 갱신된다(profilePanel.setGradeLimit 신설).
계획선 자체 재산출은 서버 재계산 몫이라 건드리지 않는다.
- 지식DB 재점검(설계제원_총괄 §4·§6·§7): 종단기울기·종단곡선 반경 표는 일치.
미해결 3건(폐지된 "지선" 선택지·등급↔설계속도 고정 매핑·최소 곡선반경 안내가
종단곡선 반경)은 값을 임의로 바꾸지 않고 주석으로 남겼다 — 사용자 협의 대기.
- 검증: pytest 113 통과, tsc, 헤드 브라우저(D군 목록 바닥막이, 기슭막이 범위+
전/후·보호공 없음, 배관 서브폼 보호공 라벨 소멸, 지형 변경 7%→10%·위반 재판정).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- 위치 마지막 칸 Tab → 상세 옵션 첫 칸(잠금 해제보다 탭 계산이 빨라 메모로
건너뛰던 문제). 화면 순서(위→아래) 그대로 이동.
- [초기선 복원] → "저장선 복원"으로 문구 교정 — 복원 목표는 마지막 저장선이다
(임시저장 시 편집이 정본에 반영). 최초 자동 계산 복귀는 [초기화] 안내.
- 종단 알약에 좌측 목록과 같은 중복 번호("옹벽 1/2") — 라벨·툴팁·벌룬 동일 표기.
- [초기화] 무동작 수정: 원인은 playwright 연결 브라우저의 네이티브 confirm 자동
취소. 공용 showConfirmDialog 모달(ui_template_elements) 신설로 교체.
- C군 옵션: 전길이 → "기준측점 전" + "기준측점 후" 신설(기본 10/5/5). 나중에
입력한 쪽이 살아남고 반대쪽 = 길이 − 그 값. 저장은 before_m 기준(후는 파생).
- B06 횡단 반폭 표시 기본 20 → 12 (계산·보유 샘플 폭은 백엔드 20m 유지 — 표시
범위만 좁힘, 저장·세션값 있으면 그 값 우선).
- 측점 범위 표기 폰트 = 기준 측점 입력 칸과 동일(13.33px), 볼드 유지.
- 검증: pytest 112 통과, tsc, 헤드 브라우저 — Tab 포커스 '높이', 전7→후3·후2→전8
연동, 범위 2+12.0~3+2.0, 알약 '옹벽 1/2', 초기화 모달 표시, 저장 왕복·원복.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- C군 기본값 확정(2026-08-19 사용자): 높이 2.5·길이 10·전길이 5 — 기본값이 생겨
required 해제. 기준 측점만 넣으면 범위가 바로 계산된다.
- 드래그 민감도 완화: 종단 알약(누적→변위 14px, 임계 전 시각 이동 금지)·배수유역도
관 마커(이동 시작 8px 임계) — 고르려다 옮겨지는 문제.
- 3D 노선 우클릭 → 구조물 배치 메뉴(신규 B05_Profile_UI_Viewer_Menu) — 지형 픽을
노선 폴리라인에 투영(15m 이내), 그래프·배수유역도와 같은 2단 메뉴·addAt 경로.
- 우클릭 메뉴 전역 단일화(ui_template_context_menu): 새 메뉴가 열리면 다른 영역
메뉴는 닫히고, 좌클릭·중간버튼은 어디서든 닫는다(캡처 단계).
- 3D 비정규 측점(배관 등) 좌표를 노선 폴리라인 chainage 투영으로 정확화 — 규칙
측점 직선보간의 곡선 모서리 잘림(노선 이탈) 해소.
- 3D 측점선(노란 띠) 도로 반폭 1.5배 연장 — 측구 방향 램프는 끝단에 따라붙음.
- 검증: pytest 112 통과(정책 테스트 기본값 반영), tsc, 헤드 브라우저 저장 왕복
(기준 3+0 → 55~65·anchor 60·before 5, 목록 '배수관 1/2' 번호 표기).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- C군 5종에 전길이(before_m, 기본 0) 옵션 신설 — 시작 = 기준 − 전길이,
후길이 = 길이 − 전길이 자동. 종단 마크 = 기준점, 띠 = 시작~종료(기존 렌더 유지).
전길이 > 기준(시작 음수)은 칸 붉힘으로 거절. 구 저장분은 폼 로드 시 역산 채움.
- 옹벽(철근콘크리트) → "옹벽" (전역 표기는 레지스트리 name 단일 원천).
- 좌측 구조물 목록: 같은 이름 여럿이면 노선 시점 가까운 순 "이름 1/2…" 번호
(numberDuplicateNames 공용 헬퍼 — B06 목록 신설 시 재사용, 2026-08-19 사용자 지시).
- 검증: pytest 112 통과, tsc, 헤드 브라우저(CDP 캐시 비움 방식) — 옵션 순서
높이→길이→전길이, 전5 범위 2+15.0~3+5.0, 목록 "옹벽 1/2", 원복 확인.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- 레지스트리: C군 5종(옹벽·돌쌓기 찰/메·흙막이·큰돌쌓기)에 길이 옵션 신설,
높이와 함께 B05 필수 입력(phase b05)으로 전환. 순서 = 높이 → 길이.
- 패널: 구간 직접 입력(시작·종료) 삭제, 기준 측점 위 '측점 범위' 표시 행 신설
— 기준+길이로 "시작 ~ 종료" 실시간 계산, 저장 시 시작=기준·종료=기준+길이.
- 구간 직입 시절 저장분 호환: 폼에 열면 길이를 시작~종료에서 자동 역산.
- 검증: pytest 111 통과(tmp/tests 정책·저장소 테스트 갱신), tsc, 헤드 브라우저
저장 왕복(60~90·len=30·h=2.5) 확인.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
측점 표기 규칙(2026-08-18 사용자 확정): 잔여거리는 [0, 간격 20m) 범위만 유효,
넘치면 측점번호로 올려 표시한다(1+30 → 2+10). 저장 수치는 원래 누가거리라
계산은 이미 동일했고, 입력 칸 표시를 확정(change) 시점에 정규화한다.
시작·기준·종료 측점 세 칸 공통(stationFields 확장).
검증(헤드 브라우저): 1+30→2+10.0, 0+45→2+5.0, 3+19.9 유지, 3+20→4+0.0 PASS.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
- 3-1 우클릭 메뉴 일원화: 종단그래프·배수유역도 빈 자리 메뉴 = 사이드 「구조물
배치」와 같은 구조물군(A~G·기타) → 종류 2단 아코디언(공용 컨텍스트 메뉴에
하위 메뉴 지원 추가). 선택 = 즉시 추가가 아니라 폼 자동 지정(addAt, A군은
임시 배치까지). 구 [배관 추가]·평면 나열·기성막이/대피로 항목 흡수.
- 3-2 유역 리스트 문구: 세월교 검토→세월교 제안, BOX암거 후보→BOX암거 제안,
추천 Ø{d} 배관→Ø{d} 배관 제안.
- 3-3 구조물 알약 벌룬: 아래(테이블에 가림) 대신 위로 — 1행 좌상, 2행 우상,
솔로 위 중앙.
- 3-4 사이드: 스크롤 영역 상단 여유 + 페이지 설정 샘플 간격 2열(종단 먼저).
- 3-5 배수유역도 위성사진 기본 꺼짐.
- 3-6 B04 초기화 버튼 톤 다운 — 붉은 테두리·글자만, 채움 없음.
- 3-7 그래프 빈 공간 클릭 = 완전 해제 — 알약 해제 신호가 아예 없던 것 수정 +
외부 해제 시 구조물군·종류까지 리셋(선택 없을 땐 유지).
검증: tsc·vite + Playwright 헤드 실화면 V1~V8 전부 PASS (PLAN.md 3-8).
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>