Commit Graph
258 Commits
Author SHA1 Message Date
eomsangdonandClaude Opus 5 76fa78c528 feat(B05): 구간형 구조물이 놓인 자리를 종단에 늘 띠로 표시
B군(측구·맹암거 등)을 넣어도 **어디에 놓였는지 안 보였음**. 알약은 기준점 한 곳에만 찍히고,
시~종점 띠는 **고른 동안만** 폈기 때문. 측구처럼 길게 이어지는 시설은 「어디부터 어디까지」가
곧 그 시설의 내용이라 늘 보여야 함(2026-09-07 사용자 지시).

- 구간형이면 **늘** 띠를 그림. 고른 것은 두껍고 진하게(4px·0.55), 나머지는 얇고 옅게(3px·0.45).
- **레인 높이는 42px 그대로** — 2-5b 에서 줄여 둔 것을 안 부풀림.
- B05 가 레지스트리를 **한 번만 받아 쓰는 자리에 따라 나눔** — 추가 메뉴는 켜진 종류만
  (종전대로 여섯 군), **알약 레인·군 판정은 전부**. B06 에서 넣은 B군은 레지스트리에서
  꺼져 있어, 켜진 것만 주면 종단에 이름·색 없이 「?」 회색 알약으로 떴을 자리였음.

실측(용화, 구간형 7건) — 띠 **0 → 7개**, 높이 3px, 레인 42px 그대로, 알약 18개 그대로.
B군 6종은 전부 `interval` 이라 확인한 C군과 같은 경로를 탐. 타입 응답 36종에 B군 6종 포함 확인.

평면(지도)은 손대지 않음 — 노선 위에 구간을 얹는 방법이 지금 없어 **별건**으로 둠.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-07 15:06:53 +09:00
eomsangdonandClaude Opus 5 692dc1dbaa refactor(B05): 노선 편집 모달 곡선 셈 분리 — 700줄 제한 준수
- `B05_Profile_UI_RouteEdit_Curve.ts` 신설(68줄) — 손잡이 끌기 역셈
  (`dragHandleTo`)·직선 교점(`intersect`)·내각(`innerAngleDeg`) 순수 기하만.
- 모달은 740 → 692줄. 화면 배선·동작 불변, 셈 식 그대로 옮김.
- 서버 정셈과 짝임을 새 파일 머리에 명시. 거울 시험은 새 자리를 보게 갱신.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-07 09:36:32 +09:00
eomsangdonandClaude Opus 5 a0bd9c8775 fix(B05): 한 번 편집하면 맞춰 둔 반지름이 하한으로 눌리던 것
실화면에서 잡음 — 곡선 하나만 지워도 나머지 반지름이 **R 12~199m → 전부 12m**
로 되돌아갔음.

원인은 편집 모델과 저장 모델이 다른 것이었음. 서버는 처음 만들 때 이어진
꺾임을 한 곡선으로 묶는데, 그 곡선의 교각점은 앞뒤 직선을 늘려 만나는 자리라
**원본 꺾임점 중 어느 것도 아님**. 편집은 「꺾임점 하나 = 곡선 하나」로
표현되므로, 묶인 채로 두면 한 번만 손대도 묶음이 낱개로 흩어지고 각 자리가
하한으로 떨어졌음.

모달이 자료를 읽을 때 **묶인 구간을 그 교각점 하나로 갈아 끼우게** 함 —
안쪽 꺾임점은 그 곡선이 대신하므로 뺌. 앞뒤 직선과 반지름이 그대로라
그려지는 선은 똑같고, 이제 손대도 안 흩어짐. 서버가 고른 반지름도 함께
들고 가 [확인] 때 되돌려 보냄.

시험 tmp/tests/test_route_polyline_apex_roundtrip.py 2건 — 갈아 끼워도 곡선
수·반지름이 같고 선이 **1mm 안**에서 일치, 그리고 안 갈아 끼우면 실제로
하한으로 눌리는 것(왜 필요한지)도 못박음.

실화면 확인(용화) — 되돌린 뒤 곡선 26곳 R 최대 198.9m → 곡선 하나 지우고
[확인] → 곡선 25곳, **R 42.5·51·198.9m 그대로**.

tsc --noEmit 통과 · pytest 432 passed.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-07 08:46:09 +09:00
eomsangdonandClaude Opus 5 20f343902d feat(B05): 곡선 시작·끝점을 끌면 직선 각도와 R 이 함께 바뀜
사용자 확정(2026-09-07) — 「직선과 곡선 교차점 이동 시에는 직선의 각도와
반지름 값 변경이 되」. 되물어 확정: 끄는 점은 **곡선 시작·끝점**.

셈은 서버의 반대 방향임 — 서버는 교각점·R 에서 접선점을 내고, 화면은 끈
접선점에서 교각점·R 을 구함. 끈 쪽 직선만 돌리고 반대쪽은 옛 교각점을 지나는
그대로 두어, 두 직선이 만나는 자리가 새 교각점이 되고
R = |새 교각점 − 끈 자리| / tan(교각/2) 로 나옴.

**두 벌이 아니라 짝임** — 왕복이 제자리인지를
tmp/tests/test_route_polyline_handle_drag.py 3건이 지킴. 화면이 낸 (교각점, R)
을 서버 셈에 넣으면 끈 자리가 **1µm 안**으로 되돌아옴. 접선점을 직선 위에서만
밀면 교각점이 안 움직이고 R 만 커지는 것도 확인.

손잡이를 **속 빈 네모(5px)** 로 키움 — 3.5px 채운 네모는 선·노드와 색이 같아
눈에도 안 띄고 집기도 어려웠음(실화면). 곡선을 지운 자리에는 손잡이도 없음.

실화면 확인(용화) — 손잡이를 끌자 「14번째 꺾임점 · R 16.6 · 값 지정」으로
바뀜. 서버가 곡선 성분 22개(예: R 44.3m, 노드 1~3을 한 곡선으로)를 내려줌.

⚠ 그 확인 도중 **서버가 또 옛 코드로 돌고 있었음**(응답에 `curves` 키 자체가
없었음). 오늘 두 번째임 — 화면 검증 전에 응답에 새 필드가 있는지부터 볼 것.

tsc --noEmit 통과 · pytest 430 passed.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-07 08:34:04 +09:00
eomsangdon 39fbdb9539 Merge remote-tracking branch 'origin/sub_laptop_1' into main_laptop_1 2026-09-07 08:21:55 +09:00
eomsangdonandClaude Opus 5 679726124b feat(B06): 종단 구조물 표시를 B05 와 통일 — 같은 알약 레인
PLAN 2-5 (2026-09-07 사용자 지시 4 「구조물 표시 통일」). B06 종단에는 구조물 마크가
아예 없었고 배수관·세월교만 다른 계통으로 그려졌음.

- B05 가 쓰는 **같은 부품**(`buildStructureLane`)을 B06 종단 그래프 아래에 붙임.
  이름·중복 번호·툴팁·약호가 전부 B05 규칙 그대로 따라옴.
- 종단 차트가 `toX`(누가거리 → px)를 내보내 레인이 그래프와 같은 자리를 씀.
- 좌측 「구조물 배치」가 목록을 받을 때마다 `setStructureMarks` 로 넘김(구조물 정본 +
  관 정본). 관을 가상 구조물로 바꾸는 `pipesToStructureMarks` 는 B05 안에 있던 것을
  공용 자리(`B05_Profile_Api_Structures`)로 올려 **한 벌만** 둠.
- 알약을 누르면 그 자리에 가장 가까운 측점 카드를 고름(좌측 목록 클릭과 같은 규칙).
  끌기는 B05 몫이라 여기서는 자리만 보임(배수유역 재분할이 걸림).
- 검증 훅 `window.__b06Marks` 추가(`__corridorBuild` 등과 같은 용도).

