Commit Graph
139 Commits
Author SHA1 Message Date
eomsangdonandClaude Opus 5 933f513da0 feat(B06): 세월교 노면을 월류 높이만큼 내려 앉힌다
횡단도에 그려지던 노면은 사실 월류부 가장 아래를 자른 자리다 — 구체 위 노면은
월류 높이만큼 낮게 앉아야 한다(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>
2026-08-30 12:54:29 +09:00
eomsangdonandClaude Opus 5 7385b4d76f fix(B06): 월류 폭을 바꾸면 월류 높이(필요 수심)가 따라오게 한다
`syncFordSummary`가 "빈 칸이거나 계산값보다 작을 때만" 높이를 채워 넣어, 폭을
바꿔 필요 수심이 **낮아지는** 쪽으로 움직이면 칸이 옛 값 그대로 남았다. 안내문
(설계유량·필요 수심·필요 단면)만 바뀌고 높이는 안 바뀌는 것으로 보였다.

칸이 아직 **직전에 자동으로 채운 값 그대로**인지 본다(±0.005m). 그대로면 자동
상태로 보고 계산값을 계속 따라가고, 사용자가 다른 값을 넣었으면 그때부터 그
값이 이긴다. 시설을 새로 폼에 올릴 때는 추적을 끊어(`fordAutoDepthM = null`)
저장돼 있던 높이를 사용자 값으로 대접한다.

자체검증(공용 브라우저 5174, 측점 7+9.7 세월교, 설계유량 36.734 ㎥/s):
- tsc --noEmit 통과.
- 자동 상태(폭 10 → 높이 0.39)에서 폭 5 → 0.63(올라감), 폭 20 → 0.25(내려감).
  수정 전에는 0.63에서 내려오지 않았다.
- 높이를 2.5로 직접 넣은 뒤 폭 20→5 → 2.5 유지(사용자 값 우선).
- 조작값은 원래대로(폭 10 · 높이 자동 0.39) 되돌려 놓았다.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-30 12:23:38 +09:00
eomsangdonandClaude Opus 5 e78feab5ed feat(B06): 세월교 좌측 폼을 배관 측점과 같은 양식으로 맞춘다
사용자 지시 3건(2026-08-30).

1) 횡단도에서 세월교 측벽을 고르면 좌측 폼의 그 측 칸이 선다 — 배관 기슭막이가
   쓰던 경로를 그대로 탄다. `SideSlots`에 `wingInlet`·`wingOutlet`을 더하고
   `slotOf()`가 배관 칸 → 독립 기슭막이 칸 → 날개벽 칸 순으로 지금 서 있는 쪽을
   고른다. 세월교 조정창은 `dataset.side`·`dataset.panelTitle`을 세워 어느 칸을
   가리키는지 알린다. 벽 선택 뒤 `refreshAdjustSlots()`도 배관과 같게 붙였다.

2) 월류 폭·높이·수량·개략 단면 결과를 관경 바로 다음으로 옮겼다(바닥 경사도 같은
   묶음에 둔다 — 물넘이포장 전용이라 세월교에서는 숨는다). 개략 단면 안내문은
   읽는 글이라 10px·opacity 0.75로 줄였다.

3) 세월교·날개벽 숫자 칸을 기슭막이·집수정과 같은 [-][값][+] 묶음으로 바꿨다
   (월류 폭·높이·바닥 경사·수량·날개 높이·길이·각도). `stepper()`에 표시 자릿수
   인자를 더해 련·각도는 정수로 보인다("1.0련"·"45.0°" 방지). 기본값 1이라 기존
   호출부는 그대로다.

자체검증(공용 브라우저 5174, 측점 7+9.7 세월교):
- tsc --noEmit 통과.
- 유입 측벽 선택 → `날개벽(유입) (세월교)` 칸 강조, 유출 측벽 → `날개벽(유출)
  (세월교)`. 한 번에 한 칸만 선다.
- 폼 항목 순서 실측: 관종 > 관경 > 월류 폭 > 월류 높이 > 수량 > 날개벽(유입) …
  안내문 font-size 10px, 보이는 스텝 묶음 9개.
- 수량 + → 폼 "2"·조정창 "2련", 각도 + → 폼 "50"·조정창 "50°" (소수점 없음).
- 회귀: 배관 측점 기슭막이 선택 → `유출구 (기슭막이(유출))` 강조 + 높이·길이·
  기준측점 전/후 행 이식 정상.
- 조작값은 원래대로(수량 1·각도 45) 되돌려 놓았다.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-30 12:05:22 +09:00
eomsangdonandClaude Opus 5 c8b5a8bf38 fix(B05): 종단테이블 구조물 측점 빈 칸의 세로선 제거
구조물 측점(세월교·BOX암거 등) 빈 칸의 border-left가 이웃 규칙 측점 값
한가운데를 지나고, 곡선 L/R 칸 경계와 x가 정확히 겹쳐 절토고~곡선 R를
관통하는 한 줄로 보였다. 값 행에서만 선을 지우고 곡선 행 구분선은 유지한다.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-30 00:32:09 +09:00
eomsangdon 60bbe89edb fix(B03): rerun analysis on reupload
Clear derived browser caches so B05 loads the newly generated route and section data.
2026-08-29 20:17:37 +09:00
eomsangdon e6ac071f58 merge: 구조물 패널 후속 수정 4건을 통합한다 2026-08-29 19:29:39 +09:00
eomsangdonandClaude Opus 5 2ee71eff19 fix(B05,B06): 독립 기슭막이 폼에서 단 수를 뺀다
조정창에서 옮겨 오는 [추가 기슭막이(단)] 행과 같은 값이라 두 벌이었다.
단 수 정본은 횡단 설계 patch(extra_wall_counts)이고 조작은 그 행이 한다 —
폼 칸과 그 값을 나르던 배선(tierCount·setTierCount)을 함께 걷어낸다.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-29 19:17:07 +09:00
eomsangdon 184afa6e89 merge: B05·B06 구조물 패널 일원화 작업을 B07 작업 브랜치로 가져온다
다른 브랜치(feat/B05B06-구조물패널-일원화)의 B05·B06 구조물 배치 폼 일원화
결과를 받는다. 충돌 없음 — 그쪽은 B05/B06만, 이쪽은 B07/B08만 건드렸다.

