Commit Graph
100 Commits
Author SHA1 Message Date
eomsangdon 45c6f9aa07 feat(B06): 기준 측점 이동을 B06 구조물 배치에서도 한다
2026-08-29 사용자: 구조물 배치 컨테이너를 B06으로 가져온 이유가 이것이다 —
폼의 기준 측점 숫자를 바꾸면 관이 옮겨져야 한다.

- 관 저장기에 이동 예약(queueMove)을 더했다. 세션 키 b06:culvertmove에 담고,
  [저장]·[확정]의 flush가 옵션 병합과 함께 pipe_points에 반영한다. 세부 배수유역
  재분할은 서버가 관 목록을 다시 받아 처리한다.
- 폼에서 측점을 옮기면 목록·폼 캐시가 즉시 새 자리를 따르고, 옵션은 옮기기 전
  자리로 예약한다(저장 시점 관 목록이 그 자리 기준).
- 이동 안내는 차단 문구에서 '저장 뒤 다시 계산' 알림으로 바꿨다.
2026-08-29 16:53:17 +09:00
eomsangdon effd5e59d3 auto: 2026-08-29 16:49 (EOMSANGDON-HOME) 2026-08-29 16:49:38 +09:00
eomsangdon ce0bc7d234 fix(B06): 값만 고쳐도 뜨던 '기준 측점 이동' 안내를 없앤다
원인: 이동 판정 기준이 0.005m였다. 폼의 측점 표기는 0.1m로 반올림되므로(관이
275.71m면 폼은 275.7m) 높이만 고쳐도 0.01m 차이가 나 이동으로 잡혔다.

- 판정 기준을 관 매칭과 같은 0.51m로 맞춘다.
- 실제로 옮긴 경우에도 함께 온 제원은 반영한다 — 위치만 못 옮길 뿐 B05·B06이
  같은 데이터를 쓰는 한 화면이다.
2026-08-29 16:49:37 +09:00
eomsangdon 7fa005dce1 auto: 2026-08-29 16:45 (EOMSANGDON-HOME) 2026-08-29 16:45:54 +09:00
eomsangdon 936def3755 fix(B06): 폼에서 바꾼 관 제원이 횡단도에 바로 반영되게 한다
원인: 좌측 폼의 실시간 반영이 세션 예약(culvertopt)과 목록 캐시까지만 갱신하고,
횡단 캐시(section.culvert 스펙)를 건드리지 않아 카드가 옛 값으로 남았다.

- 폼 옵션 키를 culvert 스펙 자리로 옮기는 applyPipeOptionsToCache 추가
  (관종·관경, 유입·유출 기슭막이 형태·높이·길이·전/후, 집수정 구간값, 구조).
- 연장이 덮는 범위의 카드만 다시 그린다 — 조정창 조작과 같은 흐름.
- 화면 캐시만 고친다. 영구저장은 [저장]·[확정] 그대로.
2026-08-29 16:45:52 +09:00
eomsangdon c641fa98b2 auto: 2026-08-29 16:42 (EOMSANGDON-HOME) 2026-08-29 16:42:58 +09:00
eomsangdon 16d89b7976 fix(B06): 옮긴 조정 행이 사라지던 문제와 단 수 표시 범위를 고친다
원인: 단 수·옵션 행 DOM을 유입구·유출구로 옮긴 뒤, 다음 선택에서 clearSideSlots가
replaceChildren으로 그 행을 없앴다. 창은 카드당 하나라 같은 카드에서 다른 벽을
고르면 행이 영영 돌아오지 않았다.

- 옮길 때 제자리에 주석 표식을 남기고, 선택이 바뀌면 그 자리로 되돌린다.
- 단 수 행은 **기준(소유) 측점**에서만 다룬다 — 연동으로 넘어온 링크 카드에서는
  숨긴다(2026-08-29 사용자).

검증: 유출→유입→유출로 옮겨 다녀도 단 수·옵션이 매번 제자리에 복귀.
링크 카드(4+0.0)에서는 옵션만 뜨고 단 수는 숨김.
2026-08-29 16:42:57 +09:00
eomsangdon 47bfc88714 auto: 2026-08-29 16:28 (EOMSANGDON-HOME) 2026-08-29 16:28:13 +09:00
eomsangdon a485e22851 fix(B06): 측점만 골랐을 때 기슭막이 조정창이 뜨지 않게 한다
원인: 앞선 커밋에서 넣은 '대표 벽 자동 선택'(selectDefaultWall)이 카드를 고를
때마다 벽을 골라 조정창까지 띄웠다(2026-08-29 사용자 보고: 기슭막이(유출) 창이
계속 떠 있음).

- selectDefaultWall과 그에 딸린 자동 선택 표시(withAutoAdopt 등)를 걷어냈다.
- 카드·목록 선택 리스너에서 남아 있는 벽 선택을 비운다. 벽을 찍어 카드가 딸려
  선택된 경우는 건드리지 않는다(isWallSelecting 가드).

부작용: 단 수·옵션 행은 도면에서 벽을 고른 뒤에만 유입구·유출구에 붙는다
(사용자 확인: 측점 선택 시 자동 구조물 선택은 필요 없음).

검증: 목록 선택·카드 선택 시 조정창 0개·강조 없음, 벽 클릭 시 기슭막이(유출) 창
+ 유출구 강조 + 단 수·옵션 행 표시.
2026-08-29 16:28:11 +09:00
eomsangdon 068eac7f6a auto: 2026-08-29 16:17 (EOMSANGDON-HOME) 2026-08-29 16:17:51 +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 1516710afe auto: 2026-08-29 16:13 (EOMSANGDON-HOME) 2026-08-29 16:13:45 +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 889f746a61 fix(B06): 폼에서 관 옵션을 고칠 수 있게 하고 단 수·옵션 행을 되살린다
2026-08-29 사용자 보고 두 건:
① 값을 고치고 [수정]하면 '추가·이동·삭제는 B05에서' 안내만 떴다 — B06에서 관
   옵션 저장 경로가 없었다. 조정창 구간값과 같은 저장기(culvertOptions.queue)로
   보내 캐시에 예약하고 [저장]·[확정]이 정본에 쓴다. 기준점 이동만 배수유역
   재분할이 걸려 B05 몫으로 남기고, 안내 문구도 추가·삭제/이동으로 나눴다.
② 연동·경사 옵션과 단 수 행이 구조물 배치에서 사라졌다 — 그 행들은 고른 벽에
   딸린 것이라 도면에서 벽을 찍기 전에는 이식될 창 자체가 없었다. 시설을 폼에
   올릴 때 대표 벽(유출 기준벽, 없으면 유입)을 함께 골라 행이 서게 한다.

검증: 목록에서 배수관 선택 → 유출구 그룹에 [추가 기슭막이(단)]·[옵션] 표시.
길이 + 후 [수정] → '수정한 값은 [저장]·[확정] 때 반영됩니다' + 세션
b06:culvertopt에 inlet_revet_length_m 11 예약 확인. typecheck 통과.
2026-08-29 15:45:15 +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 608bdc9f90 fix(B06): 조정창 군더더기 문구를 뺀다
- BOX암거: 구체 길이 뒤 '(이 측 자동)' 삭제(조정값이 있을 때의 '(이 측 +Xm)'은 유지)
- 세월교: 바닥판 뒤 '(날개벽 각도 종속)' 삭제