자체검증 — 실화면 B06 종단에 레인 1줄·알약 10개(「배수관 1~8 · 세월교 1~2」)가 B05 와
같은 표기로 뜸. 훅 값 `{structures:10, types:36}`. tsc 통과.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-07 08:20:59 +09:00
eomsangdonandClaude Opus 5 43d55f498c fix(B05): 곡선 편집줄이 캔버스 밑에 깔리던 것
실화면에서 [곡선 지우기] 클릭이 캔버스에 가로채였음 —
「<canvas …> subtree intercepts pointer events」.

원인은 세로 배치임. 상자가 세로 flex 이고 캔버스 자리가 `flex: 1 1 auto` 라
공간을 다 먹는데, 새로 넣은 편집줄에 `flex: none` 이 없어 눌려 없어지고
캔버스가 그 위를 덮었음. 바닥 단추줄과 같이 `flex: none` 을 주고
`z-index: 1` 로 올림.

실화면 재확인(용화, 20번째 꺾임점) — 네 조작 모두 동작:
  고름   → 「자동 · 내각 155° · 법정 하한 12m」
  곡선끔 → 「곡선 없음 — 직선이 그대로 꺾입니다」 · 상태줄 「곡선 지움 1곳」
  곡선켬 → 「자동」으로 복귀
  R 40   → 「값 지정」 · 상태줄 「R 지정 1곳」
  자동   → 값 비고 「자동」으로 복귀

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-07 08:19:06 +09:00
eomsangdonandClaude Opus 5 56a6d18611 feat(B05): 노선 편집 모달에 곡선 제어 — R 칸·곡선 지우기/넣기·시작끝점 표시
사용자 지시(2026-09-07) — 「r과 직선 삭제나 추가가 있어야 하지 않을까?」
서버 쪽(f59932de)에 이어 화면을 붙임.

  - **곡선 시작·끝점을 네모로 표시** — 어디부터 어디까지가 한 곡선인지 눈으로 봄.
  - 꺾임점을 누르면 **고른 자리**가 되고 아래 편집줄이 그 곡선을 만짐.
  - **R 입력칸** — 값을 넣으면 그 반지름으로 못박고, [반지름 자동]으로 되돌리면
    서버가 예정노선에 맞춰 다시 고름.
  - **[곡선 지우기] / [곡선 넣기]** — 지운 자리는 가운데가 빈 동그라미로 보이고
    직선이 그대로 꺾임.
  - 직선 삭제·추가는 원래 있던 조작 그대로(오른쪽 클릭 삭제 · 두 번 클릭 삽입).
    다만 그때 **편집값 목록도 같은 자리에서 밀어 준다** — 안 그러면 뒤 노드의
    R·켬끔이 한 칸씩 어긋남.
  - [확인]이 정점마다 `curve`·`radius_m` 을 함께 보냄.

상태줄에 「곡선 N곳(하한 R m) · 곡선 지움 N곳 · R 지정 N곳」을 보임.

tsc --noEmit 통과 · pytest 427 passed.

남은 것 — **곡선 시작·끝점을 끌어 직선 각도와 R 을 함께 바꾸는 조작**은 다음 단계.
지금은 표시까지이고, 자리 옮기기는 꺾임점 끌기로 함.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-07 08:14:58 +09:00
eomsangdonandClaude Opus 5 f59932de66 feat(B05): 곡선·직선 지우고 더하기 + 반지름 못박기
사용자 지시(2026-09-07) — 「r과 직선 삭제나 추가가 있어야 하지 않을까?」

편집을 **꺾임점 목록 + 자리마다 곡선 켬끔 + 반지름** 셋으로 표현함.
  · 직선 삭제·추가 = 목록에서 점을 빼거나 더하기(빼면 앞뒤 직선이 하나로
    합쳐지고, 직선 위에 더하면 둘로 갈리며 그 자리에 곡선이 생김)
  · 곡선 삭제·추가 = `curve` 를 끄고 켜기(끄면 직선이 그대로 꺾임)
  · 반지름 변경 = `radius_m` 못박기(없으면 서버가 고름)

`POST /route/replan` 의 정점마다 `curve`·`radius_m` 을 받게 함.
편집값이 오면 **묶지 않음** — 사용자가 곡선 하나로 본 것을 임의로 합치면
손잡이가 사라지기 때문. 편집값은 점과 짝이라 중복 제거도 함께 함.

시험 tmp/tests/test_route_polyline_edit.py 7건 — 곡선 삭제·추가, 반지름
못박기, **반지름을 바꿔도 교각점은 안 움직임**(「주변 직선 각도 구속」),
직선 삭제·추가. pytest 427 passed.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-07 08:08:47 +09:00
eomsangdonandClaude Opus 5 6b76a3f273 feat(구조물): C군 벽에 「설치 측」 칸 추가 + 칸을 잠글 수 있는 구조
PLAN 3-6 「설치 측을 사용자가 고르게 할지(폼에 칸 추가) 뒤에 정할 것」 — 2026-09-07
사용자 확정 「폼 선택은 가능하게 반영하고 나중에 선택 비활성화로 하자」.

- 레지스트리 C군 5종(옹벽·돌쌓기(찰)·돌쌓기(메)·흙막이·큰돌쌓기)에 `side` 선택 칸.
  선택지 「자동(성토 쪽)·좌·우」, 기본은 자동 — 종전 동작(성토 나는 쪽 자동)과 같음.
- 기하는 이미 그 값을 봄 — `B06_Section_UI_Cross_Revetment.ts:168` 이 "좌"/"우" 면 그쪽,
  아니면 `fillSide()`. 배관(`common_util_structure_walls.ts:80`)·파이썬 짝도 `options.side`
  를 이미 나름. 즉 배선은 있고 **칸만 없었음**.
- 옵션 칸에 `enabled` 를 둠(기본 참). 거짓이면 폼에 **회색으로 남고** 못 고르며 필수 검사
  에서도 빠짐 — 칸을 지우지 않고 잠그는 자리(사용자 「나중에 비활성화」).

자체검증 — 레지스트리 5종 모두 `(['자동(성토 쪽)','좌','우'], 기본 '자동(성토 쪽)', enabled True)`
로 읽힘. 실화면 B06 C군 폼에 「설치 측」 칸이 그 세 선택지로 뜸(값 자동). tsc 통과.
시험 499 통과 — 실패 1·오류 8 은 모두 `require is not defined in ES module scope` 인
JS 도우미 건으로 내 변경과 무관(도우미 파일 날짜 8/27·9/2, 다른 환경에서 만든 것).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-07 08:06:12 +09:00
eomsangdonandClaude Opus 5 a811286e52 feat(B06): 구조물 전체를 B06 에서 넣게 (B군 종단배수·F군 생태/녹화 열림)
PLAN 3-6 (2026-09-07 사용자 지시 5 「A군뿐 아니라 구조물 전체를 넣을 수 있어야 함」).

원인은 「B06 이 A군만 받는다」가 아니라 **레지스트리의 `enabled:false`** 였음 — B군 6종·
F군 2종·G군 1종이 목록에서 통째로 빠져 있었고, 두 화면이 같은 목록을 쓰므로 B05·B06 이
똑같이 A/C/D/E/G/기타 여섯 군만 보였음. 레지스트리 주석(2026-08-17)이 그 셋을 「B05
선택지에서 빼고 **B06 개별 횡단도 옵션으로 재사용**」이라 적어 둔 자리임.

- `fetchStructureTypes(includeDisabled)` 로 갈래를 둠. B06 만 참으로 부름 — B05 는 종전대로.
- 함께: 3-5 ⓑ 되돌릴 자리 주석(단 2개 이상일 때만 이어 붙이는 한 줄) 표시.

자체검증 — 공용 브라우저에서 두 화면 군 목록 대조:
  B06 A·**B**·C·D·E·**F**·G·기타 (8) / B05 A·C·D·E·G·기타 (6, 그대로)
  B06 에서 B군 6종(측구·산마루측구·소단측구·도수로·절토사면 배수로·맹암거)과
  F군 2종(야생동물 이동통로·비탈면 녹화)이 실제로 목록에 뜸. tsc 통과.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-07 07:57:15 +09:00