그쪽 브랜치에 남아 있던 옛 폴더(B08_DesignDetail, B07_Quantity)는 이쪽 삭제가
반영돼 사라진다. main.py·라우트·척도 등 B07 작업물은 이쪽 것이 유지된다.
2026-08-29 19:13:03 +09:00
eomsangdonandClaude Opus 5 091a30286e fix(B05,B06): 독립 기슭막이 단 수를 좌·우로 나누고 그 칸을 강조한다
- 단 수를 바깥 행에서 좌·우 칸 안으로 옮긴다. 저장 키도 좌·우 각각
  (inlet_revet_tiers·outlet_revet_tiers, 옛 tiers는 폴백·호환 기록).
  폼 값은 다단 채널(extra·bextra)로 가고, 폼은 그 채널 값을 되읽는다.
- 좌·우 칸에도 조정창 행 이식 자리를 두어, 벽을 고르면 배관 유입구·유출구와
  같이 그 칸이 강조되고 조정 항목이 옮겨 온다. 칸 이름표는 좌·우 그대로 둔다.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-29 19:09:42 +09:00
eomsangdonandClaude Opus 5 1165c657e7 feat(B05,B06): 독립 기슭막이를 좌·우 칸으로 나누고 단 수를 도면에 잇는다
- 설치 측·단 수를 컨테이너 밖 한 행으로 빼고, 벽 제원은 [기슭막이 (좌)]·
  [기슭막이 (우)] 두 칸으로 나눈다. 설치 측이 한쪽이면 그 칸만 뜬다.
  좌·우 이름표는 그 측점 오르막 방향(uphill_side)이 정한다.
- 두 칸은 배관 유출구와 같은 양식이다 — [구조][형태] + 길이·높이·전/후.
  구조 선택지는 기슭막이 하나뿐이지만 레이아웃을 맞추려 그대로 둔다.
- 값은 유입/유출 키로 좌·우 따로 저장한다(옛 한 벌 저장분은 양쪽으로 펼쳐 읽는다).
- 단 수에 [-][값][+] 조정칸을 붙이고, 폼 단 수를 다단 채널(extra·bextra)로
  보낸다 — 배수관 측점처럼 횡단도에 단이 선다.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-29 19:05:07 +09:00
eomsangdonandClaude Opus 5 c100daabce fix(B06): 고른 것 없이 폼을 만지면 이유를 알린다
좌측 폼의 실시간 반영은 **고른 항목**에만 걸린다. 아무것도 고르지 않은 상태에서
형태·높이를 바꾸면 값이 조용히 버려져 "형태별 모양이 안 그려진다"로 보였다.
패널에 hasSelection()을 열고, B06에서 선택 없이 폼이 바뀌면 안내한다.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-29 18:37:16 +09:00
eomsangdonandClaude Opus 5 f7be3f9765 feat(B06): 기슭막이를 형태대로 그리고 다단 칸을 따로 세운다
- 옵션 토글 켜짐 색을 주 액션 색으로 바꾼다(붉은 채움이 경고처럼 읽혔다).
- 같은 문구 토스트가 연달아 오면 앞 토스트가 떠 있는 동안 무시한다.
- 벽 내부 표현을 형태별로 나눈다: 메=돌, 찰=돌+줄눈, 콘크리트=사선 해칭,
  돌망태=2열 격자, 통나무=원, 바자=말뚝+엮음. 배관용·독립용·추가용이
  공용 `appendWallHatch` 하나를 쓴다(배관 오버레이의 중복 코드 제거).
- 추가(다단) 기슭막이를 고르면 유입구·유출구 아래에 전용 칸이 서고
  형태·높이·구간값·옵션 행이 그 칸으로 옮겨 간다. 선택이 풀리면 사라진다.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-29 18:12:28 +09:00
eomsangdon 5393ea4720 fix(B05): 라벨이 스텝 - 버튼을 집던 문제와 라벨 간격을 함께 정리한다
- <label>이 [-][입력][+] 묶음을 감싸면 암묵 대상이 **첫 버튼(-)**이 된다.
  입력에 id를 붙이고 label.htmlFor로 명시 연결해 라벨·입력 위 커서가 -를
  활성화하거나 라벨 클릭이 값을 줄이던 동작을 없앴다(2026-08-29 사용자 보고).
- 제목(라벨)과 항목 간격도 --b05-label-gap(2px) 한 변수로 묶고 라벨 글자를
  caption 크기로 맞췄다. 측점 행은 가로 배치라 열 간격 6px 유지.

검증: label.control=INPUT, 라벨 클릭 후 값 10 불변, 커서 올려도 - 배경 투명.
시설·이식·메모·구조물군 라벨 간격 모두 2px·라벨 높이 14px.
2026-08-29 16:17:50 +09:00
eomsangdon 80abf38134 style(B05): 구조물 배치 폼 컨트롤 높이를 한 변수로 묶는다
2026-08-29 사용자 지시 — 조정창에서 옮겨 온 행(단 수·옵션)의 컨트롤 높이가
기준이다. 섹션에 b05-structure-section 클래스를 붙여 스코프를 잡고,
--b05-control-h(26px) 하나로 입력·셀렉트·측점칸·메모·스텝 묶음·이식 행을 맞춘다.
높이를 바꾸려면 이 변수만 고치면 된다.

검증: 이식행·스텝 묶음·스텝 입력·시설 셀렉트·구조물군 셀렉트·측점 입력·메모가
모두 26px. 하단 액션 버튼(추가·삭제·리셋)은 종전 35px 유지.
2026-08-29 16:13:43 +09:00
eomsangdon 5d0d2f0918 feat(B05,B06): 구조물 배치를 실시간 반영으로 바꾸고 이식 행·강조를 정리한다
2026-08-29 사용자 지시 3건:
1. 유입구·유출구로 옮겨 온 단 수·옵션 행을 폼 양식(라벨+한 덩어리 컨트롤)으로
   맞추고 2열 격자로 세운다.
2. 이미 고른 항목은 값이 바뀔 때마다 곧바로 반영한다([수정] 버튼 불필요).
   버튼은 visibility만 꺼 자리를 남겨 삭제·리셋의 위치·크기가 흔들리지 않게 했다.
   정본 주입이 되먹임으로 돌아와도 편집 중인 폼은 닫지 않는다.