검증: BOX '구체 길이 5.34m', 세월교 '바닥판 7.63m'로 표시(오버레이·좌측 동일).
2026-08-29 15:03:35 +09:00
eomsangdon 59f5ee88c5 fix(B06): 횡단도 조정창을 위치제어 전용으로 줄인다
좌측 [횡단 조정]과 오버레이 조정창의 항목이 같음을 실측 대조로 확인한 뒤
(기슭막이·세월교·BOX 모두 onlyDock=[], 차이는 이동 세트뿐), 오버레이에서
중복 항목 행을 숨긴다 — 십자(이동)·9키(집수정) 버튼 세트만 남는다.
집수정 기본값(↺)도 9키와 한 세트라 오버레이 전용으로 둔다.

DOM·배선은 그대로라 두 창 값은 계속 함께 움직인다.

검증: 오버레이 rows = [이동]/[이동]/[길이◀▶·표고▲▼], 사본 rows = 형태·높이·
단 수·구간값 3행·옵션 / 측벽 높이·관경·날개벽 3행. 사본 길이 + 10.0→11.0m,
- 원복 확인.
2026-08-29 14:54:41 +09:00
eomsangdon 56ecc08891 feat(B06): 횡단도를 고르면 그 측점 구조물을 좌측 폼에 올린다
- 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-29 14:47:44 +09:00
eomsangdon 2d6d66a389 fix(B06): 구조물 배치를 좌측 최상단으로 올리고 상단 여백을 B05와 맞춘다
- 좌측 패널 순서: 구조물 배치 → 횡단 보기 설정 → 표준 횡단면 설정
  (2026-08-29 사용자 지시 — B05와 같은 첫 컨테이너)
- .b06-profile__form .ui-sidebar-scroll 상단 패딩 8px — B05
  .b05-route__panel .ui-sidebar-scroll과 같은 값으로 구분선과의 갭 일치

검증: 공용 브라우저 실측 — B06/B05 모두 첫 컨테이너 '구조물 배치',
scroll padding-top 8px, 상단 갭 8.0px 동일. typecheck 통과.
2026-08-29 14:27:34 +09:00
eomsangdon 2e28af6e86 feat(B05,B06): 구조물 배치 컨테이너를 B06에 장착하고 [횡단 조정] 사본을 세운다
B05/B06 인터페이스 일원화 1차 (2026-08-29 사용자 확정 계획):
- A00_Common/b_structures_section.ts: B05 구조물 배치 폼·목록 공용 진입점
  (재수출 + 스타일 동봉)
- B06 좌측 패널에 [구조물 배치] 섹션 + 하단 dock [목록][구분선][저장버튼]
  (B05 동일 템플릿). 구조물 조작은 세션 pending으로 쌓여 기존
  flushPendingStructures([임시저장]·[확정])로 정본 반영
- 선택 연동: 목록·폼 → 최근접 카드 focusStation, 횡단도 벽·세월교·BOX →
  좌측 폼에 소유 측점 시설 로드(wireStructureSelection)
- [횡단 조정] 하위 컨테이너(Adjust_Dock): 오버레이 조정창을 같은 deps로
  사본 인스턴스 생성해 세움 — 로직 이동 0, 카드 재렌더로 자동 동기.
  9키·십자 행은 사본에서 숨김(사용자 지시 7), 방향키 이중 입력 방지
- A군 시설 추가·이동·삭제는 B06에서 안내 토스트(배수유역 체인은 B05 몫)

검증: 공용 브라우저 — 구간값 길이 사본 + 10.0→11.0m 양측 동기,
오버레이 - 원복, ✕ 닫기 시 사본 해제. B05 회귀 없음. typecheck 통과.
2026-08-29 14:20:49 +09:00
eomsangdonandClaude Opus 5 11abd303e7 fix(B06): 세월교 측벽을 수직으로 세운다
증상(사용자): 세월교 횡단도 측벽이 기울어 있고 바닥판 모서리도 깎여 있다.
확인 결과 세로(수직)로 그려져야 한다.