eomsangdonandClaude Opus 5 e593378d33 feat(B05): 직선·곡선 성분을 정본에 남기고 화면에 내려보냄
사용자 확정(2026-09-07) — 사용자가 잡는 것은 **곡선 시작·끝점**이고 필요하면
반지름을 직접 바꿈. 정점 목록만으로는 어디부터 어디까지가 한 곡선인지,
그 반지름이 얼마인지 알 수 없어 편집·도면이 같은 값을 못 봄.

  - RouteCurve 신설 — 교각점(apex) · 반지름 · 접선길이 · 내각 ·
    곡선 시작점 · 끝점 · 그 곡선이 대신하는 꺾임점 구간 · 위반.
    반지름을 바꿔도 apex 는 안 움직임(「곡선 반지름 변경 시 주변 직선 각도
    구속」이 그 뜻 — 직선이 고정이라 접선점만 미끄러짐).
  - `planned_route_curves.json` 으로 저장(노드 CSV 옆).
  - `GET /route/plan` 이 `curves` 를 함께 돌려줌.

곁들여 고친 것 — 묶음을 도로 쪼갤 때 **별표2 의 155° 를 다시 안 봐서**
펴진 자리(내각 159°)에도 곡선이 생기고 있었음. 쪼갤 때 다시 검사함.
그 결과 용화 노선이 곡선 24 → 13곳, 정점 179 → 145 로 제자리를 찾음.

실측(용화, 예상노선 331점) — 노드 28 · 곡선 13 · 정점 145 · 위반 0 ·
벗어남 최대 3.57m 평균 1.081m(고치기 전 1.077m). 반지름은 12~28m 로
자리마다 갈림(전에는 전부 12m).

pytest 420 passed.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-07 07:53:35 +09:00
eomsangdonandClaude Opus 5 1944414940 fix(B05): [확인]을 누를 때마다 계획노선이 깎이던 것
같은 노선으로 [확인]을 되풀이하면 계획노선 정점이 136 → 134 → 119, 노드가
22 → 21 로 계속 줄었음(보조 창 실측). 사용자가 아무것도 안 옮겨도 누를
때마다 자기 노선이 뭉개졌음.

원인은 **이미 폴리라인인 것을 다시 단순화**한 것. 두 자리 모두 그랬음 —
  - read_route_plan 이 노드를 계획노선(원호 점이 섞인 폴리라인)에서 되뽑음
  - replan_route 가 화면이 보낸 노드를 또 한 번 단순화해서 씀
단순화(Douglas-Peucker)는 원본 점군에서 한 번만 돌아야 함.

고친 것:
  - build_planned_polyline 에 simplify 갈래 신설. False 면 받은 점을
    꺾임점으로 그대로 씀(중복 제거만).
  - 폴리라인을 쓸 때 그것을 낳은 **노드도 함께 저장**
    (planned_route_nodes.csv · planned_route_initial_nodes.csv).
    노드는 폴리라인에서 되뽑을 수 없으므로 낳은 값을 보관함.
  - read_route_plan 은 저장된 노드를 그대로 씀(없는 옛 프로젝트만 뽑음).
  - replan_route 는 simplify=False 로 씀.

실측(용화 b269ea34 실제 노선 파일, 4회 왕복)
  옛 방식  노드21/정점115 → 20/115 → 20/113 → 20/115  (깎이고 흔들림)
  고친 방식 노드21/정점115 → 이후 매 회차 **완전히 동일**

시험 tmp/tests/test_route_polyline_idempotent.py 2건 신설 — 노드 왕복이
제자리인지, 그리고 옛 방식이 실제로 깎이는지(갈래가 필요한 이유) 못박음.
pytest 414 passed.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-07 05:08:07 +09:00
eomsangdonandClaude Opus 5 41852da3b3 feat(B03): 초기 업로드부터 계획노선 폴리라인을 노선 정본으로 사용
자동 체인 2.7 단계 추가 — 초기 폴리라인을 세운 뒤 노선을 다시 읽어
그것을 BP·경유점·EP 로 세우고 algorithm=as_planned 로 넘김.

전에는 폴리라인 파일만 만들고 노선은 원본 점군으로 풀었음. 그래서
종횡단 측점·유토곡선·3D 코리도가 곡선 없는 점군 위에 섰음.
실측(용화 3건): DB 노선 정점 331·323 vs 초기 폴리라인 145.
노선을 한 번이라도 편집한 프로젝트만 145 로 맞아 있었음.

자체검증 — 2.7 재판독이 145 정점을 돌려주고 BP·EP 좌표가
expected_route.csv 첫·끝 줄과 같음. pytest 409 passed.

계측 로그(_log_steps) 동봉 — 재확정 체인 단계별 경과시간용.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-07 02:56:45 +09:00
eomsangdonandClaude Opus 5 fe14ad02ee feat(B05): 계획노선 초기 폴리라인을 자동설계 체인에서 세움
PLAN 0-10. 그동안은 화면이 route/plan 을 처음 부를 때 만들어져, 화면을 안 열면 값이
없었음 — 초기값은 서버가 낸다는 0-4 원칙과 어긋났음.

- 파일입력 자동설계 체인이 예상노선 정본을 남긴 직후(2.6단계) 초기 폴리라인도 세움.
  실패는 비치명적 — 없으면 화면이 처음 열릴 때 만들어짐.
- _ensure_planned_initial 이 **낡음도 본다** — 예상노선 파일이 더 나중에 쓰였으면 다시
  만듦. 파일을 다시 올리면 예상노선이 새로 깔리는데 초기본이 옛 노선인 채로 남으면
  노선 초기화가 옛 자리로 돌아갔음.

자체검증 — 예상노선 파일 시각을 최신으로 만든 뒤 route/plan 을 부르니 초기 폴리라인이
다시 만들어짐(노드 28 · 곡선 13 그대로). 시험 409 통과·17 건너뜀.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-07 01:19:28 +09:00
eomsangdonandClaude Opus 5 710169fd00 fix(B05): [3D 업데이트]가 만든 코리도를 바로 올리고 보관함에도 담음
보조 창 지적 — [3D 업데이트]를 눌러도 브라우저가 만들기만 하고 PUT 이 없었음.
저장 시점이 B05->B06 이동뿐이라(2026-08-23 확정), 누른 뒤 새로고침하면 저장본이 낡은
채라 다음 사람이 또 만들어야 했음.

- 버튼 경로 끝에서 saveCorridorIfDirty 를 부름.
- saveCorridorIfDirty 가 PUT 성공 뒤 그 봉투를 브라우저 보관함에도 담음(옛 열쇠 자리는
  치움). 그러지 않으면 다음 진입이 새 열쇠 주소로 16.9MB 를 한 번 더 받았음.

typecheck 통과. 실측 확인은 보조 창이 자기 프로젝트에서(저장본이 최신인 자리) 진행.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-07 01:09:58 +09:00
eomsangdonandClaude Opus 5 674ca82726 feat(B05): 고친 계획노선을 그대로 쓰는 입구 — 재확정 체인이 다시 풀지 않음
PLAN 0-2 항목 9 / 0-3. 노선 편집 [확인]이 재확정 체인을 타는데 그 체인이 solve_route 로
BP·CP·EP 사이를 격자에서 다시 풀었음. 사용자가 노드를 조금만 비틀어도 탐색 제약에 걸려
체인이 통째로 멈췄음(보조 창 로그: 세그먼트 1 (BP -> CP1) 경로 탐색 실패 — 종단경사
한계 26% · 최소곡선반지름 12m · 회피지역 제약).

- B05_Profile_Engine_AsPlanned.solve_as_planned 신설 — 제어점 목록이 곧 노선이고 표고만
  지표면 격자에서 뜸. 반환 꼴은 솔버와 같아 아래 단계가 그대로 이어짐.
  종단기울기·곡선반지름 위반은 **세되 막지 않음**(사용자 확정: 자동 보정·차단 없이 경고만).
- run_route_design 에 algorithm=as_planned 갈래 추가. 스키마 검증에도 허용.
- 재확정 체인이 그 갈래를 씀 — 자동탐색은 이제 초기 업로드에서만 돎.