3. 도면에서 구조물을 고르면 그쪽 그룹(유입구/유출구)을 붉은 계열로 강조한다.
   카드만 고른 경우는 강조하지 않는다 — 대표 벽은 조용히 딸려 오고, 그 벽을
   다시 눌러 선택을 풀면 강조만 꺼지고 행은 남는다.

검증: 슬롯 grid 2열, 카드 선택 시 강조 없음·행 2개, 다른 벽 클릭 시 유입구 그룹
붉은 테두리(rgb(192,57,43)), 재클릭 시 강조 해제·행 복귀. 길이 + 즉시 캐시
반영(11) 후 - 원복(10), 폼 유지. typecheck 통과.
2026-08-29 16:00:04 +09:00
eomsangdon b3e9085cb5 style(B05): 숫자 조정칸을 [-][숫자][+] 한 덩어리로 만든다
2026-08-29 사용자 지시 — 가운데 숫자, 좌 -·우 +가 테두리 하나를 나눠 쓰는
하나의 컨트롤로 보이게 한다.
- DOM 순서를 [-][input][+]로 바꿈
- 테두리·배경·모서리는 묶음(.b05-structure__stepper)이 갖고, 안쪽 입력은 테두리
  없이 가운데 정렬. 버튼은 가르는 선 하나씩만 갖는다. 포커스는 묶음 테두리로 표시.

검증: 순서 BUTTON-/INPUT/BUTTON+, 묶음 테두리 1px·radius 8px, 입력 테두리 0·
가운데 정렬, + 10.0→11.0 · - 11.0→10.0 동작 확인.
2026-08-29 15:36:58 +09:00
eomsangdon 83a3a5ffc8 style(B05): 스텝 버튼을 넓히고 숫자 칸 스피너를 없앤다
- [-][+] 버튼 폭 20px → 28px, 글자 11px → 12px(2026-08-29 사용자 지시)
- 구조물 배치 폼·시설 서브폼의 number 입력에서 브라우저 기본 증감 화살표 제거
  (증감은 스텝 버튼이 맡고, 기본 화살표는 칸 폭을 먹으며 눈금도 다르다)

검증: 배수관 폼 실측 — 버튼 28px·입력 55px, appearance textfield(스피너 없음),
길이·높이·전·후 4행 모두 동일.
2026-08-29 15:35:15 +09:00
eomsangdon 8e8849e4ac feat(B05,B06): 배관 조정 항목을 유입구·유출구 폼으로 모은다
2026-08-29 사용자 지시(좌측 패널 항목 정리 — 배관 자리):
- 길이·높이·기준측점 전/후 입력칸에 [-][+] 묶음 추가(stepper). 눈금은 조정창과
  같다(길이 1m·전후 0.5m·높이 0.1m). 래퍼 여백 0이라 2열 격자는 그대로다.
- 형태·구조물 형식 행은 [횡단 조정] 사본에서 숨김 — 폼의 형태·구조와 중복.
- 단 수·옵션 행은 고른 벽 쪽(유입구/유출구) 그룹으로 DOM 이식. 배선을 옮기지 않아
  값·동기는 그대로고, 사본이 갈릴 때마다 다시 이식한다.
- 유입구 '구조' 드롭다운에 조정창 형식(자동·집수정 I/ㄴ/ㄷ)을 병합. 고른 값이
  정본 둘(inlet_type·inlet_structure)을 함께 정하고, B06에서는 유입측 형식 제어와
  양방향으로 맞춘다. 형태·형식 연결은 미연결 상태 그대로 둔다.

검증(공용 브라우저): 유입구 스텝 5·유출구 4개, 구조 목록 6종 병합, 벽 선택 시
단 수·옵션이 유출구 그룹으로 이동(dock에는 높이·구간값만 남음). B05도 동일 폼이
정상 표시되고 [횡단 조정]은 뜨지 않음. typecheck 통과.
2026-08-29 15:30:55 +09:00
eomsangdon f99f1f68d3 feat(B03,B04,B05): 초기 설계가 도는 동안 B05·B06 진입을 막는다
파일을 올리면 지표면 확정 직후 "노선 설계로 이동하세요"를 적고 그 뒤에 초기 설계
체인이 돈다. 그 사이 B05에 들어가 만지면 반쯤 계산된 값을 편집하게 되고, 그 편집이
섞인 채 초기값 스냅샷이 찍힌다. 계산이 끝날 때까지 준비 화면(B11)에서 기다리게 한다.

- initial_snapshot: 마커(initial_design.lock) 생성·해제·조회와 스냅샷 무효화,
  편집 정본 제거를 한곳에 둔다. 마커는 스냅샷 4트리 밖(프로젝트 루트)이라 복원에
  딸려 들어가지 않는다.
- 체인 두 곳(자동·재설계): 시작에 마커, finally에서 해제. 재설계 체인은 끝에 스냅샷을
  무효화한다 — 사용자 입력을 유지한 채 재계산하므로 그 결과를 찍으면 가짜 초기값이
  된다. 다음 [초기화]가 폴백을 타며 새 초기값을 만들고 그때 촬영한다.
- /surface/status: 마커가 있으면 in_progress + initial_design으로 답한다. stage 1이
  COMPLETE면 진행률 파일을 무시하는 기존 분기를 건드리지 않으려고 별도 확인이다.
- 진입 게이트(routeAfterPreloadCheck)와 B11이 그 상태를 보고 기다린다. 상태 조회가
  실패하면 막지 않고, B11 대기는 20분 상한을 둔다.
- [초기화] 폴백: 체인 전에 structures.json과 drainage/edits를 지운다. 남기면 구조물
  측점이 사용자 편집분에서 다시 파생돼 초기값이 오염된다(2026-08-29 실측).