원인: 세월교 측벽은 ㄴ형 집수정 부재를 재사용한다(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>
2026-08-29 13:22:50 +09:00
eomsangdonandClaude Opus 5 2cad971f6a fix(B06): 연장 밖 다단은 계산에서 빼 성토면이 따라오게 한다
증상(사용자): 독립 기슭막이 우측 다단이 구간값에 따라 옆 측점에서 없어지는데,
그 위 기준벽 이후 성토면은 갱신되지 않는다.

원인: 링크 카드가 다단을 **다 세워 놓고 그림만 지웠다**(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-29 13:07:04 +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 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 213737289e fix(B06): 다단 구간값 기본을 소유 벽 연장 상속으로 바꾼다
고정 10m(전/후 5)를 기본으로 두니 소유 벽 연장(유출 12m·유입 21.5m)보다 짧아,
+5.9m 떨어진 링크 카드에서 기준벽은 서는데 다단만 사라졌다. 손대기 전에는 기준벽
연장을 그대로 따르고, 손으로 잡은 단만 그 값으로 고정한다.

- tierSpanOf: 저장값 → 소유 벽 연장(유출측은 유출 벽, 유입측은 유입 벽/집수정)
  → 마지막으로 10m(5/5). 그리기·링크 판정·조정창 표시가 이 함수 하나를 쓴다

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-29 11:01:07 +09:00
eomsangdonandClaude Opus 5 36d09dbf00 fix(B06): 링크 카드 성토부선을 기준벽 연장으로 판정한다
단별 구간값을 넣으면서 성토부선까지 단(tier) 연장으로 잠갔다. 단 기본 연장이
10m(전/후 5)라 5.9m 떨어진 링크 카드에서는 기준벽이 서는데도 그 아래 성토부선이
통째로 사라졌다. 성토부선은 기준벽 소유물이므로 inletIn/outletIn으로 판정하고,
다단 벽만 자기 구간값으로 판정한다.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-29 10:57:38 +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 35547e551d fix(B06): 유입측 다단의 좌우 조작 부호를 바로잡고 통나무·목재틀·바자 한계를 2m로 둔다
- outwardOf가 role === "inlet"만 유입측으로 봐서 유입측 다단(bextra)이 반대쪽
  부호로 움직였다. inletSideRole()로 bextra를 유입측에 포함한다
- revetHeightLimit: 통나무·목재틀·바자를 2.0m로 명시한다(종전에는 함수 기본값
  3.0으로 떨어졌다 — 2026-08-29 사용자 확정)

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-29 10:27:32 +09:00
eomsangdonandClaude Opus 5 a544436c5f fix(B06): 유입측 다단이 지형에 잘릴 때도 토스트로 알린다
syncCount가 요청 단 수를 세워진 만큼으로 되돌리는데, 안내는 유출측 채널에만
있었다. 집수정 계류측·독립 기슭막이 유입 벽에서는 +를 눌러도 아무 일이 없는
것처럼 보였다. 유출측과 같은 문구·같은 조건(그 벽이 선택된 상태)으로 알린다.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-29 10:17:00 +09:00
eomsangdonandClaude Opus 5 d78d203fe0 fix(B06): 조정창 제목을 "기슭막이(유입/유출/추가 N/독립)" 형식으로 통일한다
- 문구 교체: "유입 기슭막이" → "기슭막이(유입)", "유출 기슭막이" →
  "기슭막이(유출)", "추가 기슭막이 N" → "기슭막이(추가 N)"({n} 치환)
- 다단 벽 제목이 유출측(extra)·유입측(bextra) 모두 같은 문구를 쓰도록 isExtra에
  bextra를 포함한다. 종전에는 bextra 벽이 "유입 기슭막이"로 적혔다

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-29 10:04:55 +09:00
eomsangdonandClaude Opus 5 6d8e83bd9a fix(B06): 독립 기슭막이 조정창 제목을 "기슭막이(독립)"으로 적는다
관이 없는 독립 기슭막이인데 벽 역할 키를 따라 "유입/유출 기슭막이"로 적혀
좌·우 제목이 갈렸다. 관 숨김 세트의 기준벽은 좌우 구분 없이 기존 문구
B06_Cross_Revet_Own을 쓴다.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-29 10:01:51 +09:00
eomsangdonandClaude Opus 5 13dda88058 feat(B06): 기슭막이 조정창을 공용화하고 형태 선택지를 B05 폼과 맞춘다
조정창은 한 컴포넌트였지만 행 표시가 역할 키에 묶여 있어, 같은 성토 벽이라도
유입/유출에 따라 창 구성이 갈렸다. 재질 선택지도 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>
2026-08-29 09:54:17 +09:00
eomsangdonandClaude Opus 5 c0bebe951d fix(B06): 독립 기슭막이 좌우 로직을 성토 경로 하나로 통일한다
이관 뒤 독립 기슭막이 벽 역할이 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>
2026-08-29 09:34:45 +09:00
eomsangdon 5142bdde9b auto: 2026-08-29 09:04 (EOMSANGDON-HOME) 2026-08-29 09:04:58 +09:00
eomsangdon f11e98f8e9 auto: 2026-08-29 08:54 (EOMSANGDON-HOME) 2026-08-29 08:54:50 +09:00
eomsangdonandClaude Opus 5 23285dc016 fix(B06): 조정창 값 조절 때 스크롤이 맨 위로 튀던 것을 고친다
값 버튼을 누르면 refreshCard가 카드 DOM을 통째로 갈아끼우므로 조정창도 새로
만들어진다. 새 창의 show()가 기억해 둔 scrollTop을 되살리려 하지만 그 시점에는
카드가 아직 문서에 붙기 전이라 레이아웃이 없어 대입이 무시됐고, 붙은 뒤에는
0(제목 줄)으로 남았다.

restoreScroll()을 두어 대입이 먹지 않았으면 다음 프레임(카드가 붙은 뒤)에 한 번
더 넣는다. 그때 창이 닫혔거나 떨어져 나갔으면 건드리지 않는다.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-29 08:08:53 +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 05c4b30cab fix(B06): 물넘이 파임 선이 안 그려지던 테마 변수를 고친다
--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 13:23:55 +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
eomsangdonandClaude Opus 5 753e8fcb44 feat(B05): 투영 커브 안쪽 성토면을 셀 단위로 지운다
빨간 투영선이 감싸는 자리의 성토면을 지운다. 지울 영역은 결국 **상단 평면 스케치가
덮는 XY 영역** 하나이고 투영선이 그 XY를 그대로 복사한 것이므로, 구조물 종류를
가리지 않고 한 가지 방법으로 자른다(사용자 확정).

- Cut(신규): 격자 셀 중심·귀퉁이로 안팎을 가려 안쪽 셀은 마스크하고, 걸친 셀은
  교점 두 개가 만드는 직선으로 잘라 바깥 조각만 삼각형으로 되살린다. 정점은
  하나도 안 건드린다. 교점이 2개 미만이면 셀을 통째로 남긴다 — 모자라게 자를지언정
  더 자르지 않는다.
- Build: CorridorRibbon에 cellMask·trimTris·trimUvs 추가.
- Mesh: 마스크된 셀의 삼각형을 빼고, 조각 삼각형을 뒤에 이어 붙인다.
- Plan: BOX암거 커브의 **안쪽 끝도 박스 축**으로 쏜다(boxInner + castToToe).
  종전엔 바깥만 박스 축이고 안쪽은 노선 흐름이라 가로대가 비스듬했다 —
  평면 스케치의 횡단 방향은 박스가 놓인 전역 단방향이어야 한다(사용자 확정).
- Corridor: 저장본에 마스크·조각 삼각형을 담고 BUILD_VERSION을 올린다.

검증(공용 브라우저 캐시 데이터 실측):
- BOX 커브 가로대 각도차 0.00°(좌·우) — 전역 단방향 확인. 다른 세트는 노선 흐름대로
  1.1~48.8°.
- 걸친 셀에서 되살린 조각 면적이 기대치와 일치 — 우 2.07㎡ vs 2.07, 좌 4.60 vs 4.61.
- 커브 밖을 지운 셀 0. 도로부·측구·절토 리본 행 수 불변(1768/1768/1768/15/26/23/13).

남은 것: 걸친 셀 1,230개 중 약 600개가 교점 부족으로 통째로 남아 경계에 잔여가
생긴다(우 2.09㎡·좌 4.36㎡). 더 잘리는 문제는 없다.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-26 22:32:35 +09:00
eomsangdonandClaude Opus 5 398b5bdffd feat(B05): 탑뷰 스케치 파란 커브를 절·성토면에 투영해 빨간 선으로 얹는다
구조물 상단 +5m 평면에 눕혀 두던 파란 스케치(slope-original, 유입 실선·유출 파선)의
XY를 그대로 쓰고 z만 그 자리 절·성토면에서 읽어 빨간 투영선(slope-projected)으로
낸다. 스케치는 평면에 그대로 남는다. 투영선이 격자 모서리에 앉도록 구조물 점유
구간 경계를 코리도 행으로 끼워 넣는다.

- Plan: slope-projected 소스 추가. 리본을 받아 `측|누가거리` 색인을 만들고 그 행의
  열 폴리라인에서 z를 읽는다. 절토·성토 모두 대상. 밑에 면이 없으면 점을 버려 끊는다.
- Plan: 파란 스케치 안쪽 기준을 소유 측점 노견 편거리 → **그 행 절·성토면이 실제로
  시작하는 자리**로 바꿨다. 종전에는 노폭 연장·측구를 못 따라가 커브가 면 밖(도로 위)에
  얹혔다 — 275.71 좌측은 노견 4m 확장분만큼 안쪽이었다.
- Plan: 비탈 리본 열 순서가 좌우 반대(조각은 편거리 오름차순, +offset=좌측)라 우측에서
  안쪽 끝을 반대로 집었다. 색인 단계에서 안쪽→바깥으로 방향을 통일한다.
- Mesh: 커브를 점의 z로 그린다(평면 커브는 z에 planZ가 들어 있어 동작 불변).
  투영선 색 = 빨강 0xff3b30, 서피스에서 0.02m 띄운다.
- Build: 구조물 구간 [from, to]를 행 경계로 강제 삽입. 안쪽 블록 걸음은 상수로 뺐다
  (지금은 바깥과 같은 0.2m — 경계 행만 새로 생긴다).
- Corridor: 저장본은 입력 해시로만 무효화되어 기하 코드를 고쳐도 옛 저장본이 계속
  복원된다. ENVELOPE_VERSION을 올리고 그 함정을 주석으로 박았다.
- Viewer: 검증용 __corridorBuild 훅(기존 __corridorClip과 같은 용도).

검증(공용 브라우저 캐시 데이터 실측):
- 3D 회귀 없음 — 외곽선 행 1756→1768, 기존 1756행 좌표까지 전부 불변, 추가된 12행은
  구조물 구간 경계뿐(79.3·83.3·85.3·89.3·144.73·154.73·199.72·202.12·270.71·
  274.71·276.71·280.71). 측구·절토 리본 행 수 불변.
- 투영선 안쪽 끝 8벌 전부 서피스 시작점과 XY·Z 정확 일치(gap 0, dz 0).
- 바깥 끝은 XY 일치, z만 최대 1.01m 차 — 세월교 날개벽·다단 기슭막이 앞면처럼
  거의 수직인 면이라 한 XY에 z가 여럿인 구간이다. 규칙 확정은 후속.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-26 21:51:54 +09:00
eomsangdonandClaude Opus 5 d857d3d48a fix(B06): 인접 측점에 남던 집수정 트림을 지워 성토선을 살린다
trimLinkedLayout이 링크(인접) 측점 카드에서 구조물 도형만 지우고, 그 구조물이
만든 설계선 트림(designTrim)과 성토부선은 스프레드로 통과시켰다. 집수정은 조작량이
0이면 대체 사면선(minSlope/maxSlope)도 없어서, 구조물은 안 그려지는데 설계선만
노견 밖에서 끊겼다 — 4측점(80.0m, 관 84.3m)·14측점(280.0m, 관 275.71m) 성토선 삭제.

- keepTrimSides(): 살아남지 못한 쪽의 트림을 ∓Infinity로 되돌리고 그쪽
  Elevation·Slope를 뺀다. 양쪽 다 없으면 designTrim = null.
- 집수정은 링크 측점에 서지 않으므로 유입 쪽 트림은 언제나 지운다.
- 연장 밖 유출 벽의 outletFill, 집수정에 딸린 basinFill도 함께 비운다.
- 유입이 min·max 어느 쪽인지는 관 양 끝 offset 대소로 가른다.

3D 무영향: trimLinkedLayout은 export되지 않고 유일한 호출자 computeCardCulvert는
B06 Cross_View만 import한다. B05 코리도는 computeCulvertLayout을 직접 부르며 소유
측점에서만 돈다.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-26 19:58:05 +09:00
eomsangdonandClaude Opus 5 ce77e0735f fix(B05): 세월교 본 바닥·날개 바닥·에이프런을 한 면으로 맞춘다
원인은 종단경사 보정(dz)이었다. 세월교 본체(측벽·바닥판)는 dz를 타서 양끝이
들린 사발 모양인데(앞끝 +0.0907 / 측점 0 / 뒤끝 +0.1613) 날개벽·에이프런은
dz = 0으로 측점 표고에 평평히 놓였다. 접합부 틈 0.0907·0.1613m이 그 dz와
소수점까지 일치했다. 표고 자체는 이미 같았다 — 어긋난 건 dz뿐이다.

본체는 그대로 두고 날개·에이프런을 경사에 태운다(사용자 선택).

- `buildWingSolids`에 선택 인자 `baseElevationM` 추가 — 넘기면 날개·에이프런
  링이 붙는 자리의 dz(end.designZ − baseElevationM)를 탄다. 세월교만 넘기고
  BOX암거는 안 넘겨 dz = 0 현행을 유지한다.
- 에이프런을 본 바닥판과 한 장으로 만든다. ① 밑변을 판이 밀려들어간 뿌리가
  아니라 바닥판 끝면 그 자리에서 시작한다. ② 단면을 직사각형 근사 대신 바닥판
  폴리곤 그대로 쓴다(뿌리 offset 기준으로 다시 잡아 끝으로 갈수록 0으로 줄인다).
  종전엔 사다리꼴 바닥판(2.669~4.283)을 직사각형(2.747~4.283)으로 근사해
  안쪽 모서리가 0.078m 어긋났다.
- `BUILD_VERSION` 39 → 40.

실측: 에이프런 밑변 4꼭짓점이 본 바닥판 끝면 꼭짓점과 거리 0.0000m(4장 전부).
날개·에이프런 dz가 붙는 자리 값을 그대로 타고 같은 자리의 날개와 에이프런이
같은 dz다. 회귀 없음 — 아령 벌어짐 유지, 날개 바깥 세로선↔구체 측면 8장 전부
0.0000m, 성토끝 박스 4/4·4/4.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-26 18:38:30 +09:00
eomsangdonandClaude Opus 5 392e471dce feat(B05): BOX암거 비탈 투영 커브를 박스 방향 단일축으로 긋는다
원본·변형 성토면 투영 커브는 행마다 그 자리 노선 프레임을 써서, 곡선 구간이면
가로 방향이 행마다 틀어져 커브가 뒤틀린다. BOX암거는 날개벽처럼 박스 방향
하나로 그어야 한다(사용자).

BOX암거 세트만(`section.box`) 행 자리는 노선 위 그대로 두고 가로 방향만 기준
측점 좌향으로 고정한다. 세월교·배수관은 종전대로 그 행의 노선 프레임을 쓴다.

원본 커브의 바깥 끝은 그 행의 코리도 윤곽 점을 그대로 썼는데 가로 방향이
고정되면 어긋나므로, 고정 방향으로 쏜 선이 성토 끝선과 만나는 자리를 바깥
끝으로 삼는다(`castToToe`를 공개로 돌려 재사용). 끝은 여전히 실제 성토 끝선
위다. `BUILD_VERSION` 37 → 38.

실측(한 커브 안 행별 가로 방향 분산, ±180 감김 보정): BOX 200.92는 원본·변형
× 좌·우 4개 모두 0.00°. 배수관 84.3 11.42~50.80°, 세월교 149.73 22.84~23.02°,
배수관 275.71 2.28~8.95°로 그대로다. 행 손실 없음 — BOX 변형 6행·원본 13행.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-26 18:27:26 +09:00
eomsangdonandClaude Opus 5 96be8fe233 fix(B05): 날개벽 바깥 세로선을 구체 측면에 붙여 단을 없앤다
날개 판이 축 위에 가운데 정렬이라, 뿌리를 구체 측면에 놓으면 절반(두께/2)이
측면 밖으로 삐져나와 투영 커브에 단이 졌다.

각도 틸팅 방식은 그대로 두고 판을 축을 따라 뒤로 민다 — 바깥쪽 세로선이 측면에
닿는 데 필요한 양은 (두께/2) × |cos각| / sin각 = (두께/2) × cot각이다. 각이
아주 작으면 cot이 발산하므로 날개 길이의 절반으로 자른다. 세월교·BOX암거 둘 다
적용한다(사용자가 둘 다 지목). 에이프런도 새 뿌리를 따른다.
`BUILD_VERSION` 36 → 37.

실측: 뿌리 링의 바깥 세로선이 구체 측면 평면에서 떨어진 거리가 세월교 4장·BOX
4장 전부 0.0000m. 안쪽 세로선은 0.1414m 안으로 박힌다(= 두께 0.2 × cos45°).
회귀 없음 — 아령 벌어짐 유지(149.73 좌 10.73 → 14.02m, 우 8.63 → 10.81m,
200.92 2.26 → 5.08m), 성토끝 박스 4/4·4/4, 에이프런 4장·0장, 기준 직선 이탈 0.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-26 18:20:41 +09:00
eomsangdonandClaude Opus 5 a8793819af feat(B05): 세월교 날개 투영 커브를 노선 흐름에 맞춰 긋는다
성토끝 박스 옆선이 날개 4장 전부 기준 측점의 고정 좌향을 쓰고 있었다. 세월교는
날개가 저마다 다른 자리의 노선 프레임에 붙는데 커브만 한 방향이라 흐름과 어긋난다.
박스암거는 그 고정 방향이 맞으므로 유지한다(사용자).

방향을 구조물에 실어 보낸다 — `CorridorStructure.wingAxis`(단위벡터). 세월교
날개를 만들 때 그 자리 노선 프레임의 좌향을 담고, `wingBoxLoops`가
`structure.wingAxis ?? 측점 좌향`으로 고른다. Plan 쪽에 새 배관이 없다.
`wing` 표식은 `true` → `"station" | "route"`로 바꿔 어느 규칙으로 만든 날개인지
남긴다(문자열이라 기존 truthy 검사는 그대로 통한다). `BUILD_VERSION` 35 → 36.

실측: 149.73 날개 4장 모두 route, 실어 보낸 축이 기준 측점 좌향과 −20.45°/+2.57°
어긋난다. 박스 옆선 각도가 그 값과 정확히 일치(좌 −20.45°·2.57°, 우 159.55°·
−177.43° = 같은 축 반대 방향). 200.92는 4장 모두 station, 옆선 0°·−180°로 무변화.
박스 4/4·4/4, 에이프런 4장·0장, 기준 직선 이탈 0.0000m.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-26 18:13:29 +09:00
eomsangdonandClaude Opus 5 24bc9480a5 fix(B05): 세월교 날개벽이 오므라들던 것을 벌어지게 정정
위에서 봤을 때 아령처럼 유입·유출부가 커져야 물을 받는다(사용자). 그런데 노선
프레임 경로에서 앞뒤 기준이 뒤집혀 세월교 날개가 오므라들었다.

뿌리는 frameAt(측점 + along × 반폭)으로 누가거리 기준으로 잡는데, 벌어지는
방향은 dir × along으로 측점 로컬 기준을 쓴다. 이 프레임의 dir은 누가거리가
줄어드는 쪽을 향해 둘이 반대였다 — 뿌리가 벌어짐 방향의 반대편에 앉는다.
실측: 149.73 날개가 (−3.65, 종 3.88) → (−4.48, 종 2.06)으로 종방향이 0에
가까워졌다. BOX암거는 뿌리·방향이 같은 기준이라 영향이 없었다.

부호를 가정하지 않는다. 측점 ± 반폭 두 자리를 다 뽑아 `along` 방향에 놓인 쪽을
고른다(`endFrameOf`) — 누가거리 부호와 진행 방향 부호가 반대인 노선에도 맞는다.
`BUILD_VERSION` 34 → 35.

실측: 같은 측 두 날개의 종방향 간격이 뿌리 → 끝으로 커진다. 149.73 좌 10.89 →
14.19m, 우 8.74 → 10.92m. BOX 200.92는 2.40 → 5.22m로 정정 전과 동일(무변화).
파생물 회귀 없음 — 에이프런 4장(기준 직선 이탈 0.0000m), 성토끝 박스 4/4·4/4.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-26 18:10:50 +09:00
eomsangdonandClaude Opus 5 bad291f720 feat(B05): 세월교 날개벽을 노선 흐름에 태우고 바닥 에이프런 추가
세월교 측벽·바닥판은 ringsOf(half, half)로 노선 스플라인을 따라가는데 날개벽만
기준 측점 프레임에서 직선으로 ±월류폭/2 자리에 뿌리를 잡아, 곡선 구간에서 측벽
끝단과 어긋났다. BOX암거는 직선 규칙이 맞으므로 건드리지 않는다(사용자 ②).

- `buildWingSolids`에 선택 인자 `endFrameAt` 추가 — 넘기면 날개 뿌리와 축을
  측벽 끝단부 자리의 노선 프레임에서 뽑는다. 세월교만 `frameAt`을 넘긴다.
  안 넘기면 계산이 옛 식과 완전히 같다(BOX 무변화).
- `WingAnchor.apron` 추가 — 세월교 바닥 에이프런. 날개 끝점과 바닥판 종방향
  끝점을 이은 직선을 외곽으로 하는 평면 삼각형을 바닥 두께로 세운다(사용자 ④).
  링을 뿌리→날개끝으로 깔고 링 좌향을 뿌리→바닥판끝으로 잡아 폭을 |P0P2| → 0으로
  좁히는 방식이라 기존 로프트 모델을 그대로 쓴다.
- 에이프런도 `wing` 표식을 달므로 성토끝 박스 대상은 `kind === "revet"`로 좁혔다.
- `BUILD_VERSION` 33 → 34.

실측: BOX 200.92 날개 뿌리 이동량 0.000m·에이프런 0개(무변화 확인). 세월교
149.73 뿌리 이동량 0.102~1.119m, 뿌리가 측벽 끝단 링 횡단선 위(종방향 잔차 0).
에이프런 4장, 바깥 모서리가 기준 직선 위 이탈 0.0000m. 성토끝 박스는 두 세트
모두 4/4 — 앞서 막혀 있던 149.73 우측 날개 1장도 뿌리 이동으로 풀렸다.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-26 18:06:33 +09:00
eomsangdonandClaude Opus 5 2da4ecb038 feat(B05): 날개벽 탑뷰 투영 — 판 풋프린트 + 성토끝 사다리꼴 박스
날개벽 링은 노선이 아니라 날개 축을 탄다. 그래서 링 폴리곤의 편거리가 노선
편거리가 아니라 패널 두께(±0.10m)라 노견 클립 max(−0.10, +2)에 걸려 날개벽
투영 고리가 0개였다. 실측: 링 좌향과 노선 좌향 정렬 align=0.71, 링 중심의 노선
편거리 2.74~4.16(좌)·−4.01~−2.60(우) — 이미 노견 밖이다.

- `CorridorStructure.wing` 표식 신설, `_Structures_Wing.ts`가 붙인다.
- 날개벽은 도로부 클립을 생략하고 링 위치로 측을 가린다 — 판 두께 × 날개 길이
  직사각형이 나온다.
- `wing-box` 커브 신설(주황) — 밑변은 날개 시작~끝 모서리, 양 옆선은 구조물 축
  (측점 좌향 벡터)과 평행한 고정 직선, 바깥 변은 원본 성토 최하단 끝선 그대로.
  노선 곡률을 따라 각도가 바뀌지 않는다(사용자 확정).
- 성토 끝선은 세트 ±40m만 잘라 쓰고 사거리 15m로 조인다 — 안 조이면 헤어핀
  건너편 끝선에 걸려 −29m짜리 박스(점 115개)가 나온다.
- 교점 탐색은 광선이 아니라 양방향 직선이다 — 세월교 날개는 끝이 성토 끝선
  밖이라(4.16 vs 2.60) 바깥으로만 쏘면 통째로 빠진다.
- `BUILD_VERSION` 32 → 33.

실측: 판 고리가 측당 2장씩 n=11로 살아났다. 박스는 200.92 4/4, 149.73 3/4.
박스 바깥 변의 점은 전부 성토 끝선 위(이탈 0.000m). 149.73 우측 날개 1장은
그 자리에 끝선이 없어 안 그린다 — 처리 방침은 사용자 확정 대기.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-26 17:47:12 +09:00
eomsangdonandClaude Opus 5 5c8986d46c fix(B05): 구조물 투영 커브를 부재별로 표기 · 저장본 판번호 상향
화면이 안 바뀐 원인은 저장본 캐시였다. `BUILD_VERSION`을 안 올려 코리도 해시가
그대로였고 서버 저장본(planCurves 20개)이 재사용됐다. 실측으로 확인 — 저장본
hash 95cafe53 vs 새 코드 hash 0e67c470. 31 → 32로 올려 만료시킨다.

그리고 겹친 부재를 하나로 머지하지 않는다(사용자: "구조물은 머지하지 말고
표현할 것, 나중에 한번에 진행 예정"). 링 경로 그룹핑·구간 병합을 빼고 부재마다
고리 하나를 낸다.

배관은 투영 면적에서 계속 뺀다 — 다른 부재가 감싸고 있는 구조라는 사용자 확정.

실측(링 횡단선 되투영, 이탈 최대 0.0000m): 84.3 고리 4개 — 집수정 상·하단
2.000~2.650 각 31점, 기슭막이 상단 −2.946~−2.000·하단 −3.250~−2.000 각 47점.
149.73 8개, 200.92 10개(날개벽 4장 각각), 275.71 4개.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-26 17:25:41 +09:00
eomsangdonandClaude Opus 5 b29113ba22 fix(B05): 구조물 탑뷰 풋프린트를 링별 편거리 병합으로 재구현
기하 합집합(`_Corridor_Union.ts`)이 겹친 스트립에서 무너졌다. "같은 토막이
짝수 번 나오면 버린다"는 내부 칸막이 제거 규칙이 **외곽의 공유 변까지** 지웠다.
4+4.3(84.3m)에서 집수정 두 단은 탑뷰 사각형이 완전히 같아 네 변 전부 소멸해
좌측 커브가 0개였고, 기슭막이 두 단은 안쪽 변 −1.98을 공유해 23칸이 소멸,
0.34×0.92m 쪼가리만 남았다.

합집합을 버리고 구조물이 스윕 솔리드라는 성질을 그대로 쓴다 — 링 경로가 같은
부재끼리 묶고, 링마다 편거리 창을 도로 밖으로 자른 뒤 1차원 병합해 레인으로
잇는다. 기하 불리언이 없어 퇴화 케이스가 원리적으로 없다.

- `_Corridor_Plan_Footprint.ts` 신규 — 위 알고리즘.
- `_Corridor_Plan.ts` — `structureLoops` 제거, 신규 모듈 호출.
- `_Corridor_Carve.ts` — 죽은 `outlineLoops()` 제거(`outerAt`은 유지).
- `_Corridor_Union.ts` — 죽은 `unionOutline`·`insideQuad`·`splitEdges` 제거.
  `UnionQuad`/`unionQuadOf`는 `outerAt`이 계속 쓴다. 189 → 34줄.

실측: 커브 각 점을 가장 가까운 링 횡단선에 되투영해 편거리를 읽었다 — 링선
이탈 최대 0.0000m. 84.3 좌(집수정) 2.000~2.650 31점, 우(기슭막이) −3.250~−2.000
47점으로 전 링이 살아난다. 275.71·149.73·200.92 세트도 날개벽까지 각각 나온다.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-26 17:18:47 +09:00
eomsangdonandClaude Opus 5 25ae04b2cd fix(B05): 변형 실루엣 투영 커브를 행마다 지반선에 트림
실루엣은 소유 측점 단면 한 장이라 그대로 밀면 구간 내내 같은 폭이 됐다.
0.5m 행마다 종단경사만큼 올린 뒤 `trimSlopeToGround`로 그 행 지반과 만나는
자리까지 잘라 실제 발끝을 따라가게 한다. 표기 커브만 바뀐다 — 서피스 절단·
패치는 손대지 않았다(`CARVE_APPLY` 계속 false, 사용자 지시).

4+4.3 우측 실측: 바깥끝 offset −3.25 고정 → −2.96~−4.42(지형 따라), 행 11 → 21.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-26 16:24:56 +09:00
eomsangdonandClaude Opus 5 5a3b2ebe39 feat(B05): 구조물 탑뷰 투영 커브 재작성 — 세트별 상단+5m 평면·유입/유출 분리
구조물 추가 뒤 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>
2026-08-26 09:42:35 +09:00
eomsangdonandClaude Opus 5 f6296fa7c5 feat(B05·B06): 구조물 3D 투영 표기 · 직교 카메라 · 조정창 템플릿 일원화
3D 구조물과 성토면의 관계를 "잘라내기"에서 "투영 표기"로 바꾸고, 세월교·BOX암거
조정창을 기슭막이 템플릿으로 통일했다.

B05 코리도
- 구조물 탑뷰 투영 산출(_Corridor_Carve.ts 신규): 구조물 솔리드 링 사이 실좌표
  사각형 + 변형 성토면 영역. 노선 파라미터가 아니라 행 횡단선과 실좌표로 교차시켜
  곡선 구간·날개벽 벌어짐이 그대로 잡힌다.
- 사각형 합집합 경계(_Corridor_Union.ts 신규): 변을 교점에서 토막 내고 내부·공유
  토막을 버려 최외곽 윤곽만 남긴다(측정 133 -> 13 루프).
- 표기 모드(CARVE_APPLY=false): 서피스를 자르지 않고 ① 성토면 위 절단 투영선
  ② 원지반 최고 표고 위 Z평면에 구조물 윤곽(노랑)과 유입·유출 성토부 윤곽
  (하늘·주황)을 따로 그린다. 구조물 커브는 성토면 없다고 보고, 성토부 커브는
  구조물 없다고 보고 만든다.
- 구조물 실루엣 공용화(_Corridor_Station_Structure.ts 신규): 기슭막이·BOX암거·
  세월교 실루엣을 한 자료형으로. 성토면 강제 변형은 스위치로 끄고
  (STRUCTURE_SILHOUETTE_OVERRIDE), 노견 확장만 상시 적용(shoulderSpans).
- 평면 영역 분리(_Corridor_Region.ts), 절단기 일반화(clipGeometry가 영역 배열·
  전체 정점 속성 보간 — 리본 UV 유지). 지형은 코리도 스트립만 도려낸다.
- BOX암거 3D를 측점 프레임 직선 압출로(노선 추종 금지), 날개벽 계산 공용화
  (_Corridor_Structures_Wing.ts 신규) 후 세월교에도 날개벽 4매 추가.
- 세월교 보어를 관 련수만큼 뚫고, 보어 밴드를 단면 표고로 클램프해 구멍 찌그러짐
  제거. 날개벽 뿌리를 벽 외측면 모서리로 교정.
- 직교 카메라(_Viewer_Camera.ts 신규) — 원근 왜곡 없이 탑뷰 판독. 커서 피벗 줌이
  직교에서 camera.zoom을 함께 조인다.

B06 횡단
- 조정창 공용 부품(_Cross_Panel_Base.ts 신규): 머리줄·2행 항목·D-pad·키보드
  방향키. 세월교·BOX 조정창을 같은 템플릿으로 옮기고 십자 세트를 창 맨 아래로.
- 세월교 조정창에 날개벽 항목 추가(설치·짧은쪽 높이·길이·각도) — 정본
  pipe_points 되쓰기, 바닥판 연장은 길이×cos각으로 즉시 재계산.
- 세월교 좌우 이동을 집수정 규칙으로: 노견 수평 확장 + 상하 이동분만 성토선.

BUILD_VERSION 19 -> 31(저장 코리도 캐시 무효화). typecheck·prettier 통과.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-25 20:03:42 +09:00
eomsangdonandClaude Opus 5 07fb2ccf46 fix(B06): BOX암거·세월교를 소유 측점 횡단도 한 장에만 그림
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>
2026-08-25 17:41:40 +09:00
eomsangdonandClaude Opus 5 6e87dd6cdf fix(B05): BOX암거 날개벽을 구체 모서리에 붙이고 에이프런을 평면 사다리꼴로
날개벽 시작점을 측점 중앙 프레임으로 잡아 곡선 구간에서 구체 끝과 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>
2026-08-25 15:41:31 +09:00
eomsangdonandClaude Opus 5 a87182496e feat(B06·B05): BOX암거 조정창 · 구체 기울기 · 3D 벽 구간 정리
조정창에서 좌·우 끝을 따로 잡는다 — ◀▶는 구체 길이, ▲▼는 그 끝의 구체 표고를
0.1m씩 움직인다. 좌·우 표고가 달라지면 구체가 기울고, 날개벽 구간 바닥(에이프런)은
각 끝 표고에서 수평을 지킨다(2026-08-25 사용자).

- `computeBoxLayout`이 좌·우를 따로 푼다: 끝 표고·길이·성토선·에이프런
- 성토선은 그쪽 표고 기준으로 물매 1:1.2를 지키므로 표고를 내리면 구체가 길어진다
- 조정창 `_Cross_Box_Panel.ts` + 제어 `createBoxControls` + `design.box_adjust` 저장
  (세션 → 캐시 → 확정 payload, 세월교와 같은 체계)
- 3D: 구체 밖(날개벽 구간)에 서 있던 벽을 없앴다 — 천장 없는 통로가 생기던 자리는
  날개벽 몫이다. 이제 상판·구체 저판·측벽 2매·에이프런 2매 + 날개벽 4매로 나뉜다
- 카드 렌더러의 구체 배선(선택 토글·강조·조정창)을 `_Cross_View_Bodies.ts`로 분리,
  연동 플래그 세션은 `_Page_Link_Session.ts`로 분리(700줄 제한)

검증(10+0.9): 구체 5.34m·수평 → ◀ 1회 5.44m → ▼ 2회 표고 −0.2m·물매 1:28.4,
에이프런 두 장 모두 수평 유지, ↺ 원복. 3D 솔리드 10개(측벽 링 0.2m).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-25 15:03:53 +09:00
eomsangdonandClaude Opus 5 2fe8f617da fix(B06): BOX암거 성토선을 박스 최상단 모서리에서 끝냄
모서리 밖은 구체 개구부·날개벽이라 성토가 아니다(2026-08-25 사용자). 원지반까지
잇던 구간을 지운다 — 성토선은 노견 → (수평 연장) → 박스 최상단까지만이다.

검증(10+0.9): 좌 [(2, 535.821) → (2.744, 535.201)],
우 [(−2, 535.701) → 수평 → (−2.744, 535.201)] — 두 선 모두 상단 표고에서 종료.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-25 12:55:00 +09:00
eomsangdonandClaude Opus 5 55e97b8925 fix(B06): BOX암거 성토선 추가 — 박스 최상단을 지나고 노견은 수평 연장
박스 위 성토선이 빠져 있었다. 물매 1:1.2를 고정하고 성토선이 **박스 최상단
모서리**를 지나게 하면, 박스를 길게 잡을수록 노견이 저절로 수평 연장되고 각도는
그대로다(기슭막이 "벽이 밖으로 나간 만큼 노폭 연장"과 같은 규칙, 2026-08-25 사용자).

- 구체 길이 = 노폭 + 2 × (노면 − 박스 상단) × 1.2. 노면이 크라운이라 좌·우 필요량이
  다르므로 큰 쪽에 맞춰 대칭으로 잡고, 남는 쪽은 노견을 수평 연장한다
- 성토선 = 노견 → (수평 연장) → 박스 최상단 모서리 → 1:1.2로 원지반까지
- 노견 밖 설계선은 이 성토선이 대신하므로 다시 트림한다

검증(10+0.9): 구체 ±2.744m, 저판 8.32m, 좌 성토선 물매 1.200·1.200,
우 성토선 수평 0.144m 뒤 1.200·1.200.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-25 12:52:40 +09:00
eomsangdonandClaude Opus 5 d4c8de738a feat(B05·B06): BOX암거 횡단도 · 3D 중공 셸·날개벽·관통 보어
BOX암거를 세월교와 같은 계류 방향 절단면으로 그리고, 3D는 속이 빈 서피스 셸로
만든다. 관통 컷·중공 튜브는 배수관 세트와 세월교에도 함께 적용한다.

B06 횡단도
- `_box_set()` — 내공 폭·높이(B05 옵션), 부재 두께 세월교 승계(측벽 0.2·상하판 0.3),
  복토 0.5m(별표2 교차 참조), 날개벽 투영 저판 연장
- 상판 윗면 = 노면 − 복토, 구체는 수평 설치(교본 3장). 계류 하상과의 고저차는 툴팁 표기
- 구체가 노면 아래 묻히므로 계획선은 자르지 않는다(세월교와 다른 점)

3D
- 로프트가 **링별 폴리곤**을 받는다 — 구조물을 위·아래 조각으로 갈라 링마다 틈을
  벌려 빈 공간을 만든다. 틈 0이면 속 찬 단면 그대로라 토폴로지가 유지된다
- BOX = 내공 구간만 틈을 벌려 측벽이 저절로 생기는 셸 + 날개벽 4매(날개 축 프레임)
- 관통 보어 = 링 y에서 틈 높이 2·√(r²−(y−y₀)²) → 배관 외측 기준 원형 구멍.
  기슭막이·집수정·세월교 측벽에 적용
- 배관은 중공 튜브(바깥·안쪽 원통 + 양 끝 고리)로 내부 유로를 보인다

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-25 12:45:32 +09:00
eomsangdonandClaude Opus 5 98380056cc fix(B06): 세월교 관 위 성토 채움 삭제
채움 색이 --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>
2026-08-25 12:12:55 +09:00
eomsangdonandClaude Opus 5 81bb505cc4 feat(B05·B06): 세월교 횡단 구체·조정창·3D 예상형상
B05 세월교 옵션에 BOX암거 날개벽 한 벌(설치·짧은쪽 높이 1m·길이 2m·각도 45°)을
그대로 붙이고, B06 횡단도에 세월교 구체를 그린다. 구체는 양측 ㄴ형 집수정 측벽 +
그 사이를 잇는 바닥판 + 바닥판 상면에 안힌 관 + 관 위 성토(노체) 채움이다.

- 바닥판 편측 연장 = 날개벽 길이 × cos(각도) — 각도는 관축 기준 벌어짐각
- 측벽은 buildBasin 재사용이라 1:0.3 기움·좌우·상하 대각 이동·계류측 성토부선이
  집수정과 같은 규칙으로 따라온다 (기슭막이 다단·재질과는 연계하지 않음)
- 구체는 월류 폭만큼 도로 방향으로 이어져 기준 측점 전후 절반까지 붙는다
- 조정창: 측벽 높이·좌우·상하, 관경·수량(정본 pipe_points 되쓰기).
  높이·좌우·상하는 세션 + design.ford_adjust에 저장
- 3D 예상형상: 구체 부재는 월류 폭 구간 스윕, 관은 련수만큼 폭 안 등간격
- 부재 두께: 바닥판 0.3m(교본 물넘이 물받이 최소 30cm), 측벽 0.2m(집수정 승계)

700줄 제한으로 카드 렌더러·측점 제어기·페이지에서 배선을 분리했다
(_Cross_View_Ford, _Page_Ford_Controls, _Page_Patches, Culvert_Const 구간값 헬퍼).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-25 12:05:42 +09:00
eomsangdonandClaude Opus 5 0e2309f5ce fix(B05): 지운 구조물 부활 차단 · 임시저장이 캐시 수정분을 저장하도록
사용자 확정 모델: 초기 계산값은 **원복용으로 그대로 두고**, 사용자가 제어한 수정
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>
2026-08-24 19:56:40 +09:00
eomsangdonandClaude Opus 5 38fd7e49e0 fix(B05): 구조물 성토 비탈 Z 반전 — 유출측 실루엣 배열 방향 정규화
사용자 3D 제보: 구조물 자리의 성토가 경사 방향이 뒤집혀 위로 솟는다.

원인은 직전 커밋(b5ceb861)에서 유출측 편거리 정렬을 뺀 것이다. 뒤쪽 코드가 비탈의
도로측 끝점을 **배열 위치**로 집는다 — `slopeRelative`(Corridor_Station)와 코리도
빌더(Corridor_Build)가 `side === "right" ? points.at(-1) : points[0]`로 잡고,
비탈 보간기도 오름차순 배열을 전제한다. 유출이 우측인 측점은 수집 순서(도로 → 바깥)가
편거리 내림차순이라 **바깥 끝을 도로측으로** 잡았고, 그만큼 비탈이 뒤집혀 얹혔다.

정렬로 되돌리면 다단 계단 순서가 다시 깨지므로, 체인은 그대로 두고 **배열 방향만**
오름차순으로 뒤집는다(첫 점이 마지막 점보다 바깥이면 reverse). 점 순서(성토선 >
본 기슭막이 > 추가 성토선 > 추가 기슭막이)는 보존된다.

BUILD_VERSION 18 → 19(저장 코리도 만료).
tsc/prettier 통과, pytest 196 passed(기존 실패 1건 유지).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-24 19:42:07 +09:00
eomsangdonandClaude Opus 5 b5ceb8613a fix(B05·B06): 기슭막이 조작의 3D 즉시 반영 · 성토 비탈 실루엣 순서 복구
사용자 보고: 기슭막이 위치(본·연동)를 바꿔도, 추가(다단) 기슭막이를 세워도 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>
2026-08-24 19:37:14 +09:00
eomsangdonandClaude Opus 5 cd8d14f105 feat(B05·B06): 기슭막이 구간값 조정창 · 연동 기슭막이 옵션 · 가림 구간 선 정리
기슭막이 범위(길이·기준측점 전/후)를 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>
2026-08-24 19:26:35 +09:00
eomsangdonandClaude Opus 5 83eb58a251 feat(B05·B06): 배수관 측점 정본화 · 코리도 단차 제거 · 인접 측점 기슭막이 링크
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>
2026-08-24 17:11:21 +09:00
eomsangdonandClaude Opus 5 22d4df5aff feat(B05): 3D 예상형상에 배수관 세트 구조물 반영
B06 횡단(computeCulvertLayout)을 그대로 재사용해 3D 코리도에 기슭막이·집수정·
배관을 세우고, 그 구간 성토/절토 단면을 횡단도와 같은 형상으로 다시 그린다.

구조물 솔리드(_Corridor_Structures.ts 신규)
- 기슭막이: 단면 폴리곤을 노선 따라 1m 간격 로프트 스윕(직선 압출 아님)
- 집수정: 부재 외곽 직육면체, 연장 basin_length_m(기본 2m) 전후 균등
- 배관: 관 하단선 축 원통(관경 지름)
- 스윕 프레임에 계획고 차(dz)를 실어 종단 경사 반영
- flat 법선 + EdgesGeometry 모서리 검은선(성토부·노폭 리본과 같은 규칙)

단면 재구성(_Corridor_Station.ts)
- 구조물 측 비탈 = 노견 → (노폭 연장) → 구조물 상단 → 전면 → 벽 하단 성토부선
  → 다단 기슭막이 → 지반/절토선 끝 한 줄(PIECE_COLS cut·fill 9 -> 20)
- 성토로 늘어난 노폭은 비탈이 아니라 노견 리본을 넓힌다
- 집수정 계류측 cutLine까지 이어 원지반을 그만큼 걷어낸다

구간 처리(_Corridor_Build.ts)
- 구조물 점유 구간(기준측점 전/후)이 다음 측점을 넘어가도 같은 단면 유지
- 그 구간은 지반 트림 생략, 비탈 앵커를 노견 조각 바깥 끝으로 통일

길이·전후 입력
- 레지스트리 pipe 옵션 추가: inlet/outlet_revet_before_m·after_m(5/5),
  inlet_basin_length_m(2)
- B05 배수관 서브폼에 기준측점 전/후 칸(전+후=길이 연동)·집수정 길이 칸
- _side_spec이 전/후·집수정 길이를 CulvertSideSpec으로 전달

비정규 측점 프레임 정정(_Page_Helpers.ts)
- left_xy 선형보간(단위길이 깨짐·방향이 현 쪽으로 누움) -> 노선 폴리라인 접선에서
  직접 산출(left = (-ty, tx)), 폴백은 각도 보간

BUILD_VERSION 10 — 저장본 자동 만료

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-23 21:46:48 +09:00
eomsangdonandClaude Fable 5 5371534fc3 feat(B06): 기슭막이 높이·자리 체계 개편 + 조정창 방향키 조작
- 조정창 십자 키패드 키보드 방향키 조작(집수정 9키는 십자 축만)
- 배관 기슭막이 높이 = 순수 높이(바닥~상단), 하한·기본 = 관경+1.0m (고정 2.0m 폐기)
- 매몰 시 전면 발끝 지반 추적 바닥 심화 삭제 — 바닥 = invert-0.5 고정
- 상하(d) 0점 = 성토선 0 지점(벽 상단=노견), 기본 자리 = 성토사면 경사길이 4m 지점
  (d=null 자동, 저장·표시·정수맞춤 pinned 판정 연동, 구세션 d=0 호환)
- 유입 접속 절토선: 지반 교차 없으면 1m 공간 확보에서 끝
- 자동 집수정 기본 형상 L형 -> I형 (선택지 밖 구조물 그려지던 불일치)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-23 20:34:30 +09:00
eomsangdon 7217747e81 auto: 2026-08-23 19:34 (ESD_LAPTOP) 2026-08-23 19:34:26 +09:00
eomsangdonandClaude Opus 5 eb41202bf4 fix(B06): 교차점 자동 줌아웃이 5m 사면 규칙에 잘리던 문제
자동 줌아웃 판정이 배수관용 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>
2026-08-23 18:54:38 +09:00
eomsangdonandClaude Opus 5 91f843b5bb fix(B06): 표고(Y) 눈금 최소 단위 1m
확대하면 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>
2026-08-23 18:50:19 +09:00