솔버와 지표 계산이 겹치지만 합치지 않음 — 솔버는 0-3 으로 접히는 코드라 리팩터링해
두 곳을 얽을 값어치가 없음.

시험 409 통과·17 건너뜀, main import 스모크 통과.
실화면 [확인] 재검증은 보조 창이 자기 프로젝트에서 진행.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-07 00:49:07 +09:00
eomsangdonandClaude Opus 5 53525a1d91 fix(B05): 노선 재계산이 끊겼을 때 노선만 갈아 끼워진 채 굳던 것
보조 창 실측 — [확인] 이 체인 중간에 400 으로 끊겨도 route/replan 은 success 를 돌려주고
계획노선 CSV 는 이미 새 노선으로 바뀐 뒤였음. 그 결과 배수유역·관은 새 노선, 종횡단은
옛 노선으로 어긋난 채 굳고(배수관 측점 9 -> 0), 다시 누를수록 어긋남이 쌓였음
(1,097.4 -> 1,079.4 -> 1,064.7m).

- run_redesign_chain 이 중단 사유를 문자열로 돌려줌(성공은 None). 중단 지점 넷 모두.
- replan 이 쓰기 전에 이전 수정본을 쥐고 있다가 실패하면 되돌림. 처음 고치는 경우였으면
  파일을 지워 초기 폴리라인으로 돌아감.
- 실패는 409 + 사유로 응답 — 화면이 성공으로 보이던 것을 막음.

원인 자체(자동탐색이 사용자 노선을 다시 풀다 실패)는 별건으로 남음 — 로그 사유
「세그먼트 1 (BP -> CP1) 경로 탐색 실패: 종단경사 한계(26%)·최소곡선반지름(12m)·회피지역
제약으로 통과 경로가 없습니다」. 고친 노선을 그대로 받아 쓰는 입구가 필요함(PLAN 0-2 항목 9).

시험 409 통과·17 건너뜀.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-07 00:44:29 +09:00
eomsangdonandClaude Opus 5 eec29a90df fix(B05,B06): 최소곡선반지름 조회가 없는 컬럼을 읽던 것 · 재계산 중단 사유 로그
보조 창이 노선 편집 [확인] 실측에서 둘을 잡음.

1. projects 에 design_speed_kph / terrain_type 컬럼이 없음(있는 것은 road_type 뿐).
   매 요청마다 OperationalError(1054) 가 ERROR 로그로 남고 기본값 폴백으로 넘어갔음.
   - 임도 종류는 projects.road_type, 설계속도·지형은 워크플로 stage 2 params 에서 읽음.
   - 산식은 이미 있던 B05_Profile_Engine_Grade.legal_plan_radius_min_m 를 씀 —
     내가 같은 표를 다시 짜 두었던 것을 지움(중복 제거).
2. 재확정 체인이 solve_route 400 으로 끊길 때 상태 코드만 남겨 원인을 못 짚었음.
   본문(사유)까지 로그에 남김. 노선 편집 [확인]이 조용히 끊겨 배수유역·관은 새 노선으로
   가고 종횡단만 옛 노선에 남는 어긋남이 굳었던 자리임.

recompute 의 상세 조회와 DB 두 건도 asyncio.gather 로 묶음(원격 DB 왕복 약 12ms/질의).

시험 409 통과·17 건너뜀.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-07 00:37:53 +09:00
eomsangdonandClaude Opus 5 e2cc5e759b perf(B05): 3D 코리도를 브라우저 보관함(IndexedDB)에 담아 재수신 없앰
손 안 댄 프로젝트를 열면 저장본 열쇠가 맞아 매번 16.9MB 를 받았고, 브라우저 보관이
모듈 Map 이라 새로고침 한 번에 날아가 또 받았음(보조 창 실측: 첫 로그인~B05 도달 29MB
중 16.9MB 가 이것). 해시 질의(0b30f5b0)는 낡았을 때만 막았지 맞을 때는 그대로였음.

- b_asset_cache 에 readCachedBytes / writeCachedBytes / purgeAssetsWithPrefix 추가.
  앞 둘은 네트워크를 안 타는 순수 보관함 접근이고, 마지막은 주소 앞머리가 같은 옛
  보관본을 지움 — 코리도는 주소에 열쇠가 박혀 정본이 바뀌면 새 주소가 되므로 17MB 짜리가
  쌓이지 않게 담기 전에 치움.
- 코리도 조회가 보관함을 먼저 봄. 주소에 열쇠가 있어 정본이 바뀌면 저절로 다른 주소가
  되므로 옛 보관본을 잘못 쓸 일이 없음.

자체검증(공용 브라우저) — 담기·읽기 왕복 정상(44바이트 왕복, 해시 보존), 열쇠만 다른 두 벌을
담은 뒤 앞머리 지우기로 둘 다 제거 확인. typecheck 통과.
※ 실제 16.9MB 절감은 저장본이 최신인 프로젝트에서 확인해야 함 — 이 프로젝트는 검증 중
계획고를 만져 저장본이 낡은 상태라 코리도 자체가 안 실림(__corridorSource null).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-07 00:18:37 +09:00
eomsangdonandClaude Opus 5 9b93d3055b feat(B05): 노선 편집 모달이 정점 대신 노드를 잡게 함
사용자 지시(2026-09-06) — 노드를 제어해 계획노선을 고친다. 그동안 모달은 서버가 준
폴리라인 정점을 그대로 잡았는데, 거기에는 원호 위 점이 섞여 있어 편집 대상이 아님.

- 그려 보이는 선(폴리라인, 원호 포함)과 잡는 점(노드)을 나눔. 선은 plannedLine,
  노드는 서버가 내려 준 nodes.
- 노드에 붙은 반지름·내각·법정 위반을 화면에 실음 — 위반 노드는 붉게, 상태줄에
  곡선 수와 기준 R, 미달 개수.
- 노드를 옮기면 폴리라인은 낡은 값이므로 지우고 직선으로 미리 보임. 곡선은 [확인] 때
  서버가 같은 R 규칙으로 다시 끼움(계산을 두 벌로 짜지 않음).
- API 타입에 nodes·min_radius_m·curve_count·violation_count 추가.

자체검증(공용 브라우저, 용화) — 모달이 노드 28개로 열리고 상태줄에
「노드 28개 · 초기 폴리라인 · 곡선 13곳(R 12m)」. 전에는 폴리라인 정점 142개를 잡았음.
typecheck 통과.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-07 00:09:49 +09:00
eomsangdonandClaude Opus 5 318507c88d fix(B05,B06): 종단 알약으로 고른 구조물이 B06 횡단도에 안 잡히던 것
계획서 2-2. 3D 픽·사이드 목록에는 structure-pick 기록이 있는데 종단 그래프 알약 경로에만
빠져 있었음 — 세션 키가 아예 안 써져 B06 이 잡을 값이 없었음(보조 창 추적).

- 구조물 브리지에 markChainage 추가 — 알약 id 의 누가거리를 구조물 정본·관 정본 양쪽에서
  되읽음.
- Page 의 onStructureSelect 가 writeStructurePick 으로 그 자리를 남김(사이드 목록 경로와
  같은 창구).

자체검증(공용 브라우저, 용화) — 알약 18개 중 셋을 눌러 세션 키가
찰쌓기 1 -> {at:40} · 찰쌓기 3 -> {at:80} · 옹벽 -> {at:120} 으로 써짐(전에는 null).
그 상태로 B06 진입 시 측점 6+0.0(120m) 카드가 선택된 채로 뜸. typecheck 통과.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-06 23:58:53 +09:00
eomsangdonandClaude Opus 5 356b32163a feat(B05): 계획노선을 폴리라인으로 만드는 초기본 신설
사용자 확인(2026-09-06) — 예상노선은 폴리라인이 아니라 점 묶음이고 규칙 없는
폴리라인과도 맞지 않음. 그래서 계획노선은 원데이터를 복사해 폴리라인으로 바꾼 것이어야
하고 그것이 불변의 초기 데이터가 됨. 곡선 기준은 지식DB(별표2 I.2.다) 값을 씀.