테스트 4건 추가. 249 passed·8 failed(기슭막이 이관 때 생긴 기존 실패, 변경 전 동일).
2026-08-29 15:09:09 +09:00
eomsangdonandClaude Opus 5 0d4e4c758d feat(B06,B05): 기슭막이 연동·경사를 재정의하고 링크 카드까지 강조한다
사용자 확정(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>
2026-08-29 12:01:22 +09:00
eomsangdon cb8cf2c9d2 fix(B05,B06): 구조물 저장 실패가 횡단 저장까지 막던 것을 고친다
화면 실측에서 나왔다. 세션에 남은 미저장 구조물이 서버에 거절되자(등록되지 않은
타입 400) 예외가 B06 [저장] 전체를 중단시켜 sections/save가 아예 나가지 않았다.

flushPendingStructures를 .catch()로 감싸 토스트만 띄우고 횡단 저장은 계속한다.
미저장분은 세션에 그대로 남아 다음 [저장]에서 다시 시도된다.
2026-08-29 11:39:03 +09:00
eomsangdon 08c0dc3228 feat(B05,B06): 자동저장을 걷어내고 초기값 스냅샷으로 초기화한다
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로 바뀐 것을 테스트 미반영).
2026-08-29 11:15:29 +09:00
eomsangdonandClaude Opus 5 1e55322f2f feat(B06): 다단 기슭막이 구간값을 단별로 손수 잡는다
다단은 소유 벽 연장에 종속이라 길이·전/후를 따로 못 잡았다. 계곡부·능선부에서는
아래 단일수록 연장이 달라져 자동 규칙으로 못 잡으므로 수동 입력으로 간다.
조정창 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>
2026-08-29 10:54:03 +09:00
eomsangdonandClaude Opus 5 e959879500 fix(B05,B06): 독립 기슭막이 구간값이 조정창 키와 어긋나 리셋되던 것을 고친다
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>
2026-08-28 19:41:02 +09:00
eomsangdonandClaude Opus 5 f916aa52a1 feat(B05,B06): 독립 기슭막이를 배수관 시설 종류로 이관
독립 기슭막이(구조물 정본 D군 구간형)를 배수관 측점(pipe_points)의 시설 종류
"revetment"로 옮긴다. 관을 숨긴(hidden_pipe) 배관 세트로 얹혀 컴퓨트·렌더·패널·
연동·경사·3D·수량이 배관 경로를 그대로 탄다.

- common_util_drainage_pipes: PIPE_FACILITY_REVET 추가, 여유고 0
- B06_Section_Engine_Culvert: _revet_set/_revet_side — 관 숨김 세트 생성
- B06_Section_Router: attach_revetments 제거(관 세트에 통합)
- B05_Profile_Engine_Sections: 기슭막이는 시작·기준·종료 세 측점 심기
- B05_Profile_Structure_Types.json: revetment placement=point, managed_by=pipe_points,
  설치 측에 "양쪽" 추가
- B05_Profile_UI_Drainage_Facility: 기슭막이 옵션 폼(설치 측·형태·높이·길이·전후·단 수)
- B06_Section_UI_Cross_Culvert_Geom: 관 숨김 시 관경 하한·역경사 클램프·유출 가드 해제
- B06_Section_UI_Cross_Culvert_Wire: restrictToSide — 설치 측(좌/우)만 남기기
- B06_Section_UI_Cross_View: 옛 D군 경로 이중그리기 가드, appliedAdjust 되받기
- B06_Section_UI_Cross_Wall: 기슭막이 벽 공용 기하·그리기 층 신설
- B05_Profile_UI_Corridor_Structures: 3D도 같은 규칙(관 실린더 숨김, 관통 컷 없음)

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-28 19:09:28 +09:00
eomsangdonandClaude Opus 5 0050adf7d2 feat(B06): 독립 기슭막이 조정창 연결 + 초기화 세션·코리도 정리
백로그 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 15:41:24 +09:00
eomsangdonandClaude Opus 5 c6b7708984 feat(B06): 독립 기슭막이 다단 — 기준이 맨 위, 추가 단은 아래로
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 15:15:24 +09:00
eomsangdonandClaude Opus 5 101a6a8f79 feat(B05,B06): 독립 기슭막이 다단 — 성토선 하단에서 위로 쌓고 3D도 단마다 세운다
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>
2026-08-28 14:54:57 +09:00
eomsangdonandClaude Opus 5 e371c2754a feat(B05,B06): 물넘이 3D 파임 + 독립 기슭막이 횡단·3D + 포장 기본값·표준횡단 반영
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 12:54:45 +09:00
eomsangdonandClaude Opus 5 80c2a265f9 feat(B05,B06): 물넘이 횡단 표현 + 포장 구간 수기 지정, 경사 자동 포장 폐지
2026-08-28 사용자 확정 스펙.

물넘이포장 — 노면을 판 자리로 그린다
- 서버가 _ford_pavement_set(월류 폭·월류 높이·바닥 경사)을 만들고 물넘이만
  span 연동을 켜 **범위 안 측점 전부**에 얹는다. 깊이가 없으면 None으로 두어
  화면이 그리지 않는다(수치를 지어내지 않는다).
- 횡단도: 기존 계획고 점선 + 물넘이 바닥 실선 + 진한 회색 빗금 포장.
  깊이는 노선 중심 기준, 바닥은 유입(상단측)이 높게 기운다. 경사를 비우면
  그 측점의 노면 횡단경사를 쓴다.
- 물넘이 폼에 "바닥 경사 유입→유출(%)" 칸을 추가하고, 월류 폭 기본값
  리터럴을 config_frontend 상수로 모아 서버 값과 짝지었다.

포장 — 사용자가 구간으로 지정한다
- 구조물 레지스트리 G군 pavement_concrete 를 되살려 기준측점 + 길이 + 전/후로
  받는다(기슭막이와 같은 폼). 길이 기본값은 0 = 미지정.
- pavement_ranges/paved_at 이 구간을 판정하고, enforce_pavement_ranges 가
  저장분이 비포장이어도 구간 안이면 포장으로 다시 계산한다(사용자 조작값 승계).
- **종단경사 자동 포장 적용을 없앴다** — paved=suggested 3곳 제거. 별표1-2 상한
  초과 경고(pavement_suggested 배지·근거 문구)는 그대로 남는다.
- 포장 구간이 물넘이를 통째로 품으면 모달로 알리고 앞/뒤로 나눠 저장하고,
  끝만 걸치면 값을 고치라고 안내하고 멈춘다.

700줄 제한: _compute_default_designs 를 Router_Design 으로 옮겼다(657/306줄).

