구조물 추가 뒤 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>
attach_culvert_sets()가 도로 방향 폭(span_m)의 절반을 reach로 잡아 소유 측점
전후 측점에도 같은 스펙을 붙였다. BOX암거는 폭 2.4m라 0.92m 떨어진 옆 측점까지
걸려 횡단도가 한 벌 더 그려졌고, 같은 값을 읽는 3D도 박스 솔리드를 어긋난 채
두 벌 만들었다.
구조물 종류별 연동 표(_SPAN_LINKED_TYPES)를 두고 reach를 분기한다. 지금은 빈
집합이라 세월교·BOX암거 모두 소유 측점 한 곳에만 선다. 3D 스윕 길이는 소유
측점이 span_m으로 직접 내므로 줄지 않는다.
기슭막이 연동은 프론트 culvertOwnerFor 별도 경로라 그대로 살아 있고, 집수정은
이미 링크 카드에서 빠져 있다.
검증(프로젝트 c1bb453f route 85): pytest 208 passed. 횡단도 200.0m 카드에서
box-barrel 사라지고 200.92m에만 남음. 3D 솔리드 42 -> 32, 200.0m 10 -> 0,
200.92m 10 유지, span_m 2.4 불변.
Co-Authored-By: Claude Opus 5 <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>
채움 색이 --color-chart-0(파랑 rgb 91,141,239)이라 관 선 색(rgb 79,142,247)과
겹쳐 관 일부처럼 읽혔다. 사용자 확정에 따라 면 자체를 지운다 — 배수관 세트와
같이 관 위는 비워 둔다.
- FordLayout.fill·FordSideLayout.roadFace와 전용 보조 함수 제거
- .b06-chart__ford-fill 스타일 삭제
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>
자동 줌아웃 판정이 배수관용 slopeToeOffset(성토사면 5m에서 컷오프)을 써서, 사면이
5m를 넘는 측점은 필요 폭을 과소 산정했고 교차점이 화면 밖에 남았다(2026-08-23
사용자 보고).
- _UI_Cross_Fit 재작성: 설계선(design_line)과 지반선의 **실제 교차점**을 0.1m
간격으로 훑어 찾는다(노면·노견·측구 구간은 건너뜀). 설계선이 만나기 전에 끝나면
끝단 기울기 차로 외삽하고 1m 올림.
- 카드 자동 줌아웃(effectiveHalfWidth)이 이 값을 쓴다. 보유 샘플 밖으로는 넓히지
않는다(지반이 없어 빈 화면이 될 뿐).
- [적용]은 전 측점 중 필요한 최대 폭까지 함께 넓힌다 — 20m를 넘으면 그 값으로
재생성해 각 카드가 자동 줌아웃할 지반을 확보한다.
자체검증: node tmp/tests/test_toe_extrapolate.mjs (사면 18m 교차점을 5m 컷오프
없이 탐색, 설계선이 15m에서 끊겨도 외삽으로 같은 자리), tsc --noEmit 통과.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
확대하면 0.5m·0.2m 눈금까지 내려가 도면에서 읽을 일 없는 값이 표기됐다
(2026-08-23 사용자 지시). 횡단·종단 모두 표고 눈금 간격을 1m로 하한을 둔다
— 라벨은 항상 정수. 편거리(X) 눈금은 종전대로.
자체검증: node tmp/tests/test_cross_y_ticks.mjs (모든 눈금 정수·간격 1m 이상),
test_cross_axes_zoom.mjs 재실행, tsc --noEmit 통과.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
ui-field가 세로 배치(라벨·입력·오류 슬롯)라 [적용]이 오류 슬롯 높이만큼 내려가
수평이 안 맞았다. 이 줄에서만 필드를 가로로 눕히고(라벨-입력-버튼) 오류 슬롯을
접어 한 줄에 정렬한다.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
- 「횡단 보기 설정」 컨테이너 신설: 반폭 입력 + [적용] 버튼 한 줄. 표준 횡단면
설정의 [전체 측점 반영]에 물려 있던 반폭 적용을 떼어냈다 — 보유 샘플(20m) 안이면
전 측점 설계 재계산 없이 즉시 반영된다.
- 표준 횡단면 설정 컨테이너는 기본 접힘.
- 사이드 컨테이너 양식(여백·글자 크기·색)을 B05 .b05-route__panel-section 값으로
맞춤. 접힘 토글·외곽선은 이미 공용(ui-collapsible / ui-sidebar-section)이었다.
표준단면 「변수 위치 안내」 모식도 스타일은 현행 유지.
- 표준패널의 미사용 extraControl 인자 제거.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
_regeneration_options가 extra_stations를 넘기지 않아, 반폭 재생성 한 번에
구조물 측점 횡단도가 통째로 사라졌다(2026-08-23 사용자 보고). 옵션 스냅샷
(data.options.extra_stations)에서 목록을 읽어 재생성 옵션에 실어 준다.
자체검증: ./venv/Scripts/python.exe -m pytest tmp/tests/test_regeneration_extra_stations.py -q (2 passed)
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
⤢가 항상 교차점 맞춤 폭으로 가서 전역 보기값보다 좁아지기도 했다. 이제 초기화는
전역 보기값이 기본이고, 그 폭에서 절·성토선과 원지반 교차점이 안 보이는 측점만
교차점이 보이는 폭(그 측점의 기본값)으로 되돌린다(2026-08-23 사용자 지시).
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
- 팬 한계를 배율과 무관하게 그려진 범위(캐시 보유분)까지 연다. 확대해야만 움직여
"팬이 안 된다"고 보고됐다(2026-08-23). 캐시가 표시 폭과 같으면 종전처럼 이동 0.
- 절·성토 면적표를 그래프 가운데 상단 → **좌측 상단**(축선 안쪽 58px)으로. 세로
자리는 그대로.
화면 검증(공용 브라우저): 원배율 가운데 드래그로 translate 0→100.3px, X 눈금
-10~10 → -5~15로 캐시 구간 노출. 면적표 좌측 오프셋 58px·상단 8px 확인.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
원배율에서 +를 표시 반폭 좁히기로 돌려놨더니 배율이 1에서 올라가지 않았고,
가운데 버튼 팬은 배율>1에서만 열리므로 팬도 함께 막혔다(2026-08-23 사용자 보고).
+는 언제나 배율 확대, −만 원배율에서 표시 반폭 +1m로 되돌린다.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
- 도형(지반선·면적 밴드·설계선·암 경계선)을 표시 반폭으로 자르지 않고 **캐시 보유
전 구간**으로 그린다. 표시 반폭 밖은 기존 clip이 가리므로 원배율 화면은 그대로다.
- 팬 한계(clampPan)를 그려진 범위까지 확장 — 배율 1에서는 종전대로 이동 0으로 묶고,
확대 상태에서만 캐시 끝까지 가운데 버튼 드래그로 끌어다 볼 수 있다.
- 축 눈금은 팬 상태를 역산해 다시 그리므로(_UI_Cross_Axes) X·Y 값이 따라 움직인다.
자체검증: node tmp/tests/test_cross_pan_bounds.mjs (배율 1 고정, 2배에서 좌우 한계가
캐시 끝과 일치, 범위 밖 이탈 없음, content 없을 때 옛 규칙 유지),
test_cross_axes_zoom.mjs 재실행, tsc --noEmit 통과.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
도형만 transform으로 확대되고 축은 고정이라 확대·이동 후 눈금값이 실제 위치와
어긋났다(2026-08-23 사용자 보고). 축·격자를 별도 레이어(_UI_Cross_Axes)로 옮기고
줌·팬 상태가 바뀔 때마다 보이는 범위를 역산해 다시 그린다.
- 눈금 개수·간격도 그때 보이는 범위로 다시 고른다(1·2·5 계열, X 목표 7·Y 목표 10,
라벨 겹침 하한 X 48px·Y 14px). 확대할수록 촘촘·소수 단위로 자동 전환.
- X 눈금이 7등분 고정(비정수 간격)이던 것도 같은 규칙으로 정리.
- Cross_View는 축 코드가 빠져 622줄.
자체검증: node tmp/tests/test_cross_axes_zoom.mjs (원배율·4배 확대에서 눈금이
플롯 안에 있고, 같은 offset의 도형 자리와 눈금 자리가 일치), tsc --noEmit 통과.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
- 카드 하단 ◀/▶/↺(개별 표시 반폭) 삭제. 조작구는 우측 상단 줌 버튼 하나로.
- 줌 버튼 역할 확장(원배율에서만 반폭 조작):
− 배율>1이면 축소, 원배율이면 표시 반폭 +1m
+ 배율>1이면 확대, 원배율이면 표시 반폭 −1m(하한 2m)
⤢ 배율 원복 + 절·성토 교차점이 다 보이는 폭으로 맞춤
- 교차점 맞춤(_UI_Cross_Fit): 보유 샘플(계산 반폭 20m) 안에서 교차점을 찾고,
샘플 끝까지 안 만나면 끝단 사면·지반 기울기 차로 만나는 자리를 추세 외삽해
올림 +1m을 요청 반폭으로 쓴다(상한 60m).
- 요청 반폭 > 보유 샘플이면 그 폭으로 재생성(regenerate) 후 해당 측점만 세션
반폭으로 넓게 표시. 계산 기본 20m·캐시는 그대로, 확정 시 기존 경로대로
display_half_width_m에 영구 저장.
- 재생성 로직을 _UI_Page_Common.createSampleWidener로 분리(700줄 제한),
[전체 측점 반영]도 같은 경로 사용. 연타 중복 요청 차단.
자체검증: node tmp/tests/test_toe_extrapolate.mjs (외삽 좌/우·발산 지형·+1m 올림),
tsc --noEmit 통과.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
눈금 범위를 elevationMid 중심(±displaySpan/2)으로 잡아, displayMax에 들어간
면적표 여유(AREA_OVERLAY_HEADROOM_PX/2)만큼 아래로 밀려 최하단 격자선·라벨이
X축 라벨 줄 아래에 그려졌다. 범위를 실제 표시 상·하단(displayMax 기준)에서
역산해 플롯 안에만 눈금이 오게 한다.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
- 0.25 비율 고정 5눈금(소수 표기) → 1·2·5×10^n 중 10칸에 가장 가까운
간격으로 자동 선택. 눈금값이 딱 떨어져 표고를 읽을 수 있다.
- 라벨 겹침 방지로 눈금 간격 16px 하한(plotHeight/16 개수 상한).
- 자동 스케일(B05)은 표고 범위에 위아래 10%씩 여유 — 최고·최저점이
축선에 붙지 않는다. 공통 Y스케일(B06 yScaleOptions)은 불변.
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>
① 집수정 조작을 9키(3×3)로 — 십자 축은 이동(◀▶ 좌우 / ▲▼ 사면 대각),
네 모서리는 크기(⇕ 내부 높이 · ⇔ 내부 폭), 가운데는 초기화. 기슭막이
십자와 같은 축 규칙·버튼 크기라 행열이 맞는다. I형은 내공 개념이 없어
네 모서리만 숨긴다.
② 좌우 반전 해소 — 집수정 lateralM이 계류측(outward) 기준값에 그대로 묶여
유입이 우측인 측점에서 ◀가 화면 오른쪽으로 갔다. 버튼을 **화면 기준**으로
받아 카드가 부호를 환산한다. 노견 안쪽(음수)은 0에서 멈추고 토스트로 알린다.
③ 집수정 계류측에 **유출측과 같은 성토부 체계** 적용(ㄴ·ㄷ형) — 구조물 계류측
끝이 원지반 위로 나오면 1:1.2 성토부선을 긋고, 5m를 넘으면 다단 기슭막이를
둘 수 있다(키 , 단 수 행 별도). 원지반 안이면 종전 절토선,
I형은 관 하단 3도 되메움선이 맡는다. 좌우 이동 시 마감선을 억제하던 임시
조건은 제거했다(급경사 선의 근본 원인은 이미 설계선 트림에서 해결).
- 700줄 유지: attachBasin(Basin)·buildExtrasAt(Extra) 추출 + 주석 정리
검증: 4+4.3 ㄴ형 좌우 2m — 성토부선 2.41m가 원지반에 0.001m 오차로 접함
(종전 마감선 없음) · ◀ 클릭 시 4+4.3(유입 좌)·13+15.7(유입 우) 모두 화면
왼쪽으로 이동 · 9키 3×3 좌표 정렬 확인 · tsc·ruff·prettier 통과 ·
pytest 148 passed, 7 skipped · B06_Section 700줄 초과 0건.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>