- common_util_route_polyline.py 신설 — 점 묶음을 폴리라인으로.
  ① Douglas-Peucker 로 꺾임점(IP)만 남김. 예상노선은 격자 탐색이 낸 조밀한 점군이라
     (용화: 1,097m 에 331점) 점마다 곡선을 끼우면 접선 자리가 1.5m 뿐이라 R 이 2~6m 로
     뭉개짐. 허용오차는 격자 해상도(2m)의 두 배.
  ② 내각 155도 이상은 곡선 생략(별표2), 그 아래는 최소곡선반지름으로 원호를 끼움.
     자리가 모자라면 반지름을 줄이되 막지 않고 위반으로 표시(사용자 확정: 경고만).
  ③ 점은 옮기지 않음 — 노드는 원본 자리에 그대로 두고 그 사이에 원호를 넣음.
- planned_route_initial_path 신설 — 세 벌 구조(예상노선 점 묶음 / 초기 폴리라인 /
  수정본). load_design_route 읽는 순서에 초기 폴리라인을 예상노선보다 앞에 끼움.
- 최소곡선반지름은 임도 종류·설계속도·지형으로 고름
  (FOREST_ROAD_PROFILE_CRITERIA[min_plan_radius_m], 못 읽으면 가장 완화된 조건).
- GET /route/plan 이 노드(반지름·내각·위반)까지 함께 돌려줌 — 노드만 옮기면 선이
  저절로 규칙을 지키게 하는 것이 목적. POST /route/replan 도 받은 노드로 다시 폴리라인화.

자체검증(용화, 실화면 API) — 예상노선 331점 -> 편집 노드 25개 + 곡선 13곳
(전부 R 12m = 설계속도 20·특수지형 법정 하한, 위반 0) -> 계획노선 정점 142개. 응답 180ms.
단순화 전에는 곡선 15곳이 R 2.75~6.51m 로 전부 위반이었음.
시험 tmp/tests/test_route_polyline.py 7건(원호가 접선과 맞물리는지 좌표로 대조).
전체 407 통과·17 건너뜀.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-06 23:53:25 +09:00
eomsangdonandClaude Opus 5 e5b896283f fix(B05): 배분 선반입 되먹임을 응답 반영 구간에서 끊음
표식(측점 수 + 누계)만으로는 부족했음 — 응답을 반영해 다시 그린 뒤 누계가 아주 조금
달라지는 경로가 있어 편집이 멈춘 뒤에도 2~3바퀴가 더 돌았음(보조 창 10초 관찰:
2.8~6.3초에 걸쳐 3건).

onReady 로 다시 그리는 동안 applying 을 세워 그 그리기가 거는 schedule 을 무시함.

자체검증(공용 브라우저, 접은 채 계획고 6회 편집 후 10초 관찰 3회) — 3/3/1건 -> 2/1/1건.
편집하는 동안은 종전대로 0건(400ms 안정화 유지). typecheck 통과.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-06 23:27:37 +09:00
eomsangdonandClaude Opus 5 c8468e789d fix(B05): 유토 배분 선반입이 접힌 상태에서 안 돌던 것 · 응답이 요청을 다시 부르던 되먹임
- schedule 호출이 draw 의 접힘 가드 뒤에 있어 패널을 접어 두면 한 번도 안 불렸음.
  그래서 「편집이 멈추면 조용히 받아 둔다」가 성립하지 않고 펼친 뒤에야 첫 요청이 나갔음
  (보조 창 실측: 편집 6회에 요청 0건). 접힘 여부와 무관하게 지나는 emitSummary 로 옮김 —
  거기서 이미 고른 결과를 쓰므로 계산 비용 0.
- 응답이 오면 곡선을 다시 그리고, 그리기가 다시 schedule 을 불러 요청이 끝없이
  되돌아왔음. 입력 표식(측점 수 + 마지막·중간 누계)을 두어 같은 값이면 안 보냄.

자체검증(공용 브라우저, 패널 접은 채 계획고 6회 편집) — 요청 1건, 그 뒤 4초 동안
추가 0건. 고치기 전에는 편집 중 0건 + 펼친 뒤 1건 + 되먹임 1건이었음.
시험 400 통과·17 건너뜀, typecheck 통과.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-06 23:13:05 +09:00
eomsangdonandClaude Opus 5 c0bfcba34b feat(B05,B06): 유토 배분을 서버로 옮기고 브라우저는 미리 받아 쓰게 함
배분·운반거리 산식(_balance 523 + _settle 235 = 758줄)을 브라우저 번들에서 빼기 위한
배선. 계산은 여전히 한 벌 — 서버가 같은 TS 를 Node 로 돈다(CLAUDE.md 5장).

- POST /projects/{id}/sections/{route_id}/haul-plan 신설
  (B06_Section_Router_HaulPlan.py). 브라우저가 낸 누가토량 결과를 받아 배분만 돌려줌.
  화면이 쓰는 꼴 그대로 내보내 그리기 코드를 안 건드림. 표시 전용 — 정본은 저장 때 따로.
- B05_Profile_Api_HaulPlan.ts 신설 — 편집이 멈추면(400ms) 조용히 받아 두는 선반입기.
  늦게 온 응답은 버림(최신 요청만 채택). 못 받아도 곡선 자체는 그대로 보임.
- common_util_mass_haul.massHaulPayload 가 배분을 값으로 물던 것을 끊음(extra 인자).
  이 커플링 때문에 화면에서 안 불러도 번들에 남았음.
- 저장 경로는 곡선도 배분도 안 만듦 — 서버가 정본을 내므로 balloon 위치만 보냄.
- 죽은 파일 B06_Section_UI_Section_View_MassHaul.ts(236줄) 삭제.

자체검증(공용 브라우저, 용화 route 149) — 엔드포인트 200 · 196ms.
서버 배분과 브라우저 computeHaulPlan 결과가 JSON 문자열까지 동일(blocks 1 · steps 2 ·
spoil/borrow/hauled/transferred/fill_total 전부 일치). 시험 400 통과·17 건너뜀.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-06 22:47:22 +09:00
eomsangdonandClaude Opus 5 5967d64b63 perf(B05): 측구 방향 역반영을 스레드 한 번 + 쓰기 한 문장으로 묶음
route/confirm 이 측점마다 종단 정본을 다시 열고(13.8ms) 행마다 DB 에 썼음(24.5ms).
건당 38.3ms 라 30건이면 1.15초, 67건이면 2.57초로 측점 수에 선형으로 늘었음
(보조 창 서버 내부 측정).

- 다시 계산할 측점을 먼저 고른 뒤 asyncio.to_thread 를 **한 번만** 돌림. 그 안에서
  종단 정본과 포장 제안표·세월교 하강표를 한 번 읽어 측점마다 돌려 씀.
- read_cross_design_inputs 에 preloaded 인자 추가(종단 경로·내용·포장 제안표).
  경로 검증은 resolve_longitudinal_path 로 떼어 재사용.
- 쓰기는 merge_cross_section_designs 한 문장.

자체검증(공용 브라우저 [저장] 3회) — route/confirm 470~834ms -> 324/351/356ms.
버튼 전체 대기 2,624~3,415ms -> 1,826/2,766/2,979ms.
(오늘 누적: 4,137ms -> 1,826~2,979ms) 시험 400 통과·17 건너뜀.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-06 22:27:41 +09:00
eomsangdonandClaude Opus 5 0b30f5b035 perf(B05,B06): 지형 표고를 레이캐스팅 대신 격자 색인으로 · 카드 버튼줄 레이아웃 스래싱 제거
B05 진입이 간헐적으로 13~14초 멎던 원인을 CDP 프로파일로 잡음. 13,537ms 중
getVertexPosition 3,267 + intersectTriangle 3,178 + checkGeometryIntersection 3,012
+ _computeIntersections 2,198 = 11.6초가 three.js 레이캐스팅이었음.