검증: pytest 238 passed / 7 skipped(신규 9건). 공용 브라우저 실측 — 물넘이 임시
투입 시 240m 카드에만 파임 3요소가 그려지고(일반 포장 박스 0), 바닥이 계획고보다
0.4m 아래·노면 전폭 4.0m에 1.5% 기울기, 220·260은 비포장 유지. 겹침 규칙은
브라우저에서 모듈을 직접 불러 3분할·안내·취소를 확인했다. 실측용 임시 관 지점은
매번 원래 정본 4건으로 복구했다.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-28 12:16:38 +09:00
eomsangdonandClaude Opus 5 10bb43b380 feat(B05,B06): [임시저장] 버튼명을 [저장]으로 바꾼다
상태로 보면 '변경사항 저장'이 맞고 '확정 전 임시'라는 이름이 혼선을 줬다
(2026-08-28 사용자 지시). 기능 변경 없음 — 라벨·토스트 문구만 바꾼다.

- B05_Route_Btn_TempSave / B06_Profile_Btn_Save: 임시저장 -> 저장
- 토스트 4건: '임시 저장했습니다' -> '변경 내용을 저장했습니다' 등
- [편집 되돌리기] 툴팁의 '이미 임시저장한 내용은' 문구 정리

검증: tsc --noEmit 통과. 공용 브라우저 실측 — B05 하단 액션이
[초기화][저장][횡단 이동], '임시저장' 라벨 0건.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-28 10:14:28 +09:00
eomsangdonandClaude Opus 5 41cbc0f1e9 fix(B05): 물넘이포장을 비정규 측점에서 빼고 종단 알약으로만 둔다
횡단도로 표현할 것이 없는 시설이라 측점을 만들 이유가 없다(2026-08-28 사용자 확정).
서버는 resolve_extra_stations 에서 이미 제외했고, 프론트도 맞춘다.

