사용자 지시(2026-09-04) — 「3D 는 보조라 후순위. 조용히 뒤에서 뜨고 다 되면
팝업으로 알릴 것. 로딩 도넛은 진행률과 무관하게 계속 돌고 숫자만 갱신」,
그리고 「직교/원근 전환 단추는 넣음」.
진입 로딩
- 단계 5 → 4. ④ 종·횡단 자료까지 끝나면 로딩 표시를 걷어 화면을 바로 쓰게 함.
- 3D 지형은 finally 뒤 배경 작업으로 돌리고 끝나면 토스트 「3D 지형 준비 완료」.
실패해도 나머지 화면은 그대로 씀. [초기화]는 재진입이라 같은 규칙이 걸림.
- 「확정 지표면 없음」 판정을 ③ 뒤로 앞당김.
진행 서클(공용)
- 회전 껍데기(`__spin`)를 한 겹 두고 CSS 회전을 거기에만 검. 껍데기는 늘 돌고
안쪽 svg 는 12시 고정이라 호·숫자가 제자리에서 갱신됨. `is-indeterminate` 제거.
직교/원근 전환
- 카메라 두 벌을 `B05_Profile_UI_Viewer_Camera.ts` 가 쥐고 갈아 끼움. 위치·시선·
근평면·먼평면과 보이는 크기를 옮겨 화면이 튀지 않음.
- 마커 입력·구조물 클릭·커서 피벗 유틸이 카메라를 함수로 받게 바꿈(공용 유틸은
값도 그대로 받아 B04 호출부는 무변경).
자체검증(공용 브라우저 5173, 용화_LAS) — 진행률 25→50→75%, 회전 애니메이션
running 유지(변환행렬 표본 전부 다름), 로딩 걷힌 순간 그래프선 2·좌측단추 61·
3D 메쉬 2, 1.8초 뒤 메쉬 489 + 토스트. 초기화 재진입도 같음. 전환 단추는
Ortho ↔ Perspective 왕복 후 값 완전 일치, B04 페이지 오류 0건.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
- 계획노선 사용 범위: B02 등록에 시작·종료 누가거리 두 칸 추가, B01 수정 모달에서도
변경. projects.route_start_m·route_end_m 신설(015_route_range.sql).
load_design_route 가 범위 절단 → 서피스 트림 순서로 적용. 시작 >= 종료는 화면·서버
양쪽에서 차단. 비우면 전 구간으로 종전과 같음.
- 서피스 절단 여유 기본값 30m → 3m (SURFACE_ROUTE_EDGE_TRIM_M).
- B04 지도·B05 배수유역도 줌 상한을 「화면 폭 20m」 기준으로 계산(고정 8배·16배 폐지).
4배를 넘으면 배경 그림 흐림 보간 해제.
- 계획선 위 측점 눈금·번호 표기(측점번호+잔여거리). 관 마커와 겹치면 반대쪽으로 밀고,
되꺾임 구간에서 라벨이 겹치면 건너뜀. 그리기 코드는 두 화면 공용.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
사용자 지적(2026-09-04) — 「절토가 된 경우 측점 표시가 안 따라감」,
「측점 라벨이 5측점 기준으로만 붙음, 전체 라벨이 있으면 좋겠음」.
- 측점 막대 표고에서 `max(지반, 계획고)` 제거. 코리도가 켜져 있으면 계획고를
그대로 씀 — 절토는 계획고가 지반보다 아래라 max 로는 막대만 원지반에 떠 있었음
(실측 0측점 3.2m 공중).
- 라벨을 규칙 측점 전부에 만들고(`STATION_LABEL_STEP` 제거), 카메라 거리로 솎음
(`LABEL_LOD` 150m 미만 전부 · 400m 미만 2칸 · 그 밖 5칸). 구조물·BP·EP 는 항상.
- 뷰어 렌더 루프에서 카메라~시점 거리로 단계를 갱신(단계 불변이면 즉시 반환).
자체검증(공용 브라우저 5173, 용화_LAS) — 절토 3측점·성토 10측점 모두 막대와
코리도 표면 높이차 0.80m(띄움값). 라벨 66개 중 화면맞춤 23 · 중간 39 · 근접 66.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
사용자 지시(2026-09-04) — 「이전 방식이 좋았음」. 2026-08-25 `f6296fa7` 에서
직교로 바꿨던 것을 시야각 45° 원근으로 되돌림.
- 직교 전용 모듈 `B05_Profile_UI_Viewer_Camera.ts` 삭제, 뷰어 4자리 복원
(카메라 생성 · 리사이즈 종횡비 · 화면맞춤 · 투영행렬).
- 근평면 상한 0.4m 신설 — 휠 확대가 커서 아래 지점 0.5m 앞에서 멈추는데
근평면을 맞춤거리의 1/1000 로만 두면 긴 노선에서 그 0.5m 를 넘어 지형이 잘림
(용화_LAS 맞춤거리 1,208m → 상한 없으면 근평면 1.21m).
자체검증(공용 브라우저 5173, 용화_LAS) — 네 방향 모두 원근 45°, 확대 한계
0.516m > 근평면 0.4m 로 잘림 없음, 구조물 클릭 선택·해제 그대로.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
- 워크플로 상태 응답에 project_name 추가. 좌측 제목 패널 같은 행 오른쪽 끝에
프로젝트 이름 표기(길면 말줄임, 전체는 툴팁). 이름표를 오버레이 쪽에 두어
레이아웃을 직접 조립하는 B03 까지 여섯 화면이 한 곳으로 반영됨.
- fetchProjectWorkflowState 가 {status, workflow_state} 껍데기를 벗기도록 수정.
- 계획노선을 「업로드한 CSV」로 적은 주석을 「계획노선(정본)」으로 정리.
업로드 판정이 .csv 개수만 세어 shapefile 을 막던 것도 .shp 포함으로 맞춤.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
브라우저 세션에 남은 계획선 편집 초안이 초기값 위에 다시 얹혀,
초기화 직후에는 멀쩡하다가 새로고침 한 번에 계획선이 측점에서 떨어짐
(실측: 측점 66개 중 25개가 10cm 초과, 최대 6.0m).
- [초기화]가 `b05-profile-alignment-draft:*` 세션 초안을 전부 제거
- 노선번호 없는 초안 키(`:none`) 폐지 — 어느 노선에나 되붙어 초기화로도
떨어지지 않던 구멍
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
- 바깥 선택이 폼에 실릴 때 접힌 좌측 패널 자동 펼침(onReveal 창구)
- 계곡 통과 시설 목록 행도 보이는 자리로 스크롤(rAF)
- 종단 테이블 구조물 구간 판정을 허용오차 비교로 교체 + 병합 측점 포함(강조 0건→6건)
- 구조물 선택 세션을 소비형에서 유지형으로 — B06 카드·목록·부재 선택도 같은 칸에 기록,
양쪽 진입 시 복원(목록 지연 로드 대비 이중 복원)
- 조정창 닫기가 세션 선택까지 지우던 결함 수정(부재 해제와 측점 해제 분리)
- B05 페이지 700줄 유지 위해 상단측 세션 보관을 Page_Helpers로 이동(동작 불변)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- 이름표를 횡단도 이름으로 통일 — 세월교 유입·유출, BOX 좌·우, 본체는 body
- 조정창 하나가 여러 부재를 다루므로 `측이름:부재` 꼴 — 강조는 전체 키, 조정창은 앞칸만
- B06 갈래 태우기 신규 모듈 — 세월교/BOX/기슭막이 조정창으로 각각 보냄
- 저장 코리도 만료(BUILD_VERSION 91→92) — 재계산 비용 실측 차이 없음(5.1초 vs 4.7초)
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
- 구조물 솔리드에 부재키(key) 부여 — B06과 같은 이름(inlet·outlet·extra{i}·bextra{i}·own·basin·pipe)
- 구조물 메쉬에 신원표(userData) — 이름 규칙은 그대로 유지
- 신규 모듈 두 개 — 3D 클릭·강조(Viewer_Structure_Pick), 좌측 패널 연결·B06 넘김(Structure_Pick_Session)
- 좌측 패널·종단에서 고른 것도 3D가 따라오도록 선택 동기화에 포트 추가
- B06 진입 시 세션 넘김값으로 그 측점 카드 선택·스크롤 + 기슭막이 조정창 열기
- 저장 코리도 만료(BUILD_VERSION 90→91) — 옛 저장본엔 부재키가 없음
- 뷰어 700줄 여유 — 검증용 요약을 Viewer_Debug로 분리(동작 불변)
- 검증 수단 — __corridorScene에 camera·project 추가
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
증상: 방금 [추가]한(아직 저장 전) 항목을 목록에서 고른 뒤 [삭제]를 눌러도 목록에
그대로 남음. 저장 뒤에는 정상.
원인: 700줄 분리(2026-09-04)로 `_Commit`·`_Events` 조각이 `structures` 배열을
**참조로** 받아 두는데, `setStructures()` 가 `structures = next.map(...)` 로 배열을
갈아끼움. 화면 목록은 새 배열로 그려지고 조각은 옛 배열을 splice 해 사라지지 않았음.
같은 이유로 `renderOptionFields()` 의 `optionInputs = []` 도 조각이 옛 칸 목록을
읽을 수 있어 저장값이 어긋날 자리였음.
조치: 두 배열 모두 **자체를 바꾸지 않고 내용만 갈아끼움**(`splice` · `length = 0`)하고
`const` 로 못박아 다시 갈아끼우지 못하게 함. 데이터 흐름은 그대로 — 삭제는 캐시만
바꾸고, 영구저장은 [저장]에서만 나감(5장 데이터 3층).
검증(시험 프로젝트 8cd2e635, 공용 브라우저 실조작):
- 저장 전: [추가] → 목록 12건 → 고르고 [삭제] → **11건으로 사라짐**, 서버는
revision 4·0건 그대로(쓰기 없음), 세션 캐시도 빈 목록
- 저장 경로: [추가] → [저장] revision 5·1건 → [삭제](서버 5·1건 유지) → [저장]
revision 6·0건
- 저장값: 옹벽 옵션 `height_m 2.5 · length_m 10 · before_m 5 · after_m 5`,
기준 5+0(100m) → 구간 95~105m 정상
- 시험 프로젝트는 구조물 0건으로 되돌려 둠. `tsc --noEmit` 통과, tmp/tests 378 passed
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
B05_Profile_UI_Structures_Panel.ts 1024 -> 669줄. 조각 셋 모두 700줄 이하.
- _Structures_Panel_Types.ts(94줄) — GROUP_LABELS · PipeFacilityItem · StructuresSection ·
StructuresCallbacks · 기본 구간 길이 상수. 패널이 다시 export 해 옛 임포트 경로 유지.
- _Structures_Panel_Commit.ts(312줄) — 폼 불러오기(구조물·계곡 통과 시설)와 [추가]/[수정]
저장, 목록 알림. 검증 순서·분기·수치는 그대로이고 패널 클로저 값만 ctx 로 받음.
- _Structures_Panel_Events.ts(146줄) — 드롭다운·버튼·측점 입력 배선. 리스너 본문 그대로.
StructuresEventContext 가 StructuresCommitContext 를 상속해 패널은 컨텍스트 하나만 만듦.
가변 상태 4개(editingId · selectedPipeChainage · tempPipeChainage · positionConfirmed)의
주인은 그대로 패널이고, 읽기 함수 + set* 로만 넘김.
검증: 분리 전 export 4개가 진입 파일에서 그대로 보임, tsc --noEmit 통과, prettier 적용,
tmp/tests 183 passed. 공용 브라우저(5174)에서 실제 조작으로 저장 경로까지 확인 —
C 사면안정/옹벽을 기준측점 5+0 에 [추가] -> 목록 반영, [저장] -> 서버 정본 2건(revision 0->1),
새로고침 후 목록에서 골라 [삭제] -> [저장] -> 서버 0건(revision 4). 시험 프로젝트는 원래
상태(구조물 0건)로 되돌려 둠.
참고(분리와 무관한 기존 동작): 방금 [추가]해 아직 저장 전인 항목은 structure_id 가 없어
[삭제]가 듣지 않음. 저장 뒤에는 정상 삭제됨. 분리 전 코드와 같은 분기라 이번 변경과 무관.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
RouteProfilePanelCallbacks 인터페이스만 B05_Profile_UI_Profile_Panel_Types.ts(52줄)로
그대로 옮기고, 진입 파일에서 다시 export 해 옛 임포트 경로(_Profile_Render 등)를 유지.
런타임 코드는 한 줄도 건드리지 않음.
검증: 분리 전 export 2개(RouteProfilePanelCallbacks · createRouteProfilePanel)가 진입
파일에서 그대로 보임, tsc --noEmit 통과, prettier 유지. 공용 브라우저(5174) 실조작 —
종단 패널 측점 66개·표 12행·줌 버튼 7개·유역 11개 정상, 측점 선택 후 방향키 862.99->863.09m,
Ctrl+Z 로 862.99m 복귀.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
B05_Profile_UI_Style.css 1379줄 -> 592줄. 규칙을 값·순서 그대로 옮겨 두 조각으로 나눔.
- B05_Profile_UI_Style_Table.css (515줄) — 도면 테이블·값 열 오버레이·우클릭 메뉴·
계획고 편집 버튼·줌 조작구
- B05_Profile_UI_Style_Drainage.css (289줄) — 배수유역도 패널
진입 TS 두 곳(B05_Profile_UI_Page.ts, A00_Common/b_structures_section.ts)에서
본체 다음에 순서대로 불러 캐스케이드 순서 유지.
검증: tsc --noEmit 통과, prettier 서식 유지, 공용 브라우저(5174)에서 계산 스타일 확인
(표 이름표 sticky 78px, 구배 블록 absolute, 편집 버튼 21x17, 줌 22x22, 배수유역 flex).
종단 패널 캡처가 분리 전과 **바이트 단위로 동일**(md5 ee6bc8b52adb).
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
구배 L·H·S 는 값 열을 접는 규칙에서 제외 — 구조물로 좁게 갈라진 구간도 값을 적음
(2026-09-04 사용자 확정, 「각도 행」 = 구배 3행). 그 구간만 고를 때까지 값을 감추던
옛 규칙(2026-08-03)을 걷어내고, 구조물 구간 표시·하이라이트·툴팁은 유지.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
- 구조물 측점 판정을 문자열 키에서 누가거리 허용 오차 0.1m 비교로 바꿈 —
구조물 목록(85.59)과 계획선 측점(85.595)이 셋째 자리에서 어긋나 구조물 열을
못 알아보고 값을 그대로 펼쳐 이웃 값과 겹쳐 보였음. 이제 고른 때만 값 열 오버레이로 보임
- 대수차 1e-4% 미만이면 직선으로 보고 곡선 L·R 을 빈 칸으로 둠 — 직선화하면
대수차가 0 이 되어 R = L ÷ |대수차| 가 수억 m 로 발산했음 (표·값 열 오버레이 공통)
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
- B05_Profile_UI_Profile_Zoom.ts 신규 — 배율 상태(가로 폭 배수·세로 배율·창 중심)와
버튼 7개(+ − ⇕+ ⇕− ▲ ▼ ⤢). 자리는 요약줄 오른쪽 끝(그래프 우측 상단),
설명은 툴팁, 클릭은 stopPropagation 으로 측점 선택에 번지지 않게 함
- X 줌은 폭 배수 — computeProfileLayout 에 zoomX 를 넘겨 측점 간격·폭맞춤 폭에 함께 곱함.
그래프·테이블·편집 버튼층·구조물 레인이 같은 매핑을 쓰므로 넷의 x 정렬이 유지됨
- Y 는 표시 표고창(제안 A) — createLongitudinalProfile 에 elevationOffsetRatio 인자 추가,
창 높이 = 전범위 ÷ 배율, ▲▼ 가 창 높이의 10%씩 중심 이동. 세로 스크롤 없음
- 지반선·계획선·절성토 음영을 플롯 사각형 clipPath 안으로 옮김 — 배율을 올려도
선이 축·측점 라벨 위로 흘러나오지 않음 (B06 은 배율 1·오프셋 0 이라 표시 불변)
- 요약줄 렌더러에 trailing 자리 추가, .b05-profile__zoom* CSS 추가
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
- 방향키 청취를 패널 루트에서 window 로 옮김 — 그래프 클릭 뒤 포커스가 body 에
남아 방향키가 아무 반응이 없던 결함 수정 (패널이 화면에 없으면 무시)
- 길게 누르면 편집 버튼과 같은 속도로 연속 이동 (0.5초 뒤 0.1초 간격, OS 키 반복 무시)
- 이력에 pause()/resume() 추가 — 연속 이동 전체가 되돌리기 1회로 원위치.
구조물 이동은 서버 재계산이 뒤따르므로 키를 뗀 뒤 700ms 뒤 기록
- pause() 에서도 관 목록을 세션에 흘려 되돌리기 기준을 조작 전 값으로 고정
- 좌우 연속 이동 중에는 처음 잡은 구조물 측점·자체 누가거리 카운터 사용 —
관 측점 id 가 누가거리 기반이라 한 번 옮기면 선택이 풀려 한 칸만 움직이던 결함 수정
- ProfileTools.nudge(delta) 노출 — [쉬프트] 모드의 상·하 방향키가 도구 ▲▼ 와
같은 경로(onShift)로 구간 평행이동
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
1. [초기화] — [↶] 왼쪽에 둠. 전체 초기화가 아니라 **마지막 [저장] 시점**이 기준점임
(사용자 지시). 편집 스토어가 저장 시점 편집분(`savedBaseline`)을 들고 있다가 그리로
되돌림 — 저장한 작업은 남고 그 뒤 편집만 버림. 이력에 기록해 [↶]로 되살릴 수 있음.
되돌릴 것이 없으면 버튼은 비활성.
자동 선형까지 지우는 `resetAll`(좌측 [초기화])과는 다른 조작임.
2. [쉬프트]로 고른 직선이 화면에 표시되지 않아 무엇이 잡혔는지 알 수 없었음. 고른 구간을
빨갛게 덧그림 — 범위는 **직선 + 양 끝 라운드(R)** 임. 쉬프트는 직선을 통째로 올리고
내리므로 양 끝 호의 모양까지 함께 바뀜. 구간 시작 라운드의 BVC부터 끝 라운드의 EVC
까지 계획선 샘플을 그대로 덧그려 곡률이 따라옴. 경계에는 점선 세로 표식.
공용 종단 렌더러(`B06_Section_UI_Longitudinal`)는 건드리지 않고 별도 오버레이로 얹음
(`B05_Profile_UI_Profile_RunHighlight.ts`). Y 매핑은 그 렌더러가 넘겨 주는 축 눈금에서
되짚어 같은 스케일을 씀. 편집 버튼층(z-index 4) 아래(3)라 버튼을 가리지 않음.
`tsc --noEmit` 통과, 전체 테스트 183건 통과.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
사용자 지적 3건.
1. 상단 표시줄의 「절·성토 불균형」은 **종단 기준** 값이었음 — 계획선과 지반선 사이 세로
면적(㎡)의 비라 실제 토량이 아니고 판단 근거가 안 됨. 삭제함. 의미 있는 균형은 횡단
단면적을 쌓아 부피(㎥)로 내는 하단 유토곡선 요약이고 그쪽은 그대로 둠.
2. 계획고를 연속으로 누르면 유토곡선이 매번 사라졌다 다시 서서 깜빡였음. 재계산 대기
중에 곡선을 **지우지 않고 직전 것을 그대로 두며**, 요약줄 끝에 「다시 계산 중…」
칩만 붙임. 재계산이 브라우저 안에서 수십 ms에 끝나므로 화면이 비는 구간이 없어짐.
아직 한 번도 못 그렸을 때만 예전처럼 문구로 자리를 채움.
3. [쉬프트]에서 아무것도 안 골라지던 것은 **직선화된 구간이 없어서**였음(초기 계획선은
마디마다 기울기가 달라 직선 구간이 0개). 안내를 갈라 적음 —
「직선화된 구간이 없습니다 — [직선화]로 먼저 만드세요」 / 「…고르세요 (여러 개 가능)
— 끝내려면 [쉬프트]를 다시 누르세요」. 직선화 안내에도 취소 방법을 덧붙임.
전체 테스트 183건 통과, `tsc --noEmit` 통과.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
같은 구조물 측점이 두 좌표로 존재했음 — 종단 정본은 원값(`85.594514`), 사이드바 목록은
표기 규칙(`n+ 0.0`)대로 0.1m 로 반올림한 값(`85.6`). 중복 제거가 누가거리 키(소수 3자리)
비교라 서로 다른 측점으로 잡혀 버튼이 두 벌 섰음.
문제는 표시만이 아니었음 — 사이드바 쪽 버튼을 누르면 정본 측점에서 몇 mm 떨어진 자리에
**가짜 변화점**이 승격돼 그 구간 기울기가 수천 %로 튀었음(실측 11,858%).
겹침 판정을 키 비교에서 **거리 비교**(0.1m 이내면 같은 측점)로 바꿈. 사이드바 목록이
0.1m 반올림이라 최대 0.05m 어긋나고, 실제로 그만큼 붙은 별개 측점은 없음.
실측(route 132, 관 11개): ▲ 버튼 75 → 65개, 근접 중복쌍 9건 → 0건.
전체 테스트 183건 통과.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
B05 3D 뷰어에는 방위 표시가 없어 방향을 읽을 수 없었음(사용자 지적, 메인 창 경유).
B04 가 오늘 새로 만든 「지면에 누운 나침반 링」을 그대로 쓰되, 두 페이지가 함께 쓰도록
공용으로 옮김.
- `B04_PreProcess_UI_Compass.ts` → `ui_template/ui_template_compass.ts` (git mv).
`createTerrainCompass({ sizePx, className })` 로 크기·자리를 호출부가 정함.
- 스타일도 `ui_template/ui_template_compass.css` 로 옮기고 모듈이 직접 물고 옴.
**놓을 자리만** 페이지 CSS 몫 — B04 우하단(`b04-surface__compass`),
B05 는 ISO 버튼 아래(`b05-route__compass`).
- B05 뷰어는 104px 로 쓰고 렌더 루프에서 카메라 오프셋으로 갱신, 지형이 없으면 감춤.
좌표 규약이 B04 와 같음(북 −z · 동 +x · 표고 +y, `Viewer.ts:295` 정점 변환) — 그대로 맞음.
화면 검증(5174): B05 나침반 104×104, ISO 버튼 아래 8px. 도북 화살이 뷰 전환에 반응 —
TOP 에서 `M0.00 -33.30`(수직), ISO 에서 `M23.56 -13.60`. 후자는 카메라 오프셋 (d,d,d)
에 대한 투영 이론값(x=R/√2=23.56, y=−R/√6=−13.60)과 일치. B04 쪽도 그대로(84×84,
우하단 16px, 링·화살 색 토큰 유지). `tsc --noEmit` 통과, 뷰어 693줄로 제한 안.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
낡음 판정이 **편집 전 계획선**과 견주고 있었음. 로컬 재계산은 편집된 계획고로 횡단
설계를 갱신하는데 비교 대상은 저장분이라, 편집이 있는 동안 판정이 영원히 참으로 남아
유토곡선이 빈 화면에 문구만 남았음(사용자 보고).
- 그리기(`_Profile_Render`)와 진입 판정(`_Profile_Panel.render`) 모두 편집이 반영된
계획선(`toDesignProfile(alignment, …)`)과 견주도록 바꿈. 특히 진입 판정은 세션에
미저장 편집이 남은 채 새로고침하면 「어긋남 없음」으로 나와 재계산을 예약조차 하지
않았음 — 그래서 새로고침해도 문구가 그대로였음.
- 세월교 측점은 노면이 월류 높이만큼 일부러 내려 앉으므로, 판정에서 `surface_drop_m`
을 더해 비교. 안 그러면 계획선을 안 건드려도 늘 어긋난 것으로 나옴.
화면 검증(공용 브라우저 5174, route 132): [초기화] 직후 문구 없음·유토곡선 정상
(절토 1,113.5㎥ / 성토 1,424.1㎥). 계획고 ▲ 1클릭 시 문구 없이 즉시 갱신
(절토 1,108.3㎥ / 성토 1,428.6㎥), ↶ 로 되돌리면 원값 복귀. 임시 진단 코드는 제거함.
전체 테스트 183건 통과.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
사용자 조작 중의 계산은 브라우저 안에서 끝나야 함(2026-09-03 사용자 확정). 계획고가
바뀔 때마다 전 측점 횡단 계산이 `POST …/cross-design/preview` 로 나가 왕복이 조작
속도를 지배했음.
- `refreshCrossDesigns` 를 로컬 계산으로 교체. 계획고는 저장 선형(`base_pvi`)에 편집
델타를 얹어 TS `buildAlignment` 로 여기서 풂 — 호출부(B05·B06) 시그니처는 그대로라
단일 창구가 유지됨. 서버 프리뷰는 선형 저장분이 없는 옛 데이터 폴백으로만 남음.
- 표준단면 config 기본값·암 경계 기본 오프셋을 `sections/context` 응답에서 세션에 기억
(`rememberStandardDefaults`·`rememberRockBoundaryDefault`). 프론트에 config 수치를
복제하지 않으면서 패널 없는 B05 도 같은 값으로 계산함.
- 디바운스 250 → 60ms — 왕복이 사라져 손을 떼는 즉시 유토곡선이 따라옴.
- 낡음 판정을 `hasStaleDesigns` 로 바꿔 첫 건에서 중단하고, 계획선 유효 샘플을
샘플 배열당 한 번만 걸러 이진탐색(그리기 프레임당 2.33ms → 0.07ms 실측).
- 파이썬 엔진 2파일·프리뷰 라우터에 「TS 짝 파일과 한 벌」 경고 블록 추가.
검증: `tmp/tests/test_b06_cross_design_mirror.py` 10건 — 토사/암 2단계/L형 측구/포장/
세월교 하강/사면 미닫힘 등에서 파이썬과 TS 결과가 면적·설계선까지 일치. 전체 183건 통과.
`tsc --noEmit` 통과. 실측: route 127(측점 128) 전 측점 로컬 재계산 7.5ms.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
`3651ad98` 의 `only_explicit_curves=True` 로 전체 측점 폴리라인에서 자동 종단곡선이
전부 꺼져, B05 종단 그래프의 계획선이 직선만으로 그려졌음.
- `design_ground_following_profile()` — 변화점마다 기본 L(15m) 종단곡선을 다시 삽입.
대칭 종단곡선은 꼭짓점을 지나지 않으므로(중앙종거 |A|·L/8), 호와 측점 세로선의 교점이
지반고가 되도록 꼭짓점 표고를 최대 12회 반복 보정 — `design_pipe_anchored_profile` 과
같은 방식. 결과: 호의 중심이 측점 세로선 위, 좌우 직선과 접선, 계획고 = 지반고.
- `rebuild_alignment_profile()` — `GROUND_POLYLINE_BASIS` 곡선 생략 예외 제거.
저장된 `base_pvi` 가 보정된 꼭짓점이라 편집 델타를 지우면 초기선으로 그대로 복귀함.
- 프론트는 payload `only_explicit_curves` 를 그대로 따르므로 코드 변경 없음.
- 호 크기 기준은 현행 유지(2026-09-03 사용자 결정) — L 고정·R 은 결과값, 법정 최소반경
미만은 경고만.
검증: `tmp/tests/test_b05_ground_profile_curves.py` 4건 + 전체 173건 통과. route 127
실데이터 오프라인 산출에서 호 126개(저장분 0개), 측점 계획고−지반고 최대 0.063mm.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
`_UI_Structures_Panel` 1,078줄 → 1,024줄. 화면 요소를 세우는 몫만 `_UI_Structures_Form`
으로 옮기고 패널은 값·검증·저장·목록을 맡는다. 배치 규칙(행 순서·구분선·버튼 묶음)은
사용자 지시로 굳은 것이라 그대로 옮겼다.
아직 제한을 넘는다 — 남은 큰 덩어리는 `commit()` 약 200줄과 옵션 필드 렌더·시설 폼
로드 약 170줄이다. 둘 다 폼 상태를 깊게 만지므로 [추가]·[삭제] 실동작 검증을 붙여
따로 옮긴다.
검증 — 공용 브라우저 B05 「구조물 배치」 실측: 제목·측점 행·메모 칸·목록 1개,
버튼 [추가]·[삭제]·[리셋], 구조물군 7항목 그대로. pytest 383 passed·17 skipped,
typecheck·prettier 통과.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
노견 → 벽 상단 접근선 구간이 패치에서 빠져 있어 좁은 좌측 패치 사이로 원지반이 드러나
보였다(2026-09-03 사용자 확정: 「패치로 덮기」). 접근선은 벽보다 위라 덮어도 관 구멍·L자
통로를 막지 않는다 — 벽 전면·유로는 종전대로 리본에 담지 않는다(2026-08-25 사용자 ②).
접근선 줄을 `patchPoints` 앞에 담고 `fillRuns` 에 한 장으로 실어 「구간 1줄 = 리본 1장」
규칙을 그대로 따른다.
검증 — typecheck·prettier 통과. 3D 육안 확인은 카메라 조작이 필요해 다음 화면 검증
묶음에서 함께 수행.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
단일 링 자료형이 곧 중첩·빈공간이었다. 아래 유역이 위 유역을 감싸면 구멍이 사라져 위
유역을 통째로 덮고(합성 실측 256㎡ 전량), 한 관의 유역이 두 조각이면 작은 쪽이 사라져
빈공간이 됐다(144㎡).
- `polygon_parts()` 신설 — 조각마다 [외곽 링, 구멍 링...], 넓은 조각부터.
- `WatershedBasin.boundary_parts` 로 교체. `boundary_xy`(가장 넓은 조각의 외곽)와
`boundary_rings`(편 링 목록)는 파생 속성이라 옛 소비처는 그대로 동작한다.
- API 에 `polygon_rings_lonlat` 추가. 저장 GeoJSON 은 구멍을 가진 Polygon, 조각이
여럿이면 MultiPolygon.
- 캔버스 4곳(B04 유역화면 채움·선택, B05 배수유역도 채움·선택)은 even-odd 로 한 번에
채우고 `pointInRings()` 로 판정 — 구멍 안을 눌러도 바깥 유역이 잡히지 않는다.
중복이던 지역 `pointInRing` 은 삭제하고 공용 것으로 통일.
검증: 단위 5건 신규(도넛 구멍 보존·조각 2개 보존·파생 속성·빈 경계·조립 경로 전체에서
링 2개), 전체 168 passed, typecheck 통과. 브라우저 — API 가 유역 22개에 링 목록을 실어
보내고, 캔버스 even-odd 실측(가운데 알파 0 / 고리 255)으로 구멍이 실제로 뚫린다.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
같은 데이터를 두 화면에 보여 주는 기능인데 세 곳이 갈려 값이 달랐다(실측: 절토(자연)
B06 3,704.6㎥ ↔ B05 4,526.8㎥).
① 보는 노선 — B05는 최신 경로, B06은 최신 **확정** 경로를 열어 노선을 다시 탐색한
프로젝트에서 서로 다른 노선을 봤다(route 126 DRAFT ↔ 125 CONFIRMED, 같은 20m
측점 성토 4.82㎡ ↔ 85.9㎡). `get_workflow_route_context()`(최신 경로) 신설해 B06
화면 context가 그것을 쓴다. 납품 도면(B07)이 쓰는 확정 전용 창구는 그대로 둔다.
② 횡단 재계산 — 호출이 두 벌이라 인자가 갈렸다(B06만 표준 단면값·암 경계 오프셋 전달,
보존하는 사용자 부속값도 2개 ↔ 7개). `B06_Section_Cross_Refresh.refreshCrossDesigns()`
한 창구로 모으고 세션 편집값은 저장소에서 직접 읽어 패널 없는 B05도 같은 값을 보낸다.
③ 낡음 판정 — 「옛 암 2단계 필드 누락」 조건이 B06 페이지에만 있어 B05는 재계산을
건너뛰었다. 공용 `staleDesignChainages()` 안으로 옮겨 두 화면이 같은 시점에 같은
조치를 한다.
검증 — 공용 브라우저 실측: 두 화면 모두 route 126, 요약줄 문자열 완전 일치
(`절토(자연) 4,526.8㎥ · 성토 14,881.8㎥ · 토취 10,213.3㎥ · 최종 누가토량 −10,213.3㎥`).
pytest 370 passed·17 skipped(일원화 검사 4건 신설), typecheck·prettier·ruff 통과.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
① 저장된 횡단이 지금 계획선과 어긋나면(재계산 예약 상태) 유토곡선을 그리지 않고
「횡단을 지금 계획선에 맞춰 다시 계산하는 중입니다」만 띄움. 옛 계획고로 만든 면적이
2초간 보였다가 정본으로 갈아 끼워지던 것을 없앰(2026-09-03 사용자 확정 「새 값만
보여주기」). 용화 실측 — 65측점 중 56개가 낡음, 계획고 차 −9.36~+5.83m,
저장분 성토 42,050㎥ ↔ 정본 14,881㎥.
② 사면이 계산 반폭 끝까지 원지반과 만나지 않아 면적이 잘린 측점에 카드 경고 표기.
판정은 면적을 내는 엔진 한 곳(B06_Section_Engine_Design)에서 `slope_unclosed` 로
내려 화면·수량·도면이 같은 기준을 봄. 계산은 손대지 않음(2026-09-03 사용자 확정
「영원히 못 만나는 지형이 있을 수 있으니 경고로 대체」).
검증 — 공용 브라우저 B06 실측: 2.6초 재계산 안내만 표시(옛 값 미노출), 4.4초 정본
표시, 사면 미교차 경고 13/65측점. pytest 366 passed·17 skipped, typecheck·prettier 통과.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-02 화면 검증에서 나온 미해결 2건 처리.
- 최소고 가드: `blocksMinCover` 가 `_Profile_Render` 측점 끌기 한 곳에만 있어
[직선화]·[쉬프트]·틸팅·방향키가 그냥 지나감. 판정 로직을
`_Profile_MinCover.blocksMinCover()` 로 옮기고 모든 편집이 지나는
`_Profile_Panel.applyEdits` 한 곳에서만 호출. 화면 모듈의 사본·죽은 배선
(`enforceMinCover` ctx 필드, 미사용 import)은 제거.
- 되돌리기 범위: 이력이 `b05:`·`b05-`·`b06:`·`b06-` 세션 키만 스냅샷하는데
구조물(관) 위치는 세션 밖이라 undo 가 계획선만 되돌림. `_Profile_Panel_Tools`
가 기록 직전에 관 목록을 `b05:pipes` 로 흘리고 복원 뒤 되읽어 반영.
사용자 확정 ⑥ 「범위는 B05 페이지 조작 전부」 충족.
검증(공용 브라우저, 용화 검증 프로젝트 route_id 126):
- 최소고 강제 켠 상태로 관 측점 264.060 방향키 ↓ → 편집 미적용 + 경고
"배수관 Ø800 — 최소 계획고(지반 +1.3m) 아래로 내려갈 수 없습니다."
- 구조물 측점 264.060 → 263.960 이동 후 ↶ 1회 → 264.060 복귀,
`b05:pipes` 목록도 264.06 으로 복귀.
- `tsc --noEmit` 0, prettier 통과, pytest 366 passed / 17 skipped / 0 failed.
- 파일 줄수: Panel 699 · Panel_Tools 197 · Render 350 · MinCover 188 (700줄 제한 준수).
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
증상: 파일 입력 직후 초기 계산값과 B05 [초기화] 결과가 다름.
원인
- 자동설계 체인이 5단계까지 전부 성공해야만 `save_initial_snapshot()` 호출.
중간에 깨지면 `initial_snapshot/` 미생성 → [초기화]가 복원 대신 재계산 폴백.
- 재계산 폴백의 지표면 기준이 체인과 다름 — 체인은 stage 1 확정값
(`get_surface_confirmation_params`), [초기화]는 config 기본값
(`surface_confirmation_defaults`). 실측: 프로젝트 stage1 `classification`/5m 대
config `csf`/1m.
- 체인이 깨져도 WF1 은 초기 분석 완료 메일을 그대로 발송.
수정 (2026-09-02 사용자 확정 — 부분 결과는 분석 안 됨과 다르지 않으므로 부분
스냅샷은 만들지 않음)
- `initial_design.failed` 마커 신설 — 프로젝트 루트(스냅샷 4트리 밖), 실패 사유 기록.
체인 진입 시 옛 마커 제거, 실패 5지점 + 스냅샷 저장 실패에서 기록.
- WF1 이 마커를 읽어 완료 메일 대신 `send_initial_design_failed_email()` 발송
(관리자 주소 `ADMIN_EMAIL`, 없으면 주소 없이 안내).
- B05 [초기화]: 스냅샷 있으면 복원, 실패 마커 있으면 409 `initial_design_failed`
로 거부(DELETE 앞에서 반환 — 데이터 무변경), 옛 프로젝트만 종전 재계산 폴백.
- 재계산 폴백의 지표면 기준을 `get_surface_confirmation_params()` 로 통일.
자체검증: `pytest tmp/tests/ -q` 366 passed / 14 skipped / 0 failed (+4).
공용 브라우저 실측 — 실패 프로젝트 [초기화] 409 응답·`routes` 행 무변경 확인.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
- `B05_Profile_UI_Viewer_Marker_Input.ts`(196줄) 신설 — 지형 지점 픽킹, 마커 끌기·
고르기, 끌어놓기(drop), 리스너 등록·해제를 `bindMarkerPointerControls` 로 묶음.
- 뷰어는 `markerInput.blocked()`(회전 중심 양보 판정)·`terrainPointAt`(공개 API
`modelPointAt`)·`beginMoveSelected` 만 받아 쓰고, dispose 에서 함께 해제.
- 화면 검증: 공용 브라우저에서 BP 마커를 실제 클릭 — 선택 배율 1 → 1.35,
빈 지형 클릭으로 해제 1.0 복귀. 마커군 명중 시 이벤트 전파 차단도 유지됨.
- `tsc --noEmit` 0, prettier 통과, pytest 359 passed.
- `B05_Profile_Router_Lifecycle.py`(256줄) 신설 — `/route/confirm` · `/route/reset`
두 엔드포인트를 자체 APIRouter 로 옮김. URL·응답 스키마 불변.
- `main.py` 가 새 라우터를 함께 등록 (`b05_route_lifecycle_router`).
- 자동설계 체인이 쓰던 `B05_Profile_Router.confirm_latest_route` 경로는 재수출로 유지.
- 검증: 라우트 8개 그대로(openapi.json 실측), 백엔드 재기동 후 200, pytest 359 passed.
- `B05_Profile_Engine_RidgeValley_Graph.py`(257줄) 신설 — 격자 조회(`_Grid`),
노드 수집, 구획 마스크, 정속경사 세그먼트 판정, 엣지·연결 후보, 교각 계산.
- 원본에는 탐색(`_search_segment`)·fillet 선형·진입점 계산만 남김.
- 엣지 후보 상수(MAX_EDGE_LEN_M 등)도 함께 이동 — 쓰는 쪽이 전부 새 모듈.
- 외부 호출부 불변(`solve_ridge_valley_route` 경로 동일), pytest 359 passed.
- `__corridorScene.lookAt(x, y, z, distance, view)` — 모델 좌표 한 점으로 카메라를
바로 보냄. 측점 확인을 마우스 휠·드래그로 하면 한 번에 20~30초가 걸리고
우클릭이 구조물 메뉴를 여는 문제 해소.
- `fit()` 의 카메라 배치를 `placeCamera()` 로 공용화 — 뷰 4종(iso/top/front/side)
위치·근평면·직교 반높이 계산이 한 곳.
- `_Corridor_Cut.ts` `dropCollinear`: 추적 고리의 닫힘 중복점(머리=꼬리)을 떼고 순환
다듬기 뒤 다시 붙임. 그대로 두면 머리 점의 이웃이 자기 자신이 되어 `away=0` 으로
지워지고 양옆 일직선 점까지 빠져 8고리 전부 시작 모서리가 대각선으로 잘림(63셀 구멍).
- 공선 판정의 앞점을 마지막으로 남긴 점 기준으로 변경 — 완만한 곡선에서 누적 편차로
현이 곡선 안쪽으로 파고들던 2셀 제거(`_Region.ts` `simplifyLoop` 과 같은 규칙).
- `BUILD_VERSION` 89→90 으로 저장본 만료.
- 실측: 되살릴 영역 미포함 셀 63→0(우 7886/7886·좌 7023/7023), 8고리 닫힘,
`tmp/tests/test_b05_cut_merged_loop.py` 3건 포함 359 passed.
- PLAN.md 해당 항목 완료 표시 + 자체검증 기록(검증 대기).
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
투영 커브(구조물 노랑·날개 주황·투영선 빨강·잘린 자리 라임 등)는 구조물이 어디를
지우고 어디를 다시 채우는지 눈으로 좇으려고 만든 표기라 설계 산출물이 아니다.
사용자 지시로 **기본은 숨김**. 다만 절취·패치 기하를 다시 볼 때 쓰므로 지우지 않고
묶음째 감춘다(2026-09-02 사용자: "나중에 디버깅을 위해 재사용 가능성 있음").
- `_Corridor_Mesh.ts` — 커브를 `corridor-plan-sketch` 묶음에 담고 `visible` 을
브라우저 스위치(`frd_debug_plan_curves`, 기본 꺼짐)로 정한다.
- `_Profile_UI_Viewer.ts` — `__corridorPlanCurves(true|false)` 로 켜고 끈다. 선택은
브라우저에 남아 다음에 열 때도 유지된다. 인자 없이 부르면 지금 상태를 돌려준다.
검증(wdw · 3D 실측, 같은 화면에서 켜고 끄며 스케치 색 픽셀을 셈):
숨김(기본) — 주황 1 · 노랑 0 · 빨강 0 · 라임 0
켬 — 주황 89 · 노랑 260 · 빨강 6 · 라임 195
`__corridorPlanCurves()` 기본 false → true → false 왕복 확인.
`tsc --noEmit` 통과, `pytest tmp/tests/ -q` 356 passed / 0 failed, prettier 정합.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
패치 리본을 절취 영역 안쪽만 남기는 규칙(`patchClip`)이 세월교에만 걸려 있었다.
`_Corridor_Cut.ts` 가 표식 없는 패치는 손대지 않고 그대로 돌려주므로 BOX암거 패치는
한 번도 안 잘려 절취 영역 밖으로 넘쳤다. BOX도 `wing-box` 커브로 절취 영역을 내므로
같은 반대 규칙이 성립한다. 기슭막이(배수관 세트)는 날개 사다리꼴이 없어 제외.
기하가 바뀌므로 `BUILD_VERSION` 88 → 89 (형식은 안 바뀌어 `ENVELOPE_VERSION` 은 그대로).
검증(wdw · 저장본 실측):
- 수정 전 — BOX 패치 리본 두 벌(198.4~203.5m)에 셀 마스크 없음. `patchClip` 3개.
- 수정 후 — 두 벌 모두 마스크 생김(남은 셀 46/282 · 43/282). `patchClip` 5개.
- 3D 화면(TOP, 지형 끔) — BOX 자리 성토면이 구조물에 맞물려 끊기고, 넘침도 구멍도 없음.
- `tsc --noEmit` 통과, `pytest tmp/tests/ -q` 356 passed / 0 failed.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
- 저장본 왕복에서 `patch`·`patchClip` 두 표식이 사라졌음. 직렬화 대상에 없어
저장본으로 다시 연 코리도는 패치인 줄 몰랐고, 뷰어의 지형 스냅 제외 가드
(`B05_Profile_UI_Viewer.ts` `if (ribbon.patch) return;`)가 풀려 패치 리본
바깥 끝이 원지반까지 끌려 내려갔음(2026-08-27 실측 세로 지느러미 4.1m).
두 표식을 `serialize`·`deserialize` 에 싣고 `ENVELOPE_VERSION` 을 6으로 올림 —
`fetchStored` 가 버전이 다른 저장본을 버리므로 기존 v5 저장본은 다시 만들어짐.
- `_Corridor_Carve_Wing.ts` 의 「주황 바깥선 트림」 도구 4개(`WingTrimLine`·
`wingOuterLines`·`wingTrimAt`·`clipRunToRange`)를 제거. 버전 76~78용인데 그 판이
사용자 지시로 원복된 뒤 `BUILD_VERSION 88` 까지 다시 배선되지 않아 파일 밖 참조가
0이었음. 이력은 `_Corridor.ts` 버전 주석에 남김.
검증: 저장본 3건 실측 — 전부 `version=5`, `patch`·`patchClip` 0건으로 결함 확인.
회귀 방지 `tmp/tests/test_b05_corridor_envelope_flags.py` 7건 신규(표식 왕복·버전 상향·
뷰어 가드 존속·선택 필드 대칭·죽은 심볼 부재·살아 있는 도구 존속).
`pytest tmp/tests/ -q` 356 passed / 17 skipped / 0 failed, `tsc --noEmit` 통과.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
- B04 지표면 3D 뷰어가 지표면 범위만으로 카메라 거리를 정해, 라이다가 노선의
일부만 덮으면 나머지가 화면 밖으로 잘렸음. 카메라 타깃이 늘 지표면 중심이라
한쪽만 넓혀서는 소용이 없어, 중심에서 가장 먼 노선 정점까지를 반폭으로 잡아
대칭으로 넓힌 `fitBounds()` 를 `fitCamera()` 가 씀. 노선이 없으면 종전 동작 유지.
- 계획선 1차(배관 정착)와 2차(직선 분할 폴백)가 같은 `_profile_entry()` 를 써서
저장본 `basis` 가 둘 다 `station_alignment` 로 나갔음. `PIPE_ANCHORED_BASIS`
신규 + `_profile_entry(basis=)` 인자로 1차만 값을 가름. 2차는 종전 값 유지,
편집 재구성은 저장본 값 승계.
검증: 용화 프로젝트 공용 브라우저 실측 — 노선색 픽셀 x 범위 48~399(우측 끝에 닿음)
→ 145~386(양끝 캔버스 안). pytest 349 passed / 17 skipped / 0 failed, tsc 통과.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
기슭막이가 관 지점 시설로 옮겨간 뒤(f916aa52) 이관 경로만 옛 스펙에 남아
`POST /route/structures/migrate` 가 400 으로 통째 실패했음 — 저장소가
`managed_by` 타입을 거절하는데 이관은 계속 구조물로 만들었기 때문. 같은 요청에
실린 대피소·기타까지 하나도 이관되지 않았음. 호출부가 B05 진입 경로라 레거시
프로젝트는 진입할 때마다 실패했음.
- B05_Profile_Structures_Migration: 결과를 MigrationPlan(구조물 / 관 시설)으로 나눔.
managed_by 타입은 레지스트리 기본값(사용자 확정분)을 승계한 PipePoint 로 만듦 —
구간은 기준측점 ∓ 전/후
- B05_Profile_Structures_Router: 관 시설분을 pipe_points.json 에 덧붙임. 관 지점
파일이 없으면 그 몫만 미루고 이력도 남기지 않음(원천 측점이 남아 재이관됨)
- common_util_drainage_pipes: read_pipe_points_file · append_pipe_points_file ·
pipe_points_path_in 신설 — 저장 당시 노선 지문 보존
- 관 시설에서 투영된 "기슭막이" 라벨이 되이관돼 "기타" 로 굳던 경로도 함께 막음
- B06_Section_Engine_Revetment 삭제 — 호출자 0건(벽은 관 세트가 만듦)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
횡단배수 최소 계획고가 두 곳에서 강제됐다. 자동설계는 배관 자리 계획고를 시설
여유만큼 들어 올렸고(B05_Profile_Engine_Sections), 화면은 그 아래로 내리는 편집을
차단했다(blocksMinCover). 배수 자리마다 계획고가 끌려 올라가 제어가 어려웠다.
산식(common_util_drainage_pipes.facility_clearance_m)은 그대로 두고 적용 여부만
enforce_pipe_clearance 스위치 하나로 뺐다. 기본은 해제라 자동 전처리가 계획고를
들어 올리지 않고, 편집도 막히지 않는다. 부족분 경고(minCoverWarningText)는 켜고
끔과 무관하게 계속 뜬다. 스위치는 요청 → DB 저장값 → 해제 순으로 정해지며,
계획선 criteria와 B06 재생성 경로도 확정 당시 값을 따라간다.
3D 예상형상은 계획선 편집마다 500ms 디바운스로 따라오던 것을 없앴다. 편집이
쌓이면 상단 [3D 업데이트] 버튼에 대기 점이 붙고, 누를 때 코리도와 측점선을 한 번에
갱신한다. 최초 진입의 자동 그림은 그대로다.
계획노선 CSV는 설계 원청이 지정한 기준선이라 프로그램이 바꾸면 안 되는데,
격자 Dijkstra가 제어점 사이에 셀 중심점을 끼워 넣어 평면이 틀어졌다.
제어점은 원좌표로 되박히지만 끼어든 점은 되박히지 않기 때문이다.
원인은 셀 크기와 정점 간격의 기하 관계다. LAS 프로젝트는 비용면 셀이
2.188m(원본 DTM 해상도, 다운샘플 배수 1)라 정점 간격(평균 2.61m·최대
3.25m)이 전부 8이웃 한 걸음에 들어가 중간점이 안 생겼다. 도엽 등고선
프로젝트는 DTM이 1.0m라 배수 2로 셀이 2.0m가 되고, 20쌍이 두 걸음으로
벌어져 22개 점이 끼어들었다(최대 이탈 2.08m, 평면 +4.72m, 곡선 경고 증가).
솔버에 LAS 전용 보존 코드는 없다 — 셀 2.0m에 LAS CSV를 스냅해도 두 걸음
쌍이 16개 생긴다.
제어점 쌍의 평면 거리가 ROUTE_DIRECT_LINK_CELL_FACTOR×셀 이하면 탐색을
건너뛰고 두 칸을 그대로 잇는다. 조밀 기준선(정점 전체가 제어점)은 평면이
원본과 완전히 같아지고, 간격이 문턱을 넘는 구간만 종전대로 탐색한다.
경사·곡선 제약은 이 구간에서 경고로만 남는다.
검증(프로젝트 0aca395f 실데이터, 원본 미변경 사본에서 재탐색):
- 셀 2.0m: 정점 156→135, 끼어든 점 22→0, 이탈 2.076→0.000m,
평면 354.84→350.11m(CSV와 동일), 곡선 경고 9→8
- 셀 3.0m: 111→135 정점(같은 칸에 합쳐지던 24개도 살아남음), 이탈 0.000m
- LAS 프로젝트 c1bb453f: 재탐색 결과 xy 차 0.000000m — 결과 불변
- pytest 14 passed (직결·같은 칸 미병합·먼 구간 탐색 유지 3건 추가)
사용자 지적(2026-08-30): "결국 노선 위에 위치해야 한다".
관 자리를 정한 선(계획노선 CSV)과 화면에 그려지는 선(B05 최적 경로)은 같은 자리를
지나면서 연장이 다르다 — 실측 350.11m vs 354.83m, 전 구간 이격은 평균 0.03m·최대
1.12m뿐인데 길이가 4.7m 차이난다. pipe_points.json이 누가거리만 저장해서 읽는 쪽이
쥔 선에 따라 같은 값이 3.3~4.3m 미끄러졌고, 노선 지문도 늘 어긋나 B05는 저장된 관을
매번 통째로 버렸다("노선 지문이 달라 저장된 관 지점 4건을 쓰지 않습니다").
- PipePoint에 x·y를 두고 저장 시 채운다(save_pipe_points에 노선을 넘긴다).
- 지문이 달라도 좌표가 있으면 읽는 쪽 노선에 투영해 이월한다(project_pipe_points).
구간(start_m·end_m)은 기준점이 옮겨간 만큼 같이 민다 — 시설 치수는 불변.
- 좌표 없는 구 저장분만 종전대로 버린다.
- B05는 같은 로더(load_pipe_points_file)를 쓴다 — 두 화면의 판정 기준을 하나로.
검증: 프로젝트 5d18ebe3 실데이터 — 좌표 4/4 저장, B05 계획선으로 읽어 4건 전부 이월,
관에서 계획선까지 거리 최대 0.016m(종전 4건 전량 폐기). 누가거리는 +3.45~+4.29m
이동(연장 차이 그대로). pytest 11 passed(투영 이월·구 저장분 폐기 테스트 추가).
횡단도에 그려지던 노면은 사실 월류부 가장 아래를 자른 자리다 — 구체 위 노면은
월류 높이만큼 낮게 앉아야 한다(2026-08-30 사용자 확정). 계획고를 통째로 내려
잡아 측벽·바닥판·십자선·절성토 면적이 한 번에 따라오게 했다. 전폭 평행 하강이라
횡단경사·측구·사면 규칙은 그대로다.
백엔드
- `_ford_set`이 `ford_height_m`을 `overflow_depth_m`으로 스펙에 싣는다.
- `compute_cross_design(surface_drop_m=)` — 계획고를 그만큼 내려 계산하고, 내린
양을 `surface_drop_m`으로 echo한다(프론트가 점선을 되그리는 데 쓴다).
- `ford_surface_drops()`·`ford_drop_at()` — 관 지점 정본에서 측점별 하강량을 읽는다
(포장 구간과 같은 방식). 설계 계산 경로 전부(기본값·재계산·프리뷰·종횡단 확정)에
물렸다.
- `enforce_ford_surface_drops()` — 저장분이 옛 계획고면 상세 조회에서 다시 계산한다.
월류 높이를 지우면 같은 경로로 원래 계획고로 되돌아온다.
프론트
- 십자선을 하강량만큼 내려 찍는다(`cardDesignElevation`). 종단 계획고를 그대로
쓰던 자리다.
- `appendFordSurfaceDropPlan()` — "월류가 없었다면" 노면을 점선으로 남긴다.
물넘이포장의 「기존 계획고 점선」과 같은 표기라 한 규칙으로 읽힌다.
- 측벽·바닥판은 내려간 설계선(`design_line`)에 그대로 붙으므로 따로 손대지 않았다.
자체검증(공용 브라우저 5174 + 워크트리 백엔드 8001, 측점 7+9.7, 월류 높이 0.39m):
- `tmp/tests/test_ford_surface_drop.py` 2건 통과 — 계획고·좌우 노견이 정확히
0.39m 내려가고, 절토는 늘고 성토는 줄며, 하강이 없으면 `surface_drop_m` 키 자체가
없다.
- 화면 실측: y축 눈금 1m = 24.375px에서 십자선이 점선보다 9.50px 아래
(= 0.390m, 오차 0.001m). 수정 전에는 두 선이 같은 자리였다.
- 세월교 측벽 자동 높이 1.70m → 1.50m. 이론값 1.31m이나 하한(관경 1.0 + 최소 토피
0.5 = 1.5m)에 걸린 값이다 — 기존 규칙 그대로다.
- 성토 면적 18.46 → 13.85 ㎡. 이웃 일반 측점에는 점선이 그려지지 않는다.
- 검증에 쓴 `pipe_points.json` 임시 옵션은 백업본으로 되돌렸다(정본 무변경).
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>