- terrainElevation 이 점마다 Raycaster 로 지형 높이를 찾고 있었음(한 번 쏠 때마다
  삼각형 전수 훑기). 마커·측점선이 점마다 부르는 자리라 곱해짐. 이미 있던
  TerrainHeightIndex(격자 색인)로 돌리고, 색인도 지형과 한 벌로 보관(빌드 O(삼각형)).
  같은 교훈을 2026-08-23 비탈 투영에서 겪어 색인을 만들어 뒀는데 이 함수만 옛 길이었음.
- B06 카드 버튼줄 reflow 의 레이아웃 스래싱 제거 — 한 칸 옮길 때마다 scrollWidth 를
  다시 읽어 매번 강제 레이아웃을 냈음(카드 67장마다). 읽기/쓰기를 갈라 폭을 한 번만
  재고 옮길 개수를 계산. ResizeObserver 첫 호출도 건너뜀(rAF 와 겹쳐 두 번 돌았음).
- get_section_context·get_section_detail 의 순차 읽기를 asyncio.gather 로 묶음
  (원격 DB 왕복 약 12ms/질의).

자체검증(공용 브라우저 4왕복) — B05 진입 14,708/1,060/14,841 -> 1,269/806/726/746ms.
급등 사라짐. B06 진입 2,884 -> 2,330~2,864ms(긴 작업 합 2,313 -> 1,853).
시험 400 통과·17 건너뜀, typecheck 통과.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-06 22:22:08 +09:00
eomsangdonandClaude Opus 5 44ff1dea4d perf(B05): 화면에 들어올 때마다 지표면을 다시 파싱하던 것 제거
B05 는 해시가 바뀔 때마다 renderB05Route 로 통째로 다시 조립되어 뷰어 상태가 비워짐.
그래서 8MB 지표면을 진입할 때마다 다시 읽고 다시 파싱했고, 그것이 진입을 잡는
단일 동기 블록 14.4초였음(공용 브라우저 3왕복 실측: 14,708 / 1,060 / 14,841ms).

- 파싱해 둔 지형을 모듈 단위 cachedTerrain 에 한 벌 보관 — 같은 모델이면 새 장면에
  그대로 붙임. 다른 모델을 부르면 옛것을 버려 GPU 버퍼가 안 쌓임.
- 장면에서 뗄 때 보관본은 disposeObject 하지 않음.
- window.__surfaceTiming 디버그 훅 추가 — 단계별 시간(fetch·parse·fit·contours).

자체검증(공용 브라우저 3왕복) — B05 진입 683 / 474 / 639ms (전 14,708 / 1,060 / 14,841).
surfaceTiming: reuse 0ms · fit+markers 35ms · contours 183ms · total 222ms.
B06 진입은 2.4~2.7초로 변화 없음. 시험 395 통과·17 건너뜀, typecheck 통과.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-06 21:52:13 +09:00
eomsangdonandClaude Opus 5 3cd717d8f6 feat(B06): [저장]·[확정] 정본을 서버가 다시 계산하도록 되돌림
2026-09-06 저녁 사용자 확정. 이유는 속도가 아니라 보안 — 저장 경로가 브라우저 계산이면
유토 배분·운반거리 코드(_balance 523 + _settle 235 = 758줄)가 번들에 남아야 해서
화면에서 안 그려도 뺄 수 없음. 서버가 정본을 내면 그 몫이 번들에서 빠짐.
대가는 저장 대기 850ms -> 약 980ms(Node 131ms) — 기다리는 조작이라 허용.

- 저장·확정이 편집을 커밋한 **뒤** recompute_server_side 를 부름(전에는 편집 앞에서
  Node 없이 보정만). 바뀐 벽·측점이 면적·유토곡선에 실림.
- run_node 갈래 제거 — 이제 부르는 자리가 둘 다 Node 를 돎(체인·저장).
  쓰이지 않게 된 enforce_stored_designs 래퍼 삭제.
- 코리도 열쇠 질의: hash 를 isinstance 로 좁힘. 파이썬에서 곧바로 부르면 Query 기본값
  객체가 들어오는데 truthy 라 열쇠 없는 호출이 통째로 stale 로 떨어졌음(보조 창 지적).

시험 395 통과·17 건너뜀.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-06 21:20:55 +09:00
eomsangdonandClaude Opus 5 59df765e30 perf(B05): 3D 진입에서 저장본을 받아놓고 버리는 일 제거
B05 에 들어가는 것만으로 코리도 저장본 18.6MB 를 통째로 받고, 열쇠(해시)에 종횡단
내용이 섞여 있어 계획고를 한 번이라도 만졌으면 받아본 뒤 버리고 브라우저가 다시
만들었음(실측 화면 전환 16.7초). 3D 는 이미 [3D 업데이트] 수동인데 진입 경로만 자동.

- 서버: GET corridor 에 `?hash=` 를 받아 저장본 머리 4KB 에서 열쇠만 떼어 맞춰 봄.
  어긋나면 파일 대신 `{status:stale}` 만 냄. 맞으면 종전대로 파일 — 왕복은 한 번.
- 화면: 진입은 `loadCorridorIfFresh` 로 **저장본이 맞을 때만** 3D 에 올림.
  낡았으면 받지도 만들지도 않고 [3D 업데이트] 대기 표시만 켬.
- `ensureCorridor` 에 `rebuild` 인자 추가 — [3D 업데이트] 버튼 경로는 종전대로 재생성.

시험 tmp/tests/test_b05_corridor_hash_probe.py 3건.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-06 21:07:11 +09:00
eomsangdon 1bb367058b auto: 2026-09-06 20:58 (ESD_LAPTOP) 2026-09-06 20:58:04 +09:00
eomsangdonandClaude Opus 5 165b281fe1 fix(B05,B06): 배수관 편집을 세션 초안에 쌓아 B06 저장에도 실리게 함
관 추가·이동·삭제가 B05 패널 메모리에만 있어, B06 으로 넘어가 [저장]·[확정]하면 그
편집이 사라졌음(대응표 조사에서 드러남).

- 등록표에 `pipes` 초안(프로젝트 범위)을 되살림
- 배치가 바뀌는 순간 목록을 초안에 담고, 내보내기는 `flushPendingPipes` 한 창구로 모음
- B05 [임시저장]·B06 [저장]·[확정] 모두 이 창구를 지남 (자동저장 없음)

검증: 초안으로 관 11 → 10개 저장·초안 비움, 되돌려 11개 복구. 테스트 390 통과.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-06 20:25:12 +09:00
eomsangdonandClaude Opus 5 818042e45c feat(B05,B06): 최종 누가토량 배지 + B06 유토곡선 그래프 제거
사용자 지시(2026-09-06): 유토곡선은 자리를 많이 먹는데 정작 늘 봐야 하는 값은 마지막
지점 누가토량 하나임. 기준은 횡단으로 고정(종단 기준 곡선은 뒤에 삭제 예정).

- 총괄값 계열을 범례 선택과 무관하게 **횡단 기준**으로 뽑음
- 공용 배지 `common_util_mass_haul_badge.ts` — 종단 그래프 좌측 상단 오버레이,
  세부 내역은 툴팁
- B05: 상단줄의 「누가토량」 항목을 배지로 옮김(유토곡선 패널은 그대로 유지)
- B06: 유토곡선 그래프·범례·요약줄 삭제, 그 높이를 종단면도가 전부 사용

검증: B06 배지 −5,595.5㎥·범례 없음·종단 228px, B05 배지 −6,842.6㎥·상단줄에서 제거.
테스트 390 통과.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-06 19:44:59 +09:00
eomsangdonandClaude Opus 5 d057e64e9f style(B05): 구조물 알약을 한 단 낮춰 종단 그래프 자리 확보
사용자 지시(2026-09-06): 구조물 표시가 너무 커서 높이를 많이 차지함.

- 알약 높이 24 → 18px, 혼자 있을 때 31 → 22px, 글자 11 → 10px
- 레인 전체 54 → 42px — 그만큼 종단 그래프가 세로를 더 씀

검증: 실화면 레인 42px·알약 22px, 이름표(찰쌓기·배수관·옹벽·세월교) 모두 읽힘.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-06 19:28:44 +09:00
eomsangdonandClaude Opus 5 cdb9799e64 fix(B05): 구조물 저장이 판번호 때문에 통째로 사라지던 것 수정
화면 진입 때 받은 판번호로 저장해, 그 사이 정본이 한 번이라도 바뀌면(다른 창
저장·노선 재계산) 409 로 거절되고, 실패 처리가 세션 초안까지 지워 사용자가 넣은
구조물이 사라졌음.