표시(setIrregularStations)와 서버 전송(irregularStations())에서만 뺀다 —
관 정본 대조(setPipeChainages)는 전체 목록을 써야 물넘이가 정본에서 지워지지
않으므로 pipesToStations 는 그대로 둔다. 알약 레인은 pipesToMarks 가 따로
만들므로 구조물 버튼은 그대로 남는다.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-28 09:46:26 +09:00
eomsangdonandClaude Opus 5 0979927dfc fix(B05): 구조물 측점을 정본 파일에서 파생하는 단일 공급자를 세운다
[초기화] 후 관 지점 횡단(비정규 측점)이 통째로 사라지던 문제. 측점 정본은
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-28 08:38:58 +09:00
eomsangdonandClaude Opus 5 3a5680fd76 fix(B05): 잘린 자리 윤곽을 실좌표에서 다듬어 성토부 터짐을 막는다
유출구 쪽 성토부에 성토면도 지형도 없는 검은 쐐기가 남던 문제(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>
2026-08-27 22:38:46 +09:00
eomsangdonandClaude Opus 5 5118482767 fix(B05): 원지반 재노출 영역을 실제로 도려낸 자리 윤곽(cut-merged)으로 바꾼다
구조물 자리에 원지반이 드러나지 않거나, 드러나도 날개벽 자리에 성토면도 지형도
없는 검은 슬리버가 남던 문제.

원인은 지우는 쪽과 되살리는 쪽이 **서로 다른 자료로 영역을 만든 것**이다.
- 지우는 쪽: 성토 리본 셀 마스크(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>
2026-08-27 22:32:01 +09:00
eomsangdonandClaude Opus 5 2d897164cc feat(B05): BOX암거 구조물 자리 원지반을 재노출한다 (영역 뺄셈 방식)
BOX암거(측점 200.92) 자리에서 구조물이 들어와도 원지반이 드러나지 않던 문제.
원지반 재노출 공식은 `원본 성토선 − (구조물 ∪ 변형 성토선)`인데, BOX는 변형
성토선이 원본과 기본값에서 정확히 collinear가 되어 결과가 공집합이었다.

- Station_Structure: StructureSilhouette.endsAtStructure 신설, boxSilhouettes만 true.
  BOX 실루엣은 "구체 밖으로는 잇지 않는다" 원칙대로 구체 최상단에서 끊긴다.
- Plan: endsAtStructure면 slope-deformed를 trimSlopeToGround로 지반까지 외삽하지
  않는다. 종전엔 무조건 최대 30m 외삽해 원본 성토선과 겹쳤다. 기하(낙차 0.5m)로
  판정하지 않는 이유는 기슭막이 유출측 회귀 때문이다(실측 84.3 우측 5.10 -> 5.69m).
- Clip: 재노출 후보에 BOX의 wing-box(날개 사다리꼴) 추가 — 절취 소스에는 있고
  되살릴 목록에는 없어 그 자리가 성토면도 지반도 없는 구멍이었다. 세월교는 제외
  (그 자리는 되메움 패치가 덮는다). __corridorHoles에 exposeCells/exposeRatio 추가 —
  고리 수만으로는 되살릴 영역이 공집합인지 알 수 없었다.
- Structures/Structures_Box/Plan_Footprint: planOutline 표식(상판+에이프런)과
  outlineLoop 신설. BOX structure 탑뷰 커브를 부재별 7고리가 아니라 아령 외곽선
  한 줄로 낸다 — 부재별로 내면 날개벽이 패널 두께 슬리버로 남는다. 기하 불리언은
  쓰지 않는다(BOX 부재는 전부 같은 고정축 위 평행 링).
- BUILD_VERSION 80 -> 82.

실측(공용 브라우저, git stash 로 코드만 교체):
- slope-deformed bbox 200.92 좌 2.85x4.46 -> 2.44x1.02, 우 2.76x3.76 -> 2.42x0.88.
  세월교 149.73·배수관 84.3/275.71 은 소수점까지 불변 = 무회귀.
- structure 고리 200.92 좌우 각 7 -> 1(면적 5.18 = 상판 1.79 + 에이프런 3.39 정확 일치).
  슬리버 0.40x2, 0.15x2 소멸.
- __corridorHoles.original 8 -> 12, covered 44 -> 32.
- 지형 유지 삼각형 477,153 -> 477,613.
- tsc --noEmit 0.

미해결: 날개벽 안쪽 검은 슬리버가 화면에 남아 있고 면이 삐져나온다(사용자 확인).
다음 커밋에서 접근을 바꾼다 — 영역 뺄셈 대신 성토 격자의 groundZ(원지반 표고)를
역마스크로 복사해 도려낸 셀에 그대로 깐다. 이 커밋은 그 되돌림 지점이다.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-27 22:13:26 +09:00
eomsangdonandClaude Opus 5 521fb89c30 fix(B05): 기슭막이 상단 성토면을 되메움 리본 배열에 담는다
배수관 세트(기슭막이) 구간에서 노견 → 벽 이음선 상단 성토 사면이 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>
2026-08-27 21:02:28 +09:00
eomsangdonandClaude Opus 5 a756181067 feat(B05): 세월교 변형 성토면을 절취 영역 경계에 맞춰 채우고 잘라낸다
세월교 날개 사다리꼴이 안 채워지고, 채운 판이 절취 영역 밖으로 삐져나오던 문제.
원인은 패치 리본이 "노선 프레임 수직 행 × 소유 측점 단면 한 장" 스윕이라
비스듬한 날개 부채꼴과 방향·범위가 안 맞는 것이었다. 세월교 전용으로 고친다
(`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>
2026-08-27 20:02:00 +09:00
eomsangdonandClaude Opus 5 a864acd17d feat(B05): 변형 성토면을 지반 교차선에서 끊고 구조물 자리 원지반을 되살린다
- 패치(변형 성토면) 바깥 끝 스커트 신설(_Corridor_Skirt.ts): 지형 메시 높이를
  밑선으로 성토면 → 원지반 −1m까지 판을 세운다. 성토 빗금 메시 서피스로 그린다
  (절취 측벽은 종전대로 일반 서피스). 소유 측점 행에서 지반에 닿는 줄만 대상
  (허용 0.5m) — 벽 뒷면·구체 상단에서 끝나는 줄은 제외해 BOX암거 앞면에 없던
  서피스가 생기던 회귀를 막는다.
- 패치 행을 그 행 지반선과 직접 교차시켜 자르고 연장한다(trimSlopeToGround).
- 날개 사다리꼴 몫만큼 패치 행 범위를 넓히고(wingSpanOf), 날개벽 평면 스케치는
  트림하지 않는다. BOX암거는 행 가로 방향을 기준 측점 좌향으로 고정한다.
- 구조물 안착 자리 원지반 재노출: 원본 성토 스케치 − (구조물 ∪ 변형 성토 스케치)를
  코리도 스트립에서 빼 지형을 남긴다(PlanLoopRegion·SubtractedRegion 신설).
- BUILD_VERSION 63 → 67: 저장본을 만료시켜야 기하 수정이 화면에 반영된다.

자체검증: 스커트 3리본 56판(최대 낙차 0.92m), 원지반 +2,104 삼각형,
tmp/tests 213 passed, typecheck·prettier 통과.
남은 작업: 세월교 날개 사다리꼴 채우기(날개 축 스윕) — 다음 세션.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-27 18:42:15 +09:00
eomsangdonandClaude Opus 5 4a5a98fe44 feat(B05): 구조물 구간 성토면을 B06 구간 배열대로 되메운다
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>
2026-08-27 16:04:51 +09:00
eomsangdonandClaude Opus 5 106536b145 fix(B05): 측벽을 본 구조물·날개 **머지 영역의 경계**로만 세운다
절취 영역은 본 구조물 스트립과 날개 사다리꼴을 **합친 것**인데, 벽은 두 스케치의
변을 따로 세우고 있었다. 그래서 한쪽 영역의 변이 다른 영역 **안쪽**을 지나는 구간
(속살)에도 벽이 섰다 — 세월교 좌측 실측: 날개 광선 1.87m·1.77m 구간이 스트립 안에
통째로 잠겨 있었다(2026-08-27 사용자 지적).

직전 커밋(86d1d6ac)은 날개가 있는 측의 끝 횡단선을 통째로 뺐는데, 그러면 날개가
못 덮는 도로 쪽 구간이 비어 반대로 모자랐다. 되돌리고 **경계 트림**으로 바꾼다.

- Plan: `trimToUnionBoundary()` 신설 — 벽 후보 변을 다른 영역 테두리와의 교점에서
  쪼개고, 조각 가운데가 다른 영역 안이면 버린다. 2cm 미만 부스러기도 버린다.
  세트마다 벽 후보(스트립 끝 횡단선 2개 + 날개 [끝선→날개→끝선])와 근거 영역을
  모아 두었다가 세트가 끝날 때 한 번에 `cut-wall`로 낸다.
- BUILD_VERSION 59 → 60.

검증(실빌드 데이터, 구간 중점 양옆 2cm 판정):
- 8개 세트·측 **전부 경계 100%** — 속살(양옆 다 안쪽) 0, 영역 밖 0.
- 구간 수 세트당 2~8(84.3 2 · 149.73 6 · 200.92 8 · 275.71 2), 벽 판 16장/측,
  삼각형 32개/측, 판 길이 0.17~11.52m, 최대 높이 3.15m.
- 노견선·성토 끝선 위 판 0 유지.
- ISO 근접 캡처: BOX·세월교 절취부 옆면이 큰 판으로 이어지고 구멍 없음.
- npm run typecheck 통과, prettier 적용.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-27 12:55:49 +09:00
eomsangdonandClaude Opus 5 86d1d6ac13 fix(B05): 날개벽이 있는 측은 스트립 끝 횡단선 벽을 빼 중복을 없앤다
날개 사다리꼴 벽면이 이미 그 자리를 막는데 스트립 끝 횡단선 벽이 같은 자리에 한 장
더 서 있었다(2026-08-27 사용자). 날개벽을 낸 측은 끝 횡단선을 안 낸다.
날개가 없는 세트(파형강관 84.3·275.71)는 종전대로 끝 횡단선이 유일한 측벽이다.

- Plan: 세트마다 `wingWallSides`를 모아, 그 측은 `cut-wall`에 끝 횡단선을 안 담는다.
- BUILD_VERSION 58 → 59.

검증(실빌드 데이터):
- 벽 판 20 → **16장**(삼각형 40 → 32), 좌·우 동일.
- 세트별 벽 자리: 149.73·200.92는 날개 6마디만, 84.3·275.71은 끝 횡단선 2마디.
- 끝선(노견·성토 끝단) 위 판 0 유지, 벽 최대 높이 3.18m 불변.
- ISO 근접 캡처에서 구멍 없음 — 겹쳐 있던 판만 사라졌다.
- npm run typecheck 통과, prettier 적용.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-27 12:07:53 +09:00
eomsangdonandClaude Opus 5 93945d356c feat(B05): 측벽을 셀이 아니라 평면 스케치에서 바로 세운다
사용자 지적: 벽이 셀 단위 조각으로 깔린다(세월교 한 세트에 96~111조각, 길이
5~20cm). 절취가 평면 스케치 기준이었으니 벽도 스케치에서 바로 만들면 된다.

- Plan: `cut-wall` 소스 신설 — 벽이 설 자리만 담은 **열린 폴리라인**이다.
  · 비탈 스트립은 **양 끝 횡단선** 2개(긴 변은 노견선·성토 끝선이라 뺀다).
  · 날개 사다리꼴은 [끝선 교점 → 날개 시작 → 날개 끝 → 끝선 교점] 하나
    (`wingBoxLoops`가 `{loops, walls}`를 돌려준다).
- Cut: `SkirtBuilder`(셀 훑기) 제거. `cut-wall` 커브 마디마다 판 하나를 세우고,
  표고는 성토 리본에서 읽는다(`sampleRibbon` — 행은 XY상 직선이라 점을 그 선분에
  투영해 서피스 z·원지반 z를 같은 자리에서 보간). 밑선은 원지반 − 1m 유지.
- Mesh: `cut-wall` 커브는 선으로 안 그린다(벽 서피스로만 나간다).
- BUILD_VERSION 57 → 58.

검증(실빌드 데이터):
- 벽 삼각형 852/824 → **40/40**(판 20장씩). 판 길이 0.17~11.52m, 중앙 2.0~2.4m
  (종전 세월교 조각 길이 중앙 5~10cm).
- 노견선·성토 끝선 위를 달리는 판 **0**(양측). 벽 최대 높이 3.18m.
- ISO 근접 캡처: BOX암거·세월교 절취부 옆면이 큰 판 몇 장으로 막힌다. 바닥은 열림.
- npm run typecheck 통과, prettier 적용.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-27 12:01:50 +09:00
eomsangdonandClaude Opus 5 b9938dfa18 fix(B05): 노견 쪽·성토 끝단 쪽 리본 경계에는 측벽을 세우지 않는다
거기서 잘린 게 아니라 리본이 끝난 자리다(2026-08-27 사용자).

- 통째로 지운 셀의 모서리 벽: 열 방향 이웃이 리본 밖이면 건너뛴다(`col > 0`,
  `col < cols - 2`). 행 방향(노선 앞뒤)은 종전대로.
- 걸친 셀의 교점 직선: 두 교점이 모두 안쪽 끝(노견)이나 바깥 끝(성토 끝선) 위를
  달리면 건너뛴다. 판정은 **열 좌표 기준 ±0.1열**(셀 폭의 10%) — 커브가 끝선과
  나란히 지나면 교점이 가로 모서리에 걸려 열 좌표가 3~5mm 빗나간다(실측).
- BUILD_VERSION 55 → 57.

검증(실빌드 데이터):
- 벽 삼각형 1,450/1,358 → 852/824(우/좌).
- 성토끝단선 위를 달리는 벽 구간 8/6 → **0**, 노견선 위 2/2 → **0**
  (구간 길이 3cm 이상, 선까지 거리 1cm 이내 기준).
- 벽 ↔ 절단커브 거리는 그대로 중앙값 0.000m. 리본 조각 삼각형 814/787 불변.
- npm run typecheck 통과, prettier 적용.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-27 11:18:32 +09:00
eomsangdonandClaude Opus 5 d70908f973 fix(B05): 측벽을 격자 계단이 아니라 실제로 잘린 선에 세운다
사용자 지적: 벽이 최종 절단면이 아니라 셀 격자를 따라간다. 실측으로 확인 —
벽 정점이 리본 격자점과 100% 일치하고, 실제 절단커브까지 거리 중앙값 5.6~5.9cm
최대 21~30cm(셀 대각선 크기)였다. 격자는 재배치 후(0.2m) 격자가 맞았고, 문제는
`pushSkirt`가 마스크 격자 윤곽을 그대로 밀어낸 것이었다.

- `clipCellByChord`가 남길 조각과 함께 **교점 직선**(chord)을 돌려준다.
- `SkirtBuilder` 신설 — 걸친 셀은 그 셀을 자른 교점 직선에, 통째로 지운 셀은
  **안 지워진 이웃 쪽 모서리**에만 벽을 세운다(걸친 셀은 조각이 모서리까지 덮으므로
  모서리 벽을 세우지 않는다). 밑선은 원지반 − 1m, 교점 위 점은 원지반을 이중선형 보간.
- `traceMaskLoops` 기반 스커트 제거(그 함수는 `cut-merged` 표시 커브에만 남는다).
- BUILD_VERSION 54 → 55.

검증(실빌드 데이터):
- 벽 ↔ 절단커브 거리: 중앙값 0.000 · p90 0.000 · 평균 0.001/0.000 · 최대 0.091/0.010m
  (종전 중앙값 0.056/0.059 · 최대 0.213/0.298).
- 격자점과 일치 100% → 71.6/71.8%(나머지는 교점 위 점 = 격자 밖).
- 벽 삼각형 360/356 → 1,450/1,358. 리본 조각 삼각형 814/787 불변.
- ISO 화면에서 절취부 테두리 계단이 사라졌다.
- npm run typecheck 통과, prettier 적용.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-27 11:08:19 +09:00
eomsangdonandClaude Opus 5 e9914af675 fix(B05): 절취 측벽에서 빗금을 뺀다
벽은 방향성을 표현하는 면이 아니다(2026-08-27 사용자). 성토면 빗금은 그대로 두고
벽만 단색으로 그린다.

- Mesh: 벽 재질에서 `map`(빗금 텍스처)과 uv 속성을 뺀다.
- Cut/Build: 벽이 UV를 안 쓰므로 `CorridorCutWall.uvs`와 수집 코드를 지운다.
- Corridor: 저장본에서 `uvsBase64` 제거, ENVELOPE_VERSION 4 → 5.

검증: 재빌드 후 벽 서피스 우 360·좌 356 삼각형 그대로, `uvs` 필드 없음.
ISO 화면에서 벽면이 단색, 주변 성토면 빗금은 유지. npm run typecheck 통과, prettier 적용.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-27 10:54:04 +09:00
eomsangdonandClaude Opus 5 43804f2f5b feat(B05): 절취 측벽을 별도 서피스로 빼고 원지반 아래 1m 여유를 준다
측벽을 성토 리본 메시에 섞지 않고 **구조물처럼 별도 서피스**로 낸다(2026-08-27
사용자). UV·법선이 리본에 묶이지 않아 다음 단계(구조물 안착 바닥면·트림)에서 따로
다룰 수 있다. 밑선은 원지반에서 **1m 더 내린다** — 지반 보간 오차만큼 밑이 뜨는 걸
막고, 지형은 코리도 자리에서 이미 잘려 있어 여유분은 가려진다.

- Build: `CorridorCutWall`(삼각형 수프 + UV) 추가, 빌드 결과에 `cutWalls`로 싣는다.
- Cut: 벽 삼각형을 `trimTris`에서 분리해 `walls`로 돌려준다. `SKIRT_MARGIN_M = 1`.
- Mesh: 벽마다 메쉬를 따로 만든다(`corridor-cut-wall:{측}:{번호}`) — 색·빗금은
  성토와 같아 한 몸으로 보이되 객체는 분리.
- Corridor: 저장본에 `cutWalls` 담고 ENVELOPE_VERSION 3 → 4, BUILD_VERSION 53 → 54.
- Viewer: 검증 훅 `raw`에 `cutWalls` 추가.

검증(실빌드 데이터):
- 리본 조각 삼각형 우 814·좌 787(벽 빠짐), 벽 서피스 삼각형 우 360·좌 356.
- 벽 높이 최소 1.00m(여유분) / 평균 2.26·2.87 / 최대 3.80·6.25m — 1차 대비 정확히 +1m.
- ISO 화면: BOX·세월교 절취부 옆면이 벽으로 막히고 바닥만 열려 있다(다음 작업).
- npm run typecheck 통과, prettier 적용.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-27 10:46:19 +09:00
eomsangdonandClaude Opus 5 86a20cccb4 feat(B05): 절취 윤곽을 z로 밀어내 측벽을 세우고 성토면에 머지한다
자를 때 쓴 평면 스케치(절취 윤곽)를 z축으로 밀어내 성토면 ↔ 원지반을 잇는 측벽을
세운다(2026-08-27 사용자). 벽은 성토 리본과 **같은 메쉬·같은 재질**에 담아 별도
서피스가 아니라 머지된 한 면으로 그려진다. 구조물이 안착된 성토부 방향(바닥)
서피스는 다음 작업.

- Build: 성토 리본에 격자점별 원지반 표고 `groundZ`를 싣는다(행 지반선을 rows에
  보관해 flush에서 offset으로 읽는다). 벽의 밑선이다.
- Cut: `pushSkirt()` — 절취 윤곽 모서리마다 [서피스 z → 원지반 z] 사각형을 삼각형
  둘로 세워 `trimTris`에 함께 담는다. UV는 u=누가거리/2, v=z/2로 빗금이 세로로도
  실척으로 이어진다. 높이 0인 구간(성토 끝선)은 건너뛴다.
- Corridor: BUILD_VERSION 52 → 53.

검증(실빌드 데이터):
- 벽 삼각형 우 417·좌 418(조각 삼각형 합계 1,166·1,135), groundZ 적재 확인.
- 벽 높이 우 0.001~2.80m(평균 1.08)·좌 0.003~5.25m(평균 1.61).
- ISO 화면에서 세월교·BOX암거 절취부 옆면이 청록 벽으로 막혔다. 바닥은 아직 열림.
- npm run typecheck 통과, prettier 적용.

남은 것: 벽이 격자 모서리를 타 6cm×0.2m 계단이 보인다(교점 직선으로 세우면 없어진다).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-27 10:35:31 +09:00
eomsangdonandClaude Opus 5 2daaf127b8 feat(B05): 날개벽 사다리꼴을 절취 영역에 합치고 병합 커브를 새 데이텀에 낸다
날개벽이 있는 구조물(세월교·BOX암거)도 절취한다. 평면 스케치에 이미 사다리꼴
(`wing-box`)이 있으므로 **비탈 투영선과 XY 합집합**으로 묶어 한 번에 자른다
(2026-08-27 사용자). 어디가 잘리는지 눈으로 보려고, 잘린 자리 윤곽을 **현 평면
대비 +3m 새 데이텀**에 `cut-merged` 커브로 따로 낸다. 기존 평면 커브는 그대로 둔다.

- Cut: `CUT_SOURCES`에 `wing-box` 추가. 마스크된 셀 덩어리의 격자 윤곽을 추적해
  (`traceMaskLoops` + 직선 구간 점 버리기) 병합 커브를 만든다. 반환형이
  `{ribbons, curves}`로 바뀐다.
- Plan: `cut-merged` 소스 추가. Mesh: 라임 `0x84cc16`.
- Build: Cut이 돌려준 커브를 planCurves 뒤에 이어 붙인다.
- Corridor: BUILD_VERSION 51 → 52.

검증(실빌드 데이터):
- 마스크 셀 우 6,611→7,856(+1,245)·좌 6,237→6,925(+688). 조각 삼각형 711→814·728→787.
- 합집합 기준 안쪽인데 남은 셀 0, 완전 바깥인데 지운 셀 0(좌·우).
- 병합 커브 8개(좌우×4세트), z가 전부 세트 평면 +3.00m 정확
  (542.05→545.05 / 535.6→538.6 / 540.2→543.2 / 544.76→547.76).
- 화면: BOX·세월교 절취부가 날개 따라 사다리꼴로 벌어지고 라임 커브가 그 윤곽을 덮는다.
- npm run typecheck 통과, prettier 적용.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-27 09:59:10 +09:00
eomsangdonandClaude Opus 5 f4a3cb7b44 fix(B05): 절단 실패 셀을 안쪽 면적 비율로 갈라 경계 잔여 메쉬를 지운다
투영 커브 경계 주변에 미세한 성토 메쉬가 남던 문제(사용자 보고). 원인은
`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>
2026-08-27 07:25:39 +09:00