원인: 앞선 커밋에서 넣은 '대표 벽 자동 선택'(selectDefaultWall)이 카드를 고를
때마다 벽을 골라 조정창까지 띄웠다(2026-08-29 사용자 보고: 기슭막이(유출) 창이
계속 떠 있음).
- selectDefaultWall과 그에 딸린 자동 선택 표시(withAutoAdopt 등)를 걷어냈다.
- 카드·목록 선택 리스너에서 남아 있는 벽 선택을 비운다. 벽을 찍어 카드가 딸려
선택된 경우는 건드리지 않는다(isWallSelecting 가드).
부작용: 단 수·옵션 행은 도면에서 벽을 고른 뒤에만 유입구·유출구에 붙는다
(사용자 확인: 측점 선택 시 자동 구조물 선택은 필요 없음).
검증: 목록 선택·카드 선택 시 조정창 0개·강조 없음, 벽 클릭 시 기슭막이(유출) 창
+ 유출구 강조 + 단 수·옵션 행 표시.
- <label>이 [-][입력][+] 묶음을 감싸면 암묵 대상이 **첫 버튼(-)**이 된다.
입력에 id를 붙이고 label.htmlFor로 명시 연결해 라벨·입력 위 커서가 -를
활성화하거나 라벨 클릭이 값을 줄이던 동작을 없앴다(2026-08-29 사용자 보고).
- 제목(라벨)과 항목 간격도 --b05-label-gap(2px) 한 변수로 묶고 라벨 글자를
caption 크기로 맞췄다. 측점 행은 가로 배치라 열 간격 6px 유지.
검증: label.control=INPUT, 라벨 클릭 후 값 10 불변, 커서 올려도 - 배경 투명.
시설·이식·메모·구조물군 라벨 간격 모두 2px·라벨 높이 14px.
2026-08-29 사용자 지시 — 조정창에서 옮겨 온 행(단 수·옵션)의 컨트롤 높이가
기준이다. 섹션에 b05-structure-section 클래스를 붙여 스코프를 잡고,
--b05-control-h(26px) 하나로 입력·셀렉트·측점칸·메모·스텝 묶음·이식 행을 맞춘다.
높이를 바꾸려면 이 변수만 고치면 된다.
검증: 이식행·스텝 묶음·스텝 입력·시설 셀렉트·구조물군 셀렉트·측점 입력·메모가
모두 26px. 하단 액션 버튼(추가·삭제·리셋)은 종전 35px 유지.
2026-08-29 사용자 지시 3건:
1. 유입구·유출구로 옮겨 온 단 수·옵션 행을 폼 양식(라벨+한 덩어리 컨트롤)으로
맞추고 2열 격자로 세운다.
2. 이미 고른 항목은 값이 바뀔 때마다 곧바로 반영한다([수정] 버튼 불필요).
버튼은 visibility만 꺼 자리를 남겨 삭제·리셋의 위치·크기가 흔들리지 않게 했다.
정본 주입이 되먹임으로 돌아와도 편집 중인 폼은 닫지 않는다.
3. 도면에서 구조물을 고르면 그쪽 그룹(유입구/유출구)을 붉은 계열로 강조한다.
카드만 고른 경우는 강조하지 않는다 — 대표 벽은 조용히 딸려 오고, 그 벽을
다시 눌러 선택을 풀면 강조만 꺼지고 행은 남는다.
검증: 슬롯 grid 2열, 카드 선택 시 강조 없음·행 2개, 다른 벽 클릭 시 유입구 그룹
붉은 테두리(rgb(192,57,43)), 재클릭 시 강조 해제·행 복귀. 길이 + 즉시 캐시
반영(11) 후 - 원복(10), 폼 유지. typecheck 통과.
2026-08-29 사용자 보고 두 건:
① 값을 고치고 [수정]하면 '추가·이동·삭제는 B05에서' 안내만 떴다 — B06에서 관
옵션 저장 경로가 없었다. 조정창 구간값과 같은 저장기(culvertOptions.queue)로
보내 캐시에 예약하고 [저장]·[확정]이 정본에 쓴다. 기준점 이동만 배수유역
재분할이 걸려 B05 몫으로 남기고, 안내 문구도 추가·삭제/이동으로 나눴다.
② 연동·경사 옵션과 단 수 행이 구조물 배치에서 사라졌다 — 그 행들은 고른 벽에
딸린 것이라 도면에서 벽을 찍기 전에는 이식될 창 자체가 없었다. 시설을 폼에
올릴 때 대표 벽(유출 기준벽, 없으면 유입)을 함께 골라 행이 서게 한다.
검증: 목록에서 배수관 선택 → 유출구 그룹에 [추가 기슭막이(단)]·[옵션] 표시.
길이 + 후 [수정] → '수정한 값은 [저장]·[확정] 때 반영됩니다' + 세션
b06:culvertopt에 inlet_revet_length_m 11 예약 확인. typecheck 통과.
2026-08-29 사용자 지시 — 가운데 숫자, 좌 -·우 +가 테두리 하나를 나눠 쓰는
하나의 컨트롤로 보이게 한다.
- DOM 순서를 [-][input][+]로 바꿈
- 테두리·배경·모서리는 묶음(.b05-structure__stepper)이 갖고, 안쪽 입력은 테두리
없이 가운데 정렬. 버튼은 가르는 선 하나씩만 갖는다. 포커스는 묶음 테두리로 표시.
검증: 순서 BUTTON-/INPUT/BUTTON+, 묶음 테두리 1px·radius 8px, 입력 테두리 0·
가운데 정렬, + 10.0→11.0 · - 11.0→10.0 동작 확인.
- SectionView.setStationSelectListener 신설 — 카드(측점) 선택 변경 알림
- 좌측 패널 showAtChainage: 계곡 통과 시설(pipe_points) 우선, 없으면 그 측점을
덮는 구조물 정본 항목(점형 근사·구간형 범위), 둘 다 없으면 선택 해제
- 연동 링크 카드는 structureSpan.ownerOf로 소유 측점 시설을 보여 준다
검증(공용 브라우저): 4+4.3 배수관·7+9.7 세월교·10+0.9 BOX암거 카드 클릭 시
종류·기준 측점·목록 강조 일치. 링크 카드 4+0.0→4+4.3, 14+0.0→13+15.7 소유
측점 표시. 구조물 없는 0+0.0 카드는 폼 해제. typecheck 통과.
증상(사용자): 세월교 횡단도 측벽이 기울어 있고 바닥판 모서리도 깎여 있다.
확인 결과 세로(수직)로 그려져야 한다.
원인: 세월교 측벽은 ㄴ형 집수정 부재를 재사용한다(2026-08-25). 그래서 집수정
규칙인 전면 1:0.3 기움과 거기서 파생된 바닥판 taper(0.3 × 두께)가 딸려 왔다.
세월교 근거가 아니라 부재를 빌려 오며 따라온 값이다.
근거: 지식DB·교본 원문에 세월교 단면 기울기 수치는 없다. 교본 그림 3-18
세월교 설치 종단도의 구체가 수직 벽이고, 사용자가 그렇게 확정했다.
고침: buildBasin에 leanRatio 인자를 두고(기본 0.3) 세월교만 0을 넘긴다.
집수정 호출부는 인자를 안 넘기므로 동작이 그대로다. 벽 상단 도로측 꼭짓점
(설계선 트림 경계)은 노견 끝 그대로라 트림·접근선 규칙도 불변이고, 바닥
길이는 날개벽 투영 연장 + 벽 두께로 유지된다. 3D는 같은 폴리곤을 스윕하므로
자동 반영된다.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
증상(사용자): 독립 기슭막이 우측 다단이 구간값에 따라 옆 측점에서 없어지는데,
그 위 기준벽 이후 성토면은 갱신되지 않는다.
원인: 링크 카드가 다단을 **다 세워 놓고 그림만 지웠다**(trimLinkedLayout이
extraWalls만 걸러낸다). 성토부선(outletFill·basinFill)은 원래 단 수로 계산된
값이라 마지막 남은 벽 뒤가 비어 있었다.
고침: 이 측점까지 오지 않는 단은 계산에 들어가기 전에 뺀다(tierPrefix — 단은
위에서부터 쌓이므로 덮이는 앞부분만 남긴다). buildOutletExtras가 마지막 단 뒤
성토부선을 지반까지 다시 긋는다.
화면 실측(234.1 소유 → 240.0 링크, bextra0 후 6.0m → 1.5m): 링크 카드 벽 4 → 3,
유입부 성토부선 0.91+3.00 → 5.27 한 줄로 지반까지. 소유 카드는 불변.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
사용자 확정(2026-08-30) 세 가지를 반영한다.
① 링크 강조 — 한 기슭막이는 그 연장 안 모든 횡단도에서 같이 강조된다. 선택
(조정창)과 강조를 분리해 `highlightFor`/`applyRevetHighlight`로 카드 재생성 없이
클래스만 갈아 끼운다(줌·팬 보존). 다단은 단 번호까지 같은 키로 짝짓는다.
② 연동 = 분리가 아니다 — 길이·전/후로 이미 하나의 기슭막이다. 연동을 푼 측점은
그 자리에 서므로 3D에서 벽이 노선과 평행이 아니라 자기 경로선을 따라간다. 링마다
폴리곤을 갈아 끼워(`polygons`) 측점 사이를 Catmull-Rom으로 잇는다. 해제 시 단 수가
0이 되던 것(벽 4→2)도 고쳤다 — 소유 측점 4축에서 출발해 손댄 벽만 갈아 끼운다.
③ 경사 = 수평(Z 일원화) — 3D 링만 눕히던 것을 횡단도까지 넓혔다. 링크 카드 벽
상단 표고를 소유 측점과 맞추고(d = 소유 d + 1.2×dz) 차이는 노견 확장이 흡수한다.
그래서 수평 중에는 연동 해제 불가 — 버튼을 잠그고, 해제해 둔 값은 지운다.
곁다리: 3D 재빌드 해시에 extra_spans·revet_link_detached 추가(안 넣으면 저장
코리도가 안 만료된다), 캐시 저장 payload에 extra_spans 누락 보완, 3D 머리 주석을
"정본만 읽는다" → "캐시를 읽는다"로 정정.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
CLAUDE.md 5장(조작·데이터 흐름 정책)을 코드에 반영한다. 조작분은 세션에만 쌓고
영구저장은 [저장]·[확정]에서만 하며, [초기화]는 재계산이 아니라 초기값 복원이다.
자동저장 폐지
- B06 조정창 기준벽 구간값: 800ms 디바운스 PUT을 없애고 세션(b06:culvertopt)에
담는다. flushCulvertOptions()를 B06 [저장]·[확정]과 B05 [저장]이 부른다.
- B05 구조물: 조작 즉시 PUT + 서버 재조회로 화면을 덮어쓰던 것을 세션
(b05:structures) 적재로 바꾼다. 저장 전에도 고르고 지울 수 있도록 식별자를
crypto.randomUUID()로 미리 발급한다(서버는 빈 값일 때만 새로 발급).
- B05에서 만지고 B06에서 확정하는 경로를 위해 flushPendingStructures()를 공용화.
초기값 스냅샷
- common_util_initial_snapshot: 자동설계 체인 성공 직후 정본 파일 4트리와
routes+자식 4표를 initial_snapshot/에 뜬다. 이후 읽기 전용.
- reset_route_design: 스냅샷이 있으면 DELETE와 같은 트랜잭션에서 행을 되세우고
파일을 되돌린다. 없으면 종전 재계산 폴백. 응답에 restored를 더한다.
조작 응답
- 등고선 재적용·B06 진입 정합·[모두 적용]에서 전체 화면 오버레이 제거.
- 재계산이 design을 갈아끼울 때 extra_spans를 보존한다(다른 조작값과 동일).
테스트: tmp/tests/test_initial_snapshot.py 5건 추가. 245 passed·8 failed(기존
실패 — 기슭막이 이관 때 placement가 interval→point로 바뀐 것을 테스트 미반영).
고정 10m(전/후 5)를 기본으로 두니 소유 벽 연장(유출 12m·유입 21.5m)보다 짧아,
+5.9m 떨어진 링크 카드에서 기준벽은 서는데 다단만 사라졌다. 손대기 전에는 기준벽
연장을 그대로 따르고, 손으로 잡은 단만 그 값으로 고정한다.
- tierSpanOf: 저장값 → 소유 벽 연장(유출측은 유출 벽, 유입측은 유입 벽/집수정)
→ 마지막으로 10m(5/5). 그리기·링크 판정·조정창 표시가 이 함수 하나를 쓴다
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
단별 구간값을 넣으면서 성토부선까지 단(tier) 연장으로 잠갔다. 단 기본 연장이
10m(전/후 5)라 5.9m 떨어진 링크 카드에서는 기준벽이 서는데도 그 아래 성토부선이
통째로 사라졌다. 성토부선은 기준벽 소유물이므로 inletIn/outletIn으로 판정하고,
다단 벽만 자기 구간값으로 판정한다.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
다단은 소유 벽 연장에 종속이라 길이·전/후를 따로 못 잡았다. 계곡부·능선부에서는
아래 단일수록 연장이 달라져 자동 규칙으로 못 잡으므로 수동 입력으로 간다.
조정창 UI는 기준벽 것을 그대로 쓴다(길이·기준측점 전·후 세 행).
- Const: EXTRA_SPAN_DEFAULT(10m·5/5) + applySpanPatch()로 길이↔전/후 산식을
기준벽과 공용화
- Station_Controls: StructureSpanControl에 tierValuesFor/updateTier 추가. 값은
세션(b06:extraspan)에 담고 캐시(design.extra_spans)에도 얹는다. 단 수를 줄이면
그 단의 구간값도 지운다
- View_Structure: 다단 키면 단별 제어기로, 기준벽은 종전 경로
- Wire: culvertReach에 단별 연장을 더하고, 링크 카드에서 각 단을 자기 구간값으로
판정한다(tierSpanOf)
- Corridor_Structures: 3D 스윕 범위를 단별 구간값으로
- 저장: CrossSectionPatch.extra_spans(프론트 payload + WallSpanPatch 스키마 +
확정 병합) — [저장]·[확정] 때 정본에 남는다
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
syncCount가 요청 단 수를 세워진 만큼으로 되돌리는데, 안내는 유출측 채널에만
있었다. 집수정 계류측·독립 기슭막이 유입 벽에서는 +를 눌러도 아무 일이 없는
것처럼 보였다. 유출측과 같은 문구·같은 조건(그 벽이 선택된 상태)으로 알린다.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
관이 없는 독립 기슭막이인데 벽 역할 키를 따라 "유입/유출 기슭막이"로 적혀
좌·우 제목이 갈렸다. 관 숨김 세트의 기준벽은 좌우 구분 없이 기존 문구
B06_Cross_Revet_Own을 쓴다.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
조정창은 한 컴포넌트였지만 행 표시가 역할 키에 묶여 있어, 같은 성토 벽이라도
유입/유출에 따라 창 구성이 갈렸다. 재질 선택지도 3종뿐이라 B05 폼 형태 6종을
조정창에서 고를 수 없었다.
- 단 수 행을 하나로 합치고 자리를 높이 행 아래로 고정한다. 저장 채널(extra /
bextra)은 선택된 벽이 정하고, 등간격(≡)은 유출 채널 전용으로 제한한다
- 형태 선택지를 B05 폼과 같은 6종(REVET_FORMS)으로 바꾼다. 조작값 m의 의미를
재질 코드에서 형태 문자열로 바꾸고, 옛 저장분은 revetFormOf가 옮겨 읽는다
- 높이 한계는 revetHeightLimit(form), 돌 표현은 materialFromForm(form)으로
형태에서 파생한다. 벽 제원 계산은 revetWallSpec()으로 모아 배관·독립·추가가
같은 함수를 쓴다
- 조정창 행 라벨 "재질" → "형태"
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
이관 뒤 독립 기슭막이 벽 역할이 uphill_side로 정해져, 고른 쪽이 상단이면
유입 역할(집수정 후보·placeInletWall·다단 없음), 하단이면 유출 역할
(성토부선+buildExtrasAt)을 타서 좌우 동작이 갈렸다. 새 기하 코드를 만들지
않고 이미 있는 유출 경로 인자를 유입 쪽에도 넘겨 재사용한다.
- Const: revetHeightLimit 콘크리트 4.0 → 5.0 (materialLimit과 통일)
- Geom: hidden_pipe면 basinReason null, 유입 배치에 requireCrossing 전달,
basinExtras 채널을 유입 벽 성토부선·다단으로 사용, inletFill 생략
- Solve: placeInletWall이 requireCrossing을 통과시킨다(기본 false 유지)
- Wire: 링크 카드에서 hidden_pipe면 유입측 다단·성토부선을 벽 연장에 맞춰 유지
- Panel/View_Structure: hiddenPipe deps 추가 — 집수정 형식 숨김, 유입 벽에도
단 수 행 표시
- Culvert: hidden_pipe의 basinFill 툴팁 이름을 "유입부"로
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
값 버튼을 누르면 refreshCard가 카드 DOM을 통째로 갈아끼우므로 조정창도 새로
만들어진다. 새 창의 show()가 기억해 둔 scrollTop을 되살리려 하지만 그 시점에는
카드가 아직 문서에 붙기 전이라 레이아웃이 없어 대입이 무시됐고, 붙은 뒤에는
0(제목 줄)으로 남았다.
restoreScroll()을 두어 대입이 먹지 않았으면 다음 프레임(카드가 붙은 뒤)에 한 번
더 넣는다. 그때 창이 닫혔거나 떨어져 나갔으면 건드리지 않는다.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
B06 조정창은 구간값(길이·전/후)을 `{role}_revet_length_m` 등 배관 기슭막이와 같은
키로 pipe_points에 저장한다. 그런데 독립 기슭막이만 백엔드가 전용 키(`length_m`·
`before_m`·`after_m`)를 읽어, 조정창에서 바꾼 값이 저장돼도 매번 원래 값으로
되돌아왔다(실데이터 확인: 234.1m에 두 벌의 키가 따로 존재).
배관과 옵션 키를 한 벌로 합쳐 원천을 하나로 만든다 — 이제 두 기슭막이의 차이는
관 유무(hidden_pipe)뿐이다.
- B06_Section_Engine_Culvert `_revet_side`: `{role}_revet_*` 우선, 없으면 옛 전용
키로 폴백(구 저장분 호환)
- B05_Profile_UI_Drainage_Facility: 독립 기슭막이 폼이 OUTLET_REVET_KEYS를 쓰고
저장 시 inlet_revet_* 에도 같이 싣는다. 읽기는 legacyRevetOptions로 옛 키 폴백
검증: tmp/tests/test_revet_span_source.py 3건 통과, tsc --noEmit 통과,
load_culvert_sets 실데이터로 234.1m 유입 10.5/5/5.5 · 유출 42/21/21 반영 확인.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
백로그 3건(2026-08-28).
① 기슭막이 조정창 — 배관 세트와 같은 4축
- RevetKey 에 "own" 추가. 조작값은 배관 벽과 같은 저장소를 쓴다
(design.revet_adjust["own"] + 세션 b06:revetx:{project}:{route}).
- 벽을 누르면 조정창이 열린다(활성 표시 포함). 좌우 ◀▶ · 사면 ▲▼ · 높이 ± ·
자동 자리 초기화만 보이고 집수정·단 수·구간·연동·재질·등간격 행은 숨는다
(재질은 구조물 옵션 형태가, 단 수는 단 수(다단)가 정본).
- computeRevetmentLayout 이 x(좌우)·d(사면, +는 아래)·h(높이)를 반영하고
3D도 같은 값을 읽는다(코리도 해시에 조작값 포함).
② 초기화가 프로젝트 단위 세션 값도 비운다 — b05:uphill / b06:std-cross.
키에 route_id 가 없어 새 노선에 그대로 되붙던 값이다. 표준횡단 세션 정리는
clearStandardCrossSession() 한 곳에서 한다.
③ 초기화가 주인 없는 코리도 파일을 지운다 — prune_corridor_files(비치명,
삭제 개수 로그). 실측 배경: 유효 route 1개인데 19개(약 85MB)가 남아 있었다.
검증: pytest 245 passed / 7 skipped(코리도 정리 4건 신규). tsc·ruff·prettier 통과.
실측 — 315m 벽 클릭 시 조정창 제목 "기슭막이(독립)", 좌 1회·사면 아래 1회·
높이 +0.1 조작에 벽 좌표가 각각 이동하고 세션에
{"315.00:own":{"x":1,"d":1,"h":1.1}} 저장. 임시 구조물은 삭제하고 정본 복구.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-28 사용자 확정(자동 배치는 만들지 않는다).
- 자리: 기준(1단)이 맨 위이고 추가 단은 아래로 붙는다. 다음 단은 설계 성토면을
따라 **벽 높이만큼 내려간 자리**에 서고, 성토면 끝을 지나면 자리가 없다.
- 자리가 없으면 배관 기슭막이처럼 토스트로 알린다("3단 중 1단만 세울 수
있습니다 … 기준 올림으로 1단을 올린 뒤 단을 늘리세요"). 같은 측점을 다시
그려도 한 번만 세도록 측점 단위로 집계한다.
- 이동 수단: 레지스트리에 lift_m(기준 올림·사면 위로) / shift_m(좌우) 추가.
배관 세트의 조정창 4축 대신 구조물 옵션으로 받는다 — 조정창 연결은 별건.
- 3D는 단마다 솔리드를 스윕. BUILD_VERSION 88, 구조물 해시에 단 수·이동값 포함.
폐기한 두 시도: ① 배관 세트의 간격 규칙(하단 수평선 +근입 ↔ 전면 경사선) —
벽이 성토면 위에 서면 다음 단 시작점이 곧바로 지반 아래라 3단 요청에 1단만
섰다. ② 성토끝부터 위로 등분 — 기준이 아래가 되어 "기준=상단"과 어긋났다.
검증: pytest 241 passed / 7 skipped. 실측 — 높이 2.0·올림 0 → 1단 + 토스트,
높이 1.0·올림 5m → 315m에서 3단(540.6 → 539.6 → 538.6, 1.0m씩 하강),
3D revet 솔리드가 2D와 같은 수(285:2 · 300:2 · 315:3). 임시 구조물은 삭제하고
정본 복구(구조물 0 · 측점 23).
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-28 사용자 지시(자동 배치는 만들지 않는다).
- 레지스트리에 tiers(단 수, 기본 1 = 단일 벽) 추가. 서버가 스펙에 실어 보낸다.
- 자리 규칙: **성토선 하단(성토면 끝)이 1단**, 단을 늘리면 설계 성토면을 따라
위로 올라가며 그 아래에 단이 들어온다(배관 세트와 다른 점은 관이 없고 시작
자리가 성토선 하단이라는 것). 자리는 성토끝 → 노견 끝을 단 수로 등분해 잡는다.
- 3D는 단마다 솔리드를 스윕한다. BUILD_VERSION 87, 구조물 해시에 단 수 포함.
폐기한 시도: 배관 세트처럼 아래로 쌓기. 벽 상단이 이미 지반선이라 다음 단
시작점이 곧바로 원지반 아래가 되어 "묻힘" 가드에 걸렸다 — 3단 요청에 1단만
생성되는 것을 실측으로 확인하고 방향을 위로 바꿨다.
검증: pytest 241 passed / 7 skipped. 공용 브라우저 실측(285~315m·우측·높이 2.0·
3단) — 카드 3장 × 벽 3개 = 9개, 자리가 성토끝에서 노견 쪽으로 등간격
(315m: -5.42/538.10 → -4.28/538.94 → -3.14/539.77), 벽 하단은 상단 -2.5m
(높이 2.0 + 근입 0.5). 3D revet 솔리드 9개. 임시 데이터는 원래 정본으로 복구
(구조물 0 · 측점 23 · 관 4).
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
--color-text-primary 는 이 프로젝트 테마에 없는 이름이라 stroke 가 none 으로
풀려 물넘이 바닥 실선·포장 빗금이 화면에서 통째로 사라졌다(점선만 보였다 —
그쪽은 --color-text-secondary 를 써서 살아 있었다).
확대 캡처를 찍다가 발견했다. 있는 이름(--color-text-body)으로 바꾼다.
검증: 같은 카드를 다시 찍어 바닥 실선·빗금 포장이 나오는 것을 확인.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-28 사용자 지시 4건.
① 물넘이 파임을 3D 코리도에도 반영
classifyStation 이 노면·노견 조각 표고를 횡단도와 같은 산식(fordDeckElevationAt)
으로 내린다. 노면 밖 비탈은 그대로라 노견 끝에 수직 단차가 선다. BUILD_VERSION 85.
② 표준횡단면 저장분을 포장 재계산에 싣는다
확정 때 종단 정본 options 에 실린 standard_cross_section 을 읽어
enforce_pavement_ranges / attach_default_designs 에 넘긴다. 없으면 config 기본값.
③ 포장 구간 기본값 10 / 5 / 5 (기슭막이와 같은 출발값).
④ 독립 기슭막이
- 측점: is_station_planting_type() 규칙 신설(A군 + D군 구간형). 구간형은
시작·기준·종료에 측점을 심는다 — 길이가 길수록 횡단도가 여러 장 나온다.
구조물 재이관 차단도 같은 규칙을 본다(기타 고스트 방지).
- 서버: B06_Section_Engine_Revetment 신설 — 구간 안 측점 전부에 section.revetment.
- 횡단도: B06_Section_UI_Cross_Revetment 신설 — 벽 상단 = 성토면 끝(설계선-지반
교차점), 아래로 높이 + 근입 0.5m, 전면 1:0.3(교본 7-3). 설치 측은 사용자가
좌/우로 고른다(레지스트리 side 옵션 신설 — 자동 판정 없음).
- 3D: 같은 폴리곤을 기준측점 전/후만큼 스윕. BUILD_VERSION 86 + 구조물 해시에
물넘이·기슭막이 제원 추가.
검증: pytest 241 passed / 7 skipped(신규 4건). 공용 브라우저 실측 —
물넘이 240m 투입 시 3D 노면이 계획고보다 0.38~0.42m 아래(대조군 200m 정상),
기슭막이 225~255 투입 시 측점 23→25·횡단 카드 3장·3D revet 솔리드 3개(31링).
임시 데이터는 원래 정본으로 복구했다.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
횡단도로 표현할 것이 없는 시설이라 측점을 만들 이유가 없다(2026-08-28 사용자 확정).
서버는 resolve_extra_stations 에서 이미 제외했고, 프론트도 맞춘다.
표시(setIrregularStations)와 서버 전송(irregularStations())에서만 뺀다 —
관 정본 대조(setPipeChainages)는 전체 목록을 써야 물넘이가 정본에서 지워지지
않으므로 pipesToStations 는 그대로 둔다. 알약 레인은 pipesToMarks 가 따로
만들므로 구조물 버튼은 그대로 남는다.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
[초기화] 후 관 지점 횡단(비정규 측점)이 통째로 사라지던 문제. 측점 정본은
longitudinal.json stations + cross_*.json 파일인데, 그 파일을 쓰는
run_section_generation 에는 공급자가 없었다. 유일한 공급자가 프론트
[임시저장]이라 초기화·반폭 재생성이 파일을 다시 쓸 때마다 측점이 날아갔다
(실측: 초기화 직후 cross 23 -> 19, irregular 4 -> 0).
- resolve_extra_stations 신설 — 관 정본(pipe_points.json) + 구조물 정본 A군에서
매번 파생한다. 라벨 형식은 프론트 structureLabel()과 같다. 물넘이포장은
비정규 측점으로 올리지 않는다(2026-08-28 사용자 확정).
- run_section_generation 이 generate_sections 앞에서 이 목록을 심는다. 호출자가
넘긴 extra_stations 는 무시한다 — DB 스냅샷으로 되쓰이는 값이라 되먹이면
B04에서 지운 관이 파일 정본을 이기고 부활한다.
- _load_pipe_anchors -> _load_pipe_points. 계획선 정착과 측점 생성이 같은 한 번의
읽기를 쓴다. 지문 불일치 경고에 버린 건수를 남겨 침묵 실패를 없앤다.
- B06_Section_Router._extra_stations 삭제(자기참조라 늘 빈 값이었다).
706 -> 689줄로 700줄 규칙 위반도 함께 해소.
- 격자와 같은 정수 미터인 비정규 측점은 격자로 스냅한다 — cross_*.json 파일명이
정수 미터라 측점 수와 파일 수가 어긋났다.
- 구조물 이관 필터를 A군 6종 라벨로 넓힌다 — 서버가 심은 세월교·BOX암거 라벨을
"기타"로 재이관하던 고스트를 막는다.
검증: pytest 229 passed / 7 skipped(신규 14건). 공용 브라우저 실측 — 초기화 직후
임시저장 없이 cross 23 / irregular 4(라벨 일치), B06 카드 23장, 반폭 재생성
20->25->20 에도 23 유지, DB cross_sections 23행.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
유출구 쪽 성토부에 성토면도 지형도 없는 검은 쐐기가 남던 문제(2026-08-27 사용자:
"유출구에 일부 성토부에 터짐 있음").
원인 두 가지.
- dropCollinear 가 **격자 인덱스 공간**에서 공선 판정을 했다. 코리도 격자는 노선을
따라 휘어 있어 인덱스로 직선인 구간이 실좌표에서는 곡선이다. 그걸 지우면 폴리곤이
실제 마스크 안쪽으로 코너를 잘라먹고, 그 밖 셀은 되살릴 영역에서 빠져 구멍이 된다.
빠진 셀이 전부 편거리가 큰 바깥 끝 열(40~46)이었던 것도 이것으로 설명된다.
- traceMaskLoops 가 갈림목에서 아무 변이나 집어(pop) walk 가 남의 고리로 새 나가고,
막다른 길에서 끊긴 열린 사슬을 고리로 담았다. 표시용일 땐 안 보였지만 이 커브가
되살릴 영역의 근거가 된 뒤로는 판정을 무너뜨린다.
- Cut: dropCollinear 를 실좌표(모델 XY) 기준 수직거리 1mm 판정으로 바꾸고, 고리를
**실좌표로 옮긴 뒤** 다듬는다. traceMaskLoops 는 갈림목을 오른손 규칙(마스크를
왼쪽에 두고 담으므로 우회전 우선)으로 가르고 **닫힌 고리만** 담는다.
- BUILD_VERSION 83 -> 84.
실측(공용 브라우저) — 도려낸 셀 중 되살릴 영역이 안 덮는 셀:
- 우측 1,447/7,941(18%) -> 109/7,941(1.4%)
- 좌측 1,292/7,021(18%) -> 14/7,021(0.2%)
- 지형 유지 삼각형 476,906 -> 478,459 (+1,553)
- cut-merged 고리 8개, 점 수 47~128 (절단선 폭증 없음)
- tsc --noEmit 0
남은 0.8%: _Corridor_Region.ts simplifyLoop 의 마디 하한 0.25m 가 셀(0.06~0.2m)보다
커서 경계를 조금 더 안쪽으로 당기는 것으로 보인다. 별도 항목으로 남긴다.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
구조물 자리에 원지반이 드러나지 않거나, 드러나도 날개벽 자리에 성토면도 지형도
없는 검은 슬리버가 남던 문제.
원인은 지우는 쪽과 되살리는 쪽이 **서로 다른 자료로 영역을 만든 것**이다.
- 지우는 쪽: 성토 리본 셀 마스크(slope-projected ∪ wing-box, 0.2m 격자 + 현 절단)
- 되살리는 쪽: 원본 성토 스케치 − (구조물 ∪ 변형 성토 스케치)를 **다시 조립**
구조물 고리에는 날개벽이 패널 두께 슬리버로 섞여 있어 그 자리만 covered로 빠졌고,
BOX암거에서는 변형 성토선이 원본과 collinear가 되어 영역이 통째로 공집합이었다.
이제 지우는 쪽이 남긴 자국을 그대로 쓴다 — cut-merged 는 _Corridor_Cut.ts 의
traceMaskLoops 가 셀 마스크에서 되짚은 윤곽이다. 그 안의 지형은 도려내지 않으므로
**원본 지형 메시가 그 자리에 그대로 남는다**. 새 서피스를 만들지 않는다
(2026-08-27 사용자: "원지반 오리지널 데이터에서 가져오라니까", "회색 서피스는
있는데 원지반과 같은 메시가 아니잖아. 너가 그냥 만든 거잖아").
- Clip: withStructureHoles 를 cut-merged 한 줄로 대체. slope-original/wing-box/
structure/slope-deformed 조합과 axisLocked 분기 삭제(순 -20줄). __corridorHoles
는 고리 수 대신 exposeLoops/exposeCells/exposeRatio 를 담는다.
- BUILD_VERSION 82 -> 83.
실측(공용 브라우저):
- cut-merged 고리 8개 = 구조물 4세트 x 좌우.
- exposeCells 23/6400(0.36%) -> 164/6400(2.56%). 7배.
- 지형 유지 삼각형 474,854 -> 476,885 (+2,031).
- tsc --noEmit 0.
폐기한 시도(같은 브랜치 2d897164 이후 작업트리에서만 존재했다):
성토 격자에 원지반 표고를 재샘플링해 역마스크로 까는 방식. 경계는 정의상
일치했으나 코리도 격자로 다시 만든 면이라 원지반 메시와 형상이 달랐다.
남은 것: 유출구 일부 성토부 터짐(사용자 확인) — 다음 작업.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
배수관 세트(기슭막이) 구간에서 노견 → 벽 이음선 상단 성토 사면이 3D에서 통째로
비어 있었다. 포켓(FILL_CUT_APPLY)은 노견까지 파는데, 되메움 배열(fillRuns,
4a5a98fe)에는 벽 아래 다단 성토선·집수정 성토선만 담겨 그 위가 안 메워졌다.
culvertSilhouettes가 designTrim 사면선을 push(실루엣)만 하고 addRun(리본)은 하지
않은 게 원인 — 세월교·BOX암거는 각각 fillSegments·fillLine으로 담고 있어 무관하다.
새 면을 만들지 않는다. 실루엣이 이미 쓰던 designTrim.min/maxSlope 점 배열을 리본
배열에도 등록할 뿐이다.
- Station_Structure: pushTopSlope() 추가 — push + addRun을 함께 하되 측당 한 번만.
벽 루프와 집수정 분기가 같은 designTrim 사면선을 각각 담으므로 표시가 없으면
리본이 두 장 겹친다. 호출 3곳(벽 루프, 집수정 slope, 집수정 2점 폴백).
- Corridor: BUILD_VERSION 79 -> 80.
실측(공용 브라우저 A/B, git stash로 코드만 교체):
- 패치 리본 6 -> 10장. 늘어난 4장 전부 기슭막이 세트(측점 84.3·275.71).
- 275.71 좌측: 기존 z 531.11~535.93 바로 위에 536.85~539.92가 새로 앉았다.
사이 0.92m는 벽 전면 — 구조물 솔리드 몫이라 원래 비는 자리.
- 84.3은 수정 전 양측 0장 — 포켓만 파고 되메움이 통째로 없었다.
- 날개형 구조물(149.73·200.92) 리본 수·z 범위 소수점까지 불변 = 무회귀.
- tsc --noEmit 0.
화면 검증은 사용자가 직접 수행(2026-08-27 사용자 지시).
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
세월교 날개 사다리꼴이 안 채워지고, 채운 판이 절취 영역 밖으로 삐져나오던 문제.
원인은 패치 리본이 "노선 프레임 수직 행 × 소유 측점 단면 한 장" 스윕이라
비스듬한 날개 부채꼴과 방향·범위가 안 맞는 것이었다. 세월교 전용으로 고친다
(`section.ford` — BOX암거·기슭막이는 종전 경로 그대로).
- 종방향: `wingSpanOnRoute` 신설. 날개 고리 점을 frameAt로 0.25m 간격 훑어 노선에
실투영한다. 종전 직선 전방 투영은 곡선+먼 편거리(-7.85m)에서 3m 어긋나 고리
앞부분이 범위 밖으로 밀려났다. 범위는 점유 구간 ±5m로 묶는다.
- 횡방향: `wingRangeAt`이 행 횡단선 × 주황 고리 교점의 안쪽·바깥쪽 끝을 낸다.
바깥은 `extendRunTo`, 안쪽은 `prependRunTo`(노견 클램프). 지반 핀·스커트 유지.
- `clipRunBelow`: 바닥판 상단 Z(ford slabTopElevation)를 넘는 점을 지운다.
종단 shift 반영, 천장 지나는 마디에 교차점 삽입.
- `patchClip`: 패치를 maskFillByPlanCurves에 한 번 더 통과시켜 **지우는 규칙의
반대**를 건다 — 절취 영역 밖 셀은 지우고, 걸친 셀은 clipCellByChord(inside)로
안쪽 조각만 남긴다. 두 면이 같은 경계를 공유한다.
- 700줄 초과로 날개 몫 도구를 _Corridor_Carve_Wing.ts로 분리(순수 이동).
- `__corridorScene` 노출(검증용 레이캐스트 좌표 변환).
- BUILD_VERSION 67 -> 79.
자체검증: 삐져나온 패치 셀 우 74->0(145셀 마스크+조각 198), 좌 지느러미 47->0.
BOX암거·기슭막이 masked=0(무변화). 영역 안 구멍 우 66/98% -> 39/56%.
typecheck 통과, tmp/tests 213 passed·7 skipped, 변경 파일 prettier.
남은 작업: 좌측 96% 구멍(slope-projected 전폭 절취 vs fillSegments 0.25m),
BOX 패치 리본 축퇴 원인 확인.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
B06 횡단이 내는 변형 성토선을 3D 포켓에 되메운다. 다단 기슭막이면 횡단이
성토 > 구조물 > 성토 > 구조물 > 성토라 성토선이 하나가 아니다 — 합치지 않고
구간 1줄 = 리본 1장으로 낸다.
- Station_Structure: StructureSilhouette.fillRuns 추가. 31점 폴리라인을 인덱스로
쪼개지 않고 B06 배열(outletFill/basinFill.segments, ford fillSegments, box
fillLine)에서 바로 담는다. 벽 전면은 안 담는다 — 구조물 솔리드 몫.
- Carve: 패치 리본을 실루엣당 1장이 아니라 run당 1장. 구간 배열을 쓸 때는 벽 트림을
걸지 않는다 — 바깥 끝 하나로 자르는 규칙이라 단 사이 구간이 아랫단 벽 바깥까지
통째로 잘려 사라졌다(실측: 0.86m 구간 2개 전멸). 배열이 비면 리본도 없다.
- Build: FILL_PATCH_APPLY 신규(옛 CARVE_APPLY 대체). 투영 커브·절취를 다 낸 뒤
붙인다 — 먼저 넣으면 투영선이 패치에서 z를 읽어 절취 경계가 달라진다.
- Viewer: snapCorridorEdges에서 패치 제외. B06 성토 구간은 벽 뒷면·구체 최상단에서
끝나는 게 정상인데 지형에 스냅해 4.1m 세로 지느러미가 생겼다(실측).
- BUILD_VERSION 60 -> 63.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>