- 판번호를 **쓰기 직전에** 서버에서 다시 받아 씀.
- 저장이 실패해도 세션 초안·화면 목록을 지우지 않음(다시 [저장]하면 됨).

실화면 검증: 고치기 전 구조물 추가 후 [저장] → 정본 0건. 고친 뒤 → 정본 1건, 2건.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-06 18:38:04 +09:00
eomsangdonandClaude Opus 5 be78af0460 feat(B05): 계획노선 편집 모달 — 예상노선 위에서 노선 고치기
좌측 「계획노선」 섹션의 [계획노선 편집] 로 큰 모달을 엶(PLAN 0-2).

- 등고선 도엽 위에 예상노선(점선)·계획노선(실선)을 함께 그림. 지도 그리기는
  배수유역도와 같은 도구(`B04_PreProcess_UI_MapRender`) 재사용.
- 노드 끌어 옮기기 · 선 두 번 클릭으로 노드 끼우기 · 오른쪽 클릭으로 지우기,
  배경 끌기로 화면 이동, 휠로 확대.
- 편집 중에는 계산이 나가지 않음. [확인]에서만 서버가 배수유역부터 재계산하며
  그동안 화면을 덮는 안내를 띄움. 끝나면 세션 초안·조회 캐시를 비우고 페이지를
  다시 세움.
- [예상노선으로] 는 수정본을 지우고 같은 재계산(노선 초기화).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-06 18:14:29 +09:00
eomsangdonandClaude Opus 5 8e6a1e27eb feat(B05): 계획노선 두 벌 + 노선 갈아 끼우기·초기화 서버 경로
노선을 예상노선(원본)·계획노선(수정본) 두 벌로 나눔 (PLAN 0-7 사용자 확정).

- 예상노선 정본 `B05_Profile/route/expected_route.csv` 신설 — 자동 체인이 한 번
  씀. 초기값 스냅샷 안에도 같은 CSV 가 있으나 그 폴더는 재확정 체인이 지우므로
  스냅샷 밖에 한 벌 둠.
- 계획노선 수정본 `B05_Profile/route/planned_route.csv` — 설계 계통(`load_design_route`)이
  수정본 → 예상노선 → 스냅샷 순으로 읽음. 노선 초기화는 수정본을 지우는 것.
- `GET /route/plan` 두 노선 정점 반환(모달이 점선·실선으로 그림).
- `POST /route/replan` 고친 노선을 수정본에 쓰고(조밀화) 재확정 체인 재사용 —
  배수유역 다시 → 관 새로 → 계획선·종횡단 재생성 → 옛 측점 설계 누가거리 이월.
- `POST /route/replan/reset` 수정본 삭제 후 같은 재계산.
- 횡단배수 지점은 노선이 바뀌면 저장분을 버리고 새로 계산(사용자 확정).
  구조물은 프로젝트 정본이라 옛 측점값 그대로 남음.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-06 18:00:34 +09:00
eomsangdonandClaude Opus 5 402800435d feat(B05): 유토곡선 전체 맞춤에서 측점선 숨김·좌우 여백 축소
- 전체 맞춤(토량 분배) 보기는 측점 세로선을 그리지 않음 — 67개가 촘촘히 서서
  곡선을 덮기만 하고 자리도 못 읽음. 공용 차트에 `hideStations` 축 옵션 신설,
  분배를 끄면 예전대로 나옴.
- 같은 보기에서 좌우 여백 축소 — 종단과 맞물릴 필요가 없으므로 반 칸 들여쓰기
  (originOffset)를 빼고 우측 여백 12px. 곡선이 폭을 다 씀.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-06 17:22:43 +09:00
eomsangdonandClaude Opus 5 94266757cd feat(B05): 토량분배 시 유토곡선 전체 맞춤 + 상단 여백·테이블 높이 정리
- 토량분배를 켜면 유토곡선이 종단과의 X 맞춤·세로 창을 버리고 스크롤러 안쪽
  폭·전 구간 Y 로 그려 곡선 전체가 한 화면에 들어옴(평형선·운반 블록을 노선
  전체로 읽는 그림이라). 끄면 예전대로 종단과 맞물린 가로 스크롤로 복귀.
- 요약 막대가 상단줄로 올라간 뒤 남던 상단 여백 축소 — 범례 top 34px → 4px,
  차트 padTop 40 → 30(범례 높이 24 + 여유).
- 종단 테이블 오버레이 높이 상한 신설 — 6행 × 행 30px + 24px(=204px).
  12행에서 6행으로 줄인 뒤 같은 높이를 나눠 가지며 행이 부풀던 것을 막음.
  레이아웃 제어(리사이저·패널 비례 연동)는 그대로 두고 상한만 걸음.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-06 17:07:01 +09:00
eomsangdonandClaude Opus 5 a1a5081f28 fix(B05): 종단 그래프를 끌 때 글자가 선택되던 것 막음
포인터 잡기를 끌기 시작 뒤로 미룬 뒤(측점 선택 복구), 그 전까지 브라우저 기본
글자 선택이 살아나 끌 때마다 라벨·수치가 파랗게 잡혔다.

끌기가 실제로 시작되는 순간 `user-select: none` 을 걸고 이미 잡힌 선택을 푼다.
끝나면 원래대로 되돌린다.

검증(공용 브라우저, 실제 마우스): 끄는 동안 선택된 글자 0자, 끝난 뒤 속성 원복,
측점 클릭 선택은 그대로 동작(60m).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-06 16:43:01 +09:00
eomsangdonandClaude Opus 5 7b0aced287 fix(B05): 종단 그래프에서 측점을 골라도 선택이 풀리던 것 수정
끌어서 이동이 pointerdown 즉시 `setPointerCapture` 를 걸어, 뒤따르는 click 의
대상이 측점 `<g>` 가 아니라 스크롤러가 됐다. 그래서 측점 클릭이 배경 클릭으로
처리돼 선택이 그 자리에서 풀렸다(사용자 보고: 측점을 골라도 하이라이트가 안 됨).

- 포인터 잡기를 **끌기 문턱을 넘긴 뒤로** 미룸. 단순 클릭은 캡처가 없어 측점에
  그대로 닿고, 실제로 끌기 시작한 뒤에는 잡아 두어 창 밖에서 손을 떼도 이어진다.
- 문턱 4px → 6px. 손 떨림에 쉽게 넘어가 측점을 고르기 어려웠다.

검증(공용 브라우저, 실제 마우스 입력): 측점 클릭 → 선택·하이라이트 정상(420m),
끌기 → 선택 유지하며 팬 이동(scrollLeft 1660 → 1780).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-06 16:37:38 +09:00
eomsangdonandClaude Opus 5 df2dedcbc4 perf: 화면 진입 중복 호출 정리 — 세션·워크플로 상태·구조물 이관
실측(B05 진입): auth/session 4회, workflow-state 2회, 구조물 이관 POST 가 진입할
때마다 나갔다.

- auth/session: 라우터 가드·상단 바·워크플로 확인이 각각 부르던 것을 5초 캐시로
  묶음. 로그인·로그아웃에서 즉시 버린다.
- workflow-state: 공용 창구 하나로 모으고 같은 방식으로 캐시. 상단 오버레이가
  따로 부르던 경로를 그 창구로 돌림. 단계 이동에서 캐시를 버린다.
- 구 구조물 측점 이관 POST: 탭 수명 동안 노선마다 한 번만.

실측 결과: B05 첫 진입 20 → 18건(핵심 API 10건), B06→B05 이동 7건.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-06 15:28:20 +09:00
eomsangdonandClaude Opus 5 9da3bce1bf fix(B06): 횡단 카드 버튼이 서버에 바로 저장하던 것을 캐시로 바꿈
사용자 확정(2026-09-06): 조작은 캐시에 쌓이고 [저장]·[확정]에서만 영구저장소에
나간다. 카드 버튼(지반유형·단면유형·측구·포장·2단 비탈)은 누를 때마다
POST cross-design 이 나가 서버가 계산하고 바로 저장하고 있었다.

- 세션 초안 crossdesign 신설(B06_Section_Cross_Design_Session) — 선택값만 담는다.
- 계산은 브라우저 재계산 창구 하나로 돌린다(B05·B06 공용).
- 재계산 때 세션 선택이 정본보다 우선 — 새로고침 뒤에도 고른 값이 남는다.
- [저장]·[확정] patch 에 선택값 + 전 측점 면적을 실어 보낸다. 카드 버튼을 바꾸면
  구조물이 없는 측점 면적도 달라지므로 구조물 측점만 보내면 수량이 어긋난다.
- 구조물 목록 주석 정정(이미 초안 방식인데 옛 주석만 남아 있었다).

검증: tsc --noEmit 통과, pytest 389 passed.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-06 15:14:44 +09:00
eomsangdonandClaude Opus 5 8e3c1f91fc perf(B05/B06): 진입 시 중복 서버 호출 두 건 제거
- 확정 지표면: 목록(surface/models)으로 id 를 찾고 3D 단계에서 surface/confirmed
  를 또 부르던 것을 확정 응답 한 번으로 합침. 모델 id·범위가 그 응답에 다 있다.
- 종횡단 설정값(sections/context): ④ 계산 결과로 보고 세션에 담음. B05·B06 을
  오갈 때마다 다시 묻지 않는다. 노선 캐시를 버리는 자리에서 함께 버린다.

검증: tsc --noEmit 통과, pytest 389 passed.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-06 15:07:47 +09:00
eomsangdonandClaude Opus 5 a360c0caba feat(B05): 구조물 측점과 겹치는 규칙 측점 제거
구조물은 보조말뚝을 박아 그 자리가 정본 측점이 된다. 규칙 격자(20m)와 0.1m
안에서 만나면 세로선·라벨이 두 겹으로 겹쳐 읽히지 않았다.

- 공용 유틸 dropStationsNear 추가(B05_Profile_Util_Station).
- 종단 그래프와 3D 측점 띠가 같은 규칙을 쓴다 — 두 화면의 측점이 어긋나지 않는다.

검증: tsc --noEmit 통과, tmp/tests/test_b05_station_merge.mjs 통과.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-06 15:04:40 +09:00
eomsangdonandClaude Opus 5 bdbefd9472 feat(B06): 곡선부 확폭에 앞뒤 10m 테이퍼 추가
별표2 는 확폭량 표만 주고 붙이는 방식이 없어, 곡선 시·종점에서 폭이 뚝 끊겼다.
곡선 앞뒤 CURVE_WIDENING_TAPER_M(10m) 구간에서 0 → W 로 잇는다.

- 측점 생성 때 확폭량을 한 번에 계산해 curve_widening_m 으로 저장. 테이퍼는
  이웃 측점을 봐야 하므로 측점 단위 계산으로는 낼 수 없다.
- 설계 엔진(파이썬·TS 짝)이 저장된 확폭량을 우선 쓰고, 없으면 반경 표값으로
  되돌아간다 — 옛 저장분도 그대로 돈다.

검증: pytest 389 passed(테이퍼 경계값 테스트 2건 추가), tsc --noEmit 통과.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-06 15:02:58 +09:00
eomsangdon 1f3b30e698 @
feat(B06): 구조물 면적·유토곡선 서버 계산 자리 마련 + 상단측 저장 누락 수정

계산 자리 일원화(CLAUDE.md 5장) — 브라우저에서만 돌던 두 계산을 서버가 같은 TS 로
한 번 더 돌려 정본에 얹음. 파이썬 포팅 금지(기하가 두 벌이 되면 그림과 수량이 갈림).

- B06_Section_Server_Calc_Node.ts 신설 — 구조물 폐회로 면적 계산 후 그 위에서
  유토곡선을 쌓음(화면과 같은 순서). balloon 위치는 서버가 만들지 않음.
- B06_Section_Structure_Layouts.ts 신설 — 정본만 읽는 제어기 흉내를 B07 도면에서
  떼어 공용화. B07·서버가 같은 한 벌을 씀.
- common_util_node_bundle.py 신설 — 번들 빌드·실행 배관 공용화(코리도도 이걸 씀).
- 전처리 체인(초기값 스냅샷 앞)·[저장]·[확정]에서 서버 재계산 호출.
- 상단측(측구 방향) 변경이 B05 [임시저장]에만 실리던 것을 B06 [저장]·[확정]에도
  실음 — flushUphillOverrides.
- 죽은 세션 등록 항목 pipes 제거(읽는 곳도 쓰는 곳도 없었음).

검증: tsc --noEmit 통과, pytest 386 passed, tmp/tests/test_b06_server_calc_node.mjs 통과.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@
2026-09-06 14:20:54 +09:00
eomsangdonandClaude Opus 5 283a248f89 feat(B06): 곡선부 노폭 자동 확폭 — 평면 R 기준, 곡선 바깥쪽 편측
2026-09-06 사용자 확정. 별표2 Ⅰ.2.나.(4) 확폭표(R 10~45m → 2.25~0.25m)를 측점별
평면 곡선반경에 물려 차도 폭을 넓힌다.

- 확폭 방향은 **곡선 바깥쪽 편측** — 노선 폴리라인의 외적 부호로 회전 방향을 보고
  바깥쪽을 정한다(좌회전이면 우측). 측점 기록에 `curve_outer_side` 로 실린다.
- 차도 반폭을 좌·우로 나눠 들어 한쪽만 넓어지게 함. 확폭이 0이면 예전과 같은 대칭
  단면이다. 노견·측구·사면은 그 바깥으로 그대로 밀린다.
- 확폭을 더한 유효너비는 법정 상한 5m 에서 자른다(규격 3.0m 면 최대 2.0m 까지).
- 계산 짝을 함께 고침 — 파이썬 `compute_cross_design` 과 브라우저 `computeCrossDesign`,
  표는 양쪽에 두되 짝임을 주석으로 못 박음. 확폭 입력은 측점 기록에서 뽑는 헬퍼
  하나로 9개 호출부(횡단·확정·B07 도면)에 같은 값이 가게 함.
- 횡단도에 노폭 라벨 — 확폭이 걸리면 「노폭 4.5m (규격 3.0 + 확폭 1.5)」로 적는다.
- 확인: 표 경계·편측 적용·5m 상한·회전 방향 판정 5건(pytest) + 브라우저 표 15건 일치.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-06 12:07:56 +09:00
eomsangdonandClaude Opus 5 c7099bd1d3 feat(B05,B06): 측점별 평면 곡선반경 산출 + 법정 최소반경 경고, 자동탐색 입구 내림
2026-09-06 사용자 확정. 자동탐색은 평면만 보고 노선을 정해 실제 판단과 맞지 않으므로
쓰지 않는다 — 버튼만 감추고 코드·API 는 남긴다. 대신 법정 평면 기준을 **경고**로 낸다.

- 횡단 생성이 측점마다 평면 곡선반경(`plan_radius_m`)을 낸다. 노선 폴리라인 위에서
  앞뒤 10m 떨어진 세 점의 외접원 반경이며, 직선(1만m 이상)은 null. 반지름 50m 원호로
  50.008 이 나오는 것을 확인.
- 법정 최소곡선반지름 표(별표2 Ⅰ.2.다.(1) — 40:60/40, 30:30/20, 20:15/12)와 배향곡선
  하한 10m 를 설정에 넣고 계획선 정책에 실어 화면으로 내린다. 탐색이 쓰는 등급별
  상수와는 별개다.
- 종단 상단줄에 「곡선반경 부족 n곳 / 최소 R」 경고 추가 — 자동 보정·차단은 없다.
  툴팁에 하한값과 배향곡선 하한 미만 개수를 적는다.
- 이 반경은 다음 작업(곡선부 확폭)이 그대로 쓴다.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-06 11:55:22 